Module: Ibex::CLIDiagnostics

Defined in:
lib/ibex/cli/diagnostics.rb,
sig/ibex/cli/diagnostics.rbs

Overview

Batch frontend diagnostics subcommand.

Constant Summary collapse

DIAGNOSTICS_SCHEMA_VERSION =

Signature:

  • Integer

Returns:

  • (Integer)
1
DEFAULT_MAX_DIAGNOSTICS =

Signature:

  • Integer

Returns:

  • (Integer)
20

Instance Method Summary collapse

Instance Method Details

#diagnostics_document(result) ⇒ Hash[String, Object?]

RBS:

  • (Frontend::ParseResult result) -> Hash[String, Object?]

Parameters:

Returns:

  • (Hash[String, Object?])


107
108
109
110
111
112
113
114
115
# File 'lib/ibex/cli/diagnostics.rb', line 107

def diagnostics_document(result)
  {
    "ibex_diagnostics" => "frontend",
    "schema_version" => DIAGNOSTICS_SCHEMA_VERSION,
    "success" => result.success?,
    "ast_available" => !result.ast.nil?,
    "diagnostics" => result.diagnostics.map { |diagnostic| localized_diagnostic_hash(diagnostic) }
  }
end

#diagnostics_option_parser(settings) ⇒ OptionParser

RBS:

  • (diagnostic_settings settings) -> OptionParser

Parameters:

  • settings (diagnostic_settings)

Returns:

  • (OptionParser)


73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ibex/cli/diagnostics.rb', line 73

def diagnostics_option_parser(settings)
  OptionParser.new do |options|
    options.banner = "Usage: ibex diagnose [options] grammarfile"
    options.on("--format=FORMAT", %w[text json], "text or json") { |value| settings[:format] = value }
    options.on("--max-diagnostics=N", "positive diagnostic limit") do |value|
      settings[:max_diagnostics] = positive_diagnostic_limit(value)
    end
    options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
      set_local_configuration_option(settings, :mode, value.to_sym)
    end
    options.on("--help", "show help") { settings[:help] = true }
  end
end

#localized_diagnostic_hash(diagnostic) ⇒ Hash[Symbol, Object?]

RBS:

  • (Frontend::Diagnostic diagnostic) -> Hash[Symbol, Object?]

Parameters:

Returns:

  • (Hash[Symbol, Object?])


125
126
127
# File 'lib/ibex/cli/diagnostics.rb', line 125

def localized_diagnostic_hash(diagnostic)
  diagnostic.to_h.merge(message: localized_diagnostic_message(diagnostic))
end

#localized_diagnostic_message(diagnostic) ⇒ String

RBS:

  • (Frontend::Diagnostic diagnostic) -> String

Parameters:

Returns:

  • (String)


130
131
132
133
134
# File 'lib/ibex/cli/diagnostics.rb', line 130

def localized_diagnostic_message(diagnostic)
  id = "diagnostic.#{diagnostic.code}"
  id = "diagnostic.generic" unless Messages.known?(id)
  Messages.translate(id, language: @language, detail: diagnostic.message)
end

#localized_diagnostic_text(diagnostic) ⇒ String

RBS:

  • (Frontend::Diagnostic diagnostic) -> String

Parameters:

Returns:

  • (String)


118
119
120
121
122
# File 'lib/ibex/cli/diagnostics.rb', line 118

def localized_diagnostic_text(diagnostic)
  return diagnostic.to_s if @language == "en"

  "#{diagnostic.location}: #{localized_diagnostic_message(diagnostic)}"
end

#positive_diagnostic_limit(value) ⇒ Integer

RBS:

  • (String value) -> Integer

Parameters:

  • value (String)

Returns:

  • (Integer)


88
89
90
91
92
93
94
95
# File 'lib/ibex/cli/diagnostics.rb', line 88

def positive_diagnostic_limit(value)
  limit = Integer(value, 10)
  return limit if limit.positive?

  raise OptionParser::InvalidArgument, "max diagnostics must be positive"
rescue ArgumentError
  raise OptionParser::InvalidArgument, "max diagnostics must be a positive integer"
end

#resolution_diagnostic(error, fallback_file) ⇒ Frontend::Diagnostic

RBS:

  • (Ibex::Error error, String fallback_file) -> Frontend::Diagnostic

Parameters:

Returns:



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ibex/cli/diagnostics.rb', line 56

def resolution_diagnostic(error, fallback_file)
  match = error.message.match(/\A(.+):(\d+):(\d+): (.*)\z/m)
  location = if match
               Frontend::Location.new(
                 file: match[1].to_s, line: Integer(match[2].to_s), column: Integer(match[3].to_s)
               )
             else
               Frontend::Location.new(file: fallback_file, line: 1, column: 1)
             end
  message = match ? match[4].to_s : error.message
  Frontend::Diagnostic.new(
    code: "frontend.resolution_error", phase: :syntax, message: message,
    location: location, rendered: error.message
  )
end

#resolve_diagnostic_result(result, path, mode) ⇒ Frontend::ParseResult

Cross-file recovery is deliberately bounded to the first resolver error.

RBS:

  • (Frontend::ParseResult result, String path, Symbol mode) -> Frontend::ParseResult

Parameters:

Returns:



46
47
48
49
50
51
52
53
# File 'lib/ibex/cli/diagnostics.rb', line 46

def resolve_diagnostic_result(result, path, mode)
  Frontend::Resolver.new(path, mode: mode).resolve
  result
rescue Frontend::ResolutionIOError
  raise
rescue Ibex::Error => e
  Frontend::ParseResult.new(diagnostics: [resolution_diagnostic(e, path)], ast: nil, document: nil)
end

#run_diagnose_command(arguments) ⇒ Integer

RBS:

  • (Array[String] arguments) -> Integer

Parameters:

  • arguments (Array[String])

Returns:

  • (Integer)


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/ibex/cli/diagnostics.rb', line 23

def run_diagnose_command(arguments)
  settings = {
    format: "text", max_diagnostics: DEFAULT_MAX_DIAGNOSTICS,
    mode: Configuration::Registry.fetch("grammar.mode").default, configuration_explicit: []
  } #: diagnostic_settings
  parser = diagnostics_option_parser(settings)
  remaining = parser.parse(arguments)
  settings[:mode] = local_configuration_value(settings, "grammar.mode")
  if settings[:help]
    @stdout.puts(parser)
    return 0
  end

  path = input_path(remaining)
  result = Frontend::Parser.new(File.binread(path), file: path, mode: settings.fetch(:mode))
                           .parse_with_diagnostics(max_diagnostics: settings.fetch(:max_diagnostics))
  result = resolve_diagnostic_result(result, path, settings.fetch(:mode)) if result.success?
  write_diagnostics(result, format: settings.fetch(:format))
  result.success? ? 0 : 1
end

#write_diagnostics(result, format:) ⇒ void

This method returns an undefined value.

RBS:

  • (Frontend::ParseResult result, format: String) -> void

Parameters:



98
99
100
101
102
103
104
# File 'lib/ibex/cli/diagnostics.rb', line 98

def write_diagnostics(result, format:)
  if format == "json"
    @stdout.puts(JSON.pretty_generate(diagnostics_document(result)))
  else
    result.diagnostics.each { |diagnostic| @stdout.puts(localized_diagnostic_text(diagnostic)) }
  end
end