Class: SasLinter::Rules::SourceHeaders
- Inherits:
-
SasLinter::Rule
- Object
- SasLinter::Rule
- SasLinter::Rules::SourceHeaders
- Defined in:
- lib/sas_linter/rules/source_headers.rb
Overview
Restore the standard 90-char ‘…;` header convention to broken SAS source files. Detects header lines that look like `**`-comments but produce DEFAULT-channel tokens, and re-wraps them as proper `** … **;` rows.
Working sources use a uniform 90-char-wide header where each line is its own self-contained ‘*` comment statement:
****************************************************************************************;
** PROGRAM: ... **;
** BY: ... **;
Broken sources have lines that look like comments (start with ‘**`) but produce DEFAULT-channel tokens. Two flavors:
A. Missing trailing `;` on every header line — the whole
header is one giant unterminated `*` comment until the
first inline `;` ends it, leaking the rest of that
physical line and following lines onto DEFAULT.
B. Trailing `**;` is present but an inline `;` (e.g. a
semicolon-separated list like `First Reviewer; Second
Reviewer`) terminates the comment in the middle of the
line — what follows the inline `;` ends up on DEFAULT
even though the line *looks* terminated.
Some files also have header continuation lines (text that should be inside a ‘**` comment) that lost their `**` prefix during a text-conversion step. Those are detected only inside the file’s leading header block — before the first KW_DATA / KW_PROC token the lexer reports — so legitimate body code sandwiched between ‘**` marker comments is left alone.
Recognized config options:
autofix: true | false (default: false)
Constant Summary collapse
- TARGET_WIDTH =
90- PAD_TO =
leave 3 chars for trailing ‘**;`
TARGET_WIDTH - 3
- DEFAULT_CHANNEL =
SasLexer::Lexer::TokenChannel::DEFAULT
- KW_DATA =
SasLexer::Lexer::TokenType::KW_DATA
- KW_PROC =
SasLexer::Lexer::TokenType.const_get(:KW_PROC)
- C_STYLE_COMMENT =
SasLexer::Lexer::TokenType::C_STYLE_COMMENT
- IDENTIFIER =
SasLexer::Lexer::TokenType::IDENTIFIER
- SEMI =
SasLexer::Lexer::TokenType::SEMI
- ASSIGN =
SasLexer::Lexer::TokenType::ASSIGN
Class Method Summary collapse
Instance Method Summary collapse
- #autofix(source) ⇒ Object
-
#broken_header_lines(source) ⇒ Object
0-indexed line numbers the lexer thinks are broken header text in ‘source`.
-
#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
60 61 62 |
# File 'lib/sas_linter/rules/source_headers.rb', line 60 def self.supports_autofix? true end |
Instance Method Details
#autofix(source) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/sas_linter/rules/source_headers.rb', line 77 def autofix(source) # Step 0: expand any tab characters to 4 spaces. Tabs in # SAS source headers often come from Word docs, and # break the column-alignment of the header box. Doing this # first means every downstream check sees consistent column # offsets. text = source.gsub("\t", " ") 10.times do tokens = tokenize(text) skip = c_comment_lines(tokens) bad = broken_lines_for(text, tokens, skip) | asterisk_rows_missing_semi_for(text, skip) break if bad.empty? text = rewrite(text, bad) end text end |
#broken_header_lines(source) ⇒ Object
0-indexed line numbers the lexer thinks are broken header text in ‘source`. Public so the rule’s ‘check` can produce findings without re-tokenizing on its own.
99 100 101 102 |
# File 'lib/sas_linter/rules/source_headers.rb', line 99 def broken_header_lines(source) tokens = tokenize(source) broken_lines_for(source, tokens, c_comment_lines(tokens)) end |
#check(_tokens, path:, all_tokens: nil, source: nil) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/sas_linter/rules/source_headers.rb', line 64 def check(_tokens, path:, all_tokens: nil, source: nil) # rubocop:disable Lint/UnusedMethodArgument return [] unless source broken_header_lines(source).map do |line_idx| finding( line: line_idx + 1, column: 1, message: "broken header line#{autofix? ? ' (autofixed)' : ''}", path: path ) end end |