{"id":"92b27b50de088939","repo":"rust-lang/rust","slug":"invalid-level-lint-id-combination","errorCode":null,"errorMessage":"invalid level/lint_id combination","messagePattern":"invalid level/lint_id combination","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_middle/src/lint.rs","lineNumber":87,"sourceCode":"    level: Level,\n\n    // This field *must* be private. See the comment on `level`.\n    lint_id: Option<Id>,\n\n    pub src: LintLevelSource,\n}\n\npub type UnstableLevelSpec = LevelSpec<UnstableLintExpectationId>;\npub type StableLevelSpec = LevelSpec<StableLintExpectationId>;\n\nimpl<Id: Copy> LevelSpec<Id> {\n    // Panics if an invalid `level`/`lint_id` combination is given.\n    pub fn new(level: Level, lint_id: Option<Id>, src: LintLevelSource) -> LevelSpec<Id> {\n        match (level, lint_id) {\n            (Level::Allow | Level::Warn | Level::Deny | Level::Forbid, None) => {}\n            (Level::Expect, Some(_)) => {}\n            (Level::ForceWarn, _) => {}\n            _ => panic!(\"invalid level/lint_id combination\"),\n        }\n        LevelSpec { level, lint_id, src }\n    }\n\n    pub fn level(self) -> Level {\n        self.level\n    }\n\n    pub fn is_allow(self) -> bool {\n        self.level == Level::Allow\n    }\n\n    pub fn is_expect(self) -> bool {\n        self.level == Level::Expect\n    }\n\n    pub fn lint_id(self) -> Option<Id> {\n        self.lint_id","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_middle/src/lint.rs#L69-L105","documentation":"This panic is raised inside `LevelSpec::new` (rustc's lint-level machinery) when the supplied `Level` and `Option<lint_id>` pair is not one of the combinations the compiler treats as well-formed. The valid combinations are encoded directly in the match at lint.rs:83-88: `Allow`/`Warn`/`Deny`/`Forbid` require `lint_id == None`, `Expect` requires `lint_id == Some(_)`, and `ForceWarn` accepts either. Hitting this means rustc-internal (or plugin/lint-pass) code constructed a `LevelSpec` that violates that invariant; it is an internal compiler error, not user source-level lint configuration.","triggerScenarios":"Calling `LevelSpec::new(Level::Allow, Some(id), src)` (or any of Warn/Deny/Forbid with a Some id), `LevelSpec::new(Level::Expect, None, src)`, or constructing a `UnstableLevelSpec`/`StableLevelSpec` whose level/id pair is not in the allowed match arms. Typically introduced while wiring up a new lint expectation id, porting lint-level data across query boundaries, or by a tool that hand-builds `LevelSpec` values.","commonSituations":"Developing a rustc lint pass or clippy lint that emits expectations; refactoring the `LintLevelSource` plumbing; mismatches after a rustc upgrade where the `Level::Expect` semantics changed; buggy deserialization of lint level configs from rmeta/crater artifacts.","solutions":["Match the level/id pair to the contract in lint.rs:84-86: use `None` for Allow/Warn/Deny/Forbid and `Some(expectation_id)` for Expect.","If you hold an externally-sourced level/id pair, validate it before calling `LevelSpec::new` and map invalid pairs to a default rather than panicking.","When porting code across rustc versions, re-read the match arms in `LevelSpec::new` — the ForceWarn/Expect rules have shifted historically.","Run with `RUST_BACKTRACE=1` to identify which caller constructed the bad spec."],"exampleFix":"// before\nlet spec = LevelSpec::new(Level::Expect, None, src);\n\n// after\nlet spec = LevelSpec::new(Level::Expect, Some(expectation_id), src);","handlingStrategy":"validation","validationCode":"// rustc_middle::lint::LevelSpec::new panics on a bad (level, lint_id) pair.\n// Rule: Allow/Warn/Deny/Forbid => lint_id must be None;\n//       Expect                          => lint_id must be Some(_);\n//       ForceWarn                        => either.\nfn valid_level_lint_id_combo<Id>(level: Level, lint_id: Option<Id>) -> bool {\n    matches!(\n        (level, lint_id),\n        (Level::Allow | Level::Warn | Level::Deny | Level::Forbid, None)\n            | (Level::Expect, Some(_))\n            | (Level::ForceWarn, _)\n    )\n}\n\n// At the call site:\nif valid_level_lint_id_combo(level, lint_id) {\n    let spec = LevelSpec::new(level, lint_id, src);\n} else {\n    return Err(\"invalid level/lint_id combination\");\n}","typeGuard":null,"tryCatchPattern":"// Panics are not Result-based; the only runtime catch is catch_unwind.\nlet spec = std::panic::catch_unwind(|| LevelSpec::new(level, lint_id, src));\nmatch spec {\n    Ok(s) => /* use s */,\n    Err(_) => /* log + reject this lint registration */,\n}","preventionTips":["Centralize every LevelSpec construction in one builder helper that enforces the (level, lint_id) invariant, instead of calling LevelSpec::new directly.","Never pair Level::Expect with a missing lint_id; an expectation must name a lint.","Treat ForceWarn as the only level that ignores lint_id; for all others lint_id participates in the contract.","Add a unit test matrix over (Level x Option<Id>) so regressions in the panic condition surface in CI, not at runtime."],"tags":["rustc","lint","internal","assertion","compiler-ice"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}