Module: Rigor::ConfigAudit
- Defined in:
- lib/rigor/config_audit.rb
Overview
Audits a loaded Configuration for the class of mistake where a configured value
silently resolves to nothing — a typo'd or moved path, an unknown library name, an inert
rule id. The shared failure mode is that the loader filters the bad entry without a word,
so the only symptom is downstream and confusing: missing signatures turn every call into
the types they were meant to cover into a high-confidence call.undefined-method, and an
unrecognised suppression token leaves the rule firing as if the disable: line were
never written. This module surfaces each such entry up front so the cause is visible
instead of inferred.
Every check is held to the same bar that SignaturePathAudit set: it mirrors the
loader's own acceptance test, so a warning means the loader really did load nothing, and
it never fires on a setup that works. In particular the rule-token check only flags a
token under a built-in family (call, flow, …) — a plugin- or rbs_extended.* rule id
(whose family Rigor cannot statically enumerate, since a node_rule block emits any
rule: string it likes) is left alone, so disabling a plugin rule is never mistaken for
a typo.
Defined Under Namespace
Classes: Warning
Class Method Summary collapse
- .add_missing_dir(warnings, path, project_root, kind, key) ⇒ Object
- .add_missing_file(warnings, path, project_root, kind, key) ⇒ Object
-
.explicit_path_warnings(configuration, project_root) ⇒ Object
Explicitly-configured bundler / rbs-collection paths that do not exist.
-
.inert_builtin_token?(token) ⇒ Boolean
True when
tokenlooks like a built-in rule id but matches none — its first segment is a built-in family yet it is neither a bare family wildcard nor a known canonical id. -
.library_warnings(configuration) ⇒ Object
libraries:entries RBS does not recognise. -
.rule_token_warnings(configuration) ⇒ Object
disable:tokens andseverity_overrides:keys that name no rule. -
.signature_path_warnings(configuration) ⇒ Object
signature_paths:entries that resolve to nothing — delegated to SignaturePathAudit, which mirrors the loader'sdirectory?+ recursive**/*.rbsacceptance test. -
.unknown_key_warnings(configuration) ⇒ Object
Top-level keys the loader does not own.
- .warnings(configuration, project_root: Dir.pwd) ⇒ Array<Warning>
Class Method Details
.add_missing_dir(warnings, path, project_root, kind, key) ⇒ Object
176 177 178 179 180 181 |
# File 'lib/rigor/config_audit.rb', line 176 def self.add_missing_dir(warnings, path, project_root, kind, key) return if path.nil? || File.directory?(File.(path, project_root)) warnings << Warning.new(kind: kind, message: "#{key}: #{path.inspect} is not a directory", fields: { "path" => path }) end |
.add_missing_file(warnings, path, project_root, kind, key) ⇒ Object
183 184 185 186 187 |
# File 'lib/rigor/config_audit.rb', line 183 def self.add_missing_file(warnings, path, project_root, kind, key) return if path.nil? || File.file?(File.(path, project_root)) warnings << Warning.new(kind: kind, message: "#{key}: #{path.inspect} does not exist", fields: { "path" => path }) end |
.explicit_path_warnings(configuration, project_root) ⇒ Object
Explicitly-configured bundler / rbs-collection paths that do not exist. Only the explicit form is audited (a nil value means auto-detection, which finding nothing is normal); messages stay factual about the path rather than guessing the fallback.
165 166 167 168 169 170 171 172 173 174 |
# File 'lib/rigor/config_audit.rb', line 165 def self.explicit_path_warnings(configuration, project_root) warnings = [] add_missing_dir(warnings, configuration.bundler_bundle_path, project_root, :bundler_bundle_path, "bundler.bundle_path") add_missing_file(warnings, configuration.bundler_lockfile, project_root, :bundler_lockfile, "bundler.lockfile") add_missing_file(warnings, configuration.rbs_collection_lockfile, project_root, :rbs_collection_lockfile, "rbs_collection.lockfile") warnings end |
.inert_builtin_token?(token) ⇒ Boolean
True when token looks like a built-in rule id but matches none — its first segment
is a built-in family yet it is neither a bare family wildcard nor a known canonical id.
A token whose family is not built-in (a plugin / rbs_extended.* rule, or a bare
legacy alias) is deliberately NOT flagged: it may resolve at run time, so
under-warning is the FP-safe choice.
153 154 155 156 157 158 159 160 |
# File 'lib/rigor/config_audit.rb', line 153 def self.inert_builtin_token?(token) family = token.split(".").first return false unless Analysis::CheckRules::RULE_FAMILIES.include?(family) return false if token == family return false if Analysis::CheckRules::ALL_RULES.include?(token) true end |
.library_warnings(configuration) ⇒ Object
libraries: entries RBS does not recognise. Uses the same
RBS::EnvironmentLoader#has_library? guard the loader filters through
(Environment::RbsLoader build_env_for), so a flagged name is exactly one the loader
skipped. Fails soft: if RBS itself raises, no warning rather than a crash.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/rigor/config_audit.rb', line 107 def self.library_warnings(configuration) configured = Array(configuration.libraries) return [] if configured.empty? loader = ::RBS::EnvironmentLoader.new configured.reject { |lib| loader.has_library?(library: lib.to_s, version: nil) }.map do |lib| Warning.new( kind: :library, message: "libraries: #{lib.to_s.inspect} is not an available RBS library (no signatures loaded from it)", fields: { "name" => lib.to_s } ) end rescue StandardError [] end |
.rule_token_warnings(configuration) ⇒ Object
disable: tokens and severity_overrides: keys that name no rule. Restricted to
tokens under a built-in family so a plugin rule id is never mis-flagged (see the module
docstring).
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/rigor/config_audit.rb', line 126 def self.rule_token_warnings(configuration) disable = Array(configuration.disabled_rules).filter_map do |token| next unless inert_builtin_token?(token.to_s) Warning.new( kind: :disabled_rule, message: "disable: #{token.to_s.inspect} is not a recognized rule id; the suppression has no effect", fields: { "token" => token.to_s } ) end overrides = configuration.severity_overrides.keys.filter_map do |key| next unless inert_builtin_token?(key.to_s) Warning.new( kind: :severity_override, message: "severity_overrides: #{key.to_s.inspect} is not a recognized rule id; the override has no effect", fields: { "token" => key.to_s } ) end disable + overrides end |
.signature_path_warnings(configuration) ⇒ Object
signature_paths: entries that resolve to nothing — delegated to SignaturePathAudit,
which mirrors the loader's directory? + recursive **/*.rbs acceptance test.
93 94 95 96 97 98 99 100 101 |
# File 'lib/rigor/config_audit.rb', line 93 def self.signature_path_warnings(configuration) SignaturePathAudit.warnings(configuration.signature_paths).map do |entry| Warning.new( kind: :signature_path, message: entry., fields: { "path" => entry.path, "status" => entry.status.to_s, "rbs_file_count" => entry.rbs_file_count } ) end end |
.unknown_key_warnings(configuration) ⇒ Object
Top-level keys the loader does not own. The archetypal case is a typo — excludee: for
exclude: — which the loader drops in silence, so the exclusion never applies and the run
reports errors from the very files the user meant to skip, with nothing anywhere saying the key
did nothing. That is exactly this module's class of mistake, and it was the one whole-key
instance of it going unwarned.
A reserved namespace (Rigor::Configuration::RESERVED_NAMESPACES) is unknown on purpose — another implementation reads it from the same file — so it is exempt. Rigor::Configuration#unknown_keys already applies that exemption; warning here would push users to delete the key their other tool needs.
Top level only, and deliberately. A nested check would have to name each group's known keys,
and DEFAULTS cannot supply them: DEFAULTS["dependencies"] carries source_inference and
budget_per_gem but not budget_overrun_strategy, which is real (Configuration::Dependencies
reads it), documented, and schema-declared — so a DEFAULTS-keyed nested check would flag a
working config. severity_overrides: is an open map of rule ids besides. Nested unknown keys are
the schema tier's job: every nested object in schemas/rigor-config.schema.json is
additionalProperties: false, and config_schema_spec keeps it complete
(ADR-99, docs/internal-spec/config.md).
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rigor/config_audit.rb', line 70 def self.unknown_key_warnings(configuration) configuration.unknown_keys.map do |key| suggestion = suggest_config_key(key) hint = suggestion ? " Did you mean `#{suggestion}`?" : "" Warning.new( kind: :unknown_key, message: "`#{key}` is not a recognized configuration key; it has no effect.#{hint}", fields: { "key" => key, "suggestion" => suggestion } ) end end |
.warnings(configuration, project_root: Dir.pwd) ⇒ Array<Warning>
43 44 45 46 47 48 49 |
# File 'lib/rigor/config_audit.rb', line 43 def self.warnings(configuration, project_root: Dir.pwd) unknown_key_warnings(configuration) + signature_path_warnings(configuration) + library_warnings(configuration) + rule_token_warnings(configuration) + explicit_path_warnings(configuration, project_root) end |