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, untyped]

RBS:

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

Parameters:

Returns:

  • (Hash[String, untyped])


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

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

#diagnostics_option_parser(settings) ⇒ OptionParser

RBS:

  • (diagnostic_settings settings) -> OptionParser

Parameters:

  • settings (diagnostic_settings)

Returns:

  • (OptionParser)


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

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") { |value| settings[:mode] = value.to_sym }
    options.on("--help", "show help") { settings[:help] = true }
  end
end

#positive_diagnostic_limit(value) ⇒ Integer

RBS:

  • (String value) -> Integer

Parameters:

  • value (String)

Returns:

  • (Integer)


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

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:



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

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:



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

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)


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

def run_diagnose_command(arguments)
  settings = {
    format: "text", max_diagnostics: DEFAULT_MAX_DIAGNOSTICS, mode: :default
  } #: diagnostic_settings
  parser = diagnostics_option_parser(settings)
  remaining = parser.parse(arguments)
  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:



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

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