llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.38k stars 12.15k forks source link

#include_next <same_header_name> gives error in Clang-based tools, but not Clang #63537

Open cjdb opened 1 year ago

cjdb commented 1 year ago

Consider this header stdio.h:

// exists so it can be put in a modulemap...
#include_next <stdio.h>

// and also so we can mark a few things nodiscard
[[nodiscard]] FILE* tmpfile();
[[nodiscard]] FILE* fopen(const char* filename, const char* mode);
// ...

this gives the following error in Clang-based tooling (e.g. clangd and a Clang-aware Doxygen), but not Clang:

Main file cannot be included recursively when building a preamble clang(pp_including_mainfile_in_preamble)
Unknown type name 'FILE'

I don't necessarily know if this is a bug, or if the driver is passing info to Clang so it doesn't error out. Sadly, I can't seem to work it out.

cjdb commented 1 year ago

After adding this to my .clangd, it looks like it isn't a lack-of-driver issue?

    - -isystem=/home/cjdb/opt/lib/clang/17/include
    - -isystem=/usr/local/include
    - -isystem=/usr/lib64/gcc/x86_64-pc-linux-gnu/13.1.1/../../../../x86_64-pc-linux-gnu/include
    - -isystem=/include
    - -isystem=/usr/include
zyn0217 commented 1 year ago

After adding this to my .clangd, it looks like it isn't a lack-of-driver issue?


    - -isystem=/home/cjdb/opt/lib/clang/17/include

    - -isystem=/usr/local/include

    - -isystem=/usr/lib64/gcc/x86_64-pc-linux-gnu/13.1.1/../../../../x86_64-pc-linux-gnu/include

    - -isystem=/include

    - -isystem=/usr/include

From clangd's document:

If you’re using an unusual compiler (e.g. a cross-compiler for a different platform) you may want to pass --query-driver=/path/to/mygcc or --query-driver=/path/to/mygcc,/path/to/myg++ when using C++ to allow clangd to extract the include paths from it directly.

I think it's worth a shot.

cjdb commented 1 year ago

From clangd's document:

If you’re using an unusual compiler (e.g. a cross-compiler for a different platform) you may want to pass --query-driver=/path/to/mygcc or --query-driver=/path/to/mygcc,/path/to/myg++ when using C++ to allow clangd to extract the include paths from it directly.

I think it's worth a shot.

Thanks for the suggestion. This doesn't fix the issue, but even if it had, the issue would still be surfacing in other tools such as clang-doc and Doxygen (i.e. it's not strictly a clangd issue).

zyn0217 commented 1 year ago

Agree, it is always difficult and error-prone for libclang to cope with customized GCC installation. As of now, I use GCC_INSTALL_PREFIX to work around this issue in my local development.

zyn0217 commented 1 year ago

This doesn't fix the issue...

But for clangd, I suppose --query-driver should work. You've to make sure the argument to be the same as the one in your compile_commands.json, e.g.,

/path/to/compile_commands.json

[
  { 
    "directory": "/path/to",
    "arguments": ["/usr/local/bin/gcc-13", "-c", "-o", "one.o", "one.cpp"],
    "file": "one.cpp"
  }
]

Then try the following to see if the configure has taken effect.

$ clangd --query-driver=/usr/local/bin/gcc-13 --check=/path/to/one.cpp
cjdb commented 1 year ago

This doesn't fix the issue...

But for clangd, I suppose --query-driver should work. You've to make sure the argument to be the same as the one in your compile_commands.json, e.g.,

/path/to/compile_commands.json

[
  { 
    "directory": "/path/to",
    "arguments": ["/usr/local/bin/gcc-13", "-c", "-o", "one.o", "one.cpp"],
    "file": "one.cpp"
  }
]

Then try the following to see if the configure has taken effect.

$ clangd --query-driver=/usr/local/bin/gcc-13 --check=/path/to/one.cpp

That's what I did, except it was a path to Clang, since I'm not using GCC.

zyn0217 commented 1 year ago
- -isystem=/usr/lib64/gcc/x86_64-pc-linux-gnu/13.1.1/../../../../x86_64-pc-linux-gnu/include

Hmm, I thought you were using libclang + GCC after seeing this path.

Sorry for being verbosity, but regarding clangd, I'd still like to know if we should fix or improve the header search logic. Could you please run the previous command again and attach the log of your clangd, without specifying any include path in the .clangd file?

cjdb commented 1 year ago

Hmm, I thought you were using libclang + GCC after seeing this path.

Oh, all of those paths came from clang <flags> -###. Here's what they looked like originally:

"-internal-isystem" "/home/cjdb/opt/lib/clang/17/include"
"-internal-isystem" "/usr/local/include"
"-internal-isystem" "/usr/lib64/gcc/x86_64-pc-linux-gnu/13.1.1/../../../../x86_64-pc-linux-gnu/include"
"-internal-externc-isystem" "/include"
"-internal-externc-isystem" "/usr/include"

Apologies for the confusion there.

Sorry for being verbosity, but regarding clangd, I'd still like to know if we should fix or improve the header search logic. Could you please run the previous command again and attach the log of your clangd, without specifying any include path in the .clangd file?

It looks like my -I/path/to/include/dir/with/hello.hpp* is what's causing this. Once I removed that, things the errors disappeared. Unfortunately, that also meant that clangd couldn't find any of my other headers :(

In other words:

$ clangd --query-driver=`which clang++` --check=/path/to/include/dir/with/hello.hpp # works!
$ clangd --query-driver=`which clang++` --check=/path/to/test/one.cpp # error: can't find other headers

*hello.hpp is exactly the following:

#include_next <hello.hpp>

log.txt

zyn0217 commented 1 year ago

Thank you for providing me with more details!

The principle of #include_next could be seen from here. Generally speaking, you've to place these header search paths in correct order to make everything work. That said, the compile command for one TU that includes hello.hpp shall contain at least two include search paths, the first one is the path to hello.hpp with #include_next and the latter is to another hello.hpp (the first one trying to include) in a different location.

For example, you have /home/cjdb/projects/project/hello.hpp containing #include_next <hello.hpp> trying to include the header stored in /home/cjdb/projects/project/impl/hello.hpp. The compile command should look like:

/home/cjdb/opt/bin/clang++ -I/home/cjdb/projects/project -I/home/cjdb/projects/project/impl -c source_that_includes_hello.cpp -o something.out

And the error from the log above says [pp_including_mainfile_in_preamble] Line 1: main file cannot be included recursively when building a preamble, which IMO it looks like there's only one single hello.hpp in search paths.

If you're using hello.hpp to refer something in system library like stdlib.h, I suggest you post the complete log of clangd with and without adding extra flags aforementioned to .clangd and let's see which path impedes header searching.

cjdb commented 1 year ago

Sorry for my abrupt silence, I was hoping to get you steps for a minimal repro, but haven't had the opportunity. I made sure to put a second hello.hpp in another directory just to be sure that it wasn't something to do with standard library headers.