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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/README.md +29 -0
  4. data/exe/hashira +1 -1
  5. data/lib/hashira/analysis/finding.rb +1 -1
  6. data/lib/hashira/analysis/syntax.rb +6 -6
  7. data/lib/hashira/churn.rb +1 -10
  8. data/lib/hashira/ci/accepted.rb +9 -7
  9. data/lib/hashira/ci/baseline.rb +34 -30
  10. data/lib/hashira/ci/comparison.rb +24 -0
  11. data/lib/hashira/ci/diff.rb +0 -15
  12. data/lib/hashira/ci/gate.rb +3 -3
  13. data/lib/hashira/ci/ratchet.rb +10 -4
  14. data/lib/hashira/ci/ratchet_report.rb +4 -4
  15. data/lib/hashira/ci/scope.rb +0 -1
  16. data/lib/hashira/ci/slice.rb +17 -0
  17. data/lib/hashira/ci/sweep.rb +13 -0
  18. data/lib/hashira/cli/arguments.rb +12 -8
  19. data/lib/hashira/cli/choice.rb +9 -0
  20. data/lib/hashira/cli/command_line.rb +8 -5
  21. data/lib/hashira/cli/fail_on.rb +6 -6
  22. data/lib/hashira/cli/flag.rb +3 -1
  23. data/lib/hashira/cli/flags.rb +10 -0
  24. data/lib/hashira/cli/format.rb +1 -1
  25. data/lib/hashira/cli/needs.rb +13 -2
  26. data/lib/hashira/cli/only.rb +16 -0
  27. data/lib/hashira/cli/options.rb +8 -10
  28. data/lib/hashira/cli/package_by.rb +1 -1
  29. data/lib/hashira/cli/run.rb +9 -8
  30. data/lib/hashira/cli.rb +22 -21
  31. data/lib/hashira/complexity/cognitive_score.rb +44 -33
  32. data/lib/hashira/complexity/method_finding.rb +7 -5
  33. data/lib/hashira/complexity/method_score.rb +1 -1
  34. data/lib/hashira/complexity/scores.rb +11 -9
  35. data/lib/hashira/coupling/audiences.rb +3 -3
  36. data/lib/hashira/coupling/catalog.rb +8 -6
  37. data/lib/hashira/coupling/census.rb +42 -22
  38. data/lib/hashira/coupling/cycle_search.rb +11 -7
  39. data/lib/hashira/coupling/definitions.rb +1 -1
  40. data/lib/hashira/coupling/edge_map.rb +9 -8
  41. data/lib/hashira/coupling/folding.rb +5 -5
  42. data/lib/hashira/coupling/graph.rb +16 -9
  43. data/lib/hashira/coupling/metric.rb +3 -1
  44. data/lib/hashira/coupling/naming.rb +4 -4
  45. data/lib/hashira/coupling/references.rb +12 -12
  46. data/lib/hashira/coupling/report.rb +7 -9
  47. data/lib/hashira/coupling/roll_call.rb +1 -1
  48. data/lib/hashira/coupling/roll_call_findings.rb +1 -1
  49. data/lib/hashira/coupling/roster.rb +37 -12
  50. data/lib/hashira/coupling/rule.rb +1 -1
  51. data/lib/hashira/coupling/sdp_violation_findings.rb +3 -1
  52. data/lib/hashira/duplication/clones.rb +1 -1
  53. data/lib/hashira/duplication/clusters.rb +10 -7
  54. data/lib/hashira/duplication/delta.rb +1 -4
  55. data/lib/hashira/duplication/duplication_finding.rb +4 -2
  56. data/lib/hashira/duplication/fragment.rb +2 -2
  57. data/lib/hashira/duplication/harvest.rb +7 -5
  58. data/lib/hashira/duplication/index.rb +3 -2
  59. data/lib/hashira/duplication/similarity.rb +2 -2
  60. data/lib/hashira/duplication/variance.rb +1 -1
  61. data/lib/hashira/error.rb +0 -3
  62. data/lib/hashira/focus.rb +29 -0
  63. data/lib/hashira/git_log.rb +19 -0
  64. data/lib/hashira/hotspots/file_cost.rb +1 -1
  65. data/lib/hashira/hotspots/rollup.rb +2 -2
  66. data/lib/hashira/pipeline.rb +22 -14
  67. data/lib/hashira/project.rb +35 -34
  68. data/lib/hashira/report/columns.rb +14 -8
  69. data/lib/hashira/report/graph_payload.rb +1 -1
  70. data/lib/hashira/report/hotspot_table.rb +1 -1
  71. data/lib/hashira/report/json.rb +2 -1
  72. data/lib/hashira/report/metrics_table.rb +3 -3
  73. data/lib/hashira/report/phrases.rb +23 -18
  74. data/lib/hashira/smells/census.rb +3 -4
  75. data/lib/hashira/smells/check.rb +1 -5
  76. data/lib/hashira/smells/contexts.rb +4 -4
  77. data/lib/hashira/smells/control_parameter.rb +1 -1
  78. data/lib/hashira/smells/data_clump.rb +2 -4
  79. data/lib/hashira/smells/duplicate_method_call.rb +3 -3
  80. data/lib/hashira/smells/feature_envy.rb +3 -3
  81. data/lib/hashira/smells/foreign.rb +14 -14
  82. data/lib/hashira/smells/instance_variable_assumption.rb +3 -3
  83. data/lib/hashira/smells/kind.rb +9 -0
  84. data/lib/hashira/smells/manual_dispatch.rb +1 -1
  85. data/lib/hashira/smells/module_initialize.rb +0 -2
  86. data/lib/hashira/smells/nil_check.rb +1 -1
  87. data/lib/hashira/smells/ownership.rb +19 -7
  88. data/lib/hashira/smells/param_check.rb +2 -2
  89. data/lib/hashira/smells/refs.rb +13 -7
  90. data/lib/hashira/smells/repeated_conditional.rb +1 -3
  91. data/lib/hashira/smells/report.rb +16 -7
  92. data/lib/hashira/smells/too_many_instance_variables.rb +3 -3
  93. data/lib/hashira/smells/visibility.rb +18 -11
  94. data/lib/hashira/trees.rb +8 -4
  95. data/lib/hashira/version.rb +1 -1
  96. data/lib/hashira.rb +8 -0
  97. metadata +9 -1
@@ -10,23 +10,29 @@ class Hashira::Smells::Refs
10
10
  Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableTargetNode
11
11
  ].freeze
12
12
 
13
- def initialize(def_node)
14
- @tallies = {}
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) = @tallies.fetch(name, [])
19
+ def lines(name) = tallies.fetch(name, [])
21
20
 
22
21
  def envious
23
- peak = @tallies.values.map(&:size).max
24
- names = @tallies.filter_map { |name, sightings| name if sightings.size == peak }
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) = (@tallies[name] ||= []) << node.location.start_line
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
- @repeats ||= predicates.group_by(&:slice).except("block_given?").select { |_test, nodes| nodes.size > LIMIT }
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 = CHECKS.select(&:judge?).freeze
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.reject(&:judge?).freeze
29
+ PROBES = (CHECKS - JUDGES).freeze
25
30
 
26
31
  def initialize(project, trees)
27
- @census = Hashira::Smells::Census.new(project, trees)
28
- @types = @census.types
32
+ @project = project
33
+ @trees = trees
29
34
  end
30
35
 
31
- def findings = @findings ||= sniff(@types, JUDGES) + sniff(methods, PROBES) + sprawl
36
+ def findings = @_findings ||= sniff(types, JUDGES) + sniff(methods, PROBES) + sprawl
32
37
 
33
38
  private
34
39
 
35
- def methods = @types.flat_map(&:defs)
40
+ def census = @_census ||= Hashira::Smells::Census.new(@project, @trees)
36
41
 
37
- def sprawl = Hashira::Smells::BoundarySprawl.new(methods, @census.ownership).findings
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
- @names ||= Hashira::Smells::Scope.sweep(subject.node).select { COUNTED.include?(it.class) }.map(&:name).uniq.sort
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(type_node)
9
- @section = :public
10
- @overrides = {}
11
- @found = []
12
- scan(statements(type_node))
8
+ def initialize(type)
9
+ @node = type
13
10
  end
14
11
 
15
- def entries = @found.map { |node, section| [node, @overrides.fetch(node.name, section)] }
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 @found << [node, @section]
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) : (@section = name)
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 @found << [argument, section]
57
- when Prism::SymbolNode then @overrides[argument.unescaped.to_sym] = section
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 { @found << [it, :singleton] if it.is_a?(Prism::DefNode) }
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
- @unparsed = []
8
- @all = project.files.to_h { [it, parse(it)] }
7
+ @project = project
9
8
  end
10
9
 
11
- attr_reader :all, :unparsed
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
- @unparsed << path if result.failure?
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})")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hashira
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
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.7.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