Skip to content

Commit 74920f5

Browse files
zenspiderAle Paredes
authored and
Ale Paredes
committed
Update for filtering (#314)
* Added post_filter option to CCFlay (essentially flay 2.11's filtering). This is wired up via a new config option "post_filters". Post filtering happens BEFORE prune, so they get run before conservative/liberal pruning happens, which essentially needs be last to make sense. * Added sexp filtering and transforming for javascript to improve results. Added DEFAULT_POST_FILTERS to remove NUKE pseudo-nodes and Program nodes. Added transform_sexp to transform top-level :body node to NUKE. Wrapped the top level sexp so the whole tree gets processed. This is probably a bug in CCD and/or flay.
1 parent 1091110 commit 74920f5

File tree

6 files changed

+144
-0
lines changed

6 files changed

+144
-0
lines changed

lib/cc/engine/analyzers/analyzer_base.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ def filters
5858
engine_config.filters_for(language) | default_filters
5959
end
6060

61+
def post_filters
62+
engine_config.post_filters_for(language) | default_post_filters
63+
end
64+
6165
def language
6266
self.class::LANGUAGE
6367
end
@@ -108,6 +112,10 @@ def default_filters
108112
[]
109113
end
110114

115+
def default_post_filters
116+
[]
117+
end
118+
111119
def points_per_overage
112120
self.class::POINTS_PER_OVERAGE
113121
end

lib/cc/engine/analyzers/engine_config.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ def filters_for(language)
4242
end
4343
end
4444

45+
def post_filters_for(language)
46+
fetch_language(language).fetch("post_filters", []).map do |filter|
47+
Sexp::Matcher.parse filter
48+
end
49+
end
50+
4551
def minimum_mass_threshold_for(language)
4652
[
4753
mass_threshold_for(language, IDENTICAL_CODE_CHECK),

lib/cc/engine/analyzers/javascript/main.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,34 @@ class Main < CC::Engine::Analyzers::Base
1414
LANGUAGE = "javascript"
1515
DEFAULT_MASS_THRESHOLD = 45
1616
DEFAULT_FILTERS = [
17+
"(directives (Directive (value (DirectiveLiteral ___))))",
1718
"(ImportDeclaration ___)".freeze,
1819
"(VariableDeclarator _ (init (CallExpression (_ (Identifier require)) ___)))".freeze,
1920
].freeze
21+
DEFAULT_POST_FILTERS = [
22+
"(NUKE ___)",
23+
"(Program _ ___)",
24+
].freeze
2025
POINTS_PER_OVERAGE = 30_000
2126
REQUEST_PATH = "/javascript".freeze
2227

2328
def use_sexp_lines?
2429
false
2530
end
2631

32+
##
33+
# Transform sexp as such:
34+
#
35+
# s(:Program, :module, s(:body, ... ))
36+
# => s(:NUKE, s(:Program, :module, s(:NUKE, ... )))
37+
38+
def transform_sexp(sexp)
39+
sexp.body.sexp_type = :NUKE # negate top level body
40+
sexp = s(:NUKE, sexp) # wrap with extra node to force full process
41+
42+
sexp
43+
end
44+
2745
private
2846

2947
def process_file(file)
@@ -33,6 +51,10 @@ def process_file(file)
3351
def default_filters
3452
DEFAULT_FILTERS.map { |filter| Sexp::Matcher.parse filter }
3553
end
54+
55+
def default_post_filters
56+
DEFAULT_POST_FILTERS.map { |filter| Sexp::Matcher.parse filter }
57+
end
3658
end
3759
end
3860
end

lib/cc/engine/analyzers/reporter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ def flay_options
133133
changes = {
134134
mass: language_strategy.mass_threshold,
135135
filters: language_strategy.filters,
136+
post_filters: language_strategy.post_filters,
136137
}
137138

138139
CCFlay.default_options.merge changes

lib/ccflay.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@ def initialize(option = nil)
1818
self.identical = Concurrent::Hash.new
1919
self.masses = Concurrent::Hash.new
2020
end
21+
22+
def post_filter *patterns
23+
return if patterns.empty?
24+
25+
self.hashes.delete_if { |_, sexps|
26+
sexps.any? { |sexp|
27+
patterns.any? { |pattern|
28+
# pattern =~ sexp
29+
pattern.satisfy? sexp
30+
}
31+
}
32+
}
33+
end
34+
35+
def prune
36+
post_filter(*option[:post_filters])
37+
38+
super
39+
end
2140
end
2241

2342
class Sexp

spec/cc/engine/analyzers/javascript/main_spec.rb

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,94 @@
174174
expect(issues).to be_empty
175175
end
176176

177+
it "ignores imports and reports proper line number boundaries" do
178+
create_source_file("foo.js", <<~EOJS)
179+
'use strict';
180+
181+
import React from 'react';
182+
183+
import Translator from '../../../i18n/translator-tag.jsx';
184+
import { gettingSeniorID } from '../../../helpers/data/senior';
185+
import { choosingReducedFee } from '../../../helpers/data/reduced-fee';
186+
import { correctID } from '../../../helpers/data/card-type';
187+
188+
189+
const Senior = (props) => {
190+
if (!gettingSeniorID(props.IDApp)) { return null; }
191+
return <Translator tag='p' translationPath = 'intro.getStartedPage.whatYouAreDoing.correctingSeniorID' />
192+
};
193+
194+
const Reduced = (props) => {
195+
if (!choosingReducedFee(props.IDApp)) { return null; }
196+
return <Translator tag='p' translationPath = 'intro.getStartedPage.whatYouAreDoing.correctingReducedFeeID' />
197+
};
198+
199+
const Regular = (props) => {
200+
if (gettingSeniorID(props.IDApp) || choosingReducedFee(props.IDApp)) { return null; }
201+
return <Translator tag='p' translationPath = 'intro.getStartedPage.whatYouAreDoing.correctingID' />
202+
};
203+
204+
205+
const CorrectingIDInfo = (props) => {
206+
if(!correctID(props)) { return null; }
207+
return (
208+
<div className='correcting-id-info'>
209+
<Senior IDApp = {props.IDApp }/>
210+
<Reduced IDApp = {props.IDApp }/>
211+
<Regular IDApp = { props.IDApp}/>
212+
</div>
213+
);
214+
};
215+
216+
export default CorrectingIDInfo;
217+
EOJS
218+
219+
create_source_file("bar.js", <<~EOJS)
220+
'use strict';
221+
222+
import React from 'react';
223+
import { updateID } from '../../../helpers/data/card-type';
224+
import { gettingSeniorID } from '../../../helpers/data/senior';
225+
import { choosingReducedFee } from '../../../helpers/data/reduced-fee';
226+
import Translator from '../../../i18n/translator-tag.jsx';
227+
228+
const Senior = (props) => {
229+
if (!gettingSeniorID(props.IDApp)) { return null; }
230+
return <Translator tag='p' translationPath = 'intro.getStartedPage.whatYouAreDoing.updatingSeniorID' />
231+
};
232+
233+
const Reduced = (props) => {
234+
if (!choosingReducedFee(props.IDApp)) { return null; }
235+
return <Translator tag='p' translationPath = 'intro.getStartedPage.whatYouAreDoing.updatingReducedFeeID' />
236+
};
237+
238+
const Regular = (props) => {
239+
if (gettingSeniorID(props.IDApp) || choosingReducedFee(props.IDApp)) { return null; }
240+
return <Translator tag='p' translationPath = 'intro.getStartedPage.whatYouAreDoing.updatingID' />
241+
};
242+
243+
const UpdatingIDInfo = (props) => {
244+
if (!updateID(props)) { return null; }
245+
return (
246+
<div className='updating-id-info'>
247+
<Senior IDApp = {props.IDApp } />
248+
<Reduced IDApp = {props.IDApp } />
249+
<Regular IDApp = { props.IDApp} />
250+
</div>
251+
);
252+
};
253+
254+
export default UpdatingIDInfo;
255+
EOJS
256+
257+
issues = run_engine(engine_conf).strip.split("\0")
258+
issues = issues.map { |issue| JSON.parse issue }
259+
260+
infected = issues.any? { |i| i.dig("location", "lines", "begin") == 1 }
261+
262+
expect(infected).to be false
263+
end
264+
177265
it "outputs the correct line numbers for ASTs missing line details (codeclimate/app#6227)" do
178266
create_source_file("foo.js", <<~EOJS)
179267
`/movie?${getQueryString({ movie_id: movieId })}`

0 commit comments

Comments
 (0)