hashira 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/README.md +29 -0
- data/exe/hashira +1 -1
- data/lib/hashira/analysis/finding.rb +1 -1
- data/lib/hashira/analysis/syntax.rb +6 -6
- data/lib/hashira/churn.rb +1 -10
- data/lib/hashira/ci/accepted.rb +9 -7
- data/lib/hashira/ci/baseline.rb +34 -30
- data/lib/hashira/ci/comparison.rb +24 -0
- data/lib/hashira/ci/diff.rb +0 -15
- data/lib/hashira/ci/gate.rb +3 -3
- data/lib/hashira/ci/ratchet.rb +10 -4
- data/lib/hashira/ci/ratchet_report.rb +4 -4
- data/lib/hashira/ci/scope.rb +0 -1
- data/lib/hashira/ci/slice.rb +17 -0
- data/lib/hashira/ci/sweep.rb +13 -0
- data/lib/hashira/cli/arguments.rb +12 -8
- data/lib/hashira/cli/choice.rb +9 -0
- data/lib/hashira/cli/command_line.rb +8 -5
- data/lib/hashira/cli/fail_on.rb +6 -6
- data/lib/hashira/cli/flag.rb +3 -1
- data/lib/hashira/cli/flags.rb +10 -0
- data/lib/hashira/cli/format.rb +1 -1
- data/lib/hashira/cli/needs.rb +13 -2
- data/lib/hashira/cli/only.rb +16 -0
- data/lib/hashira/cli/options.rb +8 -10
- data/lib/hashira/cli/package_by.rb +1 -1
- data/lib/hashira/cli/run.rb +9 -8
- data/lib/hashira/cli.rb +22 -21
- data/lib/hashira/complexity/cognitive_score.rb +44 -33
- data/lib/hashira/complexity/method_finding.rb +7 -5
- data/lib/hashira/complexity/method_score.rb +1 -1
- data/lib/hashira/complexity/scores.rb +11 -9
- data/lib/hashira/coupling/audiences.rb +3 -3
- data/lib/hashira/coupling/catalog.rb +8 -6
- data/lib/hashira/coupling/census.rb +42 -22
- data/lib/hashira/coupling/cycle_search.rb +11 -7
- data/lib/hashira/coupling/definitions.rb +1 -1
- data/lib/hashira/coupling/edge_map.rb +9 -8
- data/lib/hashira/coupling/folding.rb +5 -5
- data/lib/hashira/coupling/graph.rb +16 -9
- data/lib/hashira/coupling/metric.rb +3 -1
- data/lib/hashira/coupling/naming.rb +4 -4
- data/lib/hashira/coupling/references.rb +12 -12
- data/lib/hashira/coupling/report.rb +7 -9
- data/lib/hashira/coupling/roll_call.rb +1 -1
- data/lib/hashira/coupling/roll_call_findings.rb +1 -1
- data/lib/hashira/coupling/roster.rb +37 -12
- data/lib/hashira/coupling/rule.rb +1 -1
- data/lib/hashira/coupling/sdp_violation_findings.rb +3 -1
- data/lib/hashira/duplication/clones.rb +1 -1
- data/lib/hashira/duplication/clusters.rb +10 -7
- data/lib/hashira/duplication/delta.rb +1 -4
- data/lib/hashira/duplication/duplication_finding.rb +4 -2
- data/lib/hashira/duplication/fragment.rb +2 -2
- data/lib/hashira/duplication/harvest.rb +7 -5
- data/lib/hashira/duplication/index.rb +3 -2
- data/lib/hashira/duplication/similarity.rb +2 -2
- data/lib/hashira/duplication/variance.rb +1 -1
- data/lib/hashira/error.rb +0 -3
- data/lib/hashira/focus.rb +29 -0
- data/lib/hashira/git_log.rb +19 -0
- data/lib/hashira/hotspots/file_cost.rb +1 -1
- data/lib/hashira/hotspots/rollup.rb +2 -2
- data/lib/hashira/pipeline.rb +22 -14
- data/lib/hashira/project.rb +35 -34
- data/lib/hashira/report/columns.rb +14 -8
- data/lib/hashira/report/graph_payload.rb +1 -1
- data/lib/hashira/report/hotspot_table.rb +1 -1
- data/lib/hashira/report/json.rb +2 -1
- data/lib/hashira/report/metrics_table.rb +3 -3
- data/lib/hashira/report/phrases.rb +23 -18
- data/lib/hashira/smells/census.rb +3 -4
- data/lib/hashira/smells/check.rb +1 -5
- data/lib/hashira/smells/contexts.rb +4 -4
- data/lib/hashira/smells/control_parameter.rb +1 -1
- data/lib/hashira/smells/data_clump.rb +2 -4
- data/lib/hashira/smells/duplicate_method_call.rb +3 -3
- data/lib/hashira/smells/feature_envy.rb +3 -3
- data/lib/hashira/smells/foreign.rb +14 -14
- data/lib/hashira/smells/instance_variable_assumption.rb +3 -3
- data/lib/hashira/smells/kind.rb +9 -0
- data/lib/hashira/smells/manual_dispatch.rb +1 -1
- data/lib/hashira/smells/module_initialize.rb +0 -2
- data/lib/hashira/smells/nil_check.rb +1 -1
- data/lib/hashira/smells/ownership.rb +19 -7
- data/lib/hashira/smells/param_check.rb +2 -2
- data/lib/hashira/smells/refs.rb +13 -7
- data/lib/hashira/smells/repeated_conditional.rb +1 -3
- data/lib/hashira/smells/report.rb +16 -7
- data/lib/hashira/smells/too_many_instance_variables.rb +3 -3
- data/lib/hashira/smells/visibility.rb +18 -11
- data/lib/hashira/trees.rb +8 -4
- data/lib/hashira/version.rb +1 -1
- data/lib/hashira.rb +8 -0
- metadata +9 -1
data/lib/hashira/smells/refs.rb
CHANGED
|
@@ -10,23 +10,29 @@ class Hashira::Smells::Refs
|
|
|
10
10
|
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableTargetNode
|
|
11
11
|
].freeze
|
|
12
12
|
|
|
13
|
-
def initialize(
|
|
14
|
-
@
|
|
15
|
-
Hashira::Smells::Scope.inside(def_node).each { record(it) }
|
|
13
|
+
def initialize(definition)
|
|
14
|
+
@node = definition
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def ego = lines(:self).size
|
|
19
18
|
|
|
20
|
-
def lines(name) =
|
|
19
|
+
def lines(name) = tallies.fetch(name, [])
|
|
21
20
|
|
|
22
21
|
def envious
|
|
23
|
-
peak =
|
|
24
|
-
names =
|
|
22
|
+
peak = tallies.values.map(&:size).max
|
|
23
|
+
names = tallies.filter_map { |name, sightings| name if sightings.size == peak }
|
|
25
24
|
names.include?(:self) ? [] : names
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
private
|
|
29
28
|
|
|
29
|
+
def tallies
|
|
30
|
+
return @_tallies if @_tallies
|
|
31
|
+
@_tallies = {}
|
|
32
|
+
Hashira::Smells::Scope.inside(@node).each { record(it) }
|
|
33
|
+
@_tallies
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
def record(node)
|
|
31
37
|
return note(:self, node) if selfish?(node)
|
|
32
38
|
case node
|
|
@@ -49,5 +55,5 @@ class Hashira::Smells::Refs
|
|
|
49
55
|
end
|
|
50
56
|
end
|
|
51
57
|
|
|
52
|
-
def note(name, node) = (@
|
|
58
|
+
def note(name, node) = (@_tallies[name] ||= []) << node.location.start_line
|
|
53
59
|
end
|
|
@@ -5,14 +5,12 @@ require "prism"
|
|
|
5
5
|
class Hashira::Smells::RepeatedConditional < Hashira::Smells::Check
|
|
6
6
|
LIMIT = 2
|
|
7
7
|
|
|
8
|
-
def self.judge? = true
|
|
9
|
-
|
|
10
8
|
private
|
|
11
9
|
|
|
12
10
|
def smelly? = subject.kind == :class && repeats.any?
|
|
13
11
|
|
|
14
12
|
def repeats
|
|
15
|
-
@
|
|
13
|
+
@_repeats ||= predicates.group_by(&:slice).except("block_given?").select { |_test, nodes| nodes.size > LIMIT }
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def predicates = Hashira::Smells::Scope.sweep(subject.node).filter_map { predicate(it) }
|
|
@@ -8,6 +8,7 @@ require_relative "duplicate_method_call"
|
|
|
8
8
|
require_relative "feature_envy"
|
|
9
9
|
require_relative "foreign"
|
|
10
10
|
require_relative "instance_variable_assumption"
|
|
11
|
+
require_relative "kind"
|
|
11
12
|
require_relative "manual_dispatch"
|
|
12
13
|
require_relative "module_initialize"
|
|
13
14
|
require_relative "nil_check"
|
|
@@ -19,22 +20,30 @@ require_relative "utility_function"
|
|
|
19
20
|
class Hashira::Smells::Report
|
|
20
21
|
CHECKS = Hashira::Smells::Check.subclasses.sort_by(&:name).freeze
|
|
21
22
|
|
|
22
|
-
JUDGES =
|
|
23
|
+
JUDGES = [
|
|
24
|
+
Hashira::Smells::DataClump, Hashira::Smells::InstanceVariableAssumption,
|
|
25
|
+
Hashira::Smells::ModuleInitialize, Hashira::Smells::RepeatedConditional,
|
|
26
|
+
Hashira::Smells::TooManyInstanceVariables
|
|
27
|
+
].freeze
|
|
23
28
|
|
|
24
|
-
PROBES = CHECKS
|
|
29
|
+
PROBES = (CHECKS - JUDGES).freeze
|
|
25
30
|
|
|
26
31
|
def initialize(project, trees)
|
|
27
|
-
@
|
|
28
|
-
@
|
|
32
|
+
@project = project
|
|
33
|
+
@trees = trees
|
|
29
34
|
end
|
|
30
35
|
|
|
31
|
-
def findings = @
|
|
36
|
+
def findings = @_findings ||= sniff(types, JUDGES) + sniff(methods, PROBES) + sprawl
|
|
32
37
|
|
|
33
38
|
private
|
|
34
39
|
|
|
35
|
-
def
|
|
40
|
+
def census = @_census ||= Hashira::Smells::Census.new(@project, @trees)
|
|
36
41
|
|
|
37
|
-
def
|
|
42
|
+
def types = @_types ||= census.types
|
|
43
|
+
|
|
44
|
+
def methods = types.flat_map(&:defs)
|
|
45
|
+
|
|
46
|
+
def sprawl = Hashira::Smells::BoundarySprawl.new(methods, census.ownership).findings
|
|
38
47
|
|
|
39
48
|
def sniff(subjects, checks) = subjects.flat_map { |subject| verdicts(subject, checks) }
|
|
40
49
|
|
|
@@ -10,14 +10,14 @@ class Hashira::Smells::TooManyInstanceVariables < Hashira::Smells::Check
|
|
|
10
10
|
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableTargetNode
|
|
11
11
|
].freeze
|
|
12
12
|
|
|
13
|
-
def self.judge? = true
|
|
14
|
-
|
|
15
13
|
private
|
|
16
14
|
|
|
17
15
|
def smelly? = subject.kind == :class && names.size > LIMIT
|
|
18
16
|
|
|
19
17
|
def names
|
|
20
|
-
@
|
|
18
|
+
@_names ||=
|
|
19
|
+
Hashira::Smells::Scope.sweep(subject.node).select { COUNTED.include?(it.class) }
|
|
20
|
+
.map(&:name).reject { it.start_with?("@_") }.uniq.sort
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def detail = { site:, count: names.size }
|
|
@@ -5,17 +5,24 @@ require "prism"
|
|
|
5
5
|
class Hashira::Smells::Visibility
|
|
6
6
|
MARKERS = %i[public private protected module_function].freeze
|
|
7
7
|
|
|
8
|
-
def initialize(
|
|
9
|
-
@
|
|
10
|
-
@overrides = {}
|
|
11
|
-
@found = []
|
|
12
|
-
scan(statements(type_node))
|
|
8
|
+
def initialize(type)
|
|
9
|
+
@node = type
|
|
13
10
|
end
|
|
14
11
|
|
|
15
|
-
def entries
|
|
12
|
+
def entries
|
|
13
|
+
blank
|
|
14
|
+
scan(statements(@node))
|
|
15
|
+
@_found.map { |node, section| [node, @_overrides.fetch(node.name, section)] }
|
|
16
|
+
end
|
|
16
17
|
|
|
17
18
|
private
|
|
18
19
|
|
|
20
|
+
def blank
|
|
21
|
+
@_section = :public
|
|
22
|
+
@_overrides = {}
|
|
23
|
+
@_found = []
|
|
24
|
+
end
|
|
25
|
+
|
|
19
26
|
def statements(node)
|
|
20
27
|
body = node.body
|
|
21
28
|
body.is_a?(Prism::StatementsNode) ? body.body : []
|
|
@@ -25,7 +32,7 @@ class Hashira::Smells::Visibility
|
|
|
25
32
|
|
|
26
33
|
def classify(node)
|
|
27
34
|
case node
|
|
28
|
-
when Prism::DefNode then @
|
|
35
|
+
when Prism::DefNode then @_found << [node, @_section]
|
|
29
36
|
when Prism::CallNode then heed(node)
|
|
30
37
|
else descend(node)
|
|
31
38
|
end
|
|
@@ -46,19 +53,19 @@ class Hashira::Smells::Visibility
|
|
|
46
53
|
def bare?(node) = MARKERS.include?(node.name) && !node.receiver
|
|
47
54
|
|
|
48
55
|
def switch(name, arguments)
|
|
49
|
-
arguments ? tag(name, arguments.arguments) : (@
|
|
56
|
+
arguments ? tag(name, arguments.arguments) : (@_section = name)
|
|
50
57
|
end
|
|
51
58
|
|
|
52
59
|
def tag(section, arguments) = arguments.each { note(it, section) }
|
|
53
60
|
|
|
54
61
|
def note(argument, section)
|
|
55
62
|
case argument
|
|
56
|
-
when Prism::DefNode then @
|
|
57
|
-
when Prism::SymbolNode then @
|
|
63
|
+
when Prism::DefNode then @_found << [argument, section]
|
|
64
|
+
when Prism::SymbolNode then @_overrides[argument.unescaped.to_sym] = section
|
|
58
65
|
end
|
|
59
66
|
end
|
|
60
67
|
|
|
61
68
|
def shadow(node)
|
|
62
|
-
statements(node).each { @
|
|
69
|
+
statements(node).each { @_found << [it, :singleton] if it.is_a?(Prism::DefNode) }
|
|
63
70
|
end
|
|
64
71
|
end
|
data/lib/hashira/trees.rb
CHANGED
|
@@ -4,17 +4,21 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
class Hashira::Trees
|
|
6
6
|
def initialize(project)
|
|
7
|
-
@
|
|
8
|
-
@all = project.files.to_h { [it, parse(it)] }
|
|
7
|
+
@project = project
|
|
9
8
|
end
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
def all = @_all ||= @project.files.to_h { [it, parse(it)] }
|
|
11
|
+
|
|
12
|
+
def unparsed
|
|
13
|
+
all
|
|
14
|
+
@_unparsed ||= []
|
|
15
|
+
end
|
|
12
16
|
|
|
13
17
|
private
|
|
14
18
|
|
|
15
19
|
def parse(path)
|
|
16
20
|
result = Prism.parse_file(path)
|
|
17
|
-
@
|
|
21
|
+
(@_unparsed ||= []) << path if result.failure?
|
|
18
22
|
result.value
|
|
19
23
|
rescue SystemCallError => error
|
|
20
24
|
raise(Hashira::Error, "cannot read #{path} (#{error.message})")
|
data/lib/hashira/version.rb
CHANGED
data/lib/hashira.rb
CHANGED
|
@@ -34,8 +34,10 @@ require_relative "hashira/analysis/node_walk"
|
|
|
34
34
|
require_relative "hashira/analysis/syntax"
|
|
35
35
|
require_relative "hashira/analysis/type_walk"
|
|
36
36
|
require_relative "hashira/churn"
|
|
37
|
+
require_relative "hashira/git_log"
|
|
37
38
|
require_relative "hashira/ci/accepted"
|
|
38
39
|
require_relative "hashira/ci/baseline"
|
|
40
|
+
require_relative "hashira/ci/comparison"
|
|
39
41
|
require_relative "hashira/ci/diff"
|
|
40
42
|
require_relative "hashira/ci/edge_diff_report"
|
|
41
43
|
require_relative "hashira/ci/finding_diff_report"
|
|
@@ -44,15 +46,19 @@ require_relative "hashira/ci/improvement"
|
|
|
44
46
|
require_relative "hashira/ci/ratchet"
|
|
45
47
|
require_relative "hashira/ci/ratchet_report"
|
|
46
48
|
require_relative "hashira/ci/scope"
|
|
49
|
+
require_relative "hashira/ci/slice"
|
|
47
50
|
require_relative "hashira/ci/status"
|
|
51
|
+
require_relative "hashira/ci/sweep"
|
|
48
52
|
require_relative "hashira/cli"
|
|
49
53
|
require_relative "hashira/cli/arguments"
|
|
54
|
+
require_relative "hashira/cli/choice"
|
|
50
55
|
require_relative "hashira/cli/command_line"
|
|
51
56
|
require_relative "hashira/cli/fail_on"
|
|
52
57
|
require_relative "hashira/cli/flag"
|
|
53
58
|
require_relative "hashira/cli/flags"
|
|
54
59
|
require_relative "hashira/cli/format"
|
|
55
60
|
require_relative "hashira/cli/needs"
|
|
61
|
+
require_relative "hashira/cli/only"
|
|
56
62
|
require_relative "hashira/cli/options"
|
|
57
63
|
require_relative "hashira/cli/package_by"
|
|
58
64
|
require_relative "hashira/cli/run"
|
|
@@ -117,6 +123,7 @@ require_relative "hashira/duplication/similarity"
|
|
|
117
123
|
require_relative "hashira/duplication/union_find"
|
|
118
124
|
require_relative "hashira/duplication/variance"
|
|
119
125
|
require_relative "hashira/error"
|
|
126
|
+
require_relative "hashira/focus"
|
|
120
127
|
require_relative "hashira/hotspots/file_cost"
|
|
121
128
|
require_relative "hashira/hotspots/rollup"
|
|
122
129
|
require_relative "hashira/pipeline"
|
|
@@ -136,6 +143,7 @@ require_relative "hashira/report/text"
|
|
|
136
143
|
require_relative "hashira/report/view"
|
|
137
144
|
require_relative "hashira/smells/census"
|
|
138
145
|
require_relative "hashira/smells/check"
|
|
146
|
+
require_relative "hashira/smells/kind"
|
|
139
147
|
require_relative "hashira/smells/conditions"
|
|
140
148
|
require_relative "hashira/smells/contexts"
|
|
141
149
|
require_relative "hashira/smells/control_parameter"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hashira
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Giacomo GK
|
|
@@ -33,6 +33,7 @@ files:
|
|
|
33
33
|
- lib/hashira/churn.rb
|
|
34
34
|
- lib/hashira/ci/accepted.rb
|
|
35
35
|
- lib/hashira/ci/baseline.rb
|
|
36
|
+
- lib/hashira/ci/comparison.rb
|
|
36
37
|
- lib/hashira/ci/diff.rb
|
|
37
38
|
- lib/hashira/ci/edge_diff_report.rb
|
|
38
39
|
- lib/hashira/ci/finding_diff_report.rb
|
|
@@ -41,15 +42,19 @@ files:
|
|
|
41
42
|
- lib/hashira/ci/ratchet.rb
|
|
42
43
|
- lib/hashira/ci/ratchet_report.rb
|
|
43
44
|
- lib/hashira/ci/scope.rb
|
|
45
|
+
- lib/hashira/ci/slice.rb
|
|
44
46
|
- lib/hashira/ci/status.rb
|
|
47
|
+
- lib/hashira/ci/sweep.rb
|
|
45
48
|
- lib/hashira/cli.rb
|
|
46
49
|
- lib/hashira/cli/arguments.rb
|
|
50
|
+
- lib/hashira/cli/choice.rb
|
|
47
51
|
- lib/hashira/cli/command_line.rb
|
|
48
52
|
- lib/hashira/cli/fail_on.rb
|
|
49
53
|
- lib/hashira/cli/flag.rb
|
|
50
54
|
- lib/hashira/cli/flags.rb
|
|
51
55
|
- lib/hashira/cli/format.rb
|
|
52
56
|
- lib/hashira/cli/needs.rb
|
|
57
|
+
- lib/hashira/cli/only.rb
|
|
53
58
|
- lib/hashira/cli/options.rb
|
|
54
59
|
- lib/hashira/cli/package_by.rb
|
|
55
60
|
- lib/hashira/cli/run.rb
|
|
@@ -115,6 +120,8 @@ files:
|
|
|
115
120
|
- lib/hashira/duplication/union_find.rb
|
|
116
121
|
- lib/hashira/duplication/variance.rb
|
|
117
122
|
- lib/hashira/error.rb
|
|
123
|
+
- lib/hashira/focus.rb
|
|
124
|
+
- lib/hashira/git_log.rb
|
|
118
125
|
- lib/hashira/hotspots/file_cost.rb
|
|
119
126
|
- lib/hashira/hotspots/rollup.rb
|
|
120
127
|
- lib/hashira/pipeline.rb
|
|
@@ -143,6 +150,7 @@ files:
|
|
|
143
150
|
- lib/hashira/smells/feature_envy.rb
|
|
144
151
|
- lib/hashira/smells/foreign.rb
|
|
145
152
|
- lib/hashira/smells/instance_variable_assumption.rb
|
|
153
|
+
- lib/hashira/smells/kind.rb
|
|
146
154
|
- lib/hashira/smells/manual_dispatch.rb
|
|
147
155
|
- lib/hashira/smells/module_initialize.rb
|
|
148
156
|
- lib/hashira/smells/nil_check.rb
|