RuboCop is a Ruby static code analyzer and formatter. Before contributing, read CONTRIBUTING.md and the development docs.
bundle exec rake # Full CI: codespell + doc syntax check + specs + self-lint
bundle exec rake spec # Run specs (Parser)
bundle exec rake prism_spec # Run specs (Prism parser)
bundle exec rake internal_investigation # RuboCop linting itself
bundle exec rubocop --only Department/CopName # Lint with a single copAlways run bundle exec rake before opening a PR.
lib/rubocop/cop/<department>/<cop_name>.rb # Cop source
spec/rubocop/cop/<department>/<cop_name>_spec.rb # Cop spec
config/default.yml # Default configuration for every cop
changelog/ # Pending changelog entries (one per file)
lib/rubocop.rb # Require list (auto-updated by generator)
Departments: Bundler, Gemspec, Layout, Lint, Metrics, Migration,
Naming, Security, Style, InternalAffairs.
Scaffold:
bundle exec rake 'new_cop[Department/CopName]'This generates the source file, spec file, config/default.yml entry, and
require in lib/rubocop.rb. After generation:
- Update the description in
config/default.yml. - Implement the cop.
- Write specs.
- Add a changelog entry:
bundle exec rake changelog:new.
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# One-line summary starting with a verb (e.g. "Checks for …", "Enforces …").
# Additional detail paragraph(s) if needed.
#
# @safety
# Explain why autocorrect may be unsafe, or delete this section.
#
# @example
# # bad
# bad_code
#
# # good
# good_code
#
class MyCop < Base
extend AutoCorrector
MSG = 'Use `#good_method` instead of `#bad_method`.'
RESTRICT_ON_SEND = %i[bad_method].freeze
# @!method bad_method?(node)
def_node_matcher :bad_method?, <<~PATTERN
(send nil? :bad_method ...)
PATTERN
def on_send(node)
return unless bad_method?(node)
add_offense(node) do |corrector|
corrector.replace(node, 'good_method')
end
end
alias on_csend on_send
end
end
end
endKey conventions:
RESTRICT_ON_SEND— list method names soon_sendis only called for those methods (performance optimization). Required when usingon_send.alias on_csend on_send— handle safe navigation (&.). Add this whenever you defineon_send, unless the cop explicitly does not apply to safe navigation.alias on_numblock on_blockandalias on_itblock on_block— handle numbered-parameter blocks (_1) andit-blocks. Add these whenever you defineon_block.extend AutoCorrector— declare this when the cop provides autocorrect.def_node_matcher/def_node_search— DSL for AST pattern matching. Document with a@!methodYARD tag above each matcher.- YARD
@example— every cop must have at least one# bad/# goodexample pair. Examples must be valid Ruby syntax (the CI doc-syntax check parses them). - Cop description — the first line of the YARD comment must be a complete sentence starting with a verb and ending with a period.
# frozen_string_literal: true
RSpec.describe RuboCop::Cop::Style::MyCop, :config do
it 'registers an offense when using `#bad_method`' do
expect_offense(<<~RUBY)
bad_method(foo)
^^^^^^^^^^^^^^^ Use `#good_method` instead of `#bad_method`.
RUBY
expect_correction(<<~RUBY)
good_method(foo)
RUBY
end
it 'does not register an offense when using `#good_method`' do
expect_no_offenses(<<~RUBY)
good_method(foo)
RUBY
end
endexpect_offense—^carets mark the offense range and must align exactly under the offending code. The message follows the last caret.expect_correction— expected source after autocorrect. Must followexpect_offensein the same example.expect_no_offenses— assert no violations.- Use
%{variable}inexpect_offenseheredocs to interpolate dynamic values. - Use
_{variable}for offense-range placeholders. - Use RSpec metadata tags like
:ruby27,:ruby34to set the target Ruby version for a test. - Configuration:
let(:cop_config) { { 'EnforcedStyle' => 'bar' } }.
Every user-visible change needs a changelog entry:
bundle exec rake changelog:fix # Bug fix
bundle exec rake changelog:new # New feature
bundle exec rake changelog:change # Changed behaviorFormat (single line):
* [#123](https://github.com/rubocop/rubocop/issues/123): Description. ([@username][])
- Must end with
([@username][]). spec/project_spec.rbvalidates the format in CI.- Skip the changelog only for purely internal changes (refactors with no user-visible effect).
- Prefix commit messages with
[Fix #N]when an issue exists. - Squash related commits.
- Run
bundle exec rakeand ensure it passes before pushing.
- Missing
alias on_csend on_send— cops that checkon_sendmust also handle safe navigation unless explicitly inapplicable. - Missing
alias on_numblock on_block/alias on_itblock on_block— cops that checkon_blockmust also handle numbered-parameter andit-parameter block forms. - Invalid Ruby in YARD examples — the CI
documentation_syntax_checktask parses every@exampleblock. Use only valid syntax. - Cop description not a sentence — must start with a verb and end with a
period (e.g.
# Checks for ..., not# Check for ...). - Missing
RESTRICT_ON_SEND— always define this when usingon_send. - Missing
@!methodYARD tag — everydef_node_matcher/def_node_searchneeds a@!methodtag above it. - Forgetting changelog entry — CI will flag it.
- Manually creating changelog files — use the rake tasks instead to get the correct filename format.
- Missing
extend AutoCorrector— required if the cop provides acorrectorblock inadd_offense. - Not running full
bundle exec rake— partial test runs miss lint and doc-syntax failures. - Hardcoding node types instead of using node pattern matchers — prefer
def_node_matcherover manualnode.type == :sendchecks. - Not testing both
sendandcsend— if you aliason_csend, write specs that cover the&.operator.