Repository: cpp-linter/cpp-linter (language: python)

Issue:
Action doesn't fail on compiler errors
For example: (https://github.com/zjeffer/Waybar/actions/runs/7129252582/job/19412930401)

```
  INFO:CPP Linter:Running "clang-tidy-17 --export-fixes=.cpp-linter_cache/clang_tidy_output.yml -p /__w/Waybar/Waybar/build --line-filter=[{"name": "src/modules/hyprland/workspaces.cpp", "lines": [[50, 51]]}] --extra-arg= src/modules/hyprland/workspaces.cpp"
  DEBUG:CPP Linter:Output from clang-tidy:
  ../include/bar.hpp:16:10: error: 'xdg-output-unstable-v1-client-protocol.h' file not found [clang-diagnostic-error]
     16 | #include "xdg-output-unstable-v1-client-protocol.h"
        |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  DEBUG:CPP Linter:clang-tidy made the following summary:
  13697 warnings and 2 errors generated.
  Error while processing /__w/Waybar/Waybar/src/modules/hyprland/workspaces.cpp.
  Suppressed 13697 warnings (13670 in non-user code, 17 due to line filter, 10 with check filters).
  Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
  Found compiler error(s).
```

```
Posting comment(s)
  INFO:CPP Linter:0 checks-failed
```



Relevant parts of my workflow file:
```
      with:
        style: '' # empty string => don't do clang-format checks here, we do them in clang-format.yml
        files-changed-only: true # only check files that have changed
        lines-changed-only: true # only check lines that have changed
        tidy-checks: '' # empty string => use the .clang-tidy file
        version: '17' # clang-tools version
        database: 'build' # path to the compile_commands.json file
    - name: Check if clang-tidy failed on any files
      if: steps.clang-tidy-check.outputs.checks-failed > 0
      run: echo "Some files failed the linting checks!" && exit 1
```
checks-failed is 0, because the compiler error apparently doesn't count as a failed check.

How can I ensure the action fails when there are compiler errors like missing header files?


Discussion / hints:
Hmm, after fixing this compiler error, the next job also doesn't fail even when there are 2 warnings:

https://github.com/zjeffer/Waybar/actions/runs/7129748727/job/19414516720

```
  INFO:CPP Linter:Running "clang-tidy-17 --export-fixes=.cpp-linter_cache/clang_tidy_output.yml -p /__w/Waybar/Waybar/build --line-filter=[{"name": "src/modules/hyprland/workspaces.cpp", "lines": [[50, 51]]}] --extra-arg= src/modules/hyprland/workspaces.cpp"
  DEBUG:CPP Linter:Output from clang-tidy:
  ../src/modules/hyprland/workspaces.cpp:50:8: warning: implicit conversion 'pointer' (aka 'waybar::modules::hyprland::IPC *') -> bool [readability-implicit-bool-conversion]
     50 |   if (!gIPC.get()) {
        |       ~^         
        |                   == nullptr
  ../src/modules/hyprland/workspaces.cpp:50:8: warning: redundant get() call on smart pointer [readability-redundant-smartptr-get]
     50 |   if (!gIPC.get()) {
        |        ^~~~~~~~~~
        |        gIPC
  
  DEBUG:CPP Linter:clang-tidy made the following summary:
  16524 warnings generated.
  Suppressed 16522 warnings (16510 in non-user code, 2 due to line filter, 10 with check filters).
  Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well.
  
Posting comment(s)
  INFO:CPP Linter:0 checks-failed
../src/modules/hyprland/workspaces.cpp ?= src/modules/hyprland/workspaces.cpp
../src/modules/hyprland/workspaces.cpp ?= src/modules/hyprland/workspaces.cpp
```
How come there are 0 checks failed?
> ../src/modules/hyprland/workspaces.cpp ?= src/modules/hyprland/workspaces.cpp
> ../src/modules/hyprland/workspaces.cpp ?= src/modules/hyprland/workspaces.cpp

This is actually debugging output (that I forgot to remove). Fortunately, this output tells me that it is trying to find the file corresponding to to 2 concerns (checks-failed). Since the paths don't exactly match, the checks-failed are ignored as if they were from non-user code. It should be an easy fix to remove the preceding `../` before matching the paths.

The problem is in `make_annotations()`:
https://github.com/cpp-linter/cpp-linter/blob/a2875ac00e6f1dc3eb4ac19712c7a241b5a76e83/cpp_linter/run.py#L605-L617

Fix patch:
diff --git a/cpp_linter/clang_tidy.py b/cpp_linter/clang_tidy.py
index e0ea6bb2..e5db3423 100644
--- a/cpp_linter/clang_tidy.py
+++ b/cpp_linter/clang_tidy.py
@@ -42,7 +42,10 @@ def __init__(
         self.cols = int(self.cols)
         #: The source filename concerning the notification.
         self.filename = (
-            PurePath(self.filename).as_posix().replace(Path.cwd().as_posix(), "")
+            Path(self.filename)
+            .resolve()
+            .as_posix()
+            .replace(Path.cwd().as_posix() + "/", "")
         )
         #: A `list` of lines for the code-block in the notification.
         self.fixit_lines: List[str] = []
diff --git a/cpp_linter/run.py b/cpp_linter/run.py
index 1fdd6663..a64d83e6 100644
--- a/cpp_linter/run.py
+++ b/cpp_linter/run.py
@@ -604,16 +604,16 @@ def make_annotations(
                 Globals.format_failed_count += 1
     for note in GlobalParser.tidy_notes:
         if lines_changed_only:
-            filename = note.filename.replace("\\", "/").lstrip("/")
             line_filter = []
             for file in files:
-                print(filename, "?=", file["filename"])
-                if filename == file["filename"]:
+                if note.filename == file["filename"]:
                     line_filter = cast(
                         List[int], range_of_changed_lines(file, lines_changed_only)
                     )
                     break
-            else:  # filename match not found; treat line_filter as empty list
+            else:  # pragma: no cover
+                # filename match not found; treat as error from non-user code
+                logger.debug("%s doesn't match any project files", note.filename)
                 continue
             if note.line in line_filter or not line_filter:
                 Globals.tidy_failed_count += 1


Test patch:
diff --git a/tests/capture_tools_output/test_tools_output.py b/tests/capture_tools_output/test_tools_output.py
index 215456ca..9d2f0703 100644
--- a/tests/capture_tools_output/test_tools_output.py
+++ b/tests/capture_tools_output/test_tools_output.py
@@ -283,14 +283,14 @@ def test_tidy_annotations(
     make_annotations(
         style="", file_annotations=True, lines_changed_only=lines_changed_only
     )
-    for message in [r.message for r in caplog.records if r.levelno == logging.INFO]:
+    messages = [r.message for r in caplog.records if r.levelno == logging.INFO]
+    assert messages
+    for message in messages:
         if TIDY_RECORD.search(message) is not None:
             line = int(TIDY_RECORD_LINE.sub("\\1", message))
-            file_obj = match_file_json(
-                RECORD_FILE.sub("\\1", message).replace("\\", "/")
-            )
-            if file_obj is None:
-                continue
+            filename = RECORD_FILE.sub("\\1", message).replace("\\", "/")
+            file_obj = match_file_json(filename)
+            assert file_obj is not None, f"{filename} was not matched with project src"
             ranges = cpp_linter.range_of_changed_lines(file_obj, lines_changed_only)
             if ranges:  # an empty list if lines_changed_only == 0
                 assert line in ranges