Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configuration for only posting warnings to added/modified lines #89

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
Pronto runner for [RuboCop](https://github.com/bbatsov/rubocop), ruby code
analyzer. [What is Pronto?](https://github.com/prontolabs/pronto)

- [Configuration](#configuration)
- [Usage](#usage)
- [Suggestions](#suggestions)
- [Only patched lines](#only-patched-lines)
- [RuboCop versions](#rubocop-versions)

## Configuration

Configuring RuboCop via `.rubocop.yml` will work just fine with
Expand All @@ -26,6 +32,12 @@ rubocop:

# Enable suggestions
suggestions: true

# Only report warnings on added/modified lines of code
# You can provide a number for a range to catch warnings on lines that were not modified
only_patched_lines:
enabled: false # default
range: 0 # default
```

## Suggestions
Expand All @@ -39,10 +51,35 @@ For example:

![GitHub screenshot with suggestion](https://user-images.githubusercontent.com/132/50402757-1bd75b80-0799-11e9-809f-8b8a23ed33f6.png)

## Only patched lines

When `only_patched_lines` is enabled, Rubocop warnings that start outside of the patched code will be ignored.
For example, if you add a method to a class with too many lines, the warning at the class level will not apply.

This can be useful for legacy applications with a lot of RuboCop warnings, where you want to focus on the new code.

When increasing the range, you will also catch warnings on lines that were not modified but are within the range of the modified lines.

For example, if you set `range: 1`, you will catch warnings starting before the patched lines, but only if they are within 1 line.
Phillita marked this conversation as resolved.
Show resolved Hide resolved

```ruby
# With `only_patched_lines` enabled and a default range of 1, the Metrics/ClassLength warning is not included in the results.
# However, when `range` is increased to 10, now Metrics/ClassLength will be included alongside the Metrics/MethodLength warning for the `too_long` method.
class TooLong
def just_fine
"I'm doing just fine, how about you?"
end

def too_long
# Pretend I am a new method that's 32 lines long.
end
end
```

## RuboCop versions

If you need to use RuboCop v0.84.0 or v0.85.x, you'll need to ensure that
you've also need to add `gem 'rubocop-ast', '< 0.7.0'` to your Gemfile as
you've also added `gem 'rubocop-ast', '< 0.7.0'` to your Gemfile as
these were the first versions to use rubocop-ast, and unfortunately the
dependency was loose enough that rubocop-ast versions >= 0.7.0 were allowed,
which causes `require 'rubocop'` to fail with
Expand Down
1 change: 1 addition & 0 deletions lib/pronto/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'rubocop'
require 'pronto/rubocop/patch_cop'
require 'pronto/rubocop/offense_line'
require 'pronto/rubocop/offense_suggestion'

module Pronto
class Rubocop < Runner
Expand Down
80 changes: 16 additions & 64 deletions lib/pronto/rubocop/offense_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,81 +26,29 @@ def processed_source
end

def message_text
return offense.message unless suggestion_text
return "#{indirect_message}#{offense.message}" unless suggestable?

"#{offense.message}\n\n```suggestion\n#{suggestion_text}```"
"#{offense.message}\n\n#{indirect_suggestion}#{suggestion_text}"
end

def suggestion_text
return unless patch_cop.runner.pronto_rubocop_config['suggestions']
return if corrections_count.zero?
return if corrector.nil? # possible after optimisation in https://github.com/rubocop/rubocop/pull/11264
return if differing_lines_count != corrections_count

@suggestion_text ||= corrected_lines[offense.line - 1]
end

def corrected_lines
@corrected_lines ||= corrector.rewrite.lines
def indirect_offense?
offense.location.first_line != line.new_lineno
end

def differing_lines_count
original_lines.each_with_index.count do |line, index|
line != corrected_lines[index]
end
def indirect_message
INDIRECT_MESSAGE % offense.location.first_line if indirect_offense?
end

def original_lines
processed_source.lines.join("\n").lines
def indirect_suggestion
INDIRECT_SUGGESTION % offense.location.first_line if indirect_offense?
end

if ::RuboCop::Cop::Team.respond_to?(:mobilize) && ::RuboCop::Cop::Team.public_method_defined?(:investigate)
# rubocop >= 0.87.0 has both mobilize and public investigate method
MOBILIZE = :mobilize
# rubocop 1.30.0 renamed from auto_correct to autocorrect
AUTOCORRECT = Gem::Version.new(::RuboCop::Version::STRING) >= Gem::Version.new("1.30.0") ? :autocorrect : :auto_correct

def report
@report ||= autocorrect_team.investigate(processed_source).cop_reports.first
end

def corrector
report.corrector
end

def corrections_count
# Some lines may contain more than one offense
report.offenses.map(&:line).uniq.size
end
else
# rubocop 0.85.x and 0.86.0 have mobilize, older versions don't
MOBILIZE = ::RuboCop::Cop::Team.respond_to?(:mobilize) ? :mobilize : :new
AUTOCORRECT = :auto_correct

def corrector
@corrector ||= begin
autocorrect_team.inspect_file(processed_source)
corrector = RuboCop::Cop::Corrector.new(processed_source.buffer)
corrector.corrections.concat(autocorrect_team.cops.first.corrections)
corrector
end
end

def corrections_count
@corrections_count ||= corrector.corrections.count
end
def suggestable?
OffenseSuggestion.new(patch_cop, offense, line).suggestable?
end

def autocorrect_team
@autocorrect_team ||=
::RuboCop::Cop::Team.send(MOBILIZE,
::RuboCop::Cop::Registry.new([cop_class]),
patch_cop.rubocop_config,
**{ AUTOCORRECT => true, stdin: true })
end

def cop_class
patch_cop.registry.find_by_cop_name(offense.cop_name)
def suggestion_text
OffenseSuggestion.new(patch_cop, offense, line).suggestion
end

def level
Expand Down Expand Up @@ -128,6 +76,10 @@ def config_severities
error: :error,
fatal: :fatal
}.freeze
SUGGESTION = 'suggestion'
RUBY = 'ruby'
INDIRECT_MESSAGE = "Offense generated for line %d:\n\n"
INDIRECT_SUGGESTION = "Suggestion for line %d:\n\n"

private_constant :DEFAULT_SEVERITIES
end
Expand Down
112 changes: 112 additions & 0 deletions lib/pronto/rubocop/offense_suggestion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# frozen_string_literal: true

module Pronto
class Rubocop < Runner
class OffenseSuggestion
def initialize(patch_cop, offense, line)
@patch_cop = patch_cop
@offense = offense
@line = line
end

def suggestion
return unless suggestable?

"```#{code_type}\n#{suggestion_text}```"
end

def suggestable?
# `corrector.nil?`` possible after optimisation in https://github.com/rubocop/rubocop/pull/11264
patch_cop.runner.pronto_rubocop_config.fetch('suggestions', false) &&
(!corrections_count.zero? && !corrector.nil? && differing_lines_count == corrections_count)
end

private

attr_reader :patch_cop, :offense, :line

def processed_source
patch_cop.processed_source
end

def code_type
indirect_offense? ? RUBY : SUGGESTION
end

def indirect_offense?
offense.location.first_line != line.new_lineno
end

def suggestion_text
return unless patch_cop.runner.pronto_rubocop_config['suggestions']
# `corrector.nil?`` possible after optimisation in https://github.com/rubocop/rubocop/pull/11264
return if corrections_count.zero? || corrector.nil? || differing_lines_count != corrections_count

@suggestion_text ||= corrected_lines[offense.line - 1]
end

def corrected_lines
@corrected_lines ||= corrector.rewrite.lines
end

def differing_lines_count
original_lines.each_with_index.count { |line, index| line != corrected_lines[index] }
end

def original_lines
processed_source.lines.join("\n").lines
end

if ::RuboCop::Cop::Team.respond_to?(:mobilize) && ::RuboCop::Cop::Team.public_method_defined?(:investigate)
# rubocop >= 0.87.0 has both mobilize and public investigate method
MOBILIZE = :mobilize
# rubocop 1.30.0 renamed from auto_correct to autocorrect
AUTOCORRECT = Gem::Version.new(::RuboCop::Version::STRING) >= Gem::Version.new("1.30.0") ? :autocorrect : :auto_correct

def report
@report ||= autocorrect_team.investigate(processed_source).cop_reports.first
end

def corrector
report.corrector
end

def corrections_count
# Some lines may contain more than one offense
report.offenses.map(&:line).uniq.size
end
else
# rubocop 0.85.x and 0.86.0 have mobilize, older versions don't
MOBILIZE = ::RuboCop::Cop::Team.respond_to?(:mobilize) ? :mobilize : :new
AUTOCORRECT = :auto_correct

def corrector
@corrector ||= begin
autocorrect_team.inspect_file(processed_source)
corrector = RuboCop::Cop::Corrector.new(processed_source.buffer)
corrector.corrections.concat(autocorrect_team.cops.first.corrections)
corrector
end
end

def corrections_count
@corrections_count ||= corrector.corrections.count
end
end

def autocorrect_team
@autocorrect_team ||=
::RuboCop::Cop::Team.send(MOBILIZE,
::RuboCop::Cop::Registry.new([cop_class]),
patch_cop.rubocop_config,
**{ AUTOCORRECT => true, stdin: true })
end

def cop_class
patch_cop.registry.find_by_cop_name(offense.cop_name)
end
SUGGESTION = 'suggestion'
RUBY = 'ruby'
end
end
end
19 changes: 17 additions & 2 deletions lib/pronto/rubocop/patch_cop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ def offenses
end

def offense_includes?(offense, line_number)
offense_range = (offense.location.first_line..offense.location.last_line)
offense_range.include?(line_number)
if only_patched_lines?
(offense.location.first_line..offense.location.first_line + only_patched_lines_range).include?(line_number)
else
(offense.location.first_line..offense.location.last_line).include?(line_number)
end
end

def team
Expand All @@ -91,6 +94,18 @@ def first_relevant_message(patch, offense)

OffenseLine.new(self, offense, offending_line).message
end

def only_patched_lines
runner.pronto_rubocop_config.fetch('only_patched_lines', {})
end

def only_patched_lines?
@only_patched_lines ||= only_patched_lines.fetch('enabled', false)
end

def only_patched_lines_range
@only_patched_lines_range ||= only_patched_lines.fetch('range', 0)
end
end
end
end
2 changes: 1 addition & 1 deletion spec/fixtures/test.git/git/COMMIT_EDITMSG
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Two offenses and unsuggestable offense
Update with indirect offense
Empty file.
1 change: 1 addition & 0 deletions spec/fixtures/test.git/git/ORIG_HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1bfe9ff1072c01aefe4004e7a99789e34ed8f9db
Binary file modified spec/fixtures/test.git/git/index
Binary file not shown.
2 changes: 2 additions & 0 deletions spec/fixtures/test.git/git/logs/HEAD
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ fcf76dc1596f8db6dd43f240a804c9346d0039f1 ac7e278914d5f4da3950142d416ac128cb63fb9
ac7e278914d5f4da3950142d416ac128cb63fb98 c3536be8315eef1030975d4954b87f23865db86b Sunny Ripert <sunny@sunfox.org> 1540939111 +0100 commit: Offense
c3536be8315eef1030975d4954b87f23865db86b a1095e78c5733f4a4df3862561bb52117b9f09f1 Sunny Ripert <sunny@sunfox.org> 1557921651 +0200 commit: Two offenses
a1095e78c5733f4a4df3862561bb52117b9f09f1 348c6ed6673e746c6f40e8a167b0bfe1193ff6e6 Sunny Ripert <sunny@sunfox.org> 1557923263 +0200 commit: Two offenses and unsuggestable offense
348c6ed6673e746c6f40e8a167b0bfe1193ff6e6 76a8537d7fcafc96ffa68929f41dcc53b0010d86 Tayler Phillips <tphillips@infotech.com> 1713448302 -0400 commit: New test method
76a8537d7fcafc96ffa68929f41dcc53b0010d86 901e9f5e845f63093df063c6523411c36ea27164 Tayler Phillips <tphillips@infotech.com> 1713448333 -0400 commit: Update with indirect offense
2 changes: 2 additions & 0 deletions spec/fixtures/test.git/git/logs/refs/heads/master
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ fcf76dc1596f8db6dd43f240a804c9346d0039f1 ac7e278914d5f4da3950142d416ac128cb63fb9
ac7e278914d5f4da3950142d416ac128cb63fb98 c3536be8315eef1030975d4954b87f23865db86b Sunny Ripert <sunny@sunfox.org> 1540939111 +0100 commit: Offense
c3536be8315eef1030975d4954b87f23865db86b a1095e78c5733f4a4df3862561bb52117b9f09f1 Sunny Ripert <sunny@sunfox.org> 1557921651 +0200 commit: Two offenses
a1095e78c5733f4a4df3862561bb52117b9f09f1 348c6ed6673e746c6f40e8a167b0bfe1193ff6e6 Sunny Ripert <sunny@sunfox.org> 1557923263 +0200 commit: Two offenses and unsuggestable offense
348c6ed6673e746c6f40e8a167b0bfe1193ff6e6 76a8537d7fcafc96ffa68929f41dcc53b0010d86 Tayler Phillips <tphillips@infotech.com> 1713448302 -0400 commit: New test method
76a8537d7fcafc96ffa68929f41dcc53b0010d86 901e9f5e845f63093df063c6523411c36ea27164 Tayler Phillips <tphillips@infotech.com> 1713448333 -0400 commit: Update with indirect offense
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xmϻ�0 P�|ŕ���� �QK\��K��Ɇ �%ߣ����#����1��R�,iO�g�4�+$-d��̸˭lp4Ar�o�WtY��"��h�3g~��j6��}�j�445�N�.������la]6
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xe�=�0 `��I ]i� �\�=j� �BR%���7dK~���1���m��J�P�˜�>�Le�WpYɘGjŝ*�d3@� ܲ�rh,+Wt�]˔��/Z���{j5�545�4�58�ND�����XZ
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x��Mj�0���)�Z��X�R�����yX`[FQ�}T� ��f>&�}/��ſ��_Btb�b�����^^���|�p&0��䆣��� b��I�`�z9����� 9*��kmt���nkٶr>����U�i�Hu�$�s�f�w=�j��i��껶��9n�r��g���x@=�QT�
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x��;!E�Y�ۀ�� �ckg���Ȑ������K�
�NNrn.�m���O��N�� -��HK *�D2� �3�sʳ�7*���4�`�q&����ZG+8O"J�H���6x��J�K^׼p��﹤� � ��������3ל�a��>��7أ��;Ԕ�ta_�P{
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x��Kj�0��)� �X?B��Er�V� l�h4 �}�� �{T��{�`}|��,j-�}%��=%�`s)�Q1�V��,Չ�� 1`�.�(�B9�`H�fYL%�hmtMA�c�}��l<�sm���;���7?�!}2�o��w0ѸeI�9xՋ����y��o���dx��B;jL�wV�XzU�
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
x]Ͽ
� ��f��G�s��I�A�j�s��S!���>_n8}[�7�n�2�����<;�K�9��P��Og��͕�r �-�QJpo^�p�BK���S(�&
3�o~�D�
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
xe���0 `�{�\(�W_���(���V.>=�L94i��m�х��=a���$��7��݀�J=ܐ���Ν�8�ټ�^�f�,��d-�ު}�ޒ'u��o����[������V�E�UB'AW;�,��
 ]-
Binary file not shown.
2 changes: 1 addition & 1 deletion spec/fixtures/test.git/git/refs/heads/master
Original file line number Diff line number Diff line change
@@ -1 +1 @@
348c6ed6673e746c6f40e8a167b0bfe1193ff6e6
901e9f5e845f63093df063c6523411c36ea27164
14 changes: 14 additions & 0 deletions spec/fixtures/test.git/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,17 @@ def zoo
def goo
"gar"
end

def hoo
arr = []
arr << 'test1'
arr << 'test2'
arr << 'test3'
arr << 'test4'
arr << 'test5'
arr << 'test6'
arr << 'test7'
arr << 'test8'
arr << 'test9'
arr << 'test10'
end