hashira 0.3.0 → 0.5.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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +155 -0
  3. data/README.md +127 -25
  4. data/lib/hashira/analysis/finding.rb +7 -6
  5. data/lib/hashira/analysis/node_walk.rb +3 -3
  6. data/lib/hashira/analysis/syntax.rb +21 -6
  7. data/lib/hashira/analysis/type_walk.rb +7 -9
  8. data/lib/hashira/churn.rb +11 -13
  9. data/lib/hashira/ci/accepted.rb +27 -31
  10. data/lib/hashira/ci/baseline.rb +22 -24
  11. data/lib/hashira/ci/diff.rb +8 -7
  12. data/lib/hashira/ci/edge_diff_report.rb +13 -17
  13. data/lib/hashira/ci/finding_diff_report.rb +14 -18
  14. data/lib/hashira/ci/gate.rb +19 -23
  15. data/lib/hashira/ci/improvement.rb +9 -14
  16. data/lib/hashira/ci/ratchet.rb +32 -27
  17. data/lib/hashira/ci/ratchet_report.rb +26 -32
  18. data/lib/hashira/cli/arguments.rb +23 -0
  19. data/lib/hashira/cli/command_line.rb +55 -71
  20. data/lib/hashira/cli/fail_on.rb +24 -19
  21. data/lib/hashira/cli/options.rb +9 -4
  22. data/lib/hashira/cli/package_by.rb +14 -0
  23. data/lib/hashira/cli/run.rb +33 -27
  24. data/lib/hashira/cli/skip.rb +14 -20
  25. data/lib/hashira/cli/usage.rb +44 -41
  26. data/lib/hashira/cli.rb +17 -19
  27. data/lib/hashira/complexity/boolean_run.rb +13 -15
  28. data/lib/hashira/complexity/cognitive_score.rb +68 -66
  29. data/lib/hashira/complexity/if_chain.rb +33 -38
  30. data/lib/hashira/complexity/method_finding.rb +20 -42
  31. data/lib/hashira/complexity/method_score.rb +9 -7
  32. data/lib/hashira/complexity/rescue_scan.rb +16 -21
  33. data/lib/hashira/complexity/rollup.rb +13 -15
  34. data/lib/hashira/complexity/scores.rb +44 -0
  35. data/lib/hashira/coupling/audiences.rb +49 -0
  36. data/lib/hashira/coupling/catalog.rb +27 -0
  37. data/lib/hashira/coupling/census.rb +55 -0
  38. data/lib/hashira/coupling/constant_registry.rb +47 -0
  39. data/lib/hashira/coupling/cycle_findings.rb +31 -0
  40. data/lib/hashira/coupling/cycle_search.rb +37 -0
  41. data/lib/hashira/coupling/cycles.rb +14 -0
  42. data/lib/hashira/coupling/definition.rb +22 -0
  43. data/lib/hashira/coupling/definitions.rb +31 -0
  44. data/lib/hashira/coupling/edge.rb +10 -0
  45. data/lib/hashira/coupling/edge_map.rb +33 -0
  46. data/lib/hashira/coupling/folder_placement.rb +17 -0
  47. data/lib/hashira/coupling/folding.rb +56 -0
  48. data/lib/hashira/coupling/graph.rb +63 -0
  49. data/lib/hashira/coupling/metric.rb +15 -0
  50. data/lib/hashira/coupling/mixed_audience_findings.rb +32 -0
  51. data/lib/hashira/coupling/namespace_placement.rb +28 -0
  52. data/lib/hashira/{analysis → coupling}/namespace_prefix.rb +11 -11
  53. data/lib/hashira/coupling/naming.rb +14 -0
  54. data/lib/hashira/coupling/no_folding.rb +13 -0
  55. data/lib/hashira/coupling/placement.rb +19 -0
  56. data/lib/hashira/coupling/references.rb +55 -0
  57. data/lib/hashira/coupling/report.rb +28 -0
  58. data/lib/hashira/coupling/roll_call.rb +45 -0
  59. data/lib/hashira/coupling/roll_call_findings.rb +30 -0
  60. data/lib/hashira/coupling/roster.rb +25 -0
  61. data/lib/hashira/coupling/rule.rb +18 -0
  62. data/lib/hashira/coupling/scope.rb +28 -0
  63. data/lib/hashira/coupling/sdp_check.rb +14 -0
  64. data/lib/hashira/coupling/sdp_violation_findings.rb +25 -0
  65. data/lib/hashira/coupling/wide_edge_findings.rb +25 -0
  66. data/lib/hashira/coupling/words.rb +29 -0
  67. data/lib/hashira/diagram/dot.rb +8 -11
  68. data/lib/hashira/diagram/mermaid.rb +11 -16
  69. data/lib/hashira/diagram/source.rb +15 -0
  70. data/lib/hashira/duplication/clones.rb +17 -0
  71. data/lib/hashira/duplication/cluster.rb +14 -13
  72. data/lib/hashira/duplication/clusters.rb +46 -0
  73. data/lib/hashira/duplication/delta.rb +21 -30
  74. data/lib/hashira/duplication/duplication_finding.rb +17 -23
  75. data/lib/hashira/duplication/fragment.rb +19 -23
  76. data/lib/hashira/duplication/grouping.rb +12 -16
  77. data/lib/hashira/duplication/harvest.rb +27 -0
  78. data/lib/hashira/duplication/index.rb +20 -24
  79. data/lib/hashira/duplication/maximal.rb +11 -15
  80. data/lib/hashira/duplication/near_miss.rb +19 -22
  81. data/lib/hashira/duplication/sequence.rb +20 -22
  82. data/lib/hashira/duplication/similarity.rb +31 -34
  83. data/lib/hashira/duplication/union_find.rb +10 -14
  84. data/lib/hashira/duplication/variance.rb +35 -43
  85. data/lib/hashira/error.rb +4 -2
  86. data/lib/hashira/hotspots/file_cost.rb +8 -7
  87. data/lib/hashira/hotspots/rollup.rb +19 -21
  88. data/lib/hashira/pipeline.rb +41 -31
  89. data/lib/hashira/project.rb +50 -49
  90. data/lib/hashira/report/complexity_table.rb +27 -31
  91. data/lib/hashira/report/dependency_map.rb +17 -22
  92. data/lib/hashira/report/finding_lines.rb +15 -19
  93. data/lib/hashira/report/graph_payload.rb +14 -20
  94. data/lib/hashira/report/hotspot_table.rb +32 -37
  95. data/lib/hashira/report/json.rb +32 -32
  96. data/lib/hashira/report/metrics_table.rb +46 -33
  97. data/lib/hashira/report/phrases.rb +98 -0
  98. data/lib/hashira/report/smell_phrases.rb +68 -0
  99. data/lib/hashira/report/text.rb +59 -52
  100. data/lib/hashira/smells/census.rb +32 -0
  101. data/lib/hashira/smells/check.rb +30 -0
  102. data/lib/hashira/smells/conditions.rb +44 -0
  103. data/lib/hashira/smells/contexts.rb +64 -0
  104. data/lib/hashira/smells/control_parameter.rb +22 -0
  105. data/lib/hashira/smells/data_clump.rb +30 -0
  106. data/lib/hashira/smells/duplicate_method_call.rb +54 -0
  107. data/lib/hashira/smells/feature_envy.rb +17 -0
  108. data/lib/hashira/smells/instance_variable_assumption.rb +44 -0
  109. data/lib/hashira/smells/manual_dispatch.rb +16 -0
  110. data/lib/hashira/smells/module_initialize.rb +9 -0
  111. data/lib/hashira/smells/nil_check.rb +32 -0
  112. data/lib/hashira/smells/param_check.rb +51 -0
  113. data/lib/hashira/smells/refs.rb +53 -0
  114. data/lib/hashira/smells/repeated_conditional.rb +29 -0
  115. data/lib/hashira/smells/report.rb +31 -0
  116. data/lib/hashira/smells/scope.rb +29 -0
  117. data/lib/hashira/smells/too_many_instance_variables.rb +24 -0
  118. data/lib/hashira/smells/utility_function.rb +13 -0
  119. data/lib/hashira/smells/visibility.rb +64 -0
  120. data/lib/hashira/version.rb +1 -1
  121. data/lib/hashira.rb +131 -60
  122. metadata +62 -20
  123. data/lib/hashira/analysis/census.rb +0 -39
  124. data/lib/hashira/analysis/constant_registry.rb +0 -40
  125. data/lib/hashira/analysis/cycle_findings.rb +0 -33
  126. data/lib/hashira/analysis/cycle_search.rb +0 -44
  127. data/lib/hashira/analysis/definitions.rb +0 -27
  128. data/lib/hashira/analysis/edge.rb +0 -9
  129. data/lib/hashira/analysis/edge_map.rb +0 -36
  130. data/lib/hashira/analysis/graph.rb +0 -56
  131. data/lib/hashira/analysis/metric.rb +0 -14
  132. data/lib/hashira/analysis/references.rb +0 -39
  133. data/lib/hashira/analysis/rule.rb +0 -22
  134. data/lib/hashira/analysis/sdp_check.rb +0 -18
  135. data/lib/hashira/analysis/sdp_violation_findings.rb +0 -27
  136. data/lib/hashira/complexity/analyzer.rb +0 -45
  137. data/lib/hashira/diagram/renderer.rb +0 -20
  138. data/lib/hashira/duplication/analyzer.rb +0 -21
  139. data/lib/hashira/duplication/clusterer.rb +0 -50
  140. data/lib/hashira/duplication/extractor.rb +0 -31
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::InstanceVariableAssumption < Hashira::Smells::Check
6
+ SETTERS = [
7
+ Prism::InstanceVariableWriteNode, Prism::InstanceVariableOrWriteNode,
8
+ Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode,
9
+ Prism::InstanceVariableTargetNode
10
+ ].freeze
11
+
12
+ MAYBES = [
13
+ Prism::LocalVariableOrWriteNode, Prism::InstanceVariableOrWriteNode,
14
+ Prism::ClassVariableOrWriteNode, Prism::GlobalVariableOrWriteNode,
15
+ Prism::ConstantOrWriteNode, Prism::ConstantPathOrWriteNode,
16
+ Prism::CallOrWriteNode, Prism::IndexOrWriteNode
17
+ ].freeze
18
+
19
+ module Harvest
20
+ module_function
21
+
22
+ def reads(node)
23
+ return [] if MAYBES.include?(node.class)
24
+ below = node.compact_child_nodes.flat_map { reads(it) }
25
+ node.is_a?(Prism::InstanceVariableReadNode) ? below + [node.name] : below
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def smelly? = subject.kind == :class && assumed.any?
32
+
33
+ def assumed = @assumed ||= (read - prepared).uniq.sort
34
+
35
+ def read = subject.owned.flat_map { Harvest.reads(it.node) }
36
+
37
+ def prepared
38
+ starters.flat_map { Hashira::Smells::Scope.inside(it.node) }.select { SETTERS.include?(it.class) }.map(&:name)
39
+ end
40
+
41
+ def starters = subject.owned.select { it.node.name == :initialize }
42
+
43
+ def evidence = assumed.map(&:to_s)
44
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::ManualDispatch < Hashira::Smells::Check
6
+ private
7
+
8
+ def smelly? = sightings.any?
9
+
10
+ def sightings
11
+ @sightings ||= Hashira::Smells::Scope.inside(subject.node)
12
+ .select { it.is_a?(Prism::CallNode) && it.name == :respond_to? }
13
+ end
14
+
15
+ def detail = { site: spots(sightings) }
16
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hashira::Smells::ModuleInitialize < Hashira::Smells::Check
4
+ private
5
+
6
+ def smelly? = subject.kind == :module && flagged
7
+
8
+ def flagged = subject.owned.any? { it.node.name == :initialize }
9
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::NilCheck < Hashira::Smells::Check
6
+ EQUALITY = %i[== ===].freeze
7
+
8
+ private
9
+
10
+ def smelly? = checks.any?
11
+
12
+ def checks = @checks ||= Hashira::Smells::Scope.inside(subject.node).select { check?(it) }
13
+
14
+ def check?(node)
15
+ case node
16
+ when Prism::CallNode then query?(node)
17
+ when Prism::WhenNode then node.conditions.any?(Prism::NilNode)
18
+ else false
19
+ end
20
+ end
21
+
22
+ def query?(node)
23
+ name = node.name
24
+ name == :nil? || (EQUALITY.include?(name) && equated?(node))
25
+ end
26
+
27
+ def equated?(node) = sides(node).any?(Prism::NilNode)
28
+
29
+ def sides(node) = [node.receiver] + (node.arguments&.arguments || [])
30
+
31
+ def detail = { site: spots(checks) }
32
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::ParamCheck
6
+ COMPARISONS = %i[== != =~].freeze
7
+
8
+ def initialize(node, name)
9
+ @node = node
10
+ @name = name
11
+ end
12
+
13
+ def matches
14
+ return [] if legitimate?
15
+ nested.flat_map(&:matches) + tested
16
+ end
17
+
18
+ def legitimate?
19
+ absolved? || working? || nested.any?(&:legitimate?)
20
+ end
21
+
22
+ private
23
+
24
+ def nested
25
+ @nested ||= Hashira::Smells::Conditions.nested(branches).map { self.class.new(it, @name) }
26
+ end
27
+
28
+ def branches = Hashira::Smells::Conditions.branches(@node)
29
+
30
+ def predicate
31
+ @predicate ||= spread(Hashira::Smells::Conditions.condition(@node))
32
+ end
33
+
34
+ def spread(condition) = condition ? Hashira::Analysis::NodeWalk.collect(condition) : []
35
+
36
+ def tested = reads(predicate)
37
+
38
+ def working?
39
+ reads(branches.compact.flat_map { Hashira::Smells::Conditions.plain(it) }).any?
40
+ end
41
+
42
+ def absolved?
43
+ predicate.grep(Prism::CallNode).any? { absolves?(it) }
44
+ end
45
+
46
+ def absolves?(call)
47
+ !COMPARISONS.include?(call.name) && reads(Hashira::Analysis::NodeWalk.collect(call)).any?
48
+ end
49
+
50
+ def reads(nodes) = nodes.grep(Prism::LocalVariableReadNode).select { it.name == @name }
51
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::Refs
6
+ SELVES = [
7
+ Prism::SelfNode, Prism::SuperNode, Prism::ForwardingSuperNode,
8
+ Prism::InstanceVariableReadNode, Prism::InstanceVariableWriteNode,
9
+ Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableAndWriteNode,
10
+ Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableTargetNode
11
+ ].freeze
12
+
13
+ def initialize(def_node)
14
+ @tallies = {}
15
+ Hashira::Smells::Scope.inside(def_node).each { record(it) }
16
+ end
17
+
18
+ def ego = lines(:self).size
19
+
20
+ def lines(name) = @tallies.fetch(name, [])
21
+
22
+ def envious
23
+ peak = @tallies.values.map(&:size).max
24
+ names = @tallies.filter_map { |name, sightings| name if sightings.size == peak }
25
+ names.include?(:self) ? [] : names
26
+ end
27
+
28
+ private
29
+
30
+ def record(node)
31
+ return note(:self, node) if selfish?(node)
32
+ case node
33
+ when Prism::CallNode then named(node)
34
+ when Prism::LocalVariableOperatorWriteNode then note(node.name, node)
35
+ end
36
+ end
37
+
38
+ def selfish?(node)
39
+ SELVES.include?(node.class) || implicit?(node)
40
+ end
41
+
42
+ def implicit?(node) = node.is_a?(Prism::CallNode) && !node.receiver
43
+
44
+ def named(node)
45
+ case (receiver = node.receiver)
46
+ when Prism::SelfNode then note(:self, node)
47
+ when Prism::LocalVariableReadNode, Prism::LocalVariableWriteNode
48
+ note(receiver.name, node) unless node.name == :new
49
+ end
50
+ end
51
+
52
+ def note(name, node) = (@tallies[name] ||= []) << node.location.start_line
53
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::RepeatedConditional < Hashira::Smells::Check
6
+ LIMIT = 2
7
+
8
+ private
9
+
10
+ def smelly? = subject.kind == :class && repeats.any?
11
+
12
+ def repeats
13
+ @repeats ||= predicates.group_by(&:slice).except("block_given?").select { |_test, nodes| nodes.size > LIMIT }
14
+ end
15
+
16
+ def predicates = Hashira::Smells::Scope.sweep(subject.node).filter_map { predicate(it) }
17
+
18
+ def predicate(node)
19
+ node.predicate if node.is_a?(Prism::IfNode) || node.is_a?(Prism::UnlessNode) || node.is_a?(Prism::CaseNode)
20
+ end
21
+
22
+ def detail = { site:, count: repeats.values.map(&:size).max }
23
+
24
+ def evidence = repeats.map { |test, nodes| row(test, nodes) }
25
+
26
+ def row(test, nodes)
27
+ "#{test} × #{nodes.size} (lines #{nodes.map { |node| node.location.start_line }.join(", ")})"
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hashira::Smells::Report
4
+ def initialize(project, trees)
5
+ @types = Hashira::Smells::Census.new(project, trees).types
6
+ end
7
+
8
+ def findings = @findings ||= sniff(@types, judges) + sniff(@types.flat_map(&:defs), probes)
9
+
10
+ private
11
+
12
+ def sniff(subjects, checks) = subjects.flat_map { |subject| verdicts(subject, checks) }
13
+
14
+ def verdicts(subject, checks) = checks.filter_map { |check| check.new(subject).finding }
15
+
16
+ def judges
17
+ [
18
+ Hashira::Smells::DataClump, Hashira::Smells::InstanceVariableAssumption,
19
+ Hashira::Smells::ModuleInitialize, Hashira::Smells::RepeatedConditional,
20
+ Hashira::Smells::TooManyInstanceVariables
21
+ ]
22
+ end
23
+
24
+ def probes
25
+ [
26
+ Hashira::Smells::ControlParameter, Hashira::Smells::DuplicateMethodCall,
27
+ Hashira::Smells::FeatureEnvy, Hashira::Smells::ManualDispatch,
28
+ Hashira::Smells::NilCheck, Hashira::Smells::UtilityFunction
29
+ ]
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ module Hashira::Smells::Scope
6
+ TYPES = [Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode].freeze
7
+
8
+ FENCES = (TYPES + [Prism::DefNode]).freeze
9
+
10
+ module_function
11
+
12
+ def inside(node) = below(node, FENCES)
13
+
14
+ def sweep(node) = below(node, TYPES)
15
+
16
+ def below(root, fences)
17
+ found = []
18
+ visit(root, fences) { found << it }
19
+ found
20
+ end
21
+
22
+ def visit(root, fences, &)
23
+ root.compact_child_nodes.each do |child|
24
+ next if fences.include?(child.class)
25
+ yield(child)
26
+ visit(child, fences, &)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::TooManyInstanceVariables < Hashira::Smells::Check
6
+ LIMIT = 4
7
+
8
+ COUNTED = [
9
+ Prism::InstanceVariableWriteNode, Prism::InstanceVariableAndWriteNode,
10
+ Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableTargetNode
11
+ ].freeze
12
+
13
+ private
14
+
15
+ def smelly? = subject.kind == :class && names.size > LIMIT
16
+
17
+ def names
18
+ @names ||= Hashira::Smells::Scope.sweep(subject.node).select { COUNTED.include?(it.class) }.map(&:name).uniq.sort
19
+ end
20
+
21
+ def detail = { site:, count: names.size }
22
+
23
+ def evidence = names.map(&:to_s)
24
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::UtilityFunction < Hashira::Smells::Check
6
+ private
7
+
8
+ def smelly? = subject.public? && calls? && refs.ego.zero?
9
+
10
+ def refs = Hashira::Smells::Refs.new(subject.node)
11
+
12
+ def calls? = Hashira::Smells::Scope.inside(subject.node).any?(Prism::CallNode)
13
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ class Hashira::Smells::Visibility
6
+ MARKERS = %i[public private protected module_function].freeze
7
+
8
+ def initialize(type_node)
9
+ @section = :public
10
+ @overrides = {}
11
+ @found = []
12
+ scan(statements(type_node))
13
+ end
14
+
15
+ def entries = @found.map { |node, section| [node, @overrides.fetch(node.name, section)] }
16
+
17
+ private
18
+
19
+ def statements(node)
20
+ body = node.body
21
+ body.is_a?(Prism::StatementsNode) ? body.body : []
22
+ end
23
+
24
+ def scan(nodes) = nodes.each { classify(it) }
25
+
26
+ def classify(node)
27
+ case node
28
+ when Prism::DefNode then @found << [node, @section]
29
+ when Prism::CallNode then heed(node)
30
+ else descend(node)
31
+ end
32
+ end
33
+
34
+ def descend(node)
35
+ case node
36
+ when Prism::SingletonClassNode then shadow(node)
37
+ when Prism::ClassNode, Prism::ModuleNode, Prism::ConstantWriteNode then nil
38
+ else scan(node.compact_child_nodes)
39
+ end
40
+ end
41
+
42
+ def heed(node)
43
+ bare?(node) ? switch(node.name, node.arguments) : scan(node.compact_child_nodes)
44
+ end
45
+
46
+ def bare?(node) = MARKERS.include?(node.name) && !node.receiver
47
+
48
+ def switch(name, arguments)
49
+ arguments ? tag(name, arguments.arguments) : (@section = name)
50
+ end
51
+
52
+ def tag(section, arguments) = arguments.each { note(it, section) }
53
+
54
+ def note(argument, section)
55
+ case argument
56
+ when Prism::DefNode then @found << [argument, section]
57
+ when Prism::SymbolNode then @overrides[argument.unescaped.to_sym] = section
58
+ end
59
+ end
60
+
61
+ def shadow(node)
62
+ statements(node).each { @found << [it, :singleton] if it.is_a?(Prism::DefNode) }
63
+ end
64
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hashira
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/hashira.rb CHANGED
@@ -1,78 +1,149 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "hashira/version"
4
- require_relative "hashira/error"
5
- require_relative "hashira/project"
6
- require_relative "hashira/churn"
7
- require_relative "hashira/analysis/syntax"
3
+ module Hashira
4
+ module Analysis
5
+ end
6
+
7
+ module CI
8
+ end
9
+
10
+ module Coupling
11
+ end
12
+
13
+ module Complexity
14
+ end
15
+
16
+ module Diagram
17
+ end
18
+
19
+ module Duplication
20
+ end
21
+
22
+ module Hotspots
23
+ end
24
+
25
+ module Report
26
+ end
27
+
28
+ module Smells
29
+ end
30
+ end
31
+
32
+ require_relative "hashira/analysis/finding"
8
33
  require_relative "hashira/analysis/node_walk"
34
+ require_relative "hashira/analysis/syntax"
9
35
  require_relative "hashira/analysis/type_walk"
10
- require_relative "hashira/analysis/references"
11
- require_relative "hashira/analysis/definitions"
12
- require_relative "hashira/analysis/namespace_prefix"
13
- require_relative "hashira/analysis/constant_registry"
14
- require_relative "hashira/analysis/census"
15
- require_relative "hashira/analysis/edge"
16
- require_relative "hashira/analysis/edge_map"
17
- require_relative "hashira/analysis/metric"
18
- require_relative "hashira/analysis/sdp_check"
19
- require_relative "hashira/analysis/cycle_search"
20
- require_relative "hashira/analysis/graph"
21
- require_relative "hashira/analysis/finding"
22
- require_relative "hashira/analysis/rule"
23
- require_relative "hashira/analysis/cycle_findings"
24
- require_relative "hashira/analysis/sdp_violation_findings"
25
- require_relative "hashira/complexity/method_score"
26
- require_relative "hashira/complexity/rollup"
27
- require_relative "hashira/complexity/if_chain"
36
+ require_relative "hashira/churn"
37
+ require_relative "hashira/ci/accepted"
38
+ require_relative "hashira/ci/baseline"
39
+ require_relative "hashira/ci/diff"
40
+ require_relative "hashira/ci/edge_diff_report"
41
+ require_relative "hashira/ci/finding_diff_report"
42
+ require_relative "hashira/ci/gate"
43
+ require_relative "hashira/ci/improvement"
44
+ require_relative "hashira/ci/ratchet"
45
+ require_relative "hashira/ci/ratchet_report"
46
+ require_relative "hashira/cli"
47
+ require_relative "hashira/cli/arguments"
48
+ require_relative "hashira/cli/command_line"
49
+ require_relative "hashira/cli/fail_on"
50
+ require_relative "hashira/cli/options"
51
+ require_relative "hashira/cli/package_by"
52
+ require_relative "hashira/cli/run"
53
+ require_relative "hashira/cli/skip"
54
+ require_relative "hashira/cli/usage"
28
55
  require_relative "hashira/complexity/boolean_run"
29
- require_relative "hashira/complexity/rescue_scan"
30
56
  require_relative "hashira/complexity/cognitive_score"
57
+ require_relative "hashira/complexity/if_chain"
31
58
  require_relative "hashira/complexity/method_finding"
32
- require_relative "hashira/complexity/analyzer"
33
- require_relative "hashira/duplication/fragment"
34
- require_relative "hashira/duplication/sequence"
35
- require_relative "hashira/duplication/extractor"
36
- require_relative "hashira/duplication/similarity"
37
- require_relative "hashira/duplication/union_find"
59
+ require_relative "hashira/complexity/method_score"
60
+ require_relative "hashira/complexity/rescue_scan"
61
+ require_relative "hashira/complexity/rollup"
62
+ require_relative "hashira/complexity/scores"
63
+ require_relative "hashira/coupling/audiences"
64
+ require_relative "hashira/coupling/catalog"
65
+ require_relative "hashira/coupling/census"
66
+ require_relative "hashira/coupling/constant_registry"
67
+ require_relative "hashira/coupling/cycle_findings"
68
+ require_relative "hashira/coupling/cycle_search"
69
+ require_relative "hashira/coupling/cycles"
70
+ require_relative "hashira/coupling/definition"
71
+ require_relative "hashira/coupling/definitions"
72
+ require_relative "hashira/coupling/edge"
73
+ require_relative "hashira/coupling/edge_map"
74
+ require_relative "hashira/coupling/folder_placement"
75
+ require_relative "hashira/coupling/folding"
76
+ require_relative "hashira/coupling/graph"
77
+ require_relative "hashira/coupling/metric"
78
+ require_relative "hashira/coupling/mixed_audience_findings"
79
+ require_relative "hashira/coupling/namespace_placement"
80
+ require_relative "hashira/coupling/namespace_prefix"
81
+ require_relative "hashira/coupling/naming"
82
+ require_relative "hashira/coupling/no_folding"
83
+ require_relative "hashira/coupling/placement"
84
+ require_relative "hashira/coupling/references"
85
+ require_relative "hashira/coupling/report"
86
+ require_relative "hashira/coupling/roll_call"
87
+ require_relative "hashira/coupling/roll_call_findings"
88
+ require_relative "hashira/coupling/roster"
89
+ require_relative "hashira/coupling/rule"
90
+ require_relative "hashira/coupling/scope"
91
+ require_relative "hashira/coupling/sdp_check"
92
+ require_relative "hashira/coupling/sdp_violation_findings"
93
+ require_relative "hashira/coupling/wide_edge_findings"
94
+ require_relative "hashira/coupling/words"
95
+ require_relative "hashira/diagram/dot"
96
+ require_relative "hashira/diagram/mermaid"
97
+ require_relative "hashira/diagram/source"
98
+ require_relative "hashira/duplication/clones"
38
99
  require_relative "hashira/duplication/cluster"
100
+ require_relative "hashira/duplication/clusters"
101
+ require_relative "hashira/duplication/delta"
102
+ require_relative "hashira/duplication/duplication_finding"
103
+ require_relative "hashira/duplication/fragment"
39
104
  require_relative "hashira/duplication/grouping"
105
+ require_relative "hashira/duplication/harvest"
40
106
  require_relative "hashira/duplication/index"
41
- require_relative "hashira/duplication/near_miss"
42
107
  require_relative "hashira/duplication/maximal"
43
- require_relative "hashira/duplication/clusterer"
108
+ require_relative "hashira/duplication/near_miss"
109
+ require_relative "hashira/duplication/sequence"
110
+ require_relative "hashira/duplication/similarity"
111
+ require_relative "hashira/duplication/union_find"
44
112
  require_relative "hashira/duplication/variance"
45
- require_relative "hashira/duplication/delta"
46
- require_relative "hashira/duplication/duplication_finding"
47
- require_relative "hashira/duplication/analyzer"
48
- require_relative "hashira/diagram/dot"
49
- require_relative "hashira/diagram/mermaid"
50
- require_relative "hashira/diagram/renderer"
51
- require_relative "hashira/ci/accepted"
52
- require_relative "hashira/ci/baseline"
53
- require_relative "hashira/ci/diff"
54
- require_relative "hashira/ci/improvement"
55
- require_relative "hashira/ci/edge_diff_report"
56
- require_relative "hashira/ci/finding_diff_report"
57
- require_relative "hashira/ci/ratchet_report"
58
- require_relative "hashira/ci/gate"
59
- require_relative "hashira/ci/ratchet"
113
+ require_relative "hashira/error"
60
114
  require_relative "hashira/hotspots/file_cost"
61
115
  require_relative "hashira/hotspots/rollup"
62
- require_relative "hashira/report/view"
63
- require_relative "hashira/report/graph_payload"
64
- require_relative "hashira/report/finding_lines"
65
- require_relative "hashira/report/dependency_map"
66
- require_relative "hashira/report/metrics_table"
116
+ require_relative "hashira/pipeline"
117
+ require_relative "hashira/project"
67
118
  require_relative "hashira/report/complexity_table"
119
+ require_relative "hashira/report/dependency_map"
120
+ require_relative "hashira/report/finding_lines"
121
+ require_relative "hashira/report/graph_payload"
68
122
  require_relative "hashira/report/hotspot_table"
69
123
  require_relative "hashira/report/json"
124
+ require_relative "hashira/report/metrics_table"
125
+ require_relative "hashira/report/phrases"
126
+ require_relative "hashira/report/smell_phrases"
70
127
  require_relative "hashira/report/text"
71
- require_relative "hashira/pipeline"
72
- require_relative "hashira/cli"
73
- require_relative "hashira/cli/fail_on"
74
- require_relative "hashira/cli/skip"
75
- require_relative "hashira/cli/options"
76
- require_relative "hashira/cli/command_line"
77
- require_relative "hashira/cli/usage"
78
- require_relative "hashira/cli/run"
128
+ require_relative "hashira/report/view"
129
+ require_relative "hashira/smells/census"
130
+ require_relative "hashira/smells/check"
131
+ require_relative "hashira/smells/conditions"
132
+ require_relative "hashira/smells/contexts"
133
+ require_relative "hashira/smells/control_parameter"
134
+ require_relative "hashira/smells/data_clump"
135
+ require_relative "hashira/smells/duplicate_method_call"
136
+ require_relative "hashira/smells/feature_envy"
137
+ require_relative "hashira/smells/instance_variable_assumption"
138
+ require_relative "hashira/smells/manual_dispatch"
139
+ require_relative "hashira/smells/module_initialize"
140
+ require_relative "hashira/smells/nil_check"
141
+ require_relative "hashira/smells/param_check"
142
+ require_relative "hashira/smells/refs"
143
+ require_relative "hashira/smells/repeated_conditional"
144
+ require_relative "hashira/smells/report"
145
+ require_relative "hashira/smells/scope"
146
+ require_relative "hashira/smells/too_many_instance_variables"
147
+ require_relative "hashira/smells/utility_function"
148
+ require_relative "hashira/smells/visibility"
149
+ require_relative "hashira/version"