diff --git a/lib/cc/engine/analyzers/analyzer_base.rb b/lib/cc/engine/analyzers/analyzer_base.rb
index c4409124..fda293c6 100644
--- a/lib/cc/engine/analyzers/analyzer_base.rb
+++ b/lib/cc/engine/analyzers/analyzer_base.rb
@@ -58,6 +58,10 @@ def filters
engine_config.filters_for(language) | default_filters
end
+ def post_filters
+ engine_config.post_filters_for(language) | default_post_filters
+ end
+
def language
self.class::LANGUAGE
end
@@ -108,6 +112,10 @@ def default_filters
[]
end
+ def default_post_filters
+ []
+ end
+
def points_per_overage
self.class::POINTS_PER_OVERAGE
end
diff --git a/lib/cc/engine/analyzers/engine_config.rb b/lib/cc/engine/analyzers/engine_config.rb
index 781a6e35..a1d2fe9d 100644
--- a/lib/cc/engine/analyzers/engine_config.rb
+++ b/lib/cc/engine/analyzers/engine_config.rb
@@ -42,6 +42,12 @@ def filters_for(language)
end
end
+ def post_filters_for(language)
+ fetch_language(language).fetch("post_filters", []).map do |filter|
+ Sexp::Matcher.parse filter
+ end
+ end
+
def minimum_mass_threshold_for(language)
[
mass_threshold_for(language, IDENTICAL_CODE_CHECK),
diff --git a/lib/cc/engine/analyzers/javascript/main.rb b/lib/cc/engine/analyzers/javascript/main.rb
index d02bfa94..8f7f2d7d 100644
--- a/lib/cc/engine/analyzers/javascript/main.rb
+++ b/lib/cc/engine/analyzers/javascript/main.rb
@@ -14,9 +14,14 @@ class Main < CC::Engine::Analyzers::Base
LANGUAGE = "javascript"
DEFAULT_MASS_THRESHOLD = 45
DEFAULT_FILTERS = [
+ "(directives (Directive (value (DirectiveLiteral ___))))",
"(ImportDeclaration ___)".freeze,
"(VariableDeclarator _ (init (CallExpression (_ (Identifier require)) ___)))".freeze,
].freeze
+ DEFAULT_POST_FILTERS = [
+ "(NUKE ___)",
+ "(Program _ ___)",
+ ].freeze
POINTS_PER_OVERAGE = 30_000
REQUEST_PATH = "/javascript".freeze
@@ -24,6 +29,19 @@ def use_sexp_lines?
false
end
+ ##
+ # Transform sexp as such:
+ #
+ # s(:Program, :module, s(:body, ... ))
+ # => s(:NUKE, s(:Program, :module, s(:NUKE, ... )))
+
+ def transform_sexp(sexp)
+ sexp.body.sexp_type = :NUKE # negate top level body
+ sexp = s(:NUKE, sexp) # wrap with extra node to force full process
+
+ sexp
+ end
+
private
def process_file(file)
@@ -33,6 +51,10 @@ def process_file(file)
def default_filters
DEFAULT_FILTERS.map { |filter| Sexp::Matcher.parse filter }
end
+
+ def default_post_filters
+ DEFAULT_POST_FILTERS.map { |filter| Sexp::Matcher.parse filter }
+ end
end
end
end
diff --git a/lib/cc/engine/analyzers/reporter.rb b/lib/cc/engine/analyzers/reporter.rb
index 2fee7917..c22c71e6 100644
--- a/lib/cc/engine/analyzers/reporter.rb
+++ b/lib/cc/engine/analyzers/reporter.rb
@@ -133,6 +133,7 @@ def flay_options
changes = {
mass: language_strategy.mass_threshold,
filters: language_strategy.filters,
+ post_filters: language_strategy.post_filters,
}
CCFlay.default_options.merge changes
diff --git a/lib/ccflay.rb b/lib/ccflay.rb
index a93a42d7..4bf0bcea 100644
--- a/lib/ccflay.rb
+++ b/lib/ccflay.rb
@@ -18,6 +18,25 @@ def initialize(option = nil)
self.identical = Concurrent::Hash.new
self.masses = Concurrent::Hash.new
end
+
+ def post_filter *patterns
+ return if patterns.empty?
+
+ self.hashes.delete_if { |_, sexps|
+ sexps.any? { |sexp|
+ patterns.any? { |pattern|
+ # pattern =~ sexp
+ pattern.satisfy? sexp
+ }
+ }
+ }
+ end
+
+ def prune
+ post_filter(*option[:post_filters])
+
+ super
+ end
end
class Sexp
diff --git a/spec/cc/engine/analyzers/javascript/main_spec.rb b/spec/cc/engine/analyzers/javascript/main_spec.rb
index 835d5bc5..fb8666ae 100644
--- a/spec/cc/engine/analyzers/javascript/main_spec.rb
+++ b/spec/cc/engine/analyzers/javascript/main_spec.rb
@@ -174,6 +174,94 @@
expect(issues).to be_empty
end
+ it "ignores imports and reports proper line number boundaries" do
+ create_source_file("foo.js", <<~EOJS)
+ 'use strict';
+
+ import React from 'react';
+
+ import Translator from '../../../i18n/translator-tag.jsx';
+ import { gettingSeniorID } from '../../../helpers/data/senior';
+ import { choosingReducedFee } from '../../../helpers/data/reduced-fee';
+ import { correctID } from '../../../helpers/data/card-type';
+
+
+ const Senior = (props) => {
+ if (!gettingSeniorID(props.IDApp)) { return null; }
+ return