Uh oh!
There was an error while loading. Please reload this page.
fix: use both absolute and relative header paths in header matching - #362
Conversation
This fix an issue of importing the same header by different addresses. The issue is trickier than seems, because if a user used the include dir flag `-I...` with an absolute path, then every matched header was detected to be absolute, but if the import was a relative import to the source file and the source file isn't with an absolute path, than the header is identified by the combined relative path. See https://sourceforge.net/p/cppcheck/discussion/general/thread/c01f80556b/
firewave
commented
Aug 6, 2024
This change might be problematic because it is obviously requires a test but so far there are no tests at all utilizing includes (see #261 for a related discussion). This should probably be implemented by introducing Python tests similar to what Cppcheck is doing. @danmar would that be acceptable for tests which actually require (multiple) files? |
What about these (failed) tests? https://ci.appveyor.com/project/danmar/simplecpp/builds/50353854 |
danmar
commented
Aug 12, 2024
sure, it would definitely make sense to include actual files in some python tests but as @Tal500 pointed out there are some tests for the handling at least.
yes imho those are valuable also. |
Tal500
commented
Aug 14, 2024
@danmar if I guess correctly, the failure is due to that the header files are not really in the working directory or so, right? |
danmar
commented
Aug 16, 2024
yes that sounds probable to me. I don't know how your changes will affect the output from simplecpp if all filepaths will always be absolute that is also unfortunate. Is the output changed? |
Tal500
commented
Aug 18, 2024
Yes, except for the system headers that were not found in the scan paths, e.g. STL headers (that simplecpp doesn't open them at all and it's not consider to be an error). We could have a more complicated solution, that uses two header attributes:
The first attribute is mandatory, and the second one is optional and exists only when the header is found. An alternative solution is to have a user configuration that toggles the include path absoluteness. |
Tal500
commented
Sep 4, 2024
|
danmar
commented
Sep 28, 2024
I am very sorry for the delay. I was at cppcon last week and had a presentation. As far as I understand the solution you suggest makes sense to me. We don't need to have these properties for each token right. |
danmar
commented
Sep 28, 2024
@Tal500 just making sure you see my comment. |
I'd like too see it when it will be published!
I'll look into that more |
danmar
commented
Sep 29, 2024
👍 for your information here is a short description: I guess it will take a while until it is published. |
Tal500
commented
Nov 17, 2024
I went on a different solution - just be conservative and check whether the relative version of the path or the absolute version of the path was already loaded (in case that the header file was found). If the header is included as a system header, the default expansion is to absolute path. I think that this is the sane solution for this problem. |
danmar
commented
Nov 20, 2024
ok I didn't fully dig into your solution but I will trust you for now. I think a pytest test would be a good idea. How is your pytest skills? I can provide a resource that will write the test if it feels complex for you. |
Tal500
commented
Nov 20, 2024
I know python, I think I can learn pytest a little bit if needed (where is it used and in which repo?). In addition, what is your plan for a unittest? I suggest to have a unittest that includes both realtive path and |
If you want to create a test that creates physical files in different locations and checks how simplecpp behaves with different -I flags then I think a pytest is more flexible and more proper.
Good. We use pytest in Cppcheck. See this for instance: https://github.com/danmar/cppcheck/blob/main/test/cli/other_test.py There are several tests in that file that creates testfiles using tmpdir..
more or less if you read from the actual filesystem I think it's not a unit test. It's quite common that people wants to be able to compile the test binary in a arbitrary path and run it there and if it depends on physical files then that will not work. |
Tested locally, here are my added tests (with helper functions), append them to https://github.com/danmar/cppcheck/blob/main/test/cli/other_test.py to see the results: def__test_relative_header_create_header(dir, with_pragma_once=True):
header_file=os.path.join(dir, 'test.h')
withopen(header_file, 'wt') asf:
f.write(f"""{"#pragma once"ifwith_pragma_onceelse""} #ifndef TEST_H_INCLUDED #define TEST_H_INCLUDED #else #error header_was_already_included #endif """)
returnheader_file, "error: #error header_was_already_included"def__test_relative_header_format_include_path_arg(include_path):
returnf"-I{json.dumps(str(include_path))[1:-1]}"def__test_relative_header_format_include(include, is_sys_header=False):
ifis_sys_header:
returnf"<{json.dumps(include)[1:-1]}>"else:
returnjson.dumps(include)
def__test_relative_header_create_source(dir, include1, include2, is_include1_sys=False, is_include2_sys=False):
src_file=os.path.join(dir, 'test.c')
withopen(src_file, 'wt') asf:
f.write(f""" #undef TEST_H_INCLUDED #include {__test_relative_header_format_include(include1, is_include1_sys)} #include {__test_relative_header_format_include(include2, is_include2_sys)} """)
returnsrc_filedeftest_relative_header_0_rel(tmpdir):
_, double_include_error=__test_relative_header_create_header(tmpdir, with_pragma_once=False)
test_file=__test_relative_header_create_source(tmpdir, "test.h", "test.h")
args= [test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertdouble_include_errorinstderrdeftest_relative_header_0_sys(tmpdir):
_, double_include_error=__test_relative_header_create_header(tmpdir, with_pragma_once=False)
test_file=__test_relative_header_create_source(tmpdir, "test.h", "test.h", is_include1_sys=True, is_include2_sys=True)
args= [__test_relative_header_format_include_path_arg(tmpdir), test_file]
_, _, stderr=cppcheck(args)
assertdouble_include_errorinstderrdeftest_relative_header_1_rel(tmpdir):
__test_relative_header_create_header(tmpdir)
test_file=__test_relative_header_create_source(tmpdir, "test.h", "test.h")
args= [test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertstderr==''deftest_relative_header_1_sys(tmpdir):
__test_relative_header_create_header(tmpdir)
test_file=__test_relative_header_create_source(tmpdir, "test.h", "test.h", is_include1_sys=True, is_include2_sys=True)
args= [__test_relative_header_format_include_path_arg(tmpdir), test_file]
_, _, stderr=cppcheck(args)
assertstderr==''## TODO: the following tests should pass after applying simplecpp#362deftest_relative_header_2(tmpdir):
header_file, _=__test_relative_header_create_header(tmpdir)
test_file=__test_relative_header_create_source(tmpdir, "test.h", header_file)
args= [test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertstderr==''deftest_relative_header_3(tmpdir):
test_subdir=os.path.join(tmpdir, "test_subdir")
os.mkdir(test_subdir)
header_file, _=__test_relative_header_create_header(test_subdir)
test_file=__test_relative_header_create_source(tmpdir, "test_subdir/test.h", header_file)
args= [test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertstderr==''deftest_relative_header_3_inv(tmpdir):
test_subdir=os.path.join(tmpdir, "test_subdir")
os.mkdir(test_subdir)
header_file, _=__test_relative_header_create_header(test_subdir)
test_file=__test_relative_header_create_source(tmpdir, header_file, "test_subdir/test.h")
args= [test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertstderr==''deftest_relative_header_4(tmpdir):
test_subdir=os.path.join(tmpdir, "test_subdir")
os.mkdir(test_subdir)
header_file, _=__test_relative_header_create_header(test_subdir)
test_file=__test_relative_header_create_source(tmpdir, header_file, "test2.h", is_include2_sys=True)
args= [__test_relative_header_format_include_path_arg(test_subdir), test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertstderr==''deftest_relative_header_5(tmpdir):
test_subdir=os.path.join(tmpdir, "test_subdir")
os.mkdir(test_subdir)
__test_relative_header_create_header(test_subdir)
test_file=__test_relative_header_create_source(tmpdir, "test.h", "test_subdir/test.h", is_include1_sys=True)
args= [__test_relative_header_format_include_path_arg(test_subdir), test_file]
_, _, stderr=cppcheck(args, cwd=tmpdir)
assertstderr=='' |
I prefer that you add some pytest test file to simplecpp repo. We want to test this directly here. I don't have strong opinion. But you could add
I don't understand this. The -I does not expect json. |
Will do that, I need also to copy some verification of infra functions that I had in cppcheck pytest files.
It's a common trick to extract escaped string to a string in many language to use JSON serialization for a string, and in Python it is just BTW: I'm not sure that I should have escaped the strings with the "-I..." flag, since this argument is passed directly to the process arguments, and I'm not sure if the string escapes characters (i.e. |
danmar
commented
Dec 6, 2024
ok I was also confused by the |
Tal500
commented
Dec 10, 2024
Done. Note the needed gitignore that for some reason you didn't notice |
danmar
commented
Dec 12, 2024
Thanks.. test looks good imho. I have two more small comments:
|
Tal500
commented
Dec 15, 2024
Done |
danmar
left a comment
There was a problem hiding this comment.
sorry for delayed review. please refactor testing.
Uh oh!
There was an error while loading. Please reload this page.
Tal500
commented
Dec 22, 2024
Done |
danmar
commented
Jan 1, 2025
@Tal500 Thank you! |
…eader matching (cppcheck-opensource#362)" (cppcheck-opensource#415)" This reverts commit 9ce981c.
Fixes#402
This fix an issue of importing the same header by different addresses.
The issue is trickier than seems, because if a user used the include dir flag
-I...with an absolute path, then every matched header was detected to be absolute, but if the import was a relative import to the source file and the source file isn't with an absolute path, than the header is identified by the combined relative path.See https://sourceforge.net/p/cppcheck/discussion/general/thread/c01f80556b/
Note - I tried to make the code compatible with Windows, but was tested only on linux.