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
22 changes: 18 additions & 4 deletions analyzer/codechecker_analyzer/cli/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ def get_report_dir_status(compile_commands: List[dict[str, str]],
for c in compile_commands:
for analyzer in supported_analyzers:
file, directory, cmd = c["file"], c["directory"], c["command"]
file = os.path.abspath(file)

filename = os.path.basename(file)
action_hash = analyzer_action_hash(file, directory, cmd)
Expand Down Expand Up @@ -407,10 +406,25 @@ def print_status(report_dir: str,
compile_cmd_path)
sys.exit(1)

# Convert all relative compile_cmd.json file paths to absolute
for c in compile_commands:
if not os.path.isabs(c["file"]):
c["file"] = os.path.abspath(
os.path.join(c["directory"], c["file"]))

if files:
files_filter = [os.path.abspath(fp) for fp in files]
file_filter = [os.path.abspath(fp) for fp in files]

invalid_file_filter = [fp for fp in file_filter
if not os.path.isfile(fp)]

if invalid_file_filter:
LOG.error("File filter (--file) contains nonexistent files:")
LOG.error(invalid_file_filter)
sys.exit(1)
Comment on lines +421 to +424
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is the purpose of checking the existence of the file? I think, it can be a valid scenario to ask the users to send me their report folder and I make some queries about their analyzed files on my machine where the project code is not available.

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.

In general, we assume the report_dir/ and the source files are on the same system.
Typical use case for the --status command with --file option is to check whether a local file has been analysed or not. This is also used heavily in the VSCode plugin. Moreover, we would also like to know if a file has been recently analyzed or outdated, thus we check the modification time of the local source file in function get_report_dir_status. If the modification time of the source file is more recent than the creation of the report_dir/, we consider the analysis status outdated.

This all assumes the report directory and the source files are on the same system. If this is the case, it is pointless to use the --file filter on a nonexisting file, thus an error was added.

I think, it can be a valid scenario to ask the users to send me their report folder and I make some queries about their analyzed files on my machine where the project code is not available.

While this is valid, I believe this is a separate use case. As an alternative, maybe we could add a flag like --ignore-nonexisting-files or something like that. But in the above case, we should at least somehow inform the user that they tried to query analysis information of non-existing files.


compile_commands = list(
filter(lambda c: c["file"] in files_filter, compile_commands))
filter(lambda c: c["file"] in file_filter, compile_commands))

if not compile_commands and not export:
LOG.warning("File not found in the compilation database!")
Expand Down Expand Up @@ -704,7 +718,7 @@ def get_output_file_path(default_file_name: str) -> Optional[str]:
if os.path.isdir(input_dir) and os.path.isfile(compile_cmd_json):
print_status(input_dir,
False,
getattr(args, 'files', None))
None)

if statistics.num_of_reports:
sys.exit(2)
78 changes: 70 additions & 8 deletions analyzer/tests/functional/parse_status/test_parse_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ def __run_cmd(self, cmd):
print(out)
print(err)

if process.returncode != 0:
return err

output = out.splitlines(True)
processed_output = []
for line in output:
Expand Down Expand Up @@ -107,6 +110,10 @@ def __log_and_analyze(self):

self.__run_cmd(analyze_cmd)

def __get_file_list(self, parsed_json, analyzer, list_type):
return list(map(os.path.basename,
parsed_json["analyzers"][analyzer][list_type]))

def test_parse_status_summary(self):
self.__log_and_analyze()

Expand Down Expand Up @@ -156,14 +163,69 @@ def test_parse_status_detailed_json(self):

parsed_json = json.loads(out)

def get_file_list(analyzer, list_type):
return list(map(
os.path.basename,
parsed_json["analyzers"][analyzer][list_type]))

self.assertListEqual(get_file_list("clangsa", "up-to-date"),
self.assertListEqual(self.__get_file_list(parsed_json,
"clangsa", "up-to-date"),
["a.cpp", "b.cpp"])
self.assertListEqual(get_file_list("clang-tidy", "up-to-date"),
self.assertListEqual(self.__get_file_list(parsed_json,
"clang-tidy", "up-to-date"),
[])
self.assertListEqual(get_file_list("clang-tidy", "missing"),
self.assertListEqual(self.__get_file_list(parsed_json,
"clang-tidy", "missing"),
["a.cpp", "b.cpp"])

def test_parse_status_filter(self):
self.__log_and_analyze()

file_filter = str(os.path.abspath(
os.path.join(self.test_dir, "a.cpp")))

parse_status_cmd = [self._codechecker_cmd, "parse",
"--status", "-e", "json", "--detailed",
self.report_dir, "--file", file_filter]
out = self.__run_cmd(parse_status_cmd)

parsed_json = json.loads(out)

self.assertListEqual(self.__get_file_list(parsed_json,
"clangsa", "up-to-date"),
["a.cpp"])
self.assertListEqual(self.__get_file_list(parsed_json,
"clang-tidy", "up-to-date"),
[])
self.assertListEqual(self.__get_file_list(parsed_json,
"clang-tidy", "missing"),
["a.cpp"])

def test_parse_status_relative_filter(self):
self.__log_and_analyze()

file_filter = "a.cpp"

parse_status_cmd = [self._codechecker_cmd, "parse",
"--status", "-e", "json", "--detailed",
self.report_dir, "--file", file_filter]
out = self.__run_cmd(parse_status_cmd)

parsed_json = json.loads(out)

self.assertListEqual(self.__get_file_list(parsed_json,
"clangsa", "up-to-date"),
["a.cpp"])
self.assertListEqual(self.__get_file_list(parsed_json,
"clang-tidy", "up-to-date"),
[])
self.assertListEqual(self.__get_file_list(parsed_json,
"clang-tidy", "missing"),
["a.cpp"])

def test_parse_status_invalid_filter(self):
self.__log_and_analyze()

file_filter = "nonexistent_file.cpp"

parse_status_cmd = [self._codechecker_cmd, "parse",
"--status", "-e", "json", "--detailed",
self.report_dir, "--file", file_filter]
out = self.__run_cmd(parse_status_cmd)

self.assertIn("File filter (--file) contains nonexistent files", out)