@@ -39,7 +39,7 @@ def find_clang_tidy() -> str:
3939 return 'clang-tidy'
4040 except (FileNotFoundError , subprocess .TimeoutExpired ):
4141 pass
42-
42+
4343 raise RuntimeError (
4444 "clang-tidy not found in PATH. Please install clang-tidy:\n "
4545 " Windows: Install LLVM from https://llvm.org/builds/"
@@ -70,7 +70,6 @@ def find_compile_commands(build_dir: Optional[Path] = None) -> Path:
7070 f"compile_commands.json not found in { build_dir } "
7171 )
7272
73- # Use the dedicated clang-tidy build (PCH-free, required for correct analysis)
7473 clang_tidy_build = project_root / "build" / "clang-tidy"
7574 compile_commands = clang_tidy_build / "compile_commands.json"
7675
@@ -104,11 +103,10 @@ def filter_source_files(compile_commands: List[dict],
104103 for entry in compile_commands :
105104 file_path = Path (entry ['file' ])
106105
107- # Convert to relative path for pattern matching
108106 try :
109107 rel_path = file_path .relative_to (project_root )
110108 except ValueError :
111- continue # File outside project root
109+ continue
112110
113111 rel_path_str = str (rel_path )
114112
@@ -128,7 +126,7 @@ def filter_source_files(compile_commands: List[dict],
128126def _run_batch (args : Tuple ) -> Tuple [int , Dict [str , List [str ]]]:
129127 """Helper function to run clang-tidy on a batch of files (for multiprocessing)."""
130128 batch_num , batch , compile_commands_dir , fix , extra_args , project_root , clang_tidy_exe , verbose = args
131-
129+
132130 cmd = [
133131 clang_tidy_exe ,
134132 f'-p={ compile_commands_dir } ' ,
@@ -143,44 +141,37 @@ def _run_batch(args: Tuple) -> Tuple[int, Dict[str, List[str]]]:
143141 cmd .extend (batch )
144142
145143 issues_by_file = defaultdict (list )
146-
144+
147145 try :
148146 result = subprocess .run (
149147 cmd ,
150148 cwd = project_root ,
151149 capture_output = True ,
152150 text = True
153151 )
154-
155- # Parse output to extract warnings/errors
152+
156153 if result .stdout or result .stderr :
157154 output = result .stdout + result .stderr
158-
155+
159156 for line in output .splitlines ():
160157 line = line .strip ()
161158 if not line :
162159 continue
163-
164- # clang-tidy diagnostic format: "path/to/file.cpp:line:col: level: message"
160+
165161 line_lower = line .lower ()
166162 is_warning_or_error = ('warning' in line_lower or 'error' in line_lower )
167-
168- # Only process lines that look like diagnostics (have colons and file paths)
163+
169164 if ':' in line and (is_warning_or_error or verbose ):
170- # Try to extract file path (first part before colon)
171165 parts = line .split (':' , 1 )
172166 if parts :
173167 file_path = parts [0 ].strip ()
174- # Check if it looks like a file path
175168 if any (file_path .endswith (ext ) for ext in ['.cpp' , '.cxx' , '.cc' , '.c' , '.h' , '.hpp' , '.hxx' ]):
176- # Extract relative path for cleaner output
177169 try :
178170 rel_path = Path (file_path ).relative_to (project_root )
179171 file_key = str (rel_path )
180172 except (ValueError , OSError ):
181173 file_key = file_path
182-
183- # Only add if it's a warning/error, or if verbose mode
174+
184175 if is_warning_or_error or verbose :
185176 issues_by_file [file_key ].append (line )
186177
@@ -203,21 +194,19 @@ def run_clang_tidy(source_files: List[str],
203194 print ("No source files to analyze." )
204195 return 0
205196
206- # Find clang-tidy executable
207197 clang_tidy_exe = find_clang_tidy ()
208198
209- # Process files in batches (Windows has ~8191 char command-line limit)
210199 BATCH_SIZE = 50
211200 total_files = len (source_files )
212201 batches = [source_files [i :i + BATCH_SIZE ] for i in range (0 , total_files , BATCH_SIZE )]
213202
214203 project_root = find_project_root ()
215204 compile_commands_dir = compile_commands_path .parent
216-
205+
217206 all_issues = defaultdict (list )
218207 files_with_issues = set ()
219208 total_issues = 0
220-
209+
221210 if jobs > 1 :
222211 if verbose :
223212 print (f"Running clang-tidy on { total_files } file(s) in { len (batches )} batch(es) with { jobs } workers...\n " )
@@ -233,8 +222,7 @@ def run_clang_tidy(source_files: List[str],
233222 for idx , batch in enumerate (batches )
234223 ]
235224 )
236-
237- # Collect results
225+
238226 overall_returncode = 0
239227 for returncode , issues in batch_results :
240228 if returncode != 0 :
@@ -265,8 +253,7 @@ def run_clang_tidy(source_files: List[str],
265253 returncode , issues = _run_batch ((batch_num , batch , compile_commands_dir , fix , extra_args , project_root , clang_tidy_exe , verbose ))
266254 if returncode != 0 :
267255 overall_returncode = returncode
268-
269- # Collect issues
256+
270257 for file_path , file_issues in issues .items ():
271258 if file_issues :
272259 all_issues [file_path ].extend (file_issues )
@@ -278,14 +265,12 @@ def run_clang_tidy(source_files: List[str],
278265 except KeyboardInterrupt :
279266 print ("\n Interrupted by user." )
280267 return 130
281-
268+
282269 if not verbose :
283270 print (" done." )
284271
285- # Print summary
286272 print (f"\n Summary: { len (files_with_issues )} file(s) with issues, { total_issues } total issue(s)" )
287-
288- # Print issues (only warnings/errors, not verbose informational messages)
273+
289274 if all_issues :
290275 print ("\n Issues found:" )
291276 for file_path in sorted (all_issues .keys ()):
@@ -382,23 +367,20 @@ def main():
382367 compile_commands_path = find_compile_commands (args .build_dir )
383368 print (f"Using compile commands: { compile_commands_path } \n " )
384369
385- # Check if any arguments look like file paths
386370 project_root = find_project_root ()
387371 specified_files = []
388372 clang_tidy_args = []
389-
373+
390374 for arg in args .clang_tidy_args :
391- # Check if it's a file path (exists and has source file extension)
392375 file_path = Path (arg )
393376 if not file_path .is_absolute ():
394377 file_path = project_root / file_path
395-
378+
396379 if file_path .exists () and file_path .suffix in {'.cpp' , '.cxx' , '.cc' , '.c' , '.h' , '.hpp' }:
397380 specified_files .append (str (file_path .resolve ()))
398381 else :
399382 clang_tidy_args .append (arg )
400-
401- # If specific files were provided, use them directly
383+
402384 if specified_files :
403385 if args .verbose :
404386 print (f"Analyzing { len (specified_files )} specified file(s)\n " )
@@ -411,7 +393,6 @@ def main():
411393 args .verbose
412394 )
413395
414- # Otherwise, filter from compile_commands.json
415396 compile_commands = load_compile_commands (compile_commands_path )
416397
417398 default_excludes = [
0 commit comments