Skip to content

Includes of system headers are never implicitly relative to the source file #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 18, 2023

Conversation

bavison
Copy link
Contributor

@bavison bavison commented Dec 20, 2022

Sometimes, #include directives with angle-bracket filespec delimiters are used (or abused) to defeat the preprocessor's behaviour where it tries to find a header file at a path relative to the file containing the directive.

Without this fix, any non-root header file, foo/bar.h, which does

  #include <bar.h>

while intending to include a root-level header file, will instead enter an infinite inclusion loop, terminating when the inclusion stack overflows with a #include nested too deeply error.

See also this cppcheck issue.

@danmar
Copy link
Owner

danmar commented Dec 20, 2022

is it possible to write some test for this? Something similar to this maybe:

static void nestedInclude()

@bavison
Copy link
Contributor Author

bavison commented Dec 21, 2022

Hope this will do - the test detects the difference between the old and new simplecpp behaviour. I found I had to create an empty limits.h file in the filedata map, since I guess simplecpp::preprocess doesn't know that's a standard header.

@@ -3223,7 +3223,7 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL

const Token * const inctok = inc2.cfront();

const bool systemheader = (inctok->op == '<');
const bool systemheader = (inctok->str()[0] == '<');
Copy link
Collaborator

@firewave firewave Dec 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indicates that code above which also checks op for < might never be executed,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is inctok->op supposed to have any meaning here? By dumping out its value every time we reach this point in a run of the testrunner binary, I can see it's a null character in every case except in include7() and include8(), which are both testing the case where the argument to #include is derived by expanding a preprocessor macro; in these cases it holds a < character. So perhaps it's left over from the macro expansion? (Apologies, I'm new to the cppcheck innards, I'm not familiar with how it works.)

By chance, until I added my new test, these were also the only cases where the filespec in inctok->str() used angle brackets, so for the cases tested by testrunner so far, testing inctok->op was adequate. I agree this looks wrong, but I'm unsure whether the fix should be to look at inctok->str() instead, or whether to fix the initialisation of inctok->op.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial observation was based on a short look and was wrong. I am also not familiar in how it works.

op is set to < in that code above. Since with your change that is not used anymore that can be removed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An additional note. It seems op should only be set if it is a single character token. And if the token isn't already something like a name, number or comment. That wasn't done consistently though - I addressed that within #285.

@firewave
Copy link
Collaborator

This is scary. I finally got around to checking why the issue I introduced in #276 and fixed in #281 did not trigger any test failures. And it turns out it is caused by the same code you just fixed.

simplecpp is completely void of tests with additional files at the moment. That has been a source of discussion in #261.

There's also tests lacking in cppcheck which I have partially added locally.

@danmar
Copy link
Owner

danmar commented Jan 7, 2023

hmm.. I feel I can merge this. However when I try it locally it does not seem to work.

I have this code in my file 1.c:

#include <1.h>

And 1.h contains:

int x;

when I run simplecpp with command simplecpp 1.c it includes the 1.h file.

It works as it should for you?

@firewave
Copy link
Collaborator

firewave commented Jan 7, 2023

hmm.. I feel I can merge this. However when I try it locally it does not seem to work.

That's why we need unit tests with headers/includes...

@danmar
Copy link
Owner

danmar commented Jan 7, 2023

The title of this PR says that "system headers are never relative to the source file". That is not true.

See:

$ gcc -E 1.c
1.c:1:10: fatal error: 1.h: No such file or directory

$ gcc -E -I. 1.c
=> 1.h is included.

…e file

Sometimes, #include directives with angle-bracket filespec delimiters are
used (or abused) to defeat the preprocessor's behaviour where it tries to
find a header file at a path relative to the file containing the directive.

Without this fix, any non-root header file, foo/bar.h, which does
  #include <bar.h>
while intending to include a root-level header file, will instead enter an
infinite inclusion loop, terminating when the inclusion stack overflows
with a "#include nested too deeply" error.
@bavison bavison force-pushed the fix-angle-includes branch from 2b73c45 to 81808f8 Compare January 10, 2023 16:04
@bavison
Copy link
Contributor Author

bavison commented Jan 10, 2023

when I run simplecpp with command simplecpp 1.c it includes the 1.h file.

That looks to have been caused by openHeader() also attempting to find a system header at a path relative to the source file. My latest push should have fixed that. Unfortunately, I haven't managed to work out how to make the unit test framework detect this change, but running the simplecpp binary on your 1.c now has the desired behaviour (it fails unless you explicitly use -I.).

The title of this PR says that "system headers are never relative to the source file". That is not true.

OK, I have added the word "implicitly" into the commit summary, hope this is sufficient.

For what it's worth, I found the following description in the gcc man page (I realise this isn't unversal to all compilers):

           The lookup order is as follows:

           1.  For the quote form of the include directive, the directory of
               the current file is searched first.

           2.  For the quote form of the include directive, the directories
               specified by -iquote options are searched in left-to-right
               order, as they appear on the command line.

           3.  Directories specified with -I options are scanned in left-to-
               right order.

           4.  Directories specified with -isystem options are scanned in
               left-to-right order.

           5.  Standard system directories are scanned.

           6.  Directories specified with -idirafter options are scanned in
               left-to-right order.

@bavison bavison changed the title Includes of system headers are never relative to the source file Includes of system headers are never implicitly relative to the source file Jan 10, 2023
@firewave
Copy link
Collaborator

Unfortunately, I haven't managed to work out how to make the unit test framework detect this change

I will add such tests to Cppcheck soon with the referenced PR.

@danmar
Copy link
Owner

danmar commented Jan 18, 2023

Thanks I think this looks good. If CI is happy I'll merge it. Let's consider the testing later separately. I also think we could explore using pytest in simplecpp.

@danmar danmar merged commit 7c5b21d into danmar:master Jan 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants