Class: RemLint::Rules::UnbalancedBlocks

Inherits:
RemLint::Rule show all
Defined in:
lib/remlint/rules/unbalanced_blocks.rb

Overview

Block commands that never close, close the wrong thing, or close nothing.

Remind has its own "Warning: Missing ENDIF" (src/err.h E_MISS_ENDIF), but only for the file it actually reaches the end of, and only once it has run everything above. A linter can say it before anything runs, name the line the block opened on, and cover the three context stacks Remind's own warning does not:

IF / IFTRIG ... ELSE ... ENDIF
PUSH-OMIT-CONTEXT ... POP-OMIT-CONTEXT
PUSH-VARS ... POP-VARS
PUSH-FUNCS ... POP-FUNCS

defs.rem nests IF inside ELSE inside IF for its Yom Hazikaron calculation and brackets its US Tax Day block in PUSH/POP-OMIT-CONTEXT, so all of this is load-bearing in the shipped examples.

Matching is by canonical keyword, so the abbreviations Remind allows -- PUSH for PUSH-OMIT-CONTEXT, POP for POP-OMIT-CONTEXT -- are matched like the long forms rather than missed.

Constant Summary collapse

PAIRS =
{
  "IF"                => "ENDIF",
  "IFTRIG"            => "ENDIF",
  "PUSH-OMIT-CONTEXT" => "POP-OMIT-CONTEXT",
  "PUSH-VARS"         => "POP-VARS",
  "PUSH-FUNCS"        => "POP-FUNCS",
}.freeze
CLOSERS =
PAIRS.values.uniq.freeze
MAX_IF_DEPTH =

IF_NEST in src/ifelse.c. The 65th level is an error rather than a deeper nesting, and the linter can say so before the file runs -- though a script at that depth has larger problems than this rule.

64
OPENERS_FOR =

Which openers a given closer is allowed to close.

CLOSERS.to_h do |closer|
  [closer, PAIRS.select { |_opener, close| close == closer }.keys]
end.freeze

Constants inherited from RemLint::Rule

RemLint::Rule::REGISTRY

Instance Attribute Summary

Attributes inherited from RemLint::Rule

#config, #document, #offenses

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from RemLint::Rule

all, enabled_by_default?, find, inherited, #initialize, rule_name, #rule_name, #run

Constructor Details

This class inherits a constructor from RemLint::Rule

Class Method Details

.default_severityObject



48
49
50
# File 'lib/remlint/rules/unbalanced_blocks.rb', line 48

def self.default_severity
  "error"
end

.descriptionObject



52
53
54
# File 'lib/remlint/rules/unbalanced_blocks.rb', line 52

def self.description
  "IF/ENDIF, ELSE and the PUSH/POP context commands that do not pair up."
end

Instance Method Details

#checkObject



56
57
58
59
60
61
62
63
64
# File 'lib/remlint/rules/unbalanced_blocks.rb', line 56

def check
  @stack = []

  document.code_commands.each do |command|
    dispatch(command)
  end

  report_unclosed
end