Skip to content

Commit e148473

Browse files
kim-emclaude
andcommitted
fix: warn when Where is used instead of where in declarations
This PR fixes a UX bug where writing `inductive Foo : Type Where` (with capital W) would silently parse `Where` as a universe level variable, causing confusing downstream errors about "universe level metavariables". Now the parser accepts both `where` and `Where`, but logs a warning when the capitalized form is used. This makes the code work correctly while alerting the user to the typo. Closes #11853 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c358b0c commit e148473

File tree

4 files changed

+54
-4
lines changed

4 files changed

+54
-4
lines changed

src/Lean/Elab/Inductive.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) (isCoi
8383
-- https://github.com/leanprover/lean4/issues/5236
8484
withRef decl[0] <| Linter.logLintIf Linter.linter.deprecated decl[3]
8585
"`inductive ... :=` has been deprecated in favor of `inductive ... where`"
86+
if decl[3][0].isToken "Where" then
87+
-- https://github.com/leanprover/lean4/issues/11853
88+
withRef decl[3][0] <| logWarning "`Where` should be lowercase `where`"
8689
return {
8790
ref := decl
8891
shortDeclName := name

src/Lean/Elab/Structure.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ private def expandFields (structStx : Syntax) (structModifiers : Modifiers) (str
342342
let cmd := if structStx[0].getKind == ``Parser.Command.classTk then "class" else "structure"
343343
withRef structStx[0] <| Linter.logLintIf Linter.linter.deprecated structStx[4][0]
344344
s!"`{cmd} ... :=` has been deprecated in favor of `{cmd} ... where`."
345+
if structStx[4][0].isToken "Where" then
346+
-- https://github.com/leanprover/lean4/issues/11853
347+
withRef structStx[4][0] <| logWarning "`Where` should be lowercase `where`"
345348
let fieldBinders := if structStx[4].isNone then #[] else structStx[4][2][0].getArgs
346349
fieldBinders.foldlM (init := #[]) fun (views : Array StructFieldView) fieldBinder => withRef fieldBinder do
347350
let mut fieldBinder := fieldBinder

src/Lean/Parser/Command.lean

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,20 @@ or an element `head : α` followed by a list `tail : List α`.
232232
See [Inductive types](https://lean-lang.org/theorem_proving_in_lean4/inductive_types.html)
233233
for more information.
234234
-/
235+
-- Parser that accepts both `where` and `Where` (for typo recovery).
236+
-- We use `symbol` rather than `nonReservedSymbol` because we need `Where` to be
237+
-- recognized as a keyword to prevent it from being parsed as a universe level.
238+
def whereKw : Parser := symbol " where" <|> symbol " Where"
235239
@[builtin_doc] def «inductive» := leading_parser
236-
"inductive " >> recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >> optional (symbol " :=" <|> " where") >>
240+
"inductive " >> recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >> optional (symbol " :=" <|> whereKw) >>
237241
many ctor >> optional (ppDedent ppLine >> computedFields) >> optDeriving
238242
@[builtin_doc] def «coinductive» := leading_parser
239-
"coinductive " >> recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >> optional (symbol " :=" <|> " where") >>
243+
"coinductive " >> recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >> optional (symbol " :=" <|> whereKw) >>
240244
many ctor >> optional (ppDedent ppLine >> computedFields) >> optDeriving
241245
def classInductive := leading_parser
242246
atomic (group (symbol "class " >> "inductive ")) >>
243247
recover declId skipUntilWsOrDelim >> ppIndent optDeclSig >>
244-
optional (symbol " :=" <|> " where") >> many ctor >> optDeriving
248+
optional (symbol " :=" <|> whereKw) >> many ctor >> optDeriving
245249
def structExplicitBinder := leading_parser
246250
atomic (declModifiers true >> "(") >>
247251
withoutPosition (many1 ident >> ppIndent optDeclSig >>
@@ -274,7 +278,7 @@ def «structure» := leading_parser
274278
-- Note: no error recovery here due to clashing with the `class abbrev` syntax
275279
declId >>
276280
ppIndent (optDeclSig >> optional «extends») >>
277-
optional ((symbol " := " <|> " where ") >> optional structCtor >> structFields) >>
281+
optional ((symbol " := " <|> whereKw >> ppSpace) >> optional structCtor >> structFields) >>
278282
optDeriving
279283
@[builtin_command_parser] def declaration := leading_parser
280284
declModifiers false >>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/-!
2+
# Test for warning when `Where` (capitalized) is used instead of `where`
3+
4+
Relates to: https://github.com/leanprover/lean4/issues/11853
5+
-/
6+
7+
-- Test that `Where` in inductive is accepted but generates a warning
8+
/--
9+
warning: `Where` should be lowercase `where`
10+
-/
11+
#guard_msgs in
12+
inductive MyResult (α : Type) : Type Where
13+
| Ok : α → MyResult α
14+
| Err : String → MyResult α
15+
16+
-- Verify the inductive works correctly
17+
#check MyResult.Ok
18+
#check MyResult.Err
19+
20+
-- Test that `Where` in structure is accepted but generates a warning
21+
/--
22+
warning: `Where` should be lowercase `where`
23+
-/
24+
#guard_msgs in
25+
structure MyPoint Where
26+
x : Nat
27+
y : Nat
28+
29+
-- Verify the structure works correctly
30+
#check MyPoint.mk
31+
#check MyPoint.x
32+
33+
-- Test that lowercase `where` does not generate a warning
34+
inductive MyResult2 (α : Type) : Type where
35+
| Ok : α → MyResult2 α
36+
| Err : String → MyResult2 α
37+
38+
structure MyPoint2 where
39+
x : Nat
40+
y : Nat

0 commit comments

Comments
 (0)