Skip to content

Commit 06580e2

Browse files
committed
feat: hint when an autobound variable's type fails to be a function or equality
1 parent 42ded56 commit 06580e2

File tree

8 files changed

+66
-10
lines changed

8 files changed

+66
-10
lines changed

src/Lean/Elab/App.lean

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ private def synthesizePendingAndNormalizeFunType : M Unit := do
245245
.note m!"Expected a function because this term is being applied to the argument\
246246
{indentD <| toMessageData arg}"
247247
else .nil
248-
throwError "Function expected at{indentExpr s.f}\nbut this term has type{indentExpr fType}{extra}"
248+
throwError "Function expected at{indentExpr s.f}\nbut this term has type{indentExpr fType}\
249+
{extra}\
250+
{← hintAutoImplicitFailure s.f}"
249251

250252
/-- Normalize and return the function type. -/
251253
private def normalizeFunType : M Expr := do

src/Lean/Elab/BuiltinNotation.lean

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,10 @@ private def withLocalIdentFor (stx : Term) (e : Expr) (k : Term → TermElabM Ex
461461
let heqType ← inferType heq
462462
let heqType ← instantiateMVars heqType
463463
match (← Meta.matchEq? heqType) with
464-
| none => throwError "invalid `▸` notation, argument{indentExpr heq}\nhas type{indentExpr heqType}\nequality expected"
464+
| none => throwError "invalid `▸` notation, argument{indentExpr heq}\n\
465+
has type{indentExpr heqType}\n\
466+
equality expected\
467+
{← Term.hintAutoImplicitFailure heq (expected := "an equality")}"
465468
| some (α, lhs, rhs) =>
466469
let mut lhs := lhs
467470
let mut rhs := rhs

src/Lean/Elab/ErrorUtils.lean

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ Authors: Rob Simmons
66
module
77

88
prelude
9-
import Init.Notation
10-
import Init.Data.String
9+
import Lean.Message
10+
11+
namespace Lean
1112

1213
/--
1314
Translate numbers (1, 2, 3, 212, 322 ...) to ordinals with appropriate English-language names for
@@ -35,19 +36,49 @@ def _root_.Nat.toOrdinal : Nat -> String
3536
else
3637
s!"{n}th"
3738

39+
class HasOxfordStrings α where
40+
emp : α
41+
and : α
42+
comma : α
43+
commaAnd : α
44+
45+
instance : HasOxfordStrings String where
46+
emp := ""
47+
and := " and "
48+
comma := ", "
49+
commaAnd := ", and "
50+
51+
instance : HasOxfordStrings MessageData where
52+
emp := ""
53+
and := " and "
54+
comma := ", "
55+
commaAnd := ", and "
56+
3857
/--
3958
Make an oxford-comma-separated list of strings.
4059
4160
- `["eats"].toOxford == "eats"`
4261
- `["eats", "shoots"].toOxford == "eats and shoots"`
4362
- `["eats", "shoots", "leaves"] == "eats, shoots, and leaves"`
4463
-/
45-
def _root_.List.toOxford : List String -> String
46-
| [] => ""
64+
def _root_.List.toOxford [Append α] [HasOxfordStrings α] : List α -> α
65+
| [] => HasOxfordStrings.emp
4766
| [a] => a
48-
| [a, b] => a ++ " and " ++ b
49-
| [a, b, c] => a ++ ", " ++ b ++ ", and " ++ c
50-
| a :: as => a ++ ", " ++ toOxford as
67+
| [a, b] => a ++ HasOxfordStrings.and ++ b
68+
| [a, b, c] => a ++ HasOxfordStrings.comma ++ b ++ HasOxfordStrings.commaAnd ++ c
69+
| a :: as => a ++ HasOxfordStrings.comma ++ as.toOxford
70+
71+
class HasPluralDefaults α where
72+
singular : α
73+
plural : α → α
74+
75+
instance : HasPluralDefaults String where
76+
singular := ""
77+
plural := (· ++ "s")
78+
79+
instance : HasPluralDefaults MessageData where
80+
singular := .nil
81+
plural := (m!"{·}s")
5182

5283
/--
5384
Give alternative forms of a string if the `count` is 1 or not.
@@ -59,7 +90,9 @@ Give alternative forms of a string if the `count` is 1 or not.
5990
- `(1).plural "it" "they" == "it"`
6091
- `(2).plural "it" "they" == "they"`
6192
-/
62-
def _root_.Nat.plural (count : Nat) (singular : String := "") (plural : String := singular ++ "s") :=
93+
def _root_.Nat.plural [HasPluralDefaults α] (count : Nat)
94+
(singular : α := HasPluralDefaults.singular)
95+
(plural : α := HasPluralDefaults.plural singular) :=
6396
if count = 1 then
6497
singular
6598
else

src/Lean/Elab/Term/TermElabM.lean

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,6 +2169,16 @@ def exprToSyntax (e : Expr) : TermElabM Term := withFreshMacroScope do
21692169
mvar.mvarId!.assign e
21702170
return result
21712171

2172+
def hintAutoImplicitFailure (exp : Expr) (expected := "a function") : TermElabM MessageData := do
2173+
let autoBound := (← readThe Context).autoBoundImplicitContext
2174+
unless autoBound.isSome && exp.isFVar && autoBound.get!.boundVariables.any (· == exp) do
2175+
return .nil
2176+
return .hint' m!"The identifier `{.ofName (← exp.fvarId!.getUserName)}` is unknown, \
2177+
and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly \
2178+
bound variable with an unknown type. \
2179+
However, the unknown type cannot be {expected}, and {expected} is what Lean expects here. \
2180+
This is often the result of a typo or a missing `import` or `open` statement."
2181+
21722182
end Term
21732183

21742184
open Term in

tests/lean/302.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ but this term has type
55

66
Note: Expected a function because this term is being applied to the argument
77
0
8+
9+
Hint: The identifier `m` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.

tests/lean/3989_1.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ but this term has type
66

77
Note: Expected a function because this term is being applied to the argument
88
Expr
9+
10+
Hint: The identifier `MetaM` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.

tests/lean/3989_2.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ but this term has type
66

77
Note: Expected a function because this term is being applied to the argument
88
Expr
9+
10+
Hint: The identifier `MetaM` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be a function, and a function is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.
911
Nat : Type

tests/lean/autoBoundPostponeLoop.lean.expected.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ autoBoundPostponeLoop.lean:5:12-5:18: error: invalid `▸` notation, argument
33
has type
44
?m
55
equality expected
6+
7+
Hint: The identifier `h` is unknown, and Lean's `autoImplicit` option causes an unknown identifier to be treated as an implicitly bound variable with an unknown type. However, the unknown type cannot be an equality, and an equality is what Lean expects here. This is often the result of a typo or a missing `import` or `open` statement.

0 commit comments

Comments
 (0)