lean4-htt/tests/lean/run/invalidProjection.lean
Robert J. Simmons edcef51434
feat: improve error messages for invalid field access (#11456)
This PR refines several error error messages, mostly involving invalid
use of field notation, generalized field notation, and numeric
projection. Provides a new error explanation for field notation.

## Error message changes

In general:
- Uses a slightly different convention for expression-type pairs, where
the expression is always given `indentExpr` and the type is given
`inlineExpr` treatment. This is something of a workaround for the fact
that the `Format` type is awkward for embedding possibly-linebreaking
expressions in not-linebreaking text, which may be a separate issue
worth addressing.
- Tries to give slightly more "why" reasoning — the environment does not
contain `String.parse`, and _therefore you can't project `.parse` from a
`String`_.

Some specific examples:

### No such projection function
```lean4
#check "".parse
```
before:
```
error: Invalid field `parse`: The environment does not contain `String.parse`
  ""
has type
  String
```
after:
```
error: Invalid field `parse`: The environment does not contain `String.parse`, so it is not possible to project the field `parse` from an expression
  ""
of type `String`
```

### Type does not have the correct form
```lean4
example (x : α) := (foo x).foo
```
before:
```
error: Invalid field notation: Type is not of the form `C ...` where C is a constant
  foo x
has type
  α
```
after:
```
error: Invalid field notation: Field projection operates on types of the form `C ...` where C is a constant. The expression
  foo x
has type `α` which does not have the necessary form.
```

## Refactoring
Includes some refactoring changes as well:
- factors out multiple uses of number (1, 2, 3, 212, 222) to ordinal
("first", "second", "third", "212th", "222nd") conversion into
Lean.Elab.ErrorUtils
- significant refactoring of `resolveLValAux` in `Lean.Elab.App` — in
place of five helper functions, a special-case function case analysis,
and a case analysis on the projection type and structure, there's now a
single case analysis on the projection type and structure. This allows
several error messages to be more explicit (there were a number of cases
where index projection was being described as field projection in an
error messages) and gave the opportunity to slightly improve positining
for several errors: field *notation* errors should appear on `foo.bar`,
but field *projection* errors should appear only on the `bar` part of
`foo.bar`.
2025-12-02 17:46:12 +00:00

100 lines
2.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Lean.Parser.Term
/-!
# Invalid projection error messages
This file assesses error messages produced for invalid projections.
-/
inductive P : Nat → n > 0 → Prop
| mk (n) (q : n > 0) : P n q
/--
error: Invalid projection: Index `2` is invalid for this structure; the only valid index is 1
Note: The expression
h2
has type `P m h'` which has only 1 field
-/
#guard_msgs in
example (h1 : P n h) (h2 : P m h') := h1.1 = h2.2
/--
error: Invalid projection: Projection operates on types of the form `C ...` where C is a constant. The expression
Nat
has type `Type` which does not have the necessary form.
-/
#guard_msgs in
#check Nat.3
/--
error: Invalid projection: Index `3` is invalid for this structure; it must be between 1 and 2
Note: The expression
x
has type `Nat × Nat × Nat` which has only 2 fields
Hint: n-tuples in Lean are actually nested pairs. To access the third component of this tuple, use the projection `.2.2` instead:
3̵2̲.̲2̲
-/
#guard_msgs in
example (x : Nat × Nat × Nat) := x.3
/--
error: Invalid projection: Projection operates on structure-like types with fields. The expression
h
has type `Nat` which does not have fields.
-/
#guard_msgs in
example (h : Nat) := h.2
/--
error: Invalid projection: Projections cannot be used on functions, and
f
has function type `Nat → Nat`
-/
#guard_msgs in
example (f : Nat → Nat) := f.1
-- Currently, this error can only occur metaprogrammatically:
open Lean in
macro "bad_projection" : term =>
return ⟨mkNode ``Parser.Term.proj
#[mkIdent `h, mkAtom ".", mkNode fieldIdxKind #[mkAtom "0"]]⟩
/-- error: Invalid projection: Index must be greater than 0 -/
#guard_msgs in
example (h : Nat × Nat) := bad_projection
/-! ## Would-be unsound projections -/
-- Projection to the witness should be rejected.
/--
error: Invalid projection: Cannot project a value of non-propositional type
Nat
from the expression
Exists.intro 1 (Nat.le_refl 1)
which has propositional type
∃ x, x ≥ 1
-/
#guard_msgs in
def witness : Nat := (⟨1, Nat.le_refl _⟩ : ∃ x, x ≥ 1).1
-- Projection to the property as well (it could contain the witness projection).
/--
error: (kernel) invalid projection
h.2
-/
#guard_msgs in
theorem witness_eq (h : ∃ x : Nat, True) : h.2 = h.2 := rfl
/--
error: Invalid projection: Cannot project a value of non-propositional type
Nat
from the expression
h
which has propositional type
∃ x, True
-/
#guard_msgs in
def foo (h : ∃ x: Nat, True) := h.1