Skip to content

Commit b6c70b4

Browse files
authored
fixed #14658 - do not append extension in lookup if a relative path has been provided (#8432)
1 parent 4ef0318 commit b6c70b4

6 files changed

Lines changed: 234 additions & 10 deletions

File tree

lib/library.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,13 @@ Library::Error Library::load(const char exename[], const char path[], bool debug
194194
}
195195

196196
const bool is_abs_path = Path::isAbsolute(path);
197+
const bool is_rel_path = Path::isRelative(path);
197198

198199
std::string fullfilename(path);
199200

200201
// TODO: what if the extension is not .cfg?
201-
// only append extension when we provide the library name and not a path - TODO: handle relative paths?
202-
if (!is_abs_path && Path::getFilenameExtension(fullfilename).empty())
202+
// only append extension when we provide the library name and not a path
203+
if (!is_abs_path && !is_rel_path && Path::getFilenameExtension(fullfilename).empty())
203204
fullfilename += ".cfg";
204205

205206
std::string absolute_path;

lib/path.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ bool Path::isAbsolute(const std::string& path)
189189
#endif
190190
}
191191

192+
bool Path::isRelative(const std::string& path)
193+
{
194+
const std::string p = fromNativeSeparators(path);
195+
return (p.find('/') != std::string::npos) && !isAbsolute(path);
196+
}
197+
192198
std::string Path::getRelativePath(const std::string& absolutePath, const std::vector<std::string>& basePaths)
193199
{
194200
for (const std::string &bp : basePaths) {

lib/path.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ class CPPCHECKLIB Path {
118118
*/
119119
static bool isAbsolute(const std::string& path);
120120

121+
/**
122+
* @brief Check if given path is relative
123+
* @param path Path to check
124+
* @return true if given path is relative
125+
*/
126+
static bool isRelative(const std::string& path);
127+
121128
/**
122129
* @brief Create a relative path from an absolute one, if absolute path is inside the basePaths.
123130
* @param absolutePath Path to be made relative.

lib/platform.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,12 @@ bool Platform::loadFromFile(const std::vector<std::string>& paths, const std::st
176176
std::cout << "looking for platform '" + filename + "'" << std::endl;
177177

178178
const bool is_abs_path = Path::isAbsolute(filename);
179+
const bool is_rel_path = Path::isRelative(filename);
179180

180181
std::string fullfilename(filename);
181182
// TODO: what if extension is not .xml?
182183
// only append extension when we provide the library name is not a path - TODO: handle relative paths?
183-
if (!is_abs_path && Path::getFilenameExtension(fullfilename).empty())
184+
if (!is_abs_path && !is_rel_path && Path::getFilenameExtension(fullfilename).empty())
184185
fullfilename += ".xml";
185186

186187
// TODO: use native separators

test/cli/lookup_test.py

Lines changed: 188 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,35 @@ def test_lib_lookup_relative_noext_notfound(tmpdir):
205205
assert exitcode == 1, stdout if stdout else stderr
206206
lines = __remove_std_lookup_log(stdout.splitlines(), exepath)
207207
assert lines == [
208-
"looking for library 'config/gnu.cfg'",
209-
"looking for library '{}/config/gnu.cfg'".format(exepath),
210-
"looking for library '{}/cfg/config/gnu.cfg'".format(exepath),
208+
"looking for library 'config/gnu'",
209+
"looking for library '{}/config/gnu'".format(exepath),
210+
"looking for library '{}/cfg/config/gnu'".format(exepath),
211211
"library not found: 'config/gnu'",
212212
"cppcheck: Failed to load library configuration file 'config/gnu'. File not found"
213213
]
214214

215215

216+
# TODO: this can never be found - bail out early?
217+
def test_lib_lookup_relative_noext_trailing_notfound(tmpdir):
218+
test_file = os.path.join(tmpdir, 'test.c')
219+
with open(test_file, 'wt'):
220+
pass
221+
222+
exitcode, stdout, stderr, exe = cppcheck_ex(['--debug-lookup=library', '--library=config/gnu/', test_file])
223+
exepath = os.path.dirname(exe)
224+
if sys.platform == 'win32':
225+
exepath = exepath.replace('\\', '/')
226+
assert exitcode == 1, stdout if stdout else stderr
227+
lines = __remove_std_lookup_log(stdout.splitlines(), exepath)
228+
assert lines == [
229+
"looking for library 'config/gnu/'",
230+
"looking for library '{}/config/gnu/'".format(exepath),
231+
"looking for library '{}/cfg/config/gnu/'".format(exepath),
232+
"library not found: 'config/gnu/'",
233+
"cppcheck: Failed to load library configuration file 'config/gnu/'. File not found"
234+
]
235+
236+
216237
def test_lib_lookup_absolute(tmpdir):
217238
test_file = os.path.join(tmpdir, 'test.c')
218239
with open(test_file, 'wt'):
@@ -258,6 +279,48 @@ def test_lib_lookup_absolute_notfound(tmpdir):
258279
]
259280

260281

282+
def test_lib_lookup_absolute_noext_notfound(tmpdir):
283+
test_file = os.path.join(tmpdir, 'test.c')
284+
with open(test_file, 'wt'):
285+
pass
286+
287+
cfg_file = os.path.join(tmpdir, 'test')
288+
289+
exitcode, stdout, _, exe = cppcheck_ex(['--debug-lookup=library', '--library={}'.format(cfg_file), test_file])
290+
exepath = os.path.dirname(exe)
291+
if sys.platform == 'win32':
292+
exepath = exepath.replace('\\', '/')
293+
assert exitcode == 1, stdout
294+
lines = __remove_std_lookup_log(stdout.splitlines(), exepath)
295+
assert lines == [
296+
"looking for library '{}'".format(cfg_file),
297+
"library not found: '{}'".format(cfg_file),
298+
"cppcheck: Failed to load library configuration file '{}'. File not found".format(cfg_file)
299+
]
300+
301+
302+
# TODO: this can never be found - bail out early?
303+
def test_lib_lookup_absolute_noext_trailing_notfound(tmpdir):
304+
test_file = os.path.join(tmpdir, 'test.c')
305+
with open(test_file, 'wt'):
306+
pass
307+
308+
cfg_file = os.path.join(tmpdir, 'test')
309+
cfg_file_trail = cfg_file + os.path.sep
310+
311+
exitcode, stdout, _, exe = cppcheck_ex(['--debug-lookup=library', '--library={}'.format(cfg_file_trail), test_file])
312+
exepath = os.path.dirname(exe)
313+
if sys.platform == 'win32':
314+
exepath = exepath.replace('\\', '/')
315+
assert exitcode == 1, stdout
316+
lines = __remove_std_lookup_log(stdout.splitlines(), exepath)
317+
assert lines == [
318+
"looking for library '{}'".format(cfg_file_trail),
319+
"library not found: '{}'".format(cfg_file_trail),
320+
"cppcheck: Failed to load library configuration file '{}'. File not found".format(cfg_file_trail)
321+
]
322+
323+
261324
def test_lib_lookup_nofile(tmpdir):
262325
test_file = os.path.join(tmpdir, 'test.c')
263326
with open(test_file, 'wt'):
@@ -544,14 +607,38 @@ def test_platform_lookup_relative_noext_notfound(tmpdir):
544607
lines = stdout.splitlines()
545608
assert lines == [
546609
"looking for platform 'platform/none'",
547-
"try to load platform file '{}/platform/none.xml' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platform/none.xml".format(cwd, cwd),
548-
"try to load platform file '{}/platforms/platform/none.xml' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platforms/platform/none.xml".format(cwd, cwd),
549-
"try to load platform file '{}/platform/none.xml' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platform/none.xml".format(exepath, exepath),
550-
"try to load platform file '{}/platforms/platform/none.xml' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platforms/platform/none.xml".format(exepath, exepath),
610+
"try to load platform file '{}/platform/none' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platform/none".format(cwd, cwd),
611+
"try to load platform file '{}/platforms/platform/none' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platforms/platform/none".format(cwd, cwd),
612+
"try to load platform file '{}/platform/none' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platform/none".format(exepath, exepath),
613+
"try to load platform file '{}/platforms/platform/none' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platforms/platform/none".format(exepath, exepath),
551614
"cppcheck: error: unrecognized platform: 'platform/none'."
552615
]
553616

554617

618+
# TODO: this can never be found - bail out early?
619+
def test_platform_lookup_relative_noext_trailing_notfound(tmpdir):
620+
test_file = os.path.join(tmpdir, 'test.c')
621+
with open(test_file, 'wt'):
622+
pass
623+
624+
exitcode, stdout, stderr, exe = cppcheck_ex(['--debug-lookup=platform', '--platform=platform/none/', test_file])
625+
cwd = os.getcwd()
626+
exepath = os.path.dirname(exe)
627+
if sys.platform == 'win32':
628+
cwd = cwd.replace('\\', '/')
629+
exepath = exepath.replace('\\', '/')
630+
assert exitcode == 1, stdout if stdout else stderr
631+
lines = stdout.splitlines()
632+
assert lines == [
633+
"looking for platform 'platform/none/'",
634+
"try to load platform file '{}/platform/none/' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platform/none/".format(cwd, cwd),
635+
"try to load platform file '{}/platforms/platform/none/' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platforms/platform/none/".format(cwd, cwd),
636+
"try to load platform file '{}/platform/none/' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platform/none/".format(exepath, exepath),
637+
"try to load platform file '{}/platforms/platform/none/' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}/platforms/platform/none/".format(exepath, exepath),
638+
"cppcheck: error: unrecognized platform: 'platform/none/'."
639+
]
640+
641+
555642
def test_platform_lookup_absolute(tmpdir):
556643
test_file = os.path.join(tmpdir, 'test.c')
557644
with open(test_file, 'wt'):
@@ -590,6 +677,42 @@ def test_platform_lookup_absolute_notfound(tmpdir):
590677
]
591678

592679

680+
def test_platform_lookup_absolute_noext_notfound(tmpdir):
681+
test_file = os.path.join(tmpdir, 'test.c')
682+
with open(test_file, 'wt'):
683+
pass
684+
685+
platform_file = os.path.join(tmpdir, 'test')
686+
687+
exitcode, stdout, stderr = cppcheck(['--debug-lookup=platform', '--platform={}'.format(platform_file), test_file])
688+
assert exitcode == 1, stdout if stdout else stderr
689+
lines = stdout.splitlines()
690+
assert lines == [
691+
"looking for platform '{}'".format(platform_file),
692+
"try to load platform file '{}' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}".format(platform_file, platform_file),
693+
"cppcheck: error: unrecognized platform: '{}'.".format(platform_file)
694+
]
695+
696+
697+
# TODO: this can never be found - bail out early?
698+
def test_platform_lookup_absolute_noext_trailing_notfound(tmpdir):
699+
test_file = os.path.join(tmpdir, 'test.c')
700+
with open(test_file, 'wt'):
701+
pass
702+
703+
platform_file = os.path.join(tmpdir, 'test')
704+
platform_file_trail = platform_file + os.path.sep
705+
706+
exitcode, stdout, stderr = cppcheck(['--debug-lookup=platform', '--platform={}'.format(platform_file_trail), test_file])
707+
assert exitcode == 1, stdout if stdout else stderr
708+
lines = stdout.splitlines()
709+
assert lines == [
710+
"looking for platform '{}'".format(platform_file_trail),
711+
"try to load platform file '{}' ... Error=XML_ERROR_FILE_NOT_FOUND ErrorID=3 (0x3) Line number=0: filename={}".format(platform_file_trail, platform_file_trail),
712+
"cppcheck: error: unrecognized platform: '{}'.".format(platform_file_trail)
713+
]
714+
715+
593716
@pytest.mark.skip # TODO: fails when not run from the root folder
594717
def test_platform_lookup_nofile(tmpdir):
595718
test_file = os.path.join(tmpdir, 'test.c')
@@ -759,6 +882,7 @@ def test_addon_lookup_relative_notfound(tmpdir):
759882
]
760883

761884

885+
# FIXME: an addon requires a file extension as we need to differentiate between .py and .json addons
762886
def test_addon_lookup_relative_noext_notfound(tmpdir):
763887
test_file = os.path.join(tmpdir, 'test.c')
764888
with open(test_file, 'wt'):
@@ -777,6 +901,26 @@ def test_addon_lookup_relative_noext_notfound(tmpdir):
777901
]
778902

779903

904+
# TODO: this can never be found - bail out early?
905+
def test_addon_lookup_relative_noext_trailing_notfound(tmpdir):
906+
test_file = os.path.join(tmpdir, 'test.c')
907+
with open(test_file, 'wt'):
908+
pass
909+
910+
exitcode, stdout, _, exe = cppcheck_ex(['--debug-lookup=addon', '--addon=addon/misra/', test_file])
911+
exepath = os.path.dirname(exe)
912+
exepath_sep = exepath + os.path.sep
913+
assert exitcode == 1, stdout
914+
lines = stdout.splitlines()
915+
assert lines == [
916+
# TODO: should not append extension
917+
"looking for addon 'addon/misra/.py'",
918+
"looking for addon '{}addon/misra/.py'".format(exepath_sep),
919+
"looking for addon '{}addons/addon/misra/.py'".format(exepath_sep), # TODO: mixed separators
920+
'Did not find addon addon/misra/.py'
921+
]
922+
923+
780924
def test_addon_lookup_absolute(tmpdir):
781925
test_file = os.path.join(tmpdir, 'test.c')
782926
with open(test_file, 'wt'):
@@ -811,6 +955,43 @@ def test_addon_lookup_absolute_notfound(tmpdir):
811955
]
812956

813957

958+
# FIXME: an addon requires a file extension as we need to differentiate between .py and .json addons
959+
def test_addon_lookup_absolute_noext_notfound(tmpdir):
960+
test_file = os.path.join(tmpdir, 'test.c')
961+
with open(test_file, 'wt'):
962+
pass
963+
964+
addon_file = os.path.join(tmpdir, 'test')
965+
addon_file_py = os.path.join(tmpdir, 'test.py') # TODO: do not add extension
966+
967+
exitcode, stdout, stderr = cppcheck(['--debug-lookup=addon', '--addon={}'.format(addon_file), test_file])
968+
assert exitcode == 1, stdout if stdout else stderr
969+
lines = stdout.splitlines()
970+
assert lines == [
971+
"looking for addon '{}'".format(addon_file_py),
972+
'Did not find addon {}'.format(addon_file_py)
973+
]
974+
975+
976+
# TODO: this can never be found - bail out early?
977+
def test_addon_lookup_absolute_noext_trailing_notfound(tmpdir):
978+
test_file = os.path.join(tmpdir, 'test.c')
979+
with open(test_file, 'wt'):
980+
pass
981+
982+
addon_file = os.path.join(tmpdir, 'test')
983+
addon_file_trail = addon_file + os.path.sep
984+
addon_file_trail_py = addon_file_trail + '.py' # TODO: do not add extension
985+
986+
exitcode, stdout, stderr = cppcheck(['--debug-lookup=addon', '--addon={}'.format(addon_file_trail), test_file])
987+
assert exitcode == 1, stdout if stdout else stderr
988+
lines = stdout.splitlines()
989+
assert lines == [
990+
"looking for addon '{}'".format(addon_file_trail_py),
991+
'Did not find addon {}'.format(addon_file_trail_py)
992+
]
993+
994+
814995
def test_addon_lookup_nofile(tmpdir):
815996
test_file = os.path.join(tmpdir, 'test.c')
816997
with open(test_file, 'wt'):

test/testpath.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class TestPath : public TestFixture {
5757
TEST_CASE(getAbsolutePath);
5858
TEST_CASE(exists);
5959
TEST_CASE(fromNativeSeparators);
60+
TEST_CASE(isRelative);
6061
}
6162

6263
void removeQuotationMarks() const {
@@ -618,6 +619,33 @@ class TestPath : public TestFixture {
618619
ASSERT_EQUALS("//lib/file.c", Path::fromNativeSeparators("\\\\lib\\file.c"));
619620
ASSERT_EQUALS("./lib/file.c", Path::fromNativeSeparators(".\\lib\\file.c"));
620621
}
622+
623+
void isRelative() const {
624+
ASSERT_EQUALS(true, Path::isRelative("dir/file"));
625+
ASSERT_EQUALS(true, Path::isRelative("dir\\file"));
626+
627+
// TODO: is this expected?
628+
ASSERT_EQUALS(true, Path::isRelative("file/"));
629+
ASSERT_EQUALS(true, Path::isRelative("file\\"));
630+
631+
ASSERT_EQUALS(false, Path::isRelative("file"));
632+
633+
#ifdef _WIN32
634+
// this is a relative path on Windows
635+
ASSERT_EQUALS(true, Path::isRelative("/dir/file"));
636+
#else
637+
ASSERT_EQUALS(false, Path::isRelative("/dir/file"));
638+
#endif
639+
640+
#ifdef _WIN32
641+
// TODO: this is not detected as absolute path in _WIN32 builds
642+
ASSERT_EQUALS(false, Path::isRelative("c:\\dir\\file"));
643+
ASSERT_EQUALS(false, Path::isRelative("c:/dir/file"));
644+
#else
645+
ASSERT_EQUALS(true, Path::isRelative("c:\\dir\\file"));
646+
ASSERT_EQUALS(true, Path::isRelative("c:/dir/file"));
647+
#endif
648+
}
621649
};
622650

623651
REGISTER_TEST(TestPath)

0 commit comments

Comments
 (0)