feat: require indentation in commands, allow empty tactic sequences#13229
Open
feat: require indentation in commands, allow empty tactic sequences#13229
Conversation
Collaborator
|
Reference manual CI status:
|
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 1, 2026
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 1, 2026
|
Mathlib CI status (docs):
|
|
Mathlib CI status (docs):
|
|
Mathlib CI status (docs):
|
|
Mathlib CI status (docs):
|
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 1, 2026
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 1, 2026
|
Mathlib CI status (docs):
|
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 1, 2026
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 1, 2026
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 1, 2026
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 1, 2026
|
Mathlib CI status (docs):
|
|
Mathlib CI status (docs):
|
…approach) This uses `withPosition commandParser` at the top level to enforce indentation in `by` blocks, combined with an empty-by fallback for better error messages. Compare with checkColPosGt approach in #13217. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This adds a `notFollowedBy checkColGt` guard on the empty-by fallback in
`tacticSeqIndentGt`. When indented non-tactic content follows `by`, this
produces a tactic-level parse error ("expected '{' or tactic") rather than
silently producing an empty tactic sequence. Also adds a comparison test
for first-line vs second-line non-tactic errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This reverts the `notFollowedBy checkColGt` guard on the empty-by fallback. Indented non-tactic content now consistently gets "unsolved goals" + "unexpected token; expected command", matching the behavior when non-tactic content appears on subsequent lines after a valid tactic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This adds server_interactive hover tests verifying that goal display works correctly when the empty-by `pushNone` fallback fires. Tests hovering on `by`, after `by`, and below an empty `by` block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the pushNone fallback hover tests out of plainGoal.lean (which ends with an EOF test) into a dedicated plainGoalEmptyBy.lean test file. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 2, 2026
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/mathlib4-nightly-testing
that referenced
this pull request
Apr 2, 2026
|
Mathlib CI status (docs):
|
Add `--⬑` test marker variant that targets the column of `--` itself (enabling column 0 tests, which `--^` cannot reach). Test tactic completion in empty `by` blocks for both top-level `by` (with `withPosition commandParser`) and nested `by` (inside `id <| have := by`). For each, test completion at various column positions: indented past `by`, at column 2, and at column 0. Current behavior: top-level empty `by` offers completions at all positions; nested empty `by` only offers completions when indented past the `by` token. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `--⬑` test marker variant that targets the column of `--` itself (enabling column 0 tests, which `--^` cannot reach). Add completion assertion mode: `--^ completion: has "skip"` checks that a completion item with the given label exists, and `--^ completion: is empty` checks that no completions are returned. This avoids logging the full completion JSON, making tests stable against docstring changes. Test tactic completion in empty `by` blocks for both top-level `by` and nested `by` (inside `id <| have := by`). For each, test completion at various column positions on the line below `by`: indented past `by`, at column 2, and at column 0. All positions currently offer tactic completions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ctives Remove the completion assertion mode (has/is empty) from the test runner and use plain `completion` directives in the empty-by tests instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # src/Lean/Server/Test/Runner.lean # tests/server_interactive/completionEmptyBy.lean # tests/server_interactive/completionEmptyBy.lean.out.expected
With strict `by` indentation, empty `by` blocks are now parsed successfully via `pushNone` instead of producing `.missing` syntax. This makes the `isEmptyBy` check in `goalsAt?` dead code — remove it. The `isEmpty` helper in `isSyntheticTacticCompletion` still works correctly because it handles both `.missing` and empty nodes from `pushNone` (via the vacuously-true `args.all isEmpty` on `#[]`). The `have : True := by` followed by a dedent now correctly puts `this` in scope in the outer tactic block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
aa77588 to
04386e5
Compare
mathlib-nightly-testing bot
pushed a commit
to leanprover-community/batteries
that referenced
this pull request
Apr 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR wraps the top-level command parser with
withPositionto enforce indentation inbyblocks, combined with an empty-by fallback for better error messages.This subsumes #3215 (which introduced
withPosition commandParserbut without the empty-by fallback). It is also related to #9524, which explores elaboration with empty tactic sequences — this PR reuses that idea for the empty-by fallback, so that abynot followed by an indented tactic produces an elaboration error (unsolved goals) rather than a parse error.Changes:
topLevelCommandParserFnnow uses(withPosition commandParser).fn, setting the saved position at the start of each top-level commandtacticSeqIndentGtgains an empty tactic sequence fallback (pushNone) so that missing indentation produces an elaboration error (unsolved goals) instead of a parse errorisEmptyByingoalsAt?removed: with strictbyindentation, emptybyblocks parse successfully viapushNone(producing empty nodes) rather than producing.missingsyntax, making theisEmptyBycheck dead code. TheisEmptyhelper inisSyntheticTacticCompletioncontinues to work correctly because it handles both.missingand empty nodes frompushNone(via the vacuously-trueargs.all isEmptyon#[])byblocks and expression continuations that were previously at column 0Behavior:
byblocks now require indentation (column > 0 for commands at column 0)sectionrequire proofs to be indented past the command's column#guard_msgs in example : True := byworks because tactic indentation is checked against the outermost command's columnby) must also be indented past the command, which is slightly more strict but more consistenthave : True := byfollowed by a dedent now correctly putsthisin scope in the outer tactic block (thehaveis structurally complete with an unsolved-goal error, rather than a parse error)Code changes observed in practice (lean4 test suite + Mathlib):
byblocks: top-leveltheorem ... := by/decreasing_byfollowed by tactics at column 0 must be indentedvariablecontinuations:variable {A : Type*} [Foo A]\n{B : Type*}where the second line starts at column 0 must be indented (most common category in Mathlib)def f : T :=\nexpror#synth Foo\n[args]where the body/arguments start at column 0.symm\n{ toFun := ...where the struct literal starts at column 0🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com