Module: IgniterLang::Diagnostics

Defined in:
lib/igniter_lang/diagnostics.rb

Constant Summary collapse

CATEGORIES =
{
  parse_error: "parser_error",
  parse_warning: "parser_warning",
  classified: "classifier_oof",
  typechecked: "typechecker_oof",
  assembler: "assembler_refusal",
  runtime_smoke: "runtime_smoke_failure"
}.freeze

Class Method Summary collapse

Class Method Details

.enrich(entries, category:, contract: nil) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/igniter_lang/diagnostics.rb', line 16

def enrich(entries, category:, contract: nil)
  Array(entries).map do |entry|
    normalized = stringify_keys(entry)
    diagnostic_contract = normalized.key?("contract") ? normalized.fetch("contract") : contract
    node = normalized.fetch("node", nil)
    path = normalized.fetch("path", nil) || path_for(diagnostic_contract, node, normalized)
    span = span_for(normalized)

    normalized.merge(
      "category" => normalized.fetch("category", category),
      "rule" => normalized.fetch("rule", "UNKNOWN"),
      "severity" => normalized.fetch("severity", "error"),
      "message" => normalized.fetch("message", "compiler diagnostic"),
      "contract" => diagnostic_contract,
      "node" => node,
      "path" => path,
      "span" => span
    ).reject { |key, _value| key == "line" || key == "col" }
  end
end

.errors(entries) ⇒ Object



86
87
88
# File 'lib/igniter_lang/diagnostics.rb', line 86

def errors(entries)
  Array(entries).reject { |entry| entry.fetch("severity", nil) == "warning" }
end

.from_assembler_refusal(refusal) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/igniter_lang/diagnostics.rb', line 53

def from_assembler_refusal(refusal)
  enrich(
    [
      {
        "rule" => "ASSEMBLER-REFUSAL",
        "severity" => "error",
        "message" => refusal.respond_to?(:message) ? refusal.message : refusal.to_s
      }
    ],
    category: CATEGORIES.fetch(:assembler)
  )
end

.from_classified(diagnostics, contract: nil) ⇒ Object



45
46
47
# File 'lib/igniter_lang/diagnostics.rb', line 45

def from_classified(diagnostics, contract: nil)
  enrich(diagnostics, category: CATEGORIES.fetch(:classified), contract: contract)
end

.from_parse_errors(errors) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/igniter_lang/diagnostics.rb', line 37

def from_parse_errors(errors)
  Array(errors).flat_map do |entry|
    severity = stringify_keys(entry).fetch("severity", "error")
    category = severity == "warning" ? CATEGORIES.fetch(:parse_warning) : CATEGORIES.fetch(:parse_error)
    enrich([entry], category: category)
  end
end

.from_runtime_smoke(smoke) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/igniter_lang/diagnostics.rb', line 66

def from_runtime_smoke(smoke)
  return [] if smoke.fetch("trusted", false)

  enrich(
    [
      {
        "rule" => "OOF-RUNTIME-SMOKE",
        "severity" => "error",
        "message" => "RuntimeMachine load/evaluate smoke failed",
        "details" => smoke
      }
    ],
    category: CATEGORIES.fetch(:runtime_smoke)
  )
end

.from_typechecked(diagnostics, contract: nil) ⇒ Object



49
50
51
# File 'lib/igniter_lang/diagnostics.rb', line 49

def from_typechecked(diagnostics, contract: nil)
  enrich(diagnostics, category: CATEGORIES.fetch(:typechecked), contract: contract)
end

.node_kind(entry) ⇒ Object



110
111
112
# File 'lib/igniter_lang/diagnostics.rb', line 110

def node_kind(entry)
  entry.fetch("node_kind", nil) || entry.fetch("kind", nil) || "node"
end

.path_for(contract, node, entry) ⇒ Object



101
102
103
104
105
106
107
108
# File 'lib/igniter_lang/diagnostics.rb', line 101

def path_for(contract, node, entry)
  return nil unless contract || node

  parts = []
  parts << "contract:#{contract}" if contract
  parts << "#{node_kind(entry)}:#{node}" if node
  parts.join("/")
end

.span_for(entry) ⇒ Object



114
115
116
117
118
119
120
121
122
123
# File 'lib/igniter_lang/diagnostics.rb', line 114

def span_for(entry)
  span = entry.fetch("span", nil)
  return span if span.is_a?(Hash)

  line = entry.fetch("line", nil)
  col = entry.fetch("col", nil) || entry.fetch("column", nil)
  return nil unless line && col

  { "line" => line, "col" => col }
end

.stringify_keys(value) ⇒ Object



90
91
92
93
94
95
96
97
98
99
# File 'lib/igniter_lang/diagnostics.rb', line 90

def stringify_keys(value)
  case value
  when Hash
    value.each_with_object({}) { |(key, entry), out| out[key.to_s] = stringify_keys(entry) }
  when Array
    value.map { |entry| stringify_keys(entry) }
  else
    value
  end
end

.warnings(entries) ⇒ Object



82
83
84
# File 'lib/igniter_lang/diagnostics.rb', line 82

def warnings(entries)
  Array(entries).select { |entry| entry.fetch("severity", nil) == "warning" }
end