Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
assert(!(!pathnamesRef.empty() && !fileSettingsRef.empty()));

if (!fileSettingsRef.empty()) {
// TODO: handle ignored?

// TODO: de-duplicate

std::list<FileSettings> fileSettings;
if (!mSettings.fileFilters.empty()) {
// filter only for the selected filenames from all project files
Expand Down Expand Up @@ -244,6 +248,19 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
}
}

// de-duplicate files
{
auto it = filesResolved.begin();
while (it != filesResolved.end()) {
const std::string& name = it->first;
// TODO: log if duplicated files were dropped
filesResolved.erase(std::remove_if(std::next(it), filesResolved.end(), [&](const std::pair<std::string, std::size_t>& entry) {
return entry.first == name;
}), filesResolved.end());
++it;
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I wonder if the number of files can be so large that this O(N^2) approach would be too slow?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Running a find on insert wouldn't be that much faster I reckon.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No. And it's probably more difficult to use a sorted container or something while preserving the order.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If it becomes a problem we can introduce a lookup cache.


std::list<std::pair<std::string, std::size_t>> files;
if (!mSettings.fileFilters.empty()) {
std::copy_if(filesResolved.cbegin(), filesResolved.cend(), std::inserter(files, files.end()), [&](const decltype(filesResolved)::value_type& entry) {
Expand Down
72 changes: 72 additions & 0 deletions test/cli/test-more-projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,75 @@ def test_project_file_order(tmpdir):
'4/4 files checked 0% done'
]
assert stderr == ''


def test_project_file_duplicate(tmpdir):
test_file_a = os.path.join(tmpdir, 'a.c')
with open(test_file_a, 'wt'):
pass

project_file = os.path.join(tmpdir, 'test.cppcheck')
with open(project_file, 'wt') as f:
f.write(
"""<?xml version="1.0" encoding="UTF-8"?>
<project>
<paths>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
</paths>
</project>""".format(test_file_a, test_file_a, tmpdir))

args = ['--project={}'.format(project_file)]

exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0
lines = stdout.splitlines()
assert lines == [
'Checking {} ...'.format(test_file_a)
]
assert stderr == ''


def test_project_file_duplicate_2(tmpdir):
test_file_a = os.path.join(tmpdir, 'a.c')
with open(test_file_a, 'wt'):
pass
test_file_b = os.path.join(tmpdir, 'b.c')
with open(test_file_b, 'wt'):
pass
test_file_c = os.path.join(tmpdir, 'c.c')
with open(test_file_c, 'wt'):
pass

project_file = os.path.join(tmpdir, 'test.cppcheck')
with open(project_file, 'wt') as f:
f.write(
"""<?xml version="1.0" encoding="UTF-8"?>
<project>
<paths>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
<dir name="{}"/>
</paths>
</project>""".format(test_file_c, test_file_a, test_file_b, tmpdir, test_file_b, test_file_c, test_file_a, tmpdir))

args = ['--project={}'.format(project_file)]

exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0
lines = stdout.splitlines()
assert lines == [
'Checking {} ...'.format(test_file_c),
'1/3 files checked 0% done',
'Checking {} ...'.format(test_file_a),
'2/3 files checked 0% done',
'Checking {} ...'.format(test_file_b),
'3/3 files checked 0% done'
]
assert stderr == ''
43 changes: 43 additions & 0 deletions test/cli/test-other.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,3 +782,46 @@ def test_valueflow_debug(tmpdir):
0 always 0
'''.format(test_file_cpp, test_file_h_2, test_file_h, test_file_cpp, test_file_h_2, test_file_h, test_file_cpp)
assert stderr == ''


def test_file_duplicate(tmpdir):
test_file_a = os.path.join(tmpdir, 'a.c')
with open(test_file_a, 'wt'):
pass

args = [test_file_a, test_file_a, str(tmpdir)]

exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0
lines = stdout.splitlines()
assert lines == [
'Checking {} ...'.format(test_file_a)
]
assert stderr == ''


def test_file_duplicate_2(tmpdir):
test_file_a = os.path.join(tmpdir, 'a.c')
with open(test_file_a, 'wt'):
pass
test_file_b = os.path.join(tmpdir, 'b.c')
with open(test_file_b, 'wt'):
pass
test_file_c = os.path.join(tmpdir, 'c.c')
with open(test_file_c, 'wt'):
pass

args = [test_file_c, test_file_a, test_file_b, str(tmpdir), test_file_b, test_file_c, test_file_a, str(tmpdir)]

exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0
lines = stdout.splitlines()
assert lines == [
'Checking {} ...'.format(test_file_c),
'1/3 files checked 0% done',
'Checking {} ...'.format(test_file_a),
'2/3 files checked 0% done',
'Checking {} ...'.format(test_file_b),
'3/3 files checked 0% done'
]
assert stderr == ''