rubocop-rbs_inline 1.7.0 → 2.0.0
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.
- checksums.yaml +4 -4
- data/.claude/hooks/pre-commit-check.sh +42 -2
- data/.claude/hooks/protect-sig-files.sh +45 -0
- data/.claude/hooks/self-review.sh +89 -0
- data/.claude/settings.json +16 -1
- data/.rubocop.yml +17 -12
- data/CHANGELOG.md +30 -0
- data/CLAUDE.md +11 -14
- data/README.md +51 -6
- data/Rakefile +12 -3
- data/config/default.yml +2 -4
- data/lib/rubocop/cop/rbs_inline_cops.rb +16 -7
- data/lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb +2 -9
- data/lib/rubocop/cop/style/rbs_inline/data_define_with_block.rb +13 -7
- data/lib/rubocop/cop/style/rbs_inline/embedded_rbs_spacing.rb +3 -2
- data/lib/rubocop/cop/style/rbs_inline/invalid_comment.rb +2 -2
- data/lib/rubocop/cop/style/rbs_inline/invalid_types.rb +1 -0
- data/lib/rubocop/cop/style/rbs_inline/keyword_separator.rb +2 -2
- data/lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb +3 -2
- data/lib/rubocop/cop/style/rbs_inline/missing_data_class_annotation.rb +2 -9
- data/lib/rubocop/cop/style/rbs_inline/missing_struct_class_annotation.rb +2 -16
- data/lib/rubocop/cop/style/rbs_inline/missing_type_annotation.rb +3 -16
- data/lib/rubocop/cop/style/rbs_inline/mixin/annotation_keywords.rb +16 -0
- data/lib/rubocop/cop/style/rbs_inline/mixin/ast_utils.rb +75 -0
- data/lib/rubocop/cop/style/rbs_inline/{comment_parser.rb → mixin/comment_parser.rb} +8 -0
- data/lib/rubocop/cop/style/rbs_inline/mixin/data_class_matcher.rb +22 -0
- data/lib/rubocop/cop/style/rbs_inline/{data_struct_helper.rb → mixin/data_struct_helper.rb} +14 -5
- data/lib/rubocop/cop/style/rbs_inline/mixin/file_filter.rb +72 -0
- data/lib/rubocop/cop/style/rbs_inline/mixin/mode_config.rb +39 -0
- data/lib/rubocop/cop/style/rbs_inline/{source_code_helper.rb → mixin/source_code_helper.rb} +3 -5
- data/lib/rubocop/cop/style/rbs_inline/mixin/struct_class_matcher.rb +32 -0
- data/lib/rubocop/cop/style/rbs_inline/parameters_separator.rb +2 -2
- data/lib/rubocop/cop/style/rbs_inline/redundant_annotation_with_skip.rb +2 -11
- data/lib/rubocop/cop/style/rbs_inline/redundant_instance_variable_annotation.rb +1 -0
- data/lib/rubocop/cop/style/rbs_inline/redundant_type_annotation.rb +6 -15
- data/lib/rubocop/cop/style/rbs_inline/require_rbs_inline_comment.rb +35 -23
- data/lib/rubocop/cop/style/rbs_inline/struct_class_comment_alignment.rb +2 -16
- data/lib/rubocop/cop/style/rbs_inline/struct_new_with_block.rb +13 -7
- data/lib/rubocop/cop/style/rbs_inline/unmatched_annotations.rb +31 -6
- data/lib/rubocop/cop/style/rbs_inline/untyped_instance_variable.rb +16 -6
- data/lib/rubocop/cop/style/rbs_inline/variable_comment_spacing.rb +3 -2
- data/lib/rubocop/rbs_inline/version.rb +1 -1
- data/rbs_collection.lock.yaml +11 -11
- data/sig/gems/rubocop/rubocop.rbs +14 -0
- data/sig/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rbs +4 -5
- data/sig/rubocop/cop/style/rbs_inline/data_define_with_block.rbs +9 -2
- data/sig/rubocop/cop/style/rbs_inline/embedded_rbs_spacing.rbs +2 -0
- data/sig/rubocop/cop/style/rbs_inline/invalid_comment.rbs +4 -3
- data/sig/rubocop/cop/style/rbs_inline/invalid_types.rbs +2 -0
- data/sig/rubocop/cop/style/rbs_inline/keyword_separator.rbs +4 -3
- data/sig/rubocop/cop/style/rbs_inline/method_comment_spacing.rbs +2 -0
- data/sig/rubocop/cop/style/rbs_inline/missing_data_class_annotation.rbs +4 -5
- data/sig/rubocop/cop/style/rbs_inline/missing_struct_class_annotation.rbs +4 -10
- data/sig/rubocop/cop/style/rbs_inline/missing_type_annotation.rbs +4 -8
- data/sig/rubocop/cop/style/rbs_inline/mixin/annotation_keywords.rbs +16 -0
- data/sig/rubocop/cop/style/rbs_inline/{ast_utils.rbs → mixin/ast_utils.rbs} +14 -1
- data/sig/rubocop/cop/style/rbs_inline/{class_comment_alignment.rbs → mixin/class_comment_alignment.rbs} +1 -1
- data/sig/rubocop/cop/style/rbs_inline/{comment_parser.rbs → mixin/comment_parser.rbs} +5 -1
- data/sig/rubocop/cop/style/rbs_inline/mixin/data_class_matcher.rbs +18 -0
- data/sig/rubocop/cop/style/rbs_inline/{data_struct_helper.rbs → mixin/data_struct_helper.rbs} +6 -1
- data/sig/rubocop/cop/style/rbs_inline/mixin/file_filter.rbs +53 -0
- data/sig/rubocop/cop/style/rbs_inline/{missing_class_annotation.rbs → mixin/missing_class_annotation.rbs} +1 -1
- data/sig/rubocop/cop/style/rbs_inline/mixin/mode_config.rbs +27 -0
- data/sig/rubocop/cop/style/rbs_inline/{source_code_helper.rbs → mixin/source_code_helper.rbs} +1 -1
- data/sig/rubocop/cop/style/rbs_inline/mixin/struct_class_matcher.rbs +26 -0
- data/sig/rubocop/cop/style/rbs_inline/parameters_separator.rbs +4 -3
- data/sig/rubocop/cop/style/rbs_inline/redundant_annotation_with_skip.rbs +4 -4
- data/sig/rubocop/cop/style/rbs_inline/redundant_instance_variable_annotation.rbs +2 -0
- data/sig/rubocop/cop/style/rbs_inline/redundant_type_annotation.rbs +4 -4
- data/sig/rubocop/cop/style/rbs_inline/require_rbs_inline_comment.rbs +22 -5
- data/sig/rubocop/cop/style/rbs_inline/struct_class_comment_alignment.rbs +4 -10
- data/sig/rubocop/cop/style/rbs_inline/struct_new_with_block.rbs +9 -2
- data/sig/rubocop/cop/style/rbs_inline/unmatched_annotations.rbs +13 -0
- data/sig/rubocop/cop/style/rbs_inline/untyped_instance_variable.rbs +10 -0
- data/sig/rubocop/cop/style/rbs_inline/variable_comment_spacing.rbs +2 -0
- metadata +28 -18
- data/lib/rubocop/cop/style/rbs_inline/ast_utils.rb +0 -46
- data/sig/rubocop/cop/style/rbs_inline/redundant_argument_type.rbs +0 -85
- data/sig/rubocop/cop/style/rbs_inline/redundant_return_type.rbs +0 -109
- /data/.claude/hooks/{setup.sh → claude-code-web-session-start.sh} +0 -0
- /data/lib/rubocop/cop/style/rbs_inline/{class_comment_alignment.rb → mixin/class_comment_alignment.rb} +0 -0
- /data/lib/rubocop/cop/style/rbs_inline/{missing_class_annotation.rb → mixin/missing_class_annotation.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6bb68dac2a82e53e5c2b8640e5feab967ca592c9a91927ed91f921c705629c1e
|
|
4
|
+
data.tar.gz: c35f3396438d8776982b2907e5d7cfe8d72f902b20e041158ba985a05f43aa41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d53b2590db9f7dad7887c594c687fd51e9470d7c6a6c2dfbf76b8b002389fb5c5410d05477a4e6956e5ff0ca5cc9bddecee970eb81e7e5af563091bea86348af
|
|
7
|
+
data.tar.gz: f3d1f4a2c7eec7e5cf6a6a1c9bb397a8cde822d9056df9c7e45fef360f244b8731c5afb5b6feaac3f97f664f058140e5c173c344186e85263df1efa667332f7f
|
|
@@ -18,12 +18,52 @@ fi
|
|
|
18
18
|
|
|
19
19
|
echo "Running pre-commit checks..." >&2
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
if ! bundle exec rbs
|
|
21
|
+
# Regenerate RBS from scratch so that files orphaned by deleted lib/ sources are dropped too
|
|
22
|
+
if ! bundle exec rake rbs:regenerate >&2; then
|
|
23
23
|
echo "Error: RBS generation failed" >&2
|
|
24
24
|
exit 2
|
|
25
25
|
fi
|
|
26
26
|
|
|
27
|
+
# True when $command stages the whole of sig/ itself, which the check below cannot see because
|
|
28
|
+
# this hook runs first. Staging only part of sig/ does not count - the rest still drops out.
|
|
29
|
+
stages_sig() {
|
|
30
|
+
local segment tokens token stages_everything pathspec_given
|
|
31
|
+
|
|
32
|
+
while IFS= read -r segment; do
|
|
33
|
+
segment=${segment//[\"\']/}
|
|
34
|
+
read -ra tokens <<<"${segment#*add}"
|
|
35
|
+
stages_everything=0
|
|
36
|
+
pathspec_given=0
|
|
37
|
+
|
|
38
|
+
for token in "${tokens[@]}"; do
|
|
39
|
+
case "$token" in
|
|
40
|
+
--) ;;
|
|
41
|
+
-*A*|--all) stages_everything=1 ;;
|
|
42
|
+
-*) ;;
|
|
43
|
+
.|./|sig|sig/|./sig|./sig/) return 0 ;;
|
|
44
|
+
*) pathspec_given=1 ;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# -A alone stages every path; given a pathspec it stages that path only
|
|
49
|
+
[ "$stages_everything" = 1 ] && [ "$pathspec_given" = 0 ] && return 0
|
|
50
|
+
done < <(grep -oE '(^|[;&|(])[[:space:]]*git[[:space:]]+add[^;&|(]*' <<<"$command")
|
|
51
|
+
|
|
52
|
+
return 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Stop rather than let the RBS just regenerated above drop out of the commit unnoticed
|
|
56
|
+
if [[ "$command" =~ git[[:space:]]+commit([[:space:]]|$) ]] && ! stages_sig; then
|
|
57
|
+
unstaged_sig=$(git status --porcelain --untracked-files=all -- sig/ | awk 'substr($0, 2, 1) != " "')
|
|
58
|
+
|
|
59
|
+
if [ -n "$unstaged_sig" ]; then
|
|
60
|
+
echo "Error: sig/ has changes that are not staged:" >&2
|
|
61
|
+
echo "$unstaged_sig" >&2
|
|
62
|
+
echo "Run 'git add sig/' and commit again." >&2
|
|
63
|
+
exit 2
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
|
|
27
67
|
if ! bundle exec rake >&2; then
|
|
28
68
|
echo "Error: rake checks failed" >&2
|
|
29
69
|
exit 2
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Prevents direct editing of auto-generated RBS type definition files.
|
|
3
|
+
# Type definitions in the project's own sig/ are managed by PostToolUse hooks.
|
|
4
|
+
# Hand-written stubs for third-party gems under sig/gems/ are exempt.
|
|
5
|
+
|
|
6
|
+
# Hook input is JSON from stdin
|
|
7
|
+
input=$(cat)
|
|
8
|
+
tool_name=$(echo "$input" | jq -r '.tool_name // ""')
|
|
9
|
+
|
|
10
|
+
# Block Write/Edit operations on sig/ files
|
|
11
|
+
if [[ "$tool_name" == "Write" || "$tool_name" == "Edit" ]]; then
|
|
12
|
+
file_path=$(echo "$input" | jq -r '.tool_input.file_path // ""')
|
|
13
|
+
|
|
14
|
+
# Judge the path relative to the project root: only the sig/ there is
|
|
15
|
+
# generated. A sig/ nested elsewhere is hand-written and stays editable.
|
|
16
|
+
project_root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
17
|
+
rel_path="${file_path#"${project_root}/"}"
|
|
18
|
+
|
|
19
|
+
if [[ "$rel_path" == sig/gems/* ]]; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [[ "$rel_path" == sig/* ]]; then
|
|
24
|
+
echo "❌ ERROR: Direct editing of generated sig/ files is not allowed." >&2
|
|
25
|
+
echo "📝 This repository's own types are generated: fix the inline RBS comments in the .rb file." >&2
|
|
26
|
+
echo "💡 Hand-written files are allowed only under sig/gems/ (stubs for third-party gems)." >&2
|
|
27
|
+
exit 2
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Block manual rbs-inline execution
|
|
32
|
+
if [[ "$tool_name" == "Bash" ]]; then
|
|
33
|
+
command=$(echo "$input" | jq -r '.tool_input.command // ""')
|
|
34
|
+
|
|
35
|
+
# Match rbs-inline only at a command position, not as an argument.
|
|
36
|
+
rbs_inline_re=$'(^|[;&|(\n])[[:space:]]*(bundle[[:space:]]+exec[[:space:]]+)?([^[:space:]]*/)?rbs-inline([[:space:]]|[;&|)]|$)'
|
|
37
|
+
|
|
38
|
+
if [[ "$command" =~ $rbs_inline_re ]]; then
|
|
39
|
+
echo "❌ ERROR: Manual rbs-inline execution is not allowed." >&2
|
|
40
|
+
echo "🔄 Type definitions are auto-generated by hooks." >&2
|
|
41
|
+
exit 2
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
exit 0
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# PreToolUse hook: review code before `git commit`.
|
|
4
|
+
#
|
|
5
|
+
# Fires before a Bash `git commit` command and blocks the first commit attempt
|
|
6
|
+
# for a given change-set, asking Claude to review the code before committing.
|
|
7
|
+
# The review uses the `self-review` skill when available, and falls back to the
|
|
8
|
+
# bundled `code-review` skill otherwise — so the hook works even in projects
|
|
9
|
+
# that don't ship the custom self-review skill.
|
|
10
|
+
#
|
|
11
|
+
# A per-change-set marker (keyed by a hash of the diff, stored under .git/)
|
|
12
|
+
# prevents an infinite block loop: once a change-set has been surfaced for
|
|
13
|
+
# review, the matching commit is allowed through. If the review leads to fixes,
|
|
14
|
+
# the diff changes, so the new state is reviewed once more before it commits.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# Read JSON input from stdin
|
|
20
|
+
input=$(cat)
|
|
21
|
+
|
|
22
|
+
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
|
|
23
|
+
command=$(echo "$input" | jq -r '.tool_input.command // empty')
|
|
24
|
+
|
|
25
|
+
# Only gate the Bash tool running a git commit.
|
|
26
|
+
if [[ "$tool_name" != "Bash" ]]; then
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
# Match `git commit`, or `git -C <path> commit`.
|
|
30
|
+
commit_re='git[[:space:]]+(-C[[:space:]]+[^[:space:]]+[[:space:]]+)?commit'
|
|
31
|
+
if [[ ! "$command" =~ $commit_re ]]; then
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# If the command targets a specific repo via `git -C <path>`, operate there too.
|
|
36
|
+
work_dir="."
|
|
37
|
+
if [[ "$command" =~ git[[:space:]]+-C[[:space:]]+([^[:space:]]+) ]]; then
|
|
38
|
+
work_dir="${BASH_REMATCH[1]}"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Not a git repo → nothing to do.
|
|
42
|
+
git_dir=$(git -C "$work_dir" rev-parse --absolute-git-dir 2>/dev/null || true)
|
|
43
|
+
if [[ -z "$git_dir" ]]; then
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Hash what will be committed. `git status --porcelain` is included so a commit
|
|
48
|
+
# that only adds untracked files isn't mistaken for empty; `git diff HEAD` adds
|
|
49
|
+
# tracked content. On the first commit there is no HEAD, so use the working state.
|
|
50
|
+
if git -C "$work_dir" rev-parse --verify HEAD >/dev/null 2>&1; then
|
|
51
|
+
changeset=$(git -C "$work_dir" status --porcelain; git -C "$work_dir" diff HEAD)
|
|
52
|
+
else
|
|
53
|
+
changeset=$(git -C "$work_dir" status --porcelain; git -C "$work_dir" diff; git -C "$work_dir" diff --cached)
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Nothing to review (e.g. a message-only `--amend`) → let it proceed.
|
|
57
|
+
if [[ -z "$changeset" ]]; then
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
hash=$(printf '%s' "$changeset" | git hash-object --stdin)
|
|
62
|
+
state_file="$git_dir/self-review-reviewed"
|
|
63
|
+
|
|
64
|
+
# Already surfaced for review → allow the commit.
|
|
65
|
+
if [[ -f "$state_file" ]] && grep -qxF "$hash" "$state_file"; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Record this change-set and block once to force a review first.
|
|
70
|
+
echo "$hash" >> "$state_file"
|
|
71
|
+
|
|
72
|
+
review_prompt='SELF-REVIEW REQUIRED before committing. Review the changes that are
|
|
73
|
+
about to be committed BEFORE running git commit again:
|
|
74
|
+
|
|
75
|
+
- If the `self-review` skill is available, invoke it (Skill tool, skill "self-review").
|
|
76
|
+
- Otherwise, run the bundled `/code-review` skill on the working diff.
|
|
77
|
+
|
|
78
|
+
If the review finds issues, fix them and review again. Once it is clean, run the same
|
|
79
|
+
git commit command again to proceed — it will be allowed through.'
|
|
80
|
+
|
|
81
|
+
jq -n --arg reason "$review_prompt" '{
|
|
82
|
+
"hookSpecificOutput": {
|
|
83
|
+
"hookEventName": "PreToolUse",
|
|
84
|
+
"permissionDecision": "deny",
|
|
85
|
+
"permissionDecisionReason": $reason
|
|
86
|
+
}
|
|
87
|
+
}'
|
|
88
|
+
|
|
89
|
+
exit 0
|
data/.claude/settings.json
CHANGED
|
@@ -6,19 +6,34 @@
|
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/
|
|
9
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/claude-code-web-session-start.sh",
|
|
10
10
|
"timeout": 300
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"PreToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Edit|Write|Bash",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-sig-files.sh",
|
|
22
|
+
"timeout": 5000
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
16
26
|
{
|
|
17
27
|
"matcher": "Bash",
|
|
18
28
|
"hooks": [
|
|
19
29
|
{
|
|
20
30
|
"type": "command",
|
|
21
31
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-commit-check.sh"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/self-review.sh",
|
|
36
|
+
"timeout": 10000
|
|
22
37
|
}
|
|
23
38
|
]
|
|
24
39
|
}
|
data/.rubocop.yml
CHANGED
|
@@ -44,27 +44,23 @@ Naming/FileName:
|
|
|
44
44
|
RSpec/ExampleLength:
|
|
45
45
|
Enabled: false
|
|
46
46
|
|
|
47
|
-
RSpec/MultipleMemoizedHelpers:
|
|
48
|
-
Max: 10
|
|
49
|
-
|
|
50
47
|
RSpec/MultipleExpectations:
|
|
51
48
|
Enabled: false
|
|
52
49
|
|
|
50
|
+
RSpec/MultipleMemoizedHelpers:
|
|
51
|
+
Max: 10
|
|
52
|
+
|
|
53
53
|
RSpec/NamedSubject:
|
|
54
54
|
Enabled: false
|
|
55
55
|
|
|
56
56
|
RSpec/NestedGroups:
|
|
57
57
|
Max: 5
|
|
58
58
|
|
|
59
|
-
#
|
|
60
|
-
Style
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
EnforcedStyle: doc_style_and_return_annotation
|
|
65
|
-
|
|
66
|
-
Style/RbsInline/RequireRbsInlineComment:
|
|
67
|
-
EnforcedStyle: never
|
|
59
|
+
# Mixin specs mirror `lib/.../rbs_inline/mixin/`, but the modules keep the
|
|
60
|
+
# `RuboCop::Cop::Style::RbsInline` namespace, so the path never matches the constant.
|
|
61
|
+
RSpec/SpecFilePathFormat:
|
|
62
|
+
Exclude:
|
|
63
|
+
- "spec/rubocop/cop/style/rbs_inline/mixin/**/*_spec.rb"
|
|
68
64
|
|
|
69
65
|
# Style
|
|
70
66
|
Style/AccessorGrouping:
|
|
@@ -79,6 +75,15 @@ Style/Documentation:
|
|
|
79
75
|
Style/HashSyntax:
|
|
80
76
|
EnforcedShorthandSyntax: always
|
|
81
77
|
|
|
78
|
+
Style/RbsInline:
|
|
79
|
+
Mode: opt_out
|
|
80
|
+
|
|
81
|
+
Style/RbsInline/MissingTypeAnnotation:
|
|
82
|
+
EnforcedStyle: doc_style_and_return_annotation
|
|
83
|
+
|
|
84
|
+
Style/RbsInline/RedundantTypeAnnotation:
|
|
85
|
+
EnforcedStyle: doc_style_and_return_annotation
|
|
86
|
+
|
|
82
87
|
Style/StringLiterals:
|
|
83
88
|
EnforcedStyle: double_quotes
|
|
84
89
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.0 (2026-08-05)
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- **Style/RbsInline**: `Mode` now defaults to `opt_in`, and any value other than `opt_in` / `opt_out` fails the run.
|
|
8
|
+
- **Style/RbsInline/RequireRbsInlineComment**: Removed the deprecated `EnforcedStyle`. Use `Style/RbsInline: Mode` instead: `always` → `opt_in`, `never` → `opt_out`.
|
|
9
|
+
|
|
10
|
+
### Enhancements
|
|
11
|
+
|
|
12
|
+
- **Style/RbsInline/DataDefineWithBlock**, **Style/RbsInline/StructNewWithBlock**: Clarified the offense message: it now says to keep the `Data.define` / `Struct.new` call and move the block's methods into a class that reopens it, and names that class when the definition is assigned to a constant.
|
|
13
|
+
|
|
14
|
+
## 1.8.0 (2026-07-26)
|
|
15
|
+
|
|
16
|
+
### Enhancements
|
|
17
|
+
|
|
18
|
+
- **Style/RbsInline**: Added a shared `Mode` setting (`opt_in` / `opt_out`) at the department level to filter which files each cop checks based on the `# rbs_inline: enabled` / `disabled` magic comment.
|
|
19
|
+
- **Style/RbsInline/RequireRbsInlineComment**: Added the `AllowMissingComment` option, which lets projects use rbs-inline on only some of their files while keeping the opt-in filter for the rest.
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
- **Style/RbsInline/DataClassCommentAlignment**, **Style/RbsInline/StructClassCommentAlignment**: Fixed the expected annotation column being derived from arguments that cannot carry an annotation (a splat, or for `Struct.new` a leading struct name or `keyword_init:`), which reported false positives on already aligned code and made autocorrect over-pad the annotations.
|
|
24
|
+
- **Style/RbsInline/MissingDataClassAnnotation**, **Style/RbsInline/MissingStructClassAnnotation**: Fixed autocorrect padding the inserted annotations to the width of an argument that cannot carry one (a splat, or for `Struct.new` a leading struct name or `keyword_init:`).
|
|
25
|
+
- **Style/RbsInline/UnmatchedAnnotations**: Fixed a crash on method definitions taking a destructuring argument (ex. `def foo((a, b))`). The crash also aborted the inspection of the method definition, causing false positives on its valid annotations.
|
|
26
|
+
- **Style/RbsInline/RequireRbsInlineComment**: Fixed a crash (`IndexError`) on a file whose leading comment block ends without a trailing newline. The offense is now reported and the magic comment is inserted on its own line.
|
|
27
|
+
- **Style/RbsInline/RedundantTypeAnnotation**: Fixed `--auto-gen-config` generating an `EnforcedStyle` that does not resolve the offenses (including the unsupported `return_type_annotation`, which made RuboCop fail to start on the generated `.rubocop_todo.yml`). No style can make redundant annotations offense-free, so the offending files are now excluded instead.
|
|
28
|
+
|
|
29
|
+
### Deprecations
|
|
30
|
+
|
|
31
|
+
- **Style/RbsInline/RequireRbsInlineComment**: `EnforcedStyle` is deprecated in favor of the department-level `Mode`. Migrate `always` → `Style/RbsInline: Mode: opt_in`, `never` → `Style/RbsInline: Mode: opt_out`. Will be removed in the next major version.
|
|
32
|
+
|
|
3
33
|
## 1.7.0 (2026-07-20)
|
|
4
34
|
|
|
5
35
|
### New Features
|
data/CLAUDE.md
CHANGED
|
@@ -23,7 +23,7 @@ bundle exec rake rubocop:autocorrect_all # all autocorrect
|
|
|
23
23
|
bundle exec rake steep # runs steep check
|
|
24
24
|
bundle exec rake rbs:validate # validates RBS signatures
|
|
25
25
|
|
|
26
|
-
# Default rake task (
|
|
26
|
+
# Default rake task (rubocop, specs, steep, rbs:validate)
|
|
27
27
|
bundle exec rake
|
|
28
28
|
|
|
29
29
|
# Generate a new cop
|
|
@@ -34,32 +34,29 @@ bundle exec rake 'new_cop[Style/RbsInline/CopName]'
|
|
|
34
34
|
|
|
35
35
|
### Cop Structure
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Cops live in `lib/rubocop/cop/style/rbs_inline/`, one per file; the shared modules they mix in live in `lib/rubocop/cop/style/rbs_inline/mixin/`. Both are under the `RuboCop::Cop::Style::RbsInline` namespace — only the directory separates them, as in `rubocop`'s `cop/mixin/`. `config/default.yml` lists every cop and its options; read it instead of a list kept here.
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
- **InvalidTypes** - Validates RBS type syntax in annotations
|
|
41
|
-
- **KeywordSeparator** - Ensures keywords like `module-self` aren't followed by `:`
|
|
42
|
-
- **ParametersSeparator** - Ensures parameter annotations use `:` separator
|
|
43
|
-
- **RedundantArgumentType** - Detects redundant argument type specs (configurable style)
|
|
44
|
-
- **RedundantReturnType** - Detects redundant return type specs (3 style options)
|
|
45
|
-
- **UnmatchedAnnotations** - Ensures annotation parameters match actual method parameters
|
|
39
|
+
Conventions:
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
`comment_parser.rb` is a module mixed into cops that need to parse RBS::Inline annotations. It uses `RBS::Inline::AnnotationParser` and Prism to parse comments, and provides helpers for finding leading annotations, inline comments, and return annotations relative to method definitions.
|
|
41
|
+
- Every cop `prepend`s `FileFilter` except `RequireRbsInlineComment`, which reads `Mode` through `ModeConfig` instead — it has to run on the files the filter skips.
|
|
42
|
+
- `Data.define` and `Struct.new` cops come in pairs sharing a base module. Behavior belongs in the module; only the node matcher and `MSG` in the cop.
|
|
50
43
|
|
|
51
44
|
### Plugin System
|
|
52
45
|
|
|
53
|
-
The gem integrates with RuboCop via LintRoller (`lib/rubocop/rbs_inline/plugin.rb`)
|
|
46
|
+
The gem integrates with RuboCop via LintRoller (`lib/rubocop/rbs_inline/plugin.rb`), which points RuboCop at `config/default.yml`.
|
|
54
47
|
|
|
55
48
|
### Type Signatures
|
|
56
49
|
|
|
57
|
-
|
|
50
|
+
`sig/rubocop/` is generated from `lib/` by the hooks in `.claude/hooks/`. Never write anything there. `sig/gems/` is hand-written stubs for third-party gems and is *not* regenerated — do not delete it when refreshing signatures.
|
|
58
51
|
|
|
59
52
|
### Testing Pattern
|
|
60
53
|
|
|
61
54
|
Tests use RuboCop's RSpec support helpers (`expect_offense` / `expect_no_offenses`). Test files mirror the cop file structure under `spec/rubocop/cop/style/rbs_inline/`.
|
|
62
55
|
|
|
56
|
+
`:config` specs build a bare `RuboCop::Config`, so `config/default.yml` and RuboCop's department-to-cop defaults are not merged in. A spec that sets a value at the department level proves nothing about how it resolves in a real run.
|
|
57
|
+
|
|
58
|
+
The department-level `Mode` is not merged in either, and the cops have no default for it, so `spec_helper.rb` declares `Mode: opt_out` for every `:config` spec, and `rbs_inline_config` for the specs written against the default configuration. A spec that builds its own config has to declare it itself.
|
|
59
|
+
|
|
63
60
|
## Code Style Notes
|
|
64
61
|
|
|
65
62
|
- `Layout/LeadingCommentSpace` and `Style/CommentedKeyword` are disabled because RBS::Inline `#:` comments violate these rules by design.
|
data/README.md
CHANGED
|
@@ -27,6 +27,22 @@ plugins:
|
|
|
27
27
|
|
|
28
28
|
rubocop-rbs_inline provides the following cops to validate [RBS::Inline](https://github.com/soutaro/rbs-inline) annotations:
|
|
29
29
|
|
|
30
|
+
### Style/RbsInline (shared configuration)
|
|
31
|
+
|
|
32
|
+
The `Style/RbsInline` department has a shared `Mode` setting that gates whether each `Style/RbsInline/*` cop reports offenses on a given file:
|
|
33
|
+
|
|
34
|
+
- `Mode: opt_in` (default) — cops only check files that carry `# rbs_inline: enabled`. Files with `# rbs_inline: disabled` or no magic comment are skipped. Matches RBS::Inline's opt-in mode.
|
|
35
|
+
- `Mode: opt_out` — cops check every file except those with `# rbs_inline: disabled`. Matches RBS::Inline's opt-out mode.
|
|
36
|
+
|
|
37
|
+
Declare it once at the department level in your `.rubocop.yml`:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
Style/RbsInline:
|
|
41
|
+
Mode: opt_out
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Any other value (e.g. `Mode: opt-in`) fails the run with a configuration error.
|
|
45
|
+
|
|
30
46
|
### Style/RbsInline/DataClassCommentAlignment
|
|
31
47
|
|
|
32
48
|
Checks that `#:` inline type annotations in a multiline `Data.define` call are aligned to the same column. The expected column is determined by the longest attribute name (plus its trailing comma). Folded `Data.define` calls (where multiple attributes share a line) are excluded.
|
|
@@ -238,7 +254,11 @@ Enforces that method definitions and `attr_*` declarations have RBS inline type
|
|
|
238
254
|
- `all`: Checks all methods regardless of visibility
|
|
239
255
|
- `public`: Only checks public methods and `attr_*` declarations
|
|
240
256
|
|
|
241
|
-
|
|
257
|
+
Always excluded, regardless of `EnforcedStyle`:
|
|
258
|
+
|
|
259
|
+
- Methods annotated with `# @rbs skip` or `# @rbs override`.
|
|
260
|
+
- Parameters whose name starts with `_`.
|
|
261
|
+
- The `...` forward-all parameter.
|
|
242
262
|
|
|
243
263
|
**Examples (EnforcedStyle: doc_style):**
|
|
244
264
|
```ruby
|
|
@@ -466,13 +486,24 @@ end
|
|
|
466
486
|
|
|
467
487
|
### Style/RbsInline/RequireRbsInlineComment
|
|
468
488
|
|
|
469
|
-
Enforces presence or absence of `# rbs_inline:` magic comment
|
|
489
|
+
Enforces the presence or absence of the `# rbs_inline:` magic comment, based on the shared [`Mode`](#stylerbsinline-shared-configuration) setting:
|
|
470
490
|
|
|
471
|
-
|
|
472
|
-
- `
|
|
473
|
-
|
|
491
|
+
- Under `Mode: opt_in`, files must carry `# rbs_inline: enabled` (or `# rbs_inline: disabled`).
|
|
492
|
+
- Under `Mode: opt_out`, files must not carry `# rbs_inline: enabled`.
|
|
493
|
+
|
|
494
|
+
**Configuration:**
|
|
495
|
+
|
|
496
|
+
- `AllowMissingComment` (default: `false`) — under `Mode: opt_in`, set to `true` to skip enforcement of the magic comment on individual files. Useful when only part of the project uses rbs-inline: the shared `Mode: opt_in` filter still applies to every other cop (they only report on files that opt in), but this cop stops requiring the magic comment on files that intentionally do not use rbs-inline.
|
|
497
|
+
|
|
498
|
+
```yaml
|
|
499
|
+
Style/RbsInline:
|
|
500
|
+
Mode: opt_in
|
|
474
501
|
|
|
475
|
-
|
|
502
|
+
Style/RbsInline/RequireRbsInlineComment:
|
|
503
|
+
AllowMissingComment: true
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**Examples (Mode: opt_in):**
|
|
476
507
|
```ruby
|
|
477
508
|
# bad
|
|
478
509
|
class Foo
|
|
@@ -484,6 +515,18 @@ class Foo
|
|
|
484
515
|
end
|
|
485
516
|
```
|
|
486
517
|
|
|
518
|
+
**Examples (Mode: opt_in, AllowMissingComment: true):**
|
|
519
|
+
```ruby
|
|
520
|
+
# good - the magic comment is not enforced per file
|
|
521
|
+
class Foo
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# good
|
|
525
|
+
# rbs_inline: enabled
|
|
526
|
+
class Foo
|
|
527
|
+
end
|
|
528
|
+
```
|
|
529
|
+
|
|
487
530
|
### Style/RbsInline/StructClassCommentAlignment
|
|
488
531
|
|
|
489
532
|
Checks that `#:` inline type annotations in a multiline `Struct.new` call are aligned to the same column. The expected column is determined by the longest attribute name (plus its trailing comma). Folded `Struct.new` calls (where multiple attributes share a line) are excluded.
|
|
@@ -613,6 +656,8 @@ Style/RbsInline/MissingTypeAnnotation:
|
|
|
613
656
|
Visibility: public
|
|
614
657
|
```
|
|
615
658
|
|
|
659
|
+
To check every file rather than only those that carry `# rbs_inline: enabled`, see [Style/RbsInline (shared configuration)](#stylerbsinline-shared-configuration).
|
|
660
|
+
|
|
616
661
|
See [config/default.yml](config/default.yml) for all available configuration options.
|
|
617
662
|
|
|
618
663
|
## Development
|
data/Rakefile
CHANGED
|
@@ -35,14 +35,23 @@ task :new_cop, [:cop] do |_task, args|
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
namespace :rbs do
|
|
38
|
-
|
|
38
|
+
# sig/gems holds hand-written types for external gems, so it is never regenerated
|
|
39
|
+
kept = ->(path) { path == "sig/gems" || path.start_with?("sig/gems/") }
|
|
40
|
+
|
|
41
|
+
desc "Install RBS collection"
|
|
39
42
|
task :install do
|
|
40
43
|
sh "bundle", "exec", "rbs", "collection", "install", "--frozen"
|
|
41
44
|
end
|
|
42
45
|
|
|
43
|
-
desc "
|
|
44
|
-
task :
|
|
46
|
+
desc "Regenerate all RBS files under sig/ from scratch (run manually after editing lib/)"
|
|
47
|
+
task :regenerate do
|
|
48
|
+
Dir.glob("sig/**/*.rbs").reject(&kept).each { rm _1 }
|
|
49
|
+
|
|
45
50
|
sh "rbs-inline", "--opt-out", "--output=sig", "lib"
|
|
51
|
+
|
|
52
|
+
# Drop the directories the deletion above left empty, deepest first
|
|
53
|
+
dirs = Dir.glob("sig/**/*").select { File.directory?(_1) }
|
|
54
|
+
dirs.reject(&kept).sort.reverse_each { rmdir _1 if Dir.empty?(_1) }
|
|
46
55
|
end
|
|
47
56
|
|
|
48
57
|
desc "Validate RBS files"
|
data/config/default.yml
CHANGED
|
@@ -5,6 +5,7 @@ Style/RbsInline:
|
|
|
5
5
|
Exclude:
|
|
6
6
|
- 'spec/**/*'
|
|
7
7
|
- 'test/**/*'
|
|
8
|
+
Mode: opt_in
|
|
8
9
|
|
|
9
10
|
Style/RbsInline/DataClassCommentAlignment:
|
|
10
11
|
Description: "Checks that `#:` inline type annotations in `Data.define` blocks are aligned."
|
|
@@ -94,10 +95,7 @@ Style/RbsInline/RequireRbsInlineComment:
|
|
|
94
95
|
Description: "Checks the presence or absence of `# rbs_inline: enabled` magic comment."
|
|
95
96
|
Enabled: true
|
|
96
97
|
VersionAdded: "1.5.0"
|
|
97
|
-
|
|
98
|
-
SupportedStyles:
|
|
99
|
-
- always
|
|
100
|
-
- never
|
|
98
|
+
AllowMissingComment: false
|
|
101
99
|
|
|
102
100
|
Style/RbsInline/StructClassCommentAlignment:
|
|
103
101
|
Description: "Checks that `#:` inline type annotations in `Struct.new` blocks are aligned."
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
require_relative "style/rbs_inline/
|
|
7
|
-
require_relative "style/rbs_inline/
|
|
8
|
-
require_relative "style/rbs_inline/
|
|
3
|
+
# Mixins first, in dependency order. Cops follow, alphabetically: `rake new_cop` sorts a
|
|
4
|
+
# generated require only against the others in the same directory, so the two blocks have
|
|
5
|
+
# to stay apart for it to land in the right place.
|
|
6
|
+
require_relative "style/rbs_inline/mixin/annotation_keywords"
|
|
7
|
+
require_relative "style/rbs_inline/mixin/ast_utils"
|
|
8
|
+
require_relative "style/rbs_inline/mixin/source_code_helper"
|
|
9
|
+
require_relative "style/rbs_inline/mixin/comment_parser"
|
|
10
|
+
require_relative "style/rbs_inline/mixin/data_struct_helper"
|
|
11
|
+
require_relative "style/rbs_inline/mixin/missing_class_annotation"
|
|
12
|
+
require_relative "style/rbs_inline/mixin/class_comment_alignment"
|
|
13
|
+
require_relative "style/rbs_inline/mixin/data_class_matcher"
|
|
14
|
+
require_relative "style/rbs_inline/mixin/mode_config"
|
|
15
|
+
require_relative "style/rbs_inline/mixin/file_filter"
|
|
16
|
+
require_relative "style/rbs_inline/mixin/struct_class_matcher"
|
|
17
|
+
|
|
9
18
|
require_relative "style/rbs_inline/data_class_comment_alignment"
|
|
10
19
|
require_relative "style/rbs_inline/data_define_with_block"
|
|
11
20
|
require_relative "style/rbs_inline/embedded_rbs_spacing"
|
|
@@ -14,6 +23,7 @@ require_relative "style/rbs_inline/invalid_types"
|
|
|
14
23
|
require_relative "style/rbs_inline/keyword_separator"
|
|
15
24
|
require_relative "style/rbs_inline/method_comment_spacing"
|
|
16
25
|
require_relative "style/rbs_inline/missing_data_class_annotation"
|
|
26
|
+
require_relative "style/rbs_inline/missing_struct_class_annotation"
|
|
17
27
|
require_relative "style/rbs_inline/missing_type_annotation"
|
|
18
28
|
require_relative "style/rbs_inline/parameters_separator"
|
|
19
29
|
require_relative "style/rbs_inline/redundant_annotation_with_skip"
|
|
@@ -22,7 +32,6 @@ require_relative "style/rbs_inline/redundant_type_annotation"
|
|
|
22
32
|
require_relative "style/rbs_inline/require_rbs_inline_comment"
|
|
23
33
|
require_relative "style/rbs_inline/struct_class_comment_alignment"
|
|
24
34
|
require_relative "style/rbs_inline/struct_new_with_block"
|
|
25
|
-
require_relative "style/rbs_inline/missing_struct_class_annotation"
|
|
26
35
|
require_relative "style/rbs_inline/unmatched_annotations"
|
|
27
36
|
require_relative "style/rbs_inline/untyped_instance_variable"
|
|
28
37
|
require_relative "style/rbs_inline/variable_comment_spacing"
|
|
@@ -26,7 +26,9 @@ module RuboCop
|
|
|
26
26
|
# )
|
|
27
27
|
#
|
|
28
28
|
class DataClassCommentAlignment < Base
|
|
29
|
+
prepend FileFilter
|
|
29
30
|
include ClassCommentAlignment
|
|
31
|
+
include DataClassMatcher
|
|
30
32
|
extend AutoCorrector
|
|
31
33
|
|
|
32
34
|
MSG = "Misaligned inline type annotation for Data attribute."
|
|
@@ -37,15 +39,6 @@ module RuboCop
|
|
|
37
39
|
|
|
38
40
|
check_comment_alignment(node)
|
|
39
41
|
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
44
|
-
def struct_like_class?(node) #: bool
|
|
45
|
-
return false unless node.method_name == :define
|
|
46
|
-
|
|
47
|
-
(r = node.receiver).is_a?(RuboCop::AST::ConstNode) && r.short_name == :Data
|
|
48
|
-
end
|
|
49
42
|
end
|
|
50
43
|
end
|
|
51
44
|
end
|
|
@@ -28,9 +28,13 @@ module RuboCop
|
|
|
28
28
|
# end
|
|
29
29
|
#
|
|
30
30
|
class DataDefineWithBlock < Base
|
|
31
|
+
prepend FileFilter
|
|
32
|
+
include DataClassMatcher
|
|
33
|
+
include ASTUtils
|
|
34
|
+
|
|
31
35
|
MSG = "Do not use `Data.define` with a block. RBS::Inline does not parse block contents, " \
|
|
32
|
-
"so methods defined in the block will not be recognized. " \
|
|
33
|
-
"
|
|
36
|
+
"so methods defined in the block will not be recognized. Keep the `Data.define` call " \
|
|
37
|
+
"and move the methods into %<destination>s."
|
|
34
38
|
|
|
35
39
|
# @rbs node: RuboCop::AST::SendNode
|
|
36
40
|
def on_send(node) #: void
|
|
@@ -39,16 +43,18 @@ module RuboCop
|
|
|
39
43
|
block_node = node.parent
|
|
40
44
|
return unless block_node&.block_type?
|
|
41
45
|
|
|
42
|
-
add_offense(node)
|
|
46
|
+
add_offense(node, message: format(MSG, destination: destination(block_node)))
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
private
|
|
46
50
|
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
# The constant the definition is assigned to is the class to reopen.
|
|
52
|
+
# @rbs node: RuboCop::AST::Node
|
|
53
|
+
def destination(node) #: String
|
|
54
|
+
class_name = assigned_constant_name(node)
|
|
55
|
+
return "a `class` that reopens it" unless class_name
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
"`class #{class_name} ... end` written after it"
|
|
52
58
|
end
|
|
53
59
|
end
|
|
54
60
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "source_code_helper"
|
|
4
|
-
require_relative "comment_parser"
|
|
3
|
+
require_relative "mixin/source_code_helper"
|
|
4
|
+
require_relative "mixin/comment_parser"
|
|
5
5
|
|
|
6
6
|
module RuboCop
|
|
7
7
|
module Cop
|
|
@@ -26,6 +26,7 @@ module RuboCop
|
|
|
26
26
|
# end
|
|
27
27
|
#
|
|
28
28
|
class EmbeddedRbsSpacing < Base
|
|
29
|
+
prepend FileFilter
|
|
29
30
|
extend AutoCorrector
|
|
30
31
|
include SourceCodeHelper
|
|
31
32
|
include CommentParser
|