Skip to content

Commit 7c58254

Browse files
committed
improve derivation of lines from Sexp
This started as a hunt into why an issue was reported for just a single line saying `end`, with the only other instance of the duplication being the same line: the source code in the spec is a reduced instance of the triggering code. The poor behavior results from how RubyParser/Sexp handle certain structures: the "line" of the `rescue..end` expression is parsed as the location of the `end`. This seems wrong, but I can't figure out a quick fix within those libraries. The way child sexprs get lines seems generally sane: at the very small level, lines seem pretty reliable. Strange behavior emerges at the higher level. The way we were calculating the end line of an sexpr already implicitly acknowledged this by walking the whole tree to find the highest line. This change adapts that previous algorithm to apply the same principle to finding both the start & end lines: the start line is basically the smallest line of the sexpr or any of its children, and the end line is the maximum.
1 parent a04e138 commit 7c58254

File tree

4 files changed

+80
-13
lines changed

4 files changed

+80
-13
lines changed

lib/cc/engine/analyzers/sexp_lines.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module CC
2+
module Engine
3+
module Analyzers
4+
class SexpLines
5+
attr_reader :begin_line, :end_line
6+
7+
def initialize(root_sexp)
8+
@root_sexp = root_sexp
9+
calculate
10+
end
11+
12+
private
13+
14+
attr_reader :root_sexp
15+
16+
def calculate
17+
@begin_line = root_sexp.line
18+
@end_line = root_sexp.end_line || root_sexp.line
19+
20+
root_sexp.deep_each do |sexp|
21+
@begin_line = [@begin_line, sexp.line].min
22+
@end_line = [@end_line, sexp.end_line || sexp.line].max
23+
end
24+
end
25+
end
26+
end
27+
end
28+
end

lib/cc/engine/analyzers/violation.rb

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,16 @@ def format_other_locations
6969
end
7070

7171
def format_sexp(sexp)
72+
lines = SexpLines.new(sexp)
7273
{
7374
"path": sexp.file.gsub(%r(^./), ""),
7475
"lines": {
75-
"begin": sexp.line,
76-
"end": sexp.end_line || sexp_max_line(sexp, sexp.line)
77-
}
76+
"begin": lines.begin_line,
77+
"end": lines.end_line
78+
},
7879
}
7980
end
8081

81-
def sexp_max_line(sexp_tree, default)
82-
max = default
83-
84-
sexp_tree.deep_each do |sexp|
85-
max = sexp.line if sexp.line > max
86-
end
87-
88-
max
89-
end
90-
9182
def content_body
9283
@_content_body ||= { "body": File.read(read_up_path) }
9384
end

lib/cc/engine/duplication.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require 'cc/engine/analyzers/reporter'
77
require 'cc/engine/analyzers/engine_config'
88
require 'cc/engine/analyzers/sexp'
9+
require 'cc/engine/analyzers/sexp_lines'
910
require 'flay'
1011
require 'json'
1112

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
require "spec_helper"
2+
require "cc/engine/duplication"
3+
4+
module CC::Engine::Analyzers
5+
RSpec.describe SexpLines do
6+
let(:source) do
7+
<<-SOURCE
8+
begin
9+
foo
10+
rescue SyntaxError => e
11+
Jekyll.logger.warn "YAML Exception reading \#{File.join(base, name)}: \#{e.message}"
12+
rescue Exception => e
13+
Jekyll.logger.warn "Error reading file \#{File.join(base, name)}: \#{e.message}"
14+
end
15+
SOURCE
16+
end
17+
let(:flay) do
18+
Flay.new({
19+
diff: false,
20+
mass: CC::Engine::Analyzers::Ruby::Main::DEFAULT_MASS_THRESHOLD,
21+
summary: false,
22+
verbose: false,
23+
number: true,
24+
timeout: 10,
25+
liberal: false,
26+
fuzzy: false,
27+
only: nil,
28+
})
29+
end
30+
31+
describe "violation location" do
32+
it "gets appropriate locations for rescue blocks" do
33+
sexp = RubyParser.new.process(source, "file.rb")
34+
flay.process_sexp(sexp)
35+
report = flay.analyze[0]
36+
sexps = flay.hashes[report.structural_hash]
37+
locations = sexps.map { |sexp| SexpLines.new(sexp) }
38+
39+
expect(locations.count).to eq 2
40+
expect(locations[0].begin_line).to eq(3)
41+
expect(locations[0].end_line).to eq(7)
42+
expect(locations[1].begin_line).to eq(5)
43+
expect(locations[1].end_line).to eq(7)
44+
end
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)