linthis 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {linthis-0.0.2 → linthis-0.0.3}/Cargo.lock +29 -27
  2. {linthis-0.0.2 → linthis-0.0.3}/Cargo.toml +1 -1
  3. {linthis-0.0.2 → linthis-0.0.3}/PKG-INFO +1 -1
  4. linthis-0.0.3/defaults/.clang-tidy +87 -0
  5. linthis-0.0.3/defaults/config.toml +88 -0
  6. {linthis-0.0.2 → linthis-0.0.3}/pyproject.toml +1 -1
  7. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/cpp.rs +177 -9
  8. linthis-0.0.3/src/fixers/cpplint.rs +579 -0
  9. linthis-0.0.3/src/fixers/mod.rs +15 -0
  10. linthis-0.0.3/src/formatters/cpp.rs +316 -0
  11. {linthis-0.0.2 → linthis-0.0.3}/src/lib.rs +10 -0
  12. {linthis-0.0.2 → linthis-0.0.3}/src/utils/mod.rs +3 -1
  13. {linthis-0.0.2 → linthis-0.0.3}/src/utils/output.rs +14 -2
  14. {linthis-0.0.2 → linthis-0.0.3}/src/utils/types.rs +12 -0
  15. linthis-0.0.2/defaults/config.toml +0 -63
  16. linthis-0.0.2/src/formatters/cpp.rs +0 -105
  17. {linthis-0.0.2 → linthis-0.0.3}/.github/workflows/release.yml +0 -0
  18. {linthis-0.0.2 → linthis-0.0.3}/.gitignore +0 -0
  19. {linthis-0.0.2 → linthis-0.0.3}/CHANGELOG.md +0 -0
  20. {linthis-0.0.2 → linthis-0.0.3}/README.md +0 -0
  21. {linthis-0.0.2 → linthis-0.0.3}/dev.sh +0 -0
  22. {linthis-0.0.2 → linthis-0.0.3}/docs/config-cli-design.md +0 -0
  23. {linthis-0.0.2 → linthis-0.0.3}/docs/init-hooks-design.md +0 -0
  24. {linthis-0.0.2 → linthis-0.0.3}/docs/plan-ruff-integration.md +0 -0
  25. {linthis-0.0.2 → linthis-0.0.3}/docs/tasks.md +0 -0
  26. {linthis-0.0.2 → linthis-0.0.3}/src/benchmark.rs +0 -0
  27. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/go.rs +0 -0
  28. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/java.rs +0 -0
  29. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/mod.rs +0 -0
  30. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/python.rs +0 -0
  31. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/rust.rs +0 -0
  32. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/traits.rs +0 -0
  33. {linthis-0.0.2 → linthis-0.0.3}/src/checkers/typescript.rs +0 -0
  34. {linthis-0.0.2 → linthis-0.0.3}/src/config/cli.rs +0 -0
  35. {linthis-0.0.2 → linthis-0.0.3}/src/config/mod.rs +0 -0
  36. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/go.rs +0 -0
  37. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/java.rs +0 -0
  38. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/mod.rs +0 -0
  39. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/python.rs +0 -0
  40. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/rust.rs +0 -0
  41. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/traits.rs +0 -0
  42. {linthis-0.0.2 → linthis-0.0.3}/src/formatters/typescript.rs +0 -0
  43. {linthis-0.0.2 → linthis-0.0.3}/src/main.rs +0 -0
  44. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/cache.rs +0 -0
  45. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/config_manager.rs +0 -0
  46. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/fetcher.rs +0 -0
  47. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/loader.rs +0 -0
  48. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/manifest.rs +0 -0
  49. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/mod.rs +0 -0
  50. {linthis-0.0.2 → linthis-0.0.3}/src/plugin/registry.rs +0 -0
  51. {linthis-0.0.2 → linthis-0.0.3}/src/presets/mod.rs +0 -0
  52. {linthis-0.0.2 → linthis-0.0.3}/src/utils/language.rs +0 -0
  53. {linthis-0.0.2 → linthis-0.0.3}/src/utils/walker.rs +0 -0
  54. {linthis-0.0.2 → linthis-0.0.3}/test-plugin-check/README.md +0 -0
  55. {linthis-0.0.2 → linthis-0.0.3}/test-plugin-check/linthis-plugin.toml +0 -0
  56. {linthis-0.0.2 → linthis-0.0.3}/tests/fixtures/test-plugin/linthis-plugin.toml +0 -0
  57. {linthis-0.0.2 → linthis-0.0.3}/tests/fixtures/test-plugin/python/ruff.toml +0 -0
  58. {linthis-0.0.2 → linthis-0.0.3}/tests/fixtures/test-plugin/rust/clippy.toml +0 -0
  59. {linthis-0.0.2 → linthis-0.0.3}/tests/fixtures/test-plugin/rust/rustfmt.toml +0 -0
  60. {linthis-0.0.2 → linthis-0.0.3}/tests/fixtures/us1/good.rs +0 -0
  61. {linthis-0.0.2 → linthis-0.0.3}/tests/fixtures/us1/unformatted.rs +0 -0
  62. {linthis-0.0.2 → linthis-0.0.3}/tests/integration/mod.rs +0 -0
@@ -2,15 +2,6 @@
2
2
  # It is not intended for manual editing.
3
3
  version = 4
4
4
 
5
- [[package]]
6
- name = "aho-corasick"
7
- version = "0.7.20"
8
- source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
10
- dependencies = [
11
- "memchr",
12
- ]
13
-
14
5
  [[package]]
15
6
  name = "aho-corasick"
16
7
  version = "1.1.4"
@@ -115,9 +106,9 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
115
106
 
116
107
  [[package]]
117
108
  name = "bstr"
118
- version = "1.6.0"
109
+ version = "1.12.1"
119
110
  source = "registry+https://github.com/rust-lang/crates.io-index"
120
- checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
111
+ checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
121
112
  dependencies = [
122
113
  "memchr",
123
114
  "serde",
@@ -326,12 +317,6 @@ version = "0.1.5"
326
317
  source = "registry+https://github.com/rust-lang/crates.io-index"
327
318
  checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
328
319
 
329
- [[package]]
330
- name = "fnv"
331
- version = "1.0.7"
332
- source = "registry+https://github.com/rust-lang/crates.io-index"
333
- checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
334
-
335
320
  [[package]]
336
321
  name = "fs2"
337
322
  version = "0.4.3"
@@ -367,15 +352,15 @@ dependencies = [
367
352
 
368
353
  [[package]]
369
354
  name = "globset"
370
- version = "0.4.10"
355
+ version = "0.4.14"
371
356
  source = "registry+https://github.com/rust-lang/crates.io-index"
372
- checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
357
+ checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
373
358
  dependencies = [
374
- "aho-corasick 0.7.20",
359
+ "aho-corasick",
375
360
  "bstr",
376
- "fnv",
377
361
  "log",
378
- "regex",
362
+ "regex-automata",
363
+ "regex-syntax 0.8.8",
379
364
  ]
380
365
 
381
366
  [[package]]
@@ -512,7 +497,7 @@ dependencies = [
512
497
 
513
498
  [[package]]
514
499
  name = "linthis"
515
- version = "0.0.2"
500
+ version = "0.0.3"
516
501
  dependencies = [
517
502
  "anyhow",
518
503
  "chrono",
@@ -544,9 +529,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
544
529
 
545
530
  [[package]]
546
531
  name = "log"
547
- version = "0.4.18"
532
+ version = "0.4.29"
548
533
  source = "registry+https://github.com/rust-lang/crates.io-index"
549
- checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
534
+ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
550
535
 
551
536
  [[package]]
552
537
  name = "memchr"
@@ -648,9 +633,20 @@ version = "1.8.4"
648
633
  source = "registry+https://github.com/rust-lang/crates.io-index"
649
634
  checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
650
635
  dependencies = [
651
- "aho-corasick 1.1.4",
636
+ "aho-corasick",
652
637
  "memchr",
653
- "regex-syntax",
638
+ "regex-syntax 0.7.5",
639
+ ]
640
+
641
+ [[package]]
642
+ name = "regex-automata"
643
+ version = "0.4.13"
644
+ source = "registry+https://github.com/rust-lang/crates.io-index"
645
+ checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
646
+ dependencies = [
647
+ "aho-corasick",
648
+ "memchr",
649
+ "regex-syntax 0.8.8",
654
650
  ]
655
651
 
656
652
  [[package]]
@@ -659,6 +655,12 @@ version = "0.7.5"
659
655
  source = "registry+https://github.com/rust-lang/crates.io-index"
660
656
  checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
661
657
 
658
+ [[package]]
659
+ name = "regex-syntax"
660
+ version = "0.8.8"
661
+ source = "registry+https://github.com/rust-lang/crates.io-index"
662
+ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
663
+
662
664
  [[package]]
663
665
  name = "rustix"
664
666
  version = "1.1.2"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "linthis"
3
- version = "0.0.2"
3
+ version = "0.0.3"
4
4
  edition = "2021"
5
5
  authors = ["zhlinh"]
6
6
  description = "A fast, cross-platform multi-language linter and formatter"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: linthis
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -0,0 +1,87 @@
1
+ # clang-tidy configuration for linthis
2
+ # Reference: https://clang.llvm.org/extra/clang-tidy/
3
+
4
+ ---
5
+ # 启用的检查规则
6
+ Checks: >
7
+ -*,
8
+ bugprone-*,
9
+ clang-analyzer-*,
10
+ cppcoreguidelines-*,
11
+ google-*,
12
+ misc-*,
13
+ modernize-*,
14
+ performance-*,
15
+ readability-*,
16
+ -modernize-use-trailing-return-type,
17
+ -readability-magic-numbers,
18
+ -cppcoreguidelines-avoid-magic-numbers,
19
+ -google-readability-todo,
20
+ -readability-identifier-length,
21
+ -cppcoreguidelines-pro-bounds-array-to-pointer-decay
22
+
23
+ # 将警告视为错误的规则 (可选)
24
+ WarningsAsErrors: ''
25
+
26
+ # 头文件过滤正则表达式
27
+ HeaderFilterRegex: '.*'
28
+
29
+ # 分析系统头文件
30
+ AnalyzeTemporaryDtors: false
31
+
32
+ # 格式化风格 (用于自动修复)
33
+ FormatStyle: 'google'
34
+
35
+ # 检查选项
36
+ CheckOptions:
37
+ # readability-identifier-naming: 命名规范
38
+ - key: readability-identifier-naming.ClassCase
39
+ value: CamelCase
40
+ - key: readability-identifier-naming.StructCase
41
+ value: CamelCase
42
+ - key: readability-identifier-naming.EnumCase
43
+ value: CamelCase
44
+ - key: readability-identifier-naming.FunctionCase
45
+ value: CamelCase
46
+ - key: readability-identifier-naming.MethodCase
47
+ value: CamelCase
48
+ - key: readability-identifier-naming.VariableCase
49
+ value: lower_case
50
+ - key: readability-identifier-naming.ParameterCase
51
+ value: lower_case
52
+ - key: readability-identifier-naming.MemberCase
53
+ value: lower_case
54
+ - key: readability-identifier-naming.MemberSuffix
55
+ value: '_'
56
+ - key: readability-identifier-naming.ConstantCase
57
+ value: CamelCase
58
+ - key: readability-identifier-naming.ConstantPrefix
59
+ value: 'k'
60
+ - key: readability-identifier-naming.GlobalConstantCase
61
+ value: CamelCase
62
+ - key: readability-identifier-naming.GlobalConstantPrefix
63
+ value: 'k'
64
+ - key: readability-identifier-naming.MacroDefinitionCase
65
+ value: UPPER_CASE
66
+ - key: readability-identifier-naming.NamespaceCase
67
+ value: lower_case
68
+
69
+ # modernize 相关选项
70
+ - key: modernize-use-nullptr.NullMacros
71
+ value: 'NULL'
72
+ - key: modernize-loop-convert.MinConfidence
73
+ value: reasonable
74
+ - key: modernize-pass-by-value.IncludeStyle
75
+ value: google
76
+
77
+ # performance 相关选项
78
+ - key: performance-move-const-arg.CheckTriviallyCopyableMove
79
+ value: true
80
+
81
+ # misc 相关选项
82
+ - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
83
+ value: true
84
+
85
+ # cppcoreguidelines 相关选项
86
+ - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
87
+ value: true
@@ -0,0 +1,88 @@
1
+ # Linthis Default Configuration
2
+ # This file contains built-in defaults that are applied when no user/project config exists.
3
+
4
+ # Maximum cyclomatic complexity threshold
5
+ max_complexity = 20
6
+
7
+ # Default exclusion patterns
8
+ exclude = [
9
+ ".git/**",
10
+ ".hg/**",
11
+ ".svn/**",
12
+ "node_modules/**",
13
+ "target/**",
14
+ "vendor/**",
15
+ "build/**",
16
+ "dist/**",
17
+ "__pycache__/**",
18
+ "*.pyc",
19
+ "*.pyo",
20
+ ".venv/**",
21
+ "venv/**",
22
+ ".idea/**",
23
+ ".vscode/**",
24
+ "*.min.js",
25
+ "*.min.css",
26
+ "**/Pods/**",
27
+ ]
28
+
29
+ # Output settings
30
+ [output]
31
+ format = "human" # human, json, github-actions
32
+ color = "auto" # auto, always, never
33
+
34
+ # Parallel processing
35
+ [parallel]
36
+ enabled = true
37
+ jobs = 0 # 0 = auto-detect CPU count
38
+
39
+ # Python settings (uses ruff for both linting and formatting)
40
+ [python]
41
+ # Ruff is used for both linting and formatting (10-100x faster than flake8+black)
42
+ linter = "ruff"
43
+ formatter = "ruff"
44
+ line_length = 120
45
+ target_version = "py38"
46
+
47
+ # Rust settings
48
+ [rust]
49
+ linter = "clippy"
50
+ formatter = "rustfmt"
51
+
52
+ # TypeScript/JavaScript settings
53
+ [typescript]
54
+ linter = "eslint"
55
+ formatter = "prettier"
56
+
57
+ # Go settings
58
+ [go]
59
+ linter = "go vet"
60
+ formatter = "gofmt"
61
+
62
+ # C/C++ settings
63
+ [cpp]
64
+ # Linter: "clang-tidy" (preferred, more powerful) or "cpplint" (fallback)
65
+ linter = "clang-tidy"
66
+ # Formatter: clang-format for code style, clang-tidy --fix for lint fixes
67
+ formatter = "clang-format"
68
+ # Style for clang-format (google, llvm, chromium, mozilla, webkit)
69
+ style = "google"
70
+ # Enable clang-tidy --fix during format phase (fixes C-style casts, nullptr, etc.)
71
+ clang_tidy_fix = true
72
+ # Enable cpplint fixer during format phase (fixes header guards, TODOs, etc.)
73
+ cpplint_fix = true
74
+ # Directory containing compile_commands.json (optional, auto-detected if not set)
75
+ # Auto-detection searches up to 6 levels: cmake*/, build*/, out*/, *_build/, *-build/
76
+ # and platform dirs: android/, ios/, arm*/, x86*/, debug/, release/, static/, shared/
77
+ # compile_commands_dir = "cmake_build/debug/Android/static/arm64-v8a"
78
+
79
+ # Cpplint fixer settings
80
+ [cpp.cpplint_fix]
81
+ # Header guard mode: "fix_name" (default) or "pragma_once"
82
+ header_guard_mode = "fix_name"
83
+ # Username for TODO comments (default: git user.name or $USER)
84
+ # todo_username = "your_username"
85
+ # Copyright template (optional, with {year} placeholder)
86
+ # copyright_template = "// Copyright {year} Your Company. All rights reserved."
87
+ # Header guard prefix (optional)
88
+ # header_guard_prefix = "MYPROJECT_"
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "linthis"
7
- version = "0.0.2"
7
+ version = "0.0.3"
8
8
  description = "A fast, cross-platform multi-language linter and formatter"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -13,15 +13,165 @@
13
13
  use crate::checkers::Checker;
14
14
  use crate::utils::types::{LintIssue, Severity};
15
15
  use crate::{Language, Result};
16
- use std::path::Path;
16
+ use std::path::{Path, PathBuf};
17
17
  use std::process::Command;
18
18
 
19
19
  /// C/C++ checker using clang-tidy (preferred) or cpplint.
20
- pub struct CppChecker;
20
+ pub struct CppChecker {
21
+ /// Custom .clang-tidy config path
22
+ config_path: Option<PathBuf>,
23
+ /// Custom compile_commands.json directory path
24
+ compile_commands_dir: Option<PathBuf>,
25
+ }
21
26
 
22
27
  impl CppChecker {
23
28
  pub fn new() -> Self {
24
- Self
29
+ Self {
30
+ config_path: None,
31
+ compile_commands_dir: None,
32
+ }
33
+ }
34
+
35
+ /// Set custom .clang-tidy config path
36
+ pub fn with_config(mut self, path: PathBuf) -> Self {
37
+ self.config_path = Some(path);
38
+ self
39
+ }
40
+
41
+ /// Set custom compile_commands.json directory path
42
+ /// This is the directory containing compile_commands.json, not the file itself
43
+ pub fn with_compile_commands_dir(mut self, path: PathBuf) -> Self {
44
+ self.compile_commands_dir = Some(path);
45
+ self
46
+ }
47
+
48
+ /// Find .clang-tidy config file by walking up from file path
49
+ fn find_clang_tidy_config(start_path: &Path) -> Option<PathBuf> {
50
+ let mut current = if start_path.is_file() {
51
+ start_path.parent()?.to_path_buf()
52
+ } else {
53
+ start_path.to_path_buf()
54
+ };
55
+
56
+ loop {
57
+ let config_path = current.join(".clang-tidy");
58
+ if config_path.exists() {
59
+ return Some(config_path);
60
+ }
61
+
62
+ // Also check for _clang-tidy (alternative name)
63
+ let alt_config = current.join("_clang-tidy");
64
+ if alt_config.exists() {
65
+ return Some(alt_config);
66
+ }
67
+
68
+ if !current.pop() {
69
+ break;
70
+ }
71
+ }
72
+ None
73
+ }
74
+
75
+ /// Find compile_commands.json for better analysis
76
+ /// Searches in common build directories recursively (up to max_depth levels)
77
+ fn find_compile_commands(start_path: &Path) -> Option<PathBuf> {
78
+ let mut current = if start_path.is_file() {
79
+ start_path.parent()?.to_path_buf()
80
+ } else {
81
+ start_path.to_path_buf()
82
+ };
83
+
84
+ loop {
85
+ // 1. Check in current directory directly
86
+ let direct = current.join("compile_commands.json");
87
+ if direct.exists() {
88
+ return Some(current.clone());
89
+ }
90
+
91
+ // 2. Check common fixed build directory names (1 level)
92
+ for build_dir in &[
93
+ "build",
94
+ "Build",
95
+ "out",
96
+ "output",
97
+ "cmake-build-debug",
98
+ "cmake-build-release",
99
+ "cmake-build-relwithdebinfo",
100
+ "cmake-build-minsizerel",
101
+ ".build",
102
+ "_build",
103
+ ] {
104
+ let compile_db = current.join(build_dir).join("compile_commands.json");
105
+ if compile_db.exists() {
106
+ return Some(current.join(build_dir));
107
+ }
108
+ }
109
+
110
+ // 3. Recursively search in directories matching build patterns (up to 6 levels deep)
111
+ if let Some(found) = Self::find_compile_commands_recursive(&current, 0, 6) {
112
+ return Some(found);
113
+ }
114
+
115
+ if !current.pop() {
116
+ break;
117
+ }
118
+ }
119
+ None
120
+ }
121
+
122
+ /// Recursively search for compile_commands.json in build-like directories
123
+ fn find_compile_commands_recursive(dir: &Path, depth: usize, max_depth: usize) -> Option<PathBuf> {
124
+ if depth >= max_depth {
125
+ return None;
126
+ }
127
+
128
+ let entries = std::fs::read_dir(dir).ok()?;
129
+
130
+ for entry in entries.flatten() {
131
+ let path = entry.path();
132
+ if !path.is_dir() {
133
+ continue;
134
+ }
135
+
136
+ let name = path.file_name().and_then(|n| n.to_str())?;
137
+ let name_lower = name.to_lowercase();
138
+
139
+ // Only recurse into build-related directories
140
+ let is_build_dir = name_lower.starts_with("cmake")
141
+ || name_lower.starts_with("build")
142
+ || name_lower.starts_with("out")
143
+ || name_lower.ends_with("-build")
144
+ || name_lower.ends_with("_build")
145
+ // Also allow platform/arch subdirectories inside build dirs
146
+ || (depth > 0
147
+ && (name_lower.contains("android")
148
+ || name_lower.contains("ios")
149
+ || name_lower.contains("linux")
150
+ || name_lower.contains("windows")
151
+ || name_lower.contains("macos")
152
+ || name_lower.contains("darwin")
153
+ || name_lower.contains("arm")
154
+ || name_lower.contains("x86")
155
+ || name_lower.contains("x64")
156
+ || name_lower.contains("static")
157
+ || name_lower.contains("shared")
158
+ || name_lower.contains("debug")
159
+ || name_lower.contains("release")));
160
+
161
+ if is_build_dir {
162
+ // Check if compile_commands.json exists here
163
+ let compile_db = path.join("compile_commands.json");
164
+ if compile_db.exists() {
165
+ return Some(path);
166
+ }
167
+
168
+ // Recurse deeper
169
+ if let Some(found) = Self::find_compile_commands_recursive(&path, depth + 1, max_depth) {
170
+ return Some(found);
171
+ }
172
+ }
173
+ }
174
+ None
25
175
  }
26
176
 
27
177
  /// Check if clang-tidy is available
@@ -42,11 +192,29 @@ impl CppChecker {
42
192
  .unwrap_or(false)
43
193
  }
44
194
 
45
- /// Run clang-tidy on a file
46
- fn run_clang_tidy(path: &Path) -> Result<Vec<LintIssue>> {
47
- let output = Command::new("clang-tidy")
48
- .arg(path)
49
- .arg("--")
195
+ /// Run clang-tidy on a file (check only, no fix)
196
+ fn run_clang_tidy(&self, path: &Path) -> Result<Vec<LintIssue>> {
197
+ let mut cmd = Command::new("clang-tidy");
198
+ cmd.arg(path);
199
+
200
+ // Add config file if specified or found
201
+ if let Some(ref config) = self.config_path {
202
+ cmd.arg(format!("--config-file={}", config.display()));
203
+ } else if let Some(config) = Self::find_clang_tidy_config(path) {
204
+ cmd.arg(format!("--config-file={}", config.display()));
205
+ }
206
+
207
+ // Add compile_commands.json path: user-specified > auto-detected
208
+ if let Some(ref build_path) = self.compile_commands_dir {
209
+ cmd.arg(format!("-p={}", build_path.display()));
210
+ } else if let Some(build_path) = Self::find_compile_commands(path) {
211
+ cmd.arg(format!("-p={}", build_path.display()));
212
+ } else {
213
+ // Use -- to separate clang-tidy args from compiler args
214
+ cmd.arg("--");
215
+ }
216
+
217
+ let output = cmd
50
218
  .output()
51
219
  .map_err(|e| crate::LintisError::Checker(format!("Failed to run clang-tidy: {}", e)))?;
52
220
 
@@ -234,7 +402,7 @@ impl Checker for CppChecker {
234
402
  fn check(&self, path: &Path) -> Result<Vec<LintIssue>> {
235
403
  // Prefer clang-tidy if available, fall back to cpplint
236
404
  if Self::has_clang_tidy() {
237
- Self::run_clang_tidy(path)
405
+ self.run_clang_tidy(path)
238
406
  } else if Self::has_cpplint() {
239
407
  Self::run_cpplint(path)
240
408
  } else {