From 7db331e7531448339cdaa2bc61c419416457e96f Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 27 Apr 2020 22:38:59 +0900 Subject: [PATCH] Support autocorrection for `Lint/AmbiguousRegexpLiteral` This PR supports autocorrection for `Lint/AmbiguousRegexpLiteral`. --- CHANGELOG.md | 1 + config/default.yml | 1 + lib/rubocop/cop/lint/ambiguous_operator.rb | 5 ++ .../cop/lint/ambiguous_regexp_literal.rb | 14 ++++++ .../cop/lint/useless_else_without_rescue.rb | 5 ++ lib/rubocop/cop/mixin/parser_diagnostic.rb | 2 +- lib/rubocop/cop/style/lambda_call.rb | 20 -------- lib/rubocop/cop/util.rb | 20 ++++++++ manual/cops_lint.md | 2 +- .../cop/lint/ambiguous_regexp_literal_spec.rb | 46 ++++++++++++++++++- 10 files changed, 93 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18121f1b60e..127872e3db5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### New features * [#7895](https://github.com/rubocop-hq/rubocop/pull/7895): Include `.simplecov` file by default. ([@robotdana][]) +* [#7916](https://github.com/rubocop-hq/rubocop/pull/7916): Support autocorrection for `Lint/AmbiguousRegexpLiteral`. ([@koic][]) ### Bug fixes diff --git a/config/default.yml b/config/default.yml index 1d040c3f230..25b554d530d 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1324,6 +1324,7 @@ Lint/AmbiguousRegexpLiteral: a method invocation without parentheses. Enabled: true VersionAdded: '0.17' + VersionChanged: '0.83' Lint/AssignmentInCondition: Description: "Don't use assignment in conditions." diff --git a/lib/rubocop/cop/lint/ambiguous_operator.rb b/lib/rubocop/cop/lint/ambiguous_operator.rb index 2c21717354e..a45b7b0fbdb 100644 --- a/lib/rubocop/cop/lint/ambiguous_operator.rb +++ b/lib/rubocop/cop/lint/ambiguous_operator.rb @@ -44,6 +44,11 @@ def relevant_diagnostic?(diagnostic) diagnostic.reason == :ambiguous_prefix end + def find_offense_node_by(diagnostic) + # TODO: When implementing auto-correction, this method should return + # an offense node passed as first argument of `add_offense` method. + end + def alternative_message(diagnostic) operator = diagnostic.location.source hash = AMBIGUITIES[operator] diff --git a/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb b/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb index 23a04f372fa..094eb6f1282 100644 --- a/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +++ b/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb @@ -28,12 +28,26 @@ class AmbiguousRegexpLiteral < Cop "if it's surely a regexp literal, or add a whitespace to the " \ 'right of the `/` if it should be a division.' + def autocorrect(node) + lambda do |corrector| + add_parentheses(node, corrector) + end + end + private def relevant_diagnostic?(diagnostic) diagnostic.reason == :ambiguous_literal end + def find_offense_node_by(diagnostic) + node = processed_source.ast.each_node(:regexp).find do |regexp_node| + regexp_node.source_range.begin_pos == diagnostic.location.begin_pos + end + + node.parent + end + def alternative_message(_diagnostic) MSG end diff --git a/lib/rubocop/cop/lint/useless_else_without_rescue.rb b/lib/rubocop/cop/lint/useless_else_without_rescue.rb index e44d307c55a..3ded0fd6797 100644 --- a/lib/rubocop/cop/lint/useless_else_without_rescue.rb +++ b/lib/rubocop/cop/lint/useless_else_without_rescue.rb @@ -40,6 +40,11 @@ def relevant_diagnostic?(diagnostic) diagnostic.reason == :useless_else end + def find_offense_node_by(diagnostic) + # TODO: When implementing auto-correction, this method should return + # an offense node passed as first argument of `add_offense` method. + end + def alternative_message(_diagnostic) MSG end diff --git a/lib/rubocop/cop/mixin/parser_diagnostic.rb b/lib/rubocop/cop/mixin/parser_diagnostic.rb index eb34ee0d05b..620673faef7 100644 --- a/lib/rubocop/cop/mixin/parser_diagnostic.rb +++ b/lib/rubocop/cop/mixin/parser_diagnostic.rb @@ -26,7 +26,7 @@ def investigate(processed_source) d.message.capitalize end - add_offense(nil, + add_offense(find_offense_node_by(d), location: d.location, message: message, severity: d.level) diff --git a/lib/rubocop/cop/style/lambda_call.rb b/lib/rubocop/cop/style/lambda_call.rb index 276203512e9..6fed0052e69 100644 --- a/lib/rubocop/cop/style/lambda_call.rb +++ b/lib/rubocop/cop/style/lambda_call.rb @@ -52,26 +52,6 @@ def offense?(node) implicit_style? && !node.implicit_call? end - def add_parentheses(node, corrector) - if node.arguments.empty? - corrector.insert_after(node, '()') - else - corrector.replace(args_begin(node), '(') - corrector.insert_after(args_end(node), ')') - end - end - - def args_begin(node) - loc = node.loc - selector = - node.super_type? || node.yield_type? ? loc.keyword : loc.selector - selector.end.resize(1) - end - - def args_end(node) - node.loc.expression.end - end - def message(_node) if explicit_style? 'Prefer the use of `lambda.call(...)` over `lambda.(...)`.' diff --git a/lib/rubocop/cop/util.rb b/lib/rubocop/cop/util.rb index 6a540edc64f..b5b3c91f2ad 100644 --- a/lib/rubocop/cop/util.rb +++ b/lib/rubocop/cop/util.rb @@ -26,6 +26,26 @@ def parentheses?(node) node.loc.end.is?(')') end + def add_parentheses(node, corrector) + if node.arguments.empty? + corrector.insert_after(node, '()') + else + corrector.replace(args_begin(node), '(') + corrector.insert_after(args_end(node), ')') + end + end + + def args_begin(node) + loc = node.loc + selector = + node.super_type? || node.yield_type? ? loc.keyword : loc.selector + selector.end.resize(1) + end + + def args_end(node) + node.loc.expression.end + end + def on_node(syms, sexp, excludes = [], &block) return to_enum(:on_node, syms, sexp, excludes) unless block_given? diff --git a/manual/cops_lint.md b/manual/cops_lint.md index 5f7d948b3ec..b02a75eea71 100644 --- a/manual/cops_lint.md +++ b/manual/cops_lint.md @@ -66,7 +66,7 @@ do_something(*some_array) Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged --- | --- | --- | --- | --- -Enabled | Yes | No | 0.17 | - +Enabled | Yes | Yes | 0.17 | 0.83 This cop checks for ambiguous regexp literals in the first argument of a method invocation without parentheses. diff --git a/spec/rubocop/cop/lint/ambiguous_regexp_literal_spec.rb b/spec/rubocop/cop/lint/ambiguous_regexp_literal_spec.rb index 1d53ae395cc..47e3e211528 100644 --- a/spec/rubocop/cop/lint/ambiguous_regexp_literal_spec.rb +++ b/spec/rubocop/cop/lint/ambiguous_regexp_literal_spec.rb @@ -5,11 +5,55 @@ context 'with a regexp literal in the first argument' do context 'without parentheses' do - it 'registers an offense' do + it 'registers an offense and corrects when single argument' do expect_offense(<<~RUBY) p /pattern/ ^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division. RUBY + + expect_correction(<<~RUBY) + p(/pattern/) + RUBY + end + + it 'registers an offense and corrects when multiple arguments' do + expect_offense(<<~RUBY) + p /pattern/, foo + ^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division. + RUBY + + expect_correction(<<~RUBY) + p(/pattern/, foo) + RUBY + end + + it 'registers an offense and corrects when using block argument' do + expect_offense(<<~RUBY) + p /pattern/, foo do |arg| + ^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division. + end + RUBY + + expect_correction(<<~RUBY) + p(/pattern/, foo) do |arg| + end + RUBY + end + + it 'registers an offense and corrects when nesting' do + expect_offense(<<~RUBY) + p /pattern/ do + ^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division. + p /pattern/ + ^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division. + end + RUBY + + expect_correction(<<~RUBY) + p(/pattern/) do + p(/pattern/) + end + RUBY end end