Class: SasLinter::Rules::FormatForUnknownVariable
- Inherits:
-
SasLinter::Rule
- Object
- SasLinter::Rule
- SasLinter::Rules::FormatForUnknownVariable
- Defined in:
- lib/sas_linter/rules/format_for_unknown_variable.rb
Overview
Flag ‘format` / `informat` / `attrib … format=` statements that name a variable referenced nowhere else in the file. Almost always a typo (`attrib totalscore format=flagx.;` when every other use is `total_score`). SAS itself silently binds the format to a phantom column and runs; downstream tooling that resolves variable references (e.g. sas-ruby) refuses to compile such a file.
The rule is conservative: a file that pulls variables in from an external source — ‘set`, `merge`, `update`, `infile`, `input` — is skipped entirely, since a format target may legitimately name a column the linter can’t see.
Constant Summary collapse
- TT =
SasLexer::Lexer::TokenType
- FORMAT_KIND_BY_TYPE =
{ TT::KW_FORMAT => :format, TT::KW_INFORMAT => :informat, TT::KW_ATTRIB => :attrib }.freeze
- EXTERNAL_INPUT_TYPES =
[ TT::KW_SET, TT::KW_MERGE, TT::KW_UPDATE, TT::KW_INFILE, TT::KW_INPUT ].freeze
- DECLARATION_TYPES =
Statements that name variables for declaration only — the names they reference don’t count as “real uses” because if a variable only appears in declaration statements it’s still dead code. Keyword-typed openers:
[ TT::KW_FORMAT, TT::KW_INFORMAT, TT::KW_ATTRIB, TT::KW_LABEL, TT::KW_LENGTH, TT::KW_KEEP, TT::KW_DROP, TT::KW_ARRAY ].freeze
- DECLARATION_TEXT =
IDENTIFIER-typed openers (the lexer doesn’t keyword these):
`retain` is a data-step declaration; `value` / `invalue` / `picture` introduce a `proc format` body. %w[retain value invalue picture].freeze
Instance Attribute Summary
Attributes inherited from SasLinter::Rule
Instance Method Summary collapse
-
#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, supports_autofix?
Constructor Details
This class inherits a constructor from SasLinter::Rule
Instance Method Details
#check(tokens, path:, all_tokens: nil, source: nil) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
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 |
# File 'lib/sas_linter/rules/format_for_unknown_variable.rb', line 52 def check(tokens, path:, all_tokens: nil, source: nil) # rubocop:disable Lint/UnusedMethodArgument external_input = false targets = [] use_names = Set.new each_statement(tokens) do |stmt| opener = stmt[0] if EXTERNAL_INPUT_TYPES.include?(opener[:type]) external_input = true next end kind = FORMAT_KIND_BY_TYPE[opener[:type]] if kind collect_targets(stmt, kind, targets) next end next if declaration_statement?(opener) collect_uses(stmt, use_names) end return [] if external_input targets.filter_map do |t, kind| next if use_names.include?(t[:text].downcase) finding( line: t[:start_line], column: t[:start_column] + 1, message: "`#{kind}` assigns a format to `#{t[:text]}` but " \ "that variable is not referenced anywhere else in " \ "this file — likely a typo.", path: path ) end end |