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 |