Class: Necropsy::Report
- Inherits:
-
Object
- Object
- Necropsy::Report
- Defined in:
- lib/necropsy/report.rb
Constant Summary collapse
- SCHEMA_VERSION =
2- SCHEMA_PATH =
File.("../../schema/necropsy-report-v#{SCHEMA_VERSION}.schema.json", __dir__).freeze
- ACTIONABLE_CLASSIFICATIONS =
%i[unreachable unused].freeze
- FINGERPRINT_COMPATIBILITY =
{ 'fingerprint' => 'legacy logical symbol fingerprint retained for compatibility', 'physical_fingerprint' => 'physical definition fingerprint for baselines and definition-level matching' }.freeze
Instance Attribute Summary collapse
-
#analysis_health ⇒ Object
readonly
Returns the value of attribute analysis_health.
-
#findings ⇒ Object
readonly
Returns the value of attribute findings.
-
#graph ⇒ Object
readonly
Returns the value of attribute graph.
-
#performance_profile ⇒ Object
readonly
Returns the value of attribute performance_profile.
-
#project ⇒ Object
readonly
Returns the value of attribute project.
-
#reachability ⇒ Object
readonly
Returns the value of attribute reachability.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
-
#source_snapshot ⇒ Object
readonly
Returns the value of attribute source_snapshot.
Class Method Summary collapse
Instance Method Summary collapse
-
#actionable_candidates(min_confidence: :low) ⇒ Object
Unlike the legacy dead_methods API, this excludes findings that exist to explain uncertainty or test-only reachability.
- #blocked_methods ⇒ Object
- #dead_methods(min_confidence: :low) ⇒ Object
- #diagnostic_findings ⇒ Object
- #diagnostics ⇒ Object
- #finding_for_definition(definition_id) ⇒ Object
-
#initialize(root:, graph:, findings:, reachability: nil, report_include_paths: [], report_exclude_paths: [], project: nil, source_snapshot: nil, performance_profile: nil, analysis_health: nil) ⇒ Report
constructor
A new instance of Report.
- #report_path?(path) ⇒ Boolean
- #reportable_findings ⇒ Object
- #summary ⇒ Object
- #to_h(include_graph: false) ⇒ Object
- #to_json(state = nil, include_graph: false) ⇒ Object
- #to_yaml(include_graph: false) ⇒ Object
Constructor Details
#initialize(root:, graph:, findings:, reachability: nil, report_include_paths: [], report_exclude_paths: [], project: nil, source_snapshot: nil, performance_profile: nil, analysis_health: nil) ⇒ Report
Returns a new instance of Report.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/necropsy/report.rb', line 24 def initialize(root:, graph:, findings:, reachability: nil, report_include_paths: [], report_exclude_paths: [], project: nil, source_snapshot: nil, performance_profile: nil, analysis_health: nil) @root = root @graph = graph @findings = findings.sort_by do |finding| [finding.node.file, finding.node.line, finding.node.id, finding.node.definition_id] end @reachability = reachability @project = project @source_snapshot = source_snapshot @performance_profile = performance_profile @analysis_health = analysis_health || AnalysisHealth.complete @report_include_paths = report_include_paths @report_exclude_paths = report_exclude_paths end |
Instance Attribute Details
#analysis_health ⇒ Object (readonly)
Returns the value of attribute analysis_health.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def analysis_health @analysis_health end |
#findings ⇒ Object (readonly)
Returns the value of attribute findings.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def findings @findings end |
#graph ⇒ Object (readonly)
Returns the value of attribute graph.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def graph @graph end |
#performance_profile ⇒ Object (readonly)
Returns the value of attribute performance_profile.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def performance_profile @performance_profile end |
#project ⇒ Object (readonly)
Returns the value of attribute project.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def project @project end |
#reachability ⇒ Object (readonly)
Returns the value of attribute reachability.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def reachability @reachability end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def root @root end |
#source_snapshot ⇒ Object (readonly)
Returns the value of attribute source_snapshot.
17 18 19 |
# File 'lib/necropsy/report.rb', line 17 def source_snapshot @source_snapshot end |
Class Method Details
.schema_path ⇒ Object
20 21 22 |
# File 'lib/necropsy/report.rb', line 20 def self.schema_path SCHEMA_PATH end |
Instance Method Details
#actionable_candidates(min_confidence: :low) ⇒ Object
Unlike the legacy dead_methods API, this excludes findings that exist to explain uncertainty or test-only reachability. Benchmarks and precision gates must measure only definitions that a user can actually review as a removal candidate.
48 49 50 51 52 |
# File 'lib/necropsy/report.rb', line 48 def actionable_candidates(min_confidence: :low) reported_findings.select do |finding| ACTIONABLE_CLASSIFICATIONS.include?(finding.classification) && finding.at_least?(min_confidence) end end |
#blocked_methods ⇒ Object
71 72 73 |
# File 'lib/necropsy/report.rb', line 71 def blocked_methods reported_findings.select { |finding| finding.classification == :blocked } end |
#dead_methods(min_confidence: :low) ⇒ Object
40 41 42 |
# File 'lib/necropsy/report.rb', line 40 def dead_methods(min_confidence: :low) reported_findings.select { |finding| finding.at_least?(min_confidence) } end |
#diagnostic_findings ⇒ Object
54 55 56 |
# File 'lib/necropsy/report.rb', line 54 def diagnostic_findings reported_findings.reject { |finding| ACTIONABLE_CLASSIFICATIONS.include?(finding.classification) } end |
#diagnostics ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/necropsy/report.rb', line 122 def diagnostics result = {} dynamic = graph.dynamic_evidence_diagnostic result['dynamic_evidence'] = dynamic if dynamic definition_resolution = graph.observation['definition_resolution'] result['definition_resolution'] = definition_resolution if definition_resolution = graph.observation['non_ruby_reference_barrier'] result['non_ruby_reference_barrier'] = if result['source_incompleteness'] = graph.source_incompleteness if graph.incomplete_files.any? result['analysis_scope'] = graph.scope_diagnostics unless graph.scope_diagnostics.empty? unrooted = graph.observation['unrooted_load_units'] result['unrooted_load_units'] = unrooted if unrooted && unrooted['count'].positive? generated_macros = graph.observation['rails_generated_macros'] result['rails_generated_macros'] = generated_macros if generated_macros result['performance'] = performance_profile if performance_profile result end |
#finding_for_definition(definition_id) ⇒ Object
62 63 64 65 |
# File 'lib/necropsy/report.rb', line 62 def finding_for_definition(definition_id) @findings_by_definition ||= findings.to_h { |finding| [finding.node.graph_id, finding] }.freeze @findings_by_definition[definition_id.to_s] end |
#report_path?(path) ⇒ Boolean
67 68 69 |
# File 'lib/necropsy/report.rb', line 67 def report_path?(path) included_in_report?(path) && !excluded_from_report?(path) end |
#reportable_findings ⇒ Object
58 59 60 |
# File 'lib/necropsy/report.rb', line 58 def reportable_findings reported_findings.dup end |
#summary ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/necropsy/report.rb', line 102 def summary grouped = reported_findings.group_by(&:classification) actionable = reported_findings.count { |finding| ACTIONABLE_CLASSIFICATIONS.include?(finding.classification) } blocked = grouped.fetch(:blocked, []).length { 'nodes' => graph.nodes.length, 'edges' => graph.edges.length, 'entry_points' => graph.entry_points.length, 'incomplete_files' => graph.incomplete_files.length, 'findings' => reported_findings.length, 'actionable' => actionable, 'diagnostic' => reported_findings.length - actionable - blocked, 'health_failures' => analysis_health.reasons.length, 'unreachable' => grouped.fetch(:unreachable, []).length, 'unused' => grouped.fetch(:unused, []).length, 'blocked' => blocked, 'test_only_reachable' => grouped.fetch(:test_only_reachable, []).length } end |
#to_h(include_graph: false) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/necropsy/report.rb', line 75 def to_h(include_graph: false) payload = { 'schema_version' => SCHEMA_VERSION, 'artifact_provenance' => artifact_provenance, 'compatibility' => { 'finding_fingerprints' => FINGERPRINT_COMPATIBILITY }, 'root' => root, 'analysis_health' => analysis_health.to_h, 'summary' => summary, 'findings' => reported_findings.map(&:to_h) } payload['diagnostics'] = diagnostics unless diagnostics.empty? payload['source_snapshot'] = source_snapshot if source_snapshot payload['graph'] = graph.to_h if include_graph payload end |
#to_json(state = nil, include_graph: false) ⇒ Object
91 92 93 94 95 96 |
# File 'lib/necropsy/report.rb', line 91 def to_json(state = nil, include_graph: false) payload = to_h(include_graph: include_graph) return JSON.pretty_generate(payload) unless state payload.to_json(state) end |
#to_yaml(include_graph: false) ⇒ Object
98 99 100 |
# File 'lib/necropsy/report.rb', line 98 def to_yaml(include_graph: false) to_h(include_graph: include_graph).to_yaml end |