Class: SasLinter::Rules::MalformedLabelStatement
- Inherits:
-
SasLinter::Rule
- Object
- SasLinter::Rule
- SasLinter::Rules::MalformedLabelStatement
- Defined in:
- lib/sas_linter/rules/malformed_label_statement.rb
Overview
Flag label statements where the = between the variable name
and the string literal is missing.
Motivating bug: label aHSDELIRIUM 'Delirium Screener'; in
SUITE9_HS_DELIRIUM_SCREENER_2014-04-15.TXT — SAS rejects this
with "ERROR 22-322: Syntax error, expecting one of the following:
=, ?", and the label is silently never attached. The treatment
variant of the same algorithm shipped the same typo, and several
other interRAI sources have shipped it over time.
Detection: every label statement is a KW_LABEL keyword
followed by one or more IDENT '=' STRING_LITERAL triples
separated by whitespace, terminated by ;. We walk each label
statement and, for each IDENT inside it, require the next
default-channel token to be ASSIGN (=). If instead the next
token is a STRING_LITERAL, the = was dropped.
Constant Summary collapse
- TT =
SasLexer::Lexer::TokenType
Class Method Summary collapse
Instance Method Summary collapse
- #autofix(source) ⇒ Object
-
#check(tokens, path:, all_tokens: nil, source: nil) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument.
Methods inherited from SasLinter::Rule
all, #autofix?, description, fetch, from_config, inherited, #initialize, register, registry, rule_id, severity
Constructor Details
This class inherits a constructor from SasLinter::Rule
Class Method Details
.supports_autofix? ⇒ Boolean
31 32 33 |
# File 'lib/sas_linter/rules/malformed_label_statement.rb', line 31 def self.supports_autofix? true end |
Instance Method Details
#autofix(source) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/sas_linter/rules/malformed_label_statement.rb', line 49 def autofix(source) return source if source.nil? || source.empty? lexer = SasLexer::Lexer.new begin all_tokens = lexer.tokenize(source) ensure lexer.free end tokens = all_tokens.reject do |t| t[:channel] == SasLexer::Lexer::TokenChannel::HIDDEN || t[:channel] == SasLexer::Lexer::TokenChannel::COMMENT end source_lines = source.split("\n", -1) # Collect (line_idx, col_after_ident) for each malformed label, # then apply edits right-to-left within each line so earlier # column offsets stay valid. edits_by_line = Hash.new { |h, k| h[k] = [] } each_label_violation(tokens) do |ident_t, _string_t| edits_by_line[ident_t[:start_line] - 1] << ident_t[:end_column] end edits_by_line.each do |line_idx, cols| line = source_lines[line_idx] next if line.nil? # Right-to-left so earlier insertions don't shift later columns. cols.sort.reverse.each do |col| # Insert ` =` immediately after the IDENT (consuming the # following space if there is one, preserving alignment). replacement = if col < line.length && line[col] == " " # `aHSDELIRIUM 'Delirium Screener'` → `aHSDELIRIUM = 'Delirium Screener'`. # Replace the single space with ` = ` (one space before, one after). " = #{line[(col + 1)..]}" else " = #{line[col..]}" end line = "#{line[0...col]}#{replacement}" end source_lines[line_idx] = line end source_lines.join("\n") end |
#check(tokens, path:, all_tokens: nil, source: nil) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/sas_linter/rules/malformed_label_statement.rb', line 35 def check(tokens, path:, all_tokens: nil, source: nil) # rubocop:disable Lint/UnusedMethodArgument findings = [] each_label_violation(tokens) do |ident_t, string_t| findings << finding( line: ident_t[:start_line], column: ident_t[:start_column] + 1, message: "`label #{ident_t[:text]} #{shorten(string_t[:text])}` is missing the `=` " \ "between the variable name and the label string.", path: path ) end findings end |