Signed-off-by: Adrian Cole <adrian@tetrate.io> Co-authored-by: Adrian Cole <adrian@tetrate.io>
162 lines
5.0 KiB
Bash
Executable File
162 lines
5.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Baseline clippy rules - only fail on NEW violations
|
|
#
|
|
# Format: "rule_name|violation_parser"
|
|
#
|
|
# Violation parsers (run clippy on your rule to see which fits):
|
|
# function_name - When spans show: "fn my_function(..."
|
|
# type_name - When spans show: "struct MyStruct" or "enum MyEnum"
|
|
# file_only - When spans show file-level issues
|
|
#
|
|
# Note: If your rule doesn't fit these parsers, you may need to add a new parser
|
|
# to the parse_violation() function below
|
|
#
|
|
# To add new rules:
|
|
# 1. Add rule below: "clippy::your_rule|violation_parser"
|
|
# 2. Generate baseline: ./scripts/clippy-baseline.sh generate clippy::your_rule
|
|
|
|
BASELINE_RULES=(
|
|
"clippy::too_many_lines|function_name"
|
|
)
|
|
|
|
parse_violation() {
|
|
local rule_code="$1"
|
|
local violation_parser="$2"
|
|
|
|
case "$violation_parser" in
|
|
"function_name")
|
|
jq -r 'select(.message.code.code == "'"$rule_code"'") |
|
|
.message.spans[0] as $span |
|
|
($span.text | map(.text) | map(select(test("\\bfn\\b"))) | first // "") as $line |
|
|
if $line == "" then empty else "\($span.file_name)::\($line | capture("fn\\s+(?<name>[a-z_][a-z0-9_]*)") | .name)" end'
|
|
;;
|
|
"type_name")
|
|
jq -r 'select(.message.code.code == "'"$rule_code"'") |
|
|
"\(.message.spans[0].file_name)::\(.message.spans[0].text[0].text | split(" ")[1] | split(" ")[0])"'
|
|
;;
|
|
"file_only")
|
|
jq -r 'select(.message.code.code == "'"$rule_code"'") |
|
|
"\(.message.spans[0].file_name)"'
|
|
;;
|
|
*)
|
|
echo "Unknown violation parser: $violation_parser" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
get_baseline_file() {
|
|
local rule_name="$1"
|
|
local safe_name=$(echo "$rule_name" | sed 's/clippy:://' | sed 's/:/-/g')
|
|
echo "clippy-baselines/${safe_name}.txt"
|
|
}
|
|
|
|
|
|
generate_baseline() {
|
|
local rule_name="$1"
|
|
|
|
[[ -z "$rule_name" ]] && { echo "Missing rule name"; return 1; }
|
|
|
|
local violation_parser=""
|
|
for rule in "${BASELINE_RULES[@]}"; do
|
|
[[ "${rule%|*}" == "$rule_name" ]] && { violation_parser="${rule#*|}"; break; }
|
|
done
|
|
|
|
[[ -z "$violation_parser" ]] && { echo "Unknown rule: $rule_name"; return 1; }
|
|
|
|
local baseline_file=$(get_baseline_file "$rule_name")
|
|
|
|
cargo clippy --jobs 2 --message-format=json -- -W "$rule_name" | \
|
|
parse_violation "$rule_name" "$violation_parser" | \
|
|
sort > "$baseline_file"
|
|
|
|
echo "✅ Generated baseline for $rule_name ($(wc -l < "$baseline_file") violations)"
|
|
}
|
|
|
|
|
|
# Check a single rule from pre-generated JSON (optimized version)
|
|
check_rule_from_json() {
|
|
local temp_json="$1"
|
|
local rule_name="$2"
|
|
local violation_parser="$3"
|
|
local baseline_file="$4"
|
|
|
|
echo " → Checking $rule_name"
|
|
|
|
if [[ ! -f "$baseline_file" ]]; then
|
|
echo " ❌ $rule_name: baseline file not found"
|
|
return 1
|
|
fi
|
|
|
|
local temp_parsed=$(mktemp)
|
|
cat "$temp_json" | parse_violation "$rule_name" "$violation_parser" | sort > "$temp_parsed"
|
|
|
|
local new_violations_file=$(mktemp)
|
|
diff <(sort "$baseline_file") <(sort "$temp_parsed") | grep "^>" | cut -c3- > "$new_violations_file"
|
|
|
|
if [[ -s "$new_violations_file" ]]; then
|
|
echo " ❌ $rule_name: NEW violations found:"
|
|
|
|
while IFS= read -r violation; do
|
|
# Extract all violations for this rule and find the matching one
|
|
cat "$temp_json" | jq -c 'select(.message.code.code == "'"$rule_name"'")' 2>/dev/null | while read -r json_line; do
|
|
parsed_id=$(echo "$json_line" | parse_violation "$rule_name" "$violation_parser")
|
|
if [[ "$parsed_id" == "$violation" ]]; then
|
|
echo "$json_line" | jq -r '.message.rendered' | sed 's/^/ /'
|
|
fi
|
|
done
|
|
done < "$new_violations_file"
|
|
|
|
rm "$temp_parsed" "$new_violations_file"
|
|
return 1
|
|
fi
|
|
|
|
rm "$new_violations_file"
|
|
|
|
echo " ✅ $rule_name: ok"
|
|
rm "$temp_parsed"
|
|
return 0
|
|
}
|
|
|
|
check_all_baseline_rules() {
|
|
echo "🔍 Checking baseline clippy rules..."
|
|
|
|
local clippy_flags=""
|
|
for rule in "${BASELINE_RULES[@]}"; do
|
|
local rule_name="${rule%|*}"
|
|
clippy_flags="$clippy_flags -W $rule_name"
|
|
done
|
|
|
|
local temp_json=$(mktemp)
|
|
cargo clippy --jobs 2 --message-format=json -- $clippy_flags | tee "$temp_json"
|
|
|
|
local failed_rules=()
|
|
|
|
# Check each rule against its baseline
|
|
for rule in "${BASELINE_RULES[@]}"; do
|
|
local rule_name="${rule%|*}"
|
|
local violation_parser="${rule#*|}"
|
|
local baseline_file=$(get_baseline_file "$rule_name")
|
|
|
|
if ! check_rule_from_json "$temp_json" "$rule_name" "$violation_parser" "$baseline_file"; then
|
|
failed_rules+=("$rule_name")
|
|
fi
|
|
done
|
|
|
|
rm "$temp_json"
|
|
|
|
if [[ ${#failed_rules[@]} -gt 0 ]]; then
|
|
echo ""
|
|
echo "❌ Failed baseline checks for: ${failed_rules[*]}"
|
|
exit 1
|
|
else
|
|
echo ""
|
|
echo "✅ All baseline clippy checks passed!"
|
|
fi
|
|
}
|
|
|
|
if [[ "$1" == "generate" ]]; then
|
|
generate_baseline "$2"
|
|
fi
|