summaryrefslogtreecommitdiffhomepage
path: root/bin/lint
blob: 1ac4f6ad3579d01ea77b3191a59b44d33e91d9a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/bin/sh
# bin/lint — manual linting for raylib-jamstack (Ruby + C/C++).
#
# Runs RuboCop (Ruby), clang-format (C/C++ formatting), and clang-tidy
# (C/C++ static analysis) on the project's hand-written sources. NOT automatic
# — run it deliberately, at the end of an implementation pass.
#
# Usage:
#   bin/lint              # report only (exit 1 if any issues)
#   bin/lint --fix        # safe autocorrect: RuboCop -a + clang-format -i
#                         # (clang-tidy is NEVER auto-fixed — it only reports)
#   bin/lint --ruby       # Ruby only (RuboCop)
#   bin/lint --c          # C/C++ only (clang-format + clang-tidy)
#
# What this checks:
#   Ruby (.rb):   RuboCop — style/layout/lint (.rubocop.yml: DisabledByDefault,
#                 opt-in to Layout + Lint + safe Style; excludes Metrics/length
#                 cops; TargetRubyVersion 3.4 for mruby endless-method support)
#   C/C++:        clang-format — formatting consistency (.clang-format: Allman,
#                 2-space, 100-col, return-type-on-own-line)
#                 clang-tidy — bug-finding checks (bugprone-*, cert-*,
#                 clang-analyzer-*), scoped to hand-written files only
#
# What this does NOT check (handled elsewhere):
#   - Syntax errors: caught at build time by the mruby-compiler gem (rake fails)
#   - Type errors:   Steep per-edit (RBS type checker, see opencode.json)
#
# Excludes generated/vendored code:
#   - mrbgems/raylib/src/raylib_gen.c (generated by tools/gen_raylib.rb)
#   - vendor/** (vendored mruby/raylib/rmlui/flecs/joltc)
#   - build/**, zig-out/**, .cache/**, .live/** (build/runtime artifacts)
#
# See .agents/knowledge/linting.md for the full rationale.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

# --- PATH setup (mirror rebuild.sh: strip /mnt/c, add gem bin dir) ---
CLEANPATH=$(echo "$PATH" | tr ':' '\n' | grep -v '^/mnt/c' | paste -sd:)
export PATH="/home/tradam/.local/share/gem/ruby/3.4.0/bin:$CLEANPATH"

CLANG_FORMAT="/usr/lib/llvm21/bin/clang-format"
CLANG_TIDY="/usr/lib/llvm21/bin/clang-tidy"

# Hand-written C/C++ sources (NOT raylib_gen.c which is generated, NOT vendor/).
C_SOURCES="src/main.c mrbgems/raylib/src/raylib_bindings.c mrbgems/flecs/src/flecs_bindings.c mrbgems/jolt/src/jolt_bindings.c mrbgems/rmlui/src/rml_bindings.cpp"

FIX=0
RUN_RUBY=1
RUN_C=1

for arg in "$@"; do
  case "$arg" in
    --fix)    FIX=1 ;;
    --ruby)   RUN_C=0 ;;
    --c)      RUN_RUBY=0 ;;
    -h|--help)
      sed -n '2,30p' "$0"
      exit 0
      ;;
    *)
      echo "unknown flag: $arg (try --help)" >&2
      exit 2
      ;;
  esac
done

EXIT_CODE=0

# ─── Ruby: RuboCop ───────────────────────────────────────────────────────────
if [ "$RUN_RUBY" = 1 ]; then
  echo "── RuboCop (Ruby) ──────────────────────────────────────────────────────"
  if [ "$FIX" = 1 ]; then
    echo "  running with safe autocorrect (-a)..."
    rubocop -a || EXIT_CODE=1
  else
    rubocop || EXIT_CODE=1
  fi
  echo ""
fi

# ─── C/C++: clang-format ─────────────────────────────────────────────────────
if [ "$RUN_C" = 1 ]; then
  echo "── clang-format (C/C++ formatting) ──────────────────────────────────────"
  if [ ! -x "$CLANG_FORMAT" ]; then
    echo "  SKIP: $CLANG_FORMAT not found (install llvm/clang-tools)" >&2
  elif [ "$FIX" = 1 ]; then
    echo "  rewriting in place (-i)..."
    "$CLANG_FORMAT" -i $C_SOURCES
    echo "  done (clang-format applied)"
  else
    echo "  dry-run (reporting violations)..."
    # --dry-run --Werror makes it exit non-zero if ANY file needs formatting.
    if ! "$CLANG_FORMAT" --dry-run --Werror $C_SOURCES 2>&1; then
      EXIT_CODE=1
      echo "  (run 'bin/lint --fix' to auto-format)"
    else
      echo "  all C/C++ files are clang-format clean"
    fi
  fi
  echo ""
fi

# ─── C/C++: clang-tidy (report only — NEVER auto-fix) ───────────────────────
if [ "$RUN_C" = 1 ]; then
  echo "── clang-tidy (C/C++ static analysis) ───────────────────────────────────"
  if [ ! -x "$CLANG_TIDY" ]; then
    echo "  SKIP: $CLANG_TIDY not found (install llvm/clang-tools)" >&2
  elif [ ! -f "compile_commands.json" ]; then
    echo "  SKIP: compile_commands.json not found (run ./rebuild.sh first)" >&2
  else
    echo "  running bug-finding checks (bugprone-*, cert-*, clang-analyzer-*)..."
    # --header-filter restricts diagnostics to OUR headers (not vendored).
    # compile_commands.json (-p .) provides the include roots + -DMRB_INT64.
    "$CLANG_TIDY" -p . \
      --checks='-*,bugprone-*,cert-*,clang-analyzer-*' \
      --header-filter='^mrbgems/.*/src/.*|^src/.*' \
      $C_SOURCES 2>&1 || true   # clang-tidy exits non-zero on warnings; that's OK
    echo "  done (clang-tidy reports only — no auto-fix)"
  fi
  echo ""
fi

# ─── Summary ─────────────────────────────────────────────────────────────────
if [ "$EXIT_CODE" = 0 ]; then
  echo "✓ lint clean"
else
  echo ""
  echo "✗ lint found issues (run 'bin/lint --fix' for safe autocorrects)"
fi
exit "$EXIT_CODE"