rubocop-rbs_inline 1.6.1 → 1.8.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/self-review.sh +89 -0
- data/.claude/settings.json +8 -0
- data/.rubocop.yml +9 -3
- data/CHANGELOG.md +27 -0
- data/CLAUDE.md +9 -14
- data/README.md +124 -6
- data/config/default.yml +18 -4
- data/lib/rubocop/cop/rbs_inline_cops.rb +17 -3
- data/lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb +5 -93
- data/lib/rubocop/cop/style/rbs_inline/data_define_with_block.rb +4 -11
- 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 +5 -142
- data/lib/rubocop/cop/style/rbs_inline/missing_struct_class_annotation.rb +42 -0
- data/lib/rubocop/cop/style/rbs_inline/missing_type_annotation.rb +4 -17
- data/lib/rubocop/cop/style/rbs_inline/mixin/annotation_keywords.rb +16 -0
- data/lib/rubocop/cop/style/rbs_inline/{ast_utils.rb → mixin/ast_utils.rb} +16 -0
- data/lib/rubocop/cop/style/rbs_inline/mixin/class_comment_alignment.rb +66 -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/mixin/data_struct_helper.rb +106 -0
- data/lib/rubocop/cop/style/rbs_inline/mixin/file_filter.rb +110 -0
- data/lib/rubocop/cop/style/rbs_inline/mixin/missing_class_annotation.rb +85 -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 +74 -23
- data/lib/rubocop/cop/style/rbs_inline/struct_class_comment_alignment.rb +44 -0
- data/lib/rubocop/cop/style/rbs_inline/struct_new_with_block.rb +48 -0
- 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 +5 -1
- data/sig/gems/rubocop/rubocop.rbs +8 -0
- data/sig/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rbs +3 -32
- data/sig/rubocop/cop/style/rbs_inline/data_define_with_block.rbs +4 -5
- 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 +3 -48
- data/sig/rubocop/cop/style/rbs_inline/missing_struct_class_annotation.rbs +40 -0
- 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} +9 -1
- data/sig/rubocop/cop/style/rbs_inline/mixin/class_comment_alignment.rbs +34 -0
- 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/mixin/data_struct_helper.rbs +60 -0
- data/sig/rubocop/cop/style/rbs_inline/mixin/file_filter.rbs +63 -0
- data/sig/rubocop/cop/style/rbs_inline/mixin/missing_class_annotation.rbs +41 -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 +30 -5
- data/sig/rubocop/cop/style/rbs_inline/struct_class_comment_alignment.rbs +42 -0
- data/sig/rubocop/cop/style/rbs_inline/struct_new_with_block.rbs +39 -0
- 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 +30 -11
- 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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e4bebfc6eae9ec29990f1dafdf2fc773737b2cf001c450702b138b1d9ab81dec
|
|
4
|
+
data.tar.gz: f9ebb04f3712f5f02cd6b7388ea74b056fdce19027be61ed6b651cf1b5915ca8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 46b9e51e95573630afbe686500cdf154a2cdf2d76bd19f441552fe76267682fb586118f96e7b0c5316352b49fc6e5ddc3b080fd1dc8b6711d31e382694ad767d
|
|
7
|
+
data.tar.gz: 9bd7d22ee40b4d009b1c2f2f86d855a37ee32349c12afffdebbda6cf5edf54bf9c474f67bdfb170987ea512c0934deb48d709f1afbc49acef88f968ebff4297a
|
|
@@ -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
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": ["RemoteTrigger"]
|
|
4
|
+
},
|
|
2
5
|
"hooks": {
|
|
3
6
|
"SessionStart": [
|
|
4
7
|
{
|
|
@@ -19,6 +22,11 @@
|
|
|
19
22
|
{
|
|
20
23
|
"type": "command",
|
|
21
24
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-commit-check.sh"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"type": "command",
|
|
28
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/self-review.sh",
|
|
29
|
+
"timeout": 10000
|
|
22
30
|
}
|
|
23
31
|
]
|
|
24
32
|
}
|
data/.rubocop.yml
CHANGED
|
@@ -56,16 +56,22 @@ RSpec/NamedSubject:
|
|
|
56
56
|
RSpec/NestedGroups:
|
|
57
57
|
Max: 5
|
|
58
58
|
|
|
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"
|
|
64
|
+
|
|
59
65
|
# RbsInline
|
|
66
|
+
Style/RbsInline:
|
|
67
|
+
Mode: opt_out
|
|
68
|
+
|
|
60
69
|
Style/RbsInline/MissingTypeAnnotation:
|
|
61
70
|
EnforcedStyle: doc_style_and_return_annotation
|
|
62
71
|
|
|
63
72
|
Style/RbsInline/RedundantTypeAnnotation:
|
|
64
73
|
EnforcedStyle: doc_style_and_return_annotation
|
|
65
74
|
|
|
66
|
-
Style/RbsInline/RequireRbsInlineComment:
|
|
67
|
-
EnforcedStyle: never
|
|
68
|
-
|
|
69
75
|
# Style
|
|
70
76
|
Style/AccessorGrouping:
|
|
71
77
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.0 (2026-07-26)
|
|
4
|
+
|
|
5
|
+
### Enhancements
|
|
6
|
+
|
|
7
|
+
- **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.
|
|
8
|
+
- **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.
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
- **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.
|
|
13
|
+
- **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:`).
|
|
14
|
+
- **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.
|
|
15
|
+
- **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.
|
|
16
|
+
- **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.
|
|
17
|
+
|
|
18
|
+
### Deprecations
|
|
19
|
+
|
|
20
|
+
- **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.
|
|
21
|
+
|
|
22
|
+
## 1.7.0 (2026-07-20)
|
|
23
|
+
|
|
24
|
+
### New Features
|
|
25
|
+
|
|
26
|
+
- **Style/RbsInline/MissingStructClassAnnotation**: New cop that checks each attribute passed to `Struct.new` has a trailing `#:` inline type annotation. Supports autocorrect.
|
|
27
|
+
- **Style/RbsInline/StructClassCommentAlignment**: New cop that checks `#:` inline type annotations in a multiline `Struct.new` call are aligned. Supports autocorrect.
|
|
28
|
+
- **Style/RbsInline/StructNewWithBlock**: New cop that checks for `Struct.new` calls with a block, which RBS::Inline does not parse.
|
|
29
|
+
|
|
3
30
|
## 1.6.1 (2026-07-10)
|
|
4
31
|
|
|
5
32
|
### Bug Fixes
|
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,27 @@ 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`.
|
|
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
|
+
|
|
63
58
|
## Code Style Notes
|
|
64
59
|
|
|
65
60
|
- `Layout/LeadingCommentSpace` and `Style/CommentedKeyword` are disabled because RBS::Inline `#:` comments violate these rules by design.
|
data/README.md
CHANGED
|
@@ -27,6 +27,23 @@ 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` — 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
|
+
- unset — cops check every file (legacy behavior; the default will change to `opt_in` in the next major release).
|
|
37
|
+
|
|
38
|
+
Declare it once at the department level in your `.rubocop.yml`:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
Style/RbsInline:
|
|
42
|
+
Mode: opt_in
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Unknown `Mode` values (e.g. `Mode: opt-in`) emit a warning and disable filtering for the run.
|
|
46
|
+
|
|
30
47
|
### Style/RbsInline/DataClassCommentAlignment
|
|
31
48
|
|
|
32
49
|
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.
|
|
@@ -198,6 +215,32 @@ MethodEntry = Data.define(
|
|
|
198
215
|
)
|
|
199
216
|
```
|
|
200
217
|
|
|
218
|
+
### Style/RbsInline/MissingStructClassAnnotation
|
|
219
|
+
|
|
220
|
+
Checks that each attribute passed to `Struct.new` has a trailing `#:` inline type annotation on the same line.
|
|
221
|
+
|
|
222
|
+
For folded `Struct.new` calls (where multiple attributes share a line), the cop will suggest rewriting as a multiline call so each attribute can be annotated individually. A leading string argument (the struct name) and the `keyword_init:` keyword argument are not attributes and are ignored.
|
|
223
|
+
|
|
224
|
+
Supports autocorrect.
|
|
225
|
+
|
|
226
|
+
**Examples:**
|
|
227
|
+
```ruby
|
|
228
|
+
# bad
|
|
229
|
+
Point = Struct.new(:x, :y)
|
|
230
|
+
|
|
231
|
+
# bad - missing annotation for :y
|
|
232
|
+
Point = Struct.new(
|
|
233
|
+
:x, #: Integer
|
|
234
|
+
:y
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# good
|
|
238
|
+
Point = Struct.new(
|
|
239
|
+
:x, #: Integer
|
|
240
|
+
:y #: Integer
|
|
241
|
+
)
|
|
242
|
+
```
|
|
243
|
+
|
|
201
244
|
### Style/RbsInline/MissingTypeAnnotation
|
|
202
245
|
|
|
203
246
|
Enforces that method definitions and `attr_*` declarations have RBS inline type annotations.
|
|
@@ -212,7 +255,11 @@ Enforces that method definitions and `attr_*` declarations have RBS inline type
|
|
|
212
255
|
- `all`: Checks all methods regardless of visibility
|
|
213
256
|
- `public`: Only checks public methods and `attr_*` declarations
|
|
214
257
|
|
|
215
|
-
|
|
258
|
+
Always excluded, regardless of `EnforcedStyle`:
|
|
259
|
+
|
|
260
|
+
- Methods annotated with `# @rbs skip` or `# @rbs override`.
|
|
261
|
+
- Parameters whose name starts with `_`.
|
|
262
|
+
- The `...` forward-all parameter.
|
|
216
263
|
|
|
217
264
|
**Examples (EnforcedStyle: doc_style):**
|
|
218
265
|
```ruby
|
|
@@ -440,13 +487,26 @@ end
|
|
|
440
487
|
|
|
441
488
|
### Style/RbsInline/RequireRbsInlineComment
|
|
442
489
|
|
|
443
|
-
Enforces presence or absence of `# rbs_inline:` magic comment
|
|
490
|
+
Enforces the presence or absence of the `# rbs_inline:` magic comment, based on the shared [`Mode`](#stylerbsinline-shared-configuration) setting:
|
|
444
491
|
|
|
445
|
-
|
|
446
|
-
- `
|
|
447
|
-
|
|
492
|
+
- Under `Mode: opt_in`, files must carry `# rbs_inline: enabled` (or `# rbs_inline: disabled`).
|
|
493
|
+
- Under `Mode: opt_out`, files must not carry `# rbs_inline: enabled`.
|
|
494
|
+
|
|
495
|
+
**Configuration:**
|
|
496
|
+
|
|
497
|
+
- `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.
|
|
498
|
+
|
|
499
|
+
```yaml
|
|
500
|
+
Style/RbsInline:
|
|
501
|
+
Mode: opt_in
|
|
448
502
|
|
|
449
|
-
|
|
503
|
+
Style/RbsInline/RequireRbsInlineComment:
|
|
504
|
+
AllowMissingComment: true
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
The legacy `EnforcedStyle` parameter (`always` / `never`) is deprecated in favor of `Mode`; it will be removed in the next major release.
|
|
508
|
+
|
|
509
|
+
**Examples (Mode: opt_in):**
|
|
450
510
|
```ruby
|
|
451
511
|
# bad
|
|
452
512
|
class Foo
|
|
@@ -458,6 +518,62 @@ class Foo
|
|
|
458
518
|
end
|
|
459
519
|
```
|
|
460
520
|
|
|
521
|
+
**Examples (Mode: opt_in, AllowMissingComment: true):**
|
|
522
|
+
```ruby
|
|
523
|
+
# good - the magic comment is not enforced per file
|
|
524
|
+
class Foo
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# good
|
|
528
|
+
# rbs_inline: enabled
|
|
529
|
+
class Foo
|
|
530
|
+
end
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Style/RbsInline/StructClassCommentAlignment
|
|
534
|
+
|
|
535
|
+
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.
|
|
536
|
+
|
|
537
|
+
Supports autocorrect.
|
|
538
|
+
|
|
539
|
+
**Examples:**
|
|
540
|
+
```ruby
|
|
541
|
+
# bad
|
|
542
|
+
Point = Struct.new(
|
|
543
|
+
:x, #: Integer
|
|
544
|
+
:long_attr #: Integer
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
# good
|
|
548
|
+
Point = Struct.new(
|
|
549
|
+
:x, #: Integer
|
|
550
|
+
:long_attr #: Integer
|
|
551
|
+
)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Style/RbsInline/StructNewWithBlock
|
|
555
|
+
|
|
556
|
+
Checks for `Struct.new` calls with a block. RBS::Inline does not parse block contents, so any methods defined inside the block will not be recognized for type checking. Instead, call `Struct.new` without a block and reopen the class separately to add methods.
|
|
557
|
+
|
|
558
|
+
**Examples:**
|
|
559
|
+
```ruby
|
|
560
|
+
# bad
|
|
561
|
+
User = Struct.new(:name, :role) do
|
|
562
|
+
def admin?
|
|
563
|
+
role == :admin
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# good
|
|
568
|
+
User = Struct.new(:name, :role)
|
|
569
|
+
|
|
570
|
+
class User
|
|
571
|
+
def admin? #: bool
|
|
572
|
+
role == :admin
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
```
|
|
576
|
+
|
|
461
577
|
### Style/RbsInline/UnmatchedAnnotations
|
|
462
578
|
|
|
463
579
|
Verifies that annotation parameters match the actual method parameters.
|
|
@@ -543,6 +659,8 @@ Style/RbsInline/MissingTypeAnnotation:
|
|
|
543
659
|
Visibility: public
|
|
544
660
|
```
|
|
545
661
|
|
|
662
|
+
To restrict every cop to files that carry `# rbs_inline: enabled`, see [Style/RbsInline (shared configuration)](#stylerbsinline-shared-configuration).
|
|
663
|
+
|
|
546
664
|
See [config/default.yml](config/default.yml) for all available configuration options.
|
|
547
665
|
|
|
548
666
|
## Development
|
data/config/default.yml
CHANGED
|
@@ -5,6 +5,7 @@ Style/RbsInline:
|
|
|
5
5
|
Exclude:
|
|
6
6
|
- 'spec/**/*'
|
|
7
7
|
- 'test/**/*'
|
|
8
|
+
Mode: ~
|
|
8
9
|
|
|
9
10
|
Style/RbsInline/DataClassCommentAlignment:
|
|
10
11
|
Description: "Checks that `#:` inline type annotations in `Data.define` blocks are aligned."
|
|
@@ -46,6 +47,11 @@ Style/RbsInline/MissingDataClassAnnotation:
|
|
|
46
47
|
Enabled: true
|
|
47
48
|
VersionAdded: '1.5.0'
|
|
48
49
|
|
|
50
|
+
Style/RbsInline/MissingStructClassAnnotation:
|
|
51
|
+
Description: "Checks that `Struct.new` attributes have inline type annotations."
|
|
52
|
+
Enabled: true
|
|
53
|
+
VersionAdded: '1.7.0'
|
|
54
|
+
|
|
49
55
|
Style/RbsInline/MissingTypeAnnotation:
|
|
50
56
|
Description: "Checks that method definitions and attr_* declarations have RBS inline type annotations."
|
|
51
57
|
Enabled: true
|
|
@@ -89,10 +95,18 @@ Style/RbsInline/RequireRbsInlineComment:
|
|
|
89
95
|
Description: "Checks the presence or absence of `# rbs_inline: enabled` magic comment."
|
|
90
96
|
Enabled: true
|
|
91
97
|
VersionAdded: "1.5.0"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
AllowMissingComment: false
|
|
99
|
+
EnforcedStyle: ~
|
|
100
|
+
|
|
101
|
+
Style/RbsInline/StructClassCommentAlignment:
|
|
102
|
+
Description: "Checks that `#:` inline type annotations in `Struct.new` blocks are aligned."
|
|
103
|
+
Enabled: true
|
|
104
|
+
VersionAdded: '1.7.0'
|
|
105
|
+
|
|
106
|
+
Style/RbsInline/StructNewWithBlock:
|
|
107
|
+
Description: "Checks for `Struct.new` calls with a block, which RBS::Inline does not parse."
|
|
108
|
+
Enabled: true
|
|
109
|
+
VersionAdded: '1.7.0'
|
|
96
110
|
|
|
97
111
|
Style/RbsInline/UnmatchedAnnotations:
|
|
98
112
|
Description: "Checks the rbs-inline annotation comment is surely matched."
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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/file_filter"
|
|
15
|
+
require_relative "style/rbs_inline/mixin/struct_class_matcher"
|
|
16
|
+
|
|
6
17
|
require_relative "style/rbs_inline/data_class_comment_alignment"
|
|
7
18
|
require_relative "style/rbs_inline/data_define_with_block"
|
|
8
19
|
require_relative "style/rbs_inline/embedded_rbs_spacing"
|
|
@@ -11,12 +22,15 @@ require_relative "style/rbs_inline/invalid_types"
|
|
|
11
22
|
require_relative "style/rbs_inline/keyword_separator"
|
|
12
23
|
require_relative "style/rbs_inline/method_comment_spacing"
|
|
13
24
|
require_relative "style/rbs_inline/missing_data_class_annotation"
|
|
25
|
+
require_relative "style/rbs_inline/missing_struct_class_annotation"
|
|
14
26
|
require_relative "style/rbs_inline/missing_type_annotation"
|
|
15
27
|
require_relative "style/rbs_inline/parameters_separator"
|
|
16
28
|
require_relative "style/rbs_inline/redundant_annotation_with_skip"
|
|
17
29
|
require_relative "style/rbs_inline/redundant_instance_variable_annotation"
|
|
18
30
|
require_relative "style/rbs_inline/redundant_type_annotation"
|
|
19
31
|
require_relative "style/rbs_inline/require_rbs_inline_comment"
|
|
32
|
+
require_relative "style/rbs_inline/struct_class_comment_alignment"
|
|
33
|
+
require_relative "style/rbs_inline/struct_new_with_block"
|
|
20
34
|
require_relative "style/rbs_inline/unmatched_annotations"
|
|
21
35
|
require_relative "style/rbs_inline/untyped_instance_variable"
|
|
22
36
|
require_relative "style/rbs_inline/variable_comment_spacing"
|
|
@@ -26,106 +26,18 @@ module RuboCop
|
|
|
26
26
|
# )
|
|
27
27
|
#
|
|
28
28
|
class DataClassCommentAlignment < Base
|
|
29
|
-
|
|
30
|
-
include
|
|
31
|
-
include
|
|
29
|
+
prepend FileFilter
|
|
30
|
+
include ClassCommentAlignment
|
|
31
|
+
include DataClassMatcher
|
|
32
32
|
extend AutoCorrector
|
|
33
33
|
|
|
34
34
|
MSG = "Misaligned inline type annotation for Data attribute."
|
|
35
35
|
|
|
36
36
|
# @rbs node: RuboCop::AST::SendNode
|
|
37
37
|
def on_send(node) #: void
|
|
38
|
-
return unless
|
|
39
|
-
return if folded_data_class?(node)
|
|
38
|
+
return unless struct_like_class?(node)
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
47
|
-
def data_define?(node) #: bool
|
|
48
|
-
return false unless node.method_name == :define
|
|
49
|
-
|
|
50
|
-
case (r = node.receiver)
|
|
51
|
-
when RuboCop::AST::ConstNode
|
|
52
|
-
r.short_name == :Data
|
|
53
|
-
else
|
|
54
|
-
false
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# @rbs arg: RuboCop::AST::Node
|
|
59
|
-
def data_attribute?(arg) #: bool
|
|
60
|
-
arg.sym_type? || arg.str_type?
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
64
|
-
def data_attributes(node) #: Array[RuboCop::AST::Node]
|
|
65
|
-
node.arguments.select { data_attribute?(_1) }
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
69
|
-
def folded_data_class?(node) #: bool
|
|
70
|
-
args = node.arguments
|
|
71
|
-
return false if args.empty?
|
|
72
|
-
|
|
73
|
-
lines = args.map { _1.location.line }
|
|
74
|
-
lines.uniq.length < args.length || lines.include?(node.location.line)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
78
|
-
def check_annotation_alignment(node) #: void
|
|
79
|
-
annotated = data_attributes(node).filter_map do |arg|
|
|
80
|
-
comment = inline_type_comment(arg.location.line)
|
|
81
|
-
[arg, comment] if comment #: [RuboCop::AST::Node, Parser::Source::Comment]
|
|
82
|
-
end
|
|
83
|
-
return if annotated.size < 2
|
|
84
|
-
|
|
85
|
-
expected_col = annotation_column(node)
|
|
86
|
-
annotated.each do |arg, comment|
|
|
87
|
-
actual_col = comment.location.column
|
|
88
|
-
next if actual_col == expected_col
|
|
89
|
-
|
|
90
|
-
add_offense(comment.source_range) do |corrector|
|
|
91
|
-
correct_alignment(corrector, arg, comment, expected_col)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# @rbs line: Integer
|
|
97
|
-
def inline_type_comment(line) #: Parser::Source::Comment?
|
|
98
|
-
comment = comment_at(line)
|
|
99
|
-
comment if comment&.text&.match?(/\A#:/)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
103
|
-
def annotation_column(node) #: Integer
|
|
104
|
-
last_arg = node.arguments.last
|
|
105
|
-
max_end_col = node.arguments.map do |arg|
|
|
106
|
-
comma_length = arg.equal?(last_arg) ? 0 : 1
|
|
107
|
-
arg.location.column + source!(arg).length + comma_length
|
|
108
|
-
end.max || 0
|
|
109
|
-
|
|
110
|
-
max_end_col + 2
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# @rbs corrector: RuboCop::Cop::Corrector
|
|
114
|
-
# @rbs arg: RuboCop::AST::Node
|
|
115
|
-
# @rbs comment: Parser::Source::Comment
|
|
116
|
-
# @rbs expected_col: Integer
|
|
117
|
-
def correct_alignment(corrector, arg, comment, expected_col) #: void # rubocop:disable Metrics/AbcSize
|
|
118
|
-
line = arg.location.line
|
|
119
|
-
line_source = source_code_at(line)
|
|
120
|
-
source = line_source[...comment.location.column] || raise
|
|
121
|
-
content_end_col = source.rstrip.length
|
|
122
|
-
padding = [expected_col - content_end_col, 1].max || raise
|
|
123
|
-
|
|
124
|
-
line_begin = processed_source.buffer.line_range(line).begin_pos
|
|
125
|
-
replace_start = line_begin + content_end_col
|
|
126
|
-
replace_end = line_begin + comment.location.column
|
|
127
|
-
|
|
128
|
-
corrector.replace(range_between(replace_start, replace_end), " " * padding)
|
|
40
|
+
check_comment_alignment(node)
|
|
129
41
|
end
|
|
130
42
|
end
|
|
131
43
|
end
|
|
@@ -28,29 +28,22 @@ module RuboCop
|
|
|
28
28
|
# end
|
|
29
29
|
#
|
|
30
30
|
class DataDefineWithBlock < Base
|
|
31
|
+
prepend FileFilter
|
|
32
|
+
include DataClassMatcher
|
|
33
|
+
|
|
31
34
|
MSG = "Do not use `Data.define` with a block. RBS::Inline does not parse block contents, " \
|
|
32
35
|
"so methods defined in the block will not be recognized. " \
|
|
33
36
|
"Use a separate class definition instead."
|
|
34
37
|
|
|
35
38
|
# @rbs node: RuboCop::AST::SendNode
|
|
36
39
|
def on_send(node) #: void
|
|
37
|
-
return unless
|
|
40
|
+
return unless struct_like_class?(node)
|
|
38
41
|
|
|
39
42
|
block_node = node.parent
|
|
40
43
|
return unless block_node&.block_type?
|
|
41
44
|
|
|
42
45
|
add_offense(node)
|
|
43
46
|
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
# @rbs node: RuboCop::AST::SendNode
|
|
48
|
-
def data_define?(node) #: bool
|
|
49
|
-
return false unless node.method_name == :define
|
|
50
|
-
|
|
51
|
-
receiver = node.receiver
|
|
52
|
-
receiver.is_a?(RuboCop::AST::ConstNode) && receiver.short_name == :Data
|
|
53
|
-
end
|
|
54
47
|
end
|
|
55
48
|
end
|
|
56
49
|
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
|
|
@@ -20,12 +20,12 @@ module RuboCop
|
|
|
20
20
|
# # @rbs param: String
|
|
21
21
|
#
|
|
22
22
|
class InvalidComment < Base
|
|
23
|
+
prepend FileFilter
|
|
23
24
|
extend AutoCorrector
|
|
25
|
+
include AnnotationKeywords
|
|
24
26
|
|
|
25
27
|
MSG = "Invalid RBS annotation comment found."
|
|
26
28
|
|
|
27
|
-
# refs: https://github.com/soutaro/rbs-inline/blob/main/lib/rbs/inline/annotation_parser/tokenizer.rb
|
|
28
|
-
RBS_INLINE_KEYWORDS = %w[inherits override use module-self generic skip module class].freeze #: Array[String]
|
|
29
29
|
RBS_INLINE_KEYWORD_PATTERN = RBS_INLINE_KEYWORDS.join("|") #: String
|
|
30
30
|
|
|
31
31
|
SIGNATURE_PATTERN = '\(.*\)\s*(\??\s*{.*?}\s*)?->\s*.*'
|