Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - feat: the multiGoal linter #12339

Closed
wants to merge 109 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
dfe64d6
feat: the `multiGoal` linter
adomani Apr 22, 2024
475c98a
import at bottom and imports
adomani Apr 22, 2024
c6e3489
more exclude/ignore
adomani Apr 23, 2024
97a7d06
ignore conv_lhs/rhs
adomani Apr 23, 2024
1821772
add test file
adomani Apr 23, 2024
31d0e2a
linterate
adomani Apr 23, 2024
fa6d4dd
repeat'
adomani Apr 23, 2024
dc3f312
case'
adomani Apr 23, 2024
032028f
Unlint `assumption'` and `focus`.
adomani Apr 23, 2024
f03feb1
docs
adomani Apr 23, 2024
214ba12
Add todo
adomani Apr 23, 2024
79fe485
Do not import at the bottom of the hyerarchy
adomani Apr 23, 2024
fa9d019
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Apr 23, 2024
c526603
only lint, `Mathlib`, its libraries and the linter's own test file
adomani Apr 23, 2024
59042a7
Merge branch 'master' into adomani/lint_multiple_goals
adomani May 11, 2024
93e1ddf
Std --> Batteries and adaptation_note
adomani May 11, 2024
d912f74
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani May 11, 2024
ffc6b69
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani May 13, 2024
962c67d
fix test messages, but tests are still noisy
adomani May 13, 2024
cba8899
Merge branch 'master' into adomani/lint_multiple_goals
adomani May 13, 2024
080b0c0
import early and shake
adomani May 14, 2024
75a0e92
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani May 14, 2024
ce9a338
unlint two files
adomani May 14, 2024
1b55583
more mathlib fixes
adomani May 14, 2024
cfe902d
one more cdot
adomani May 14, 2024
1b9d82e
silence Archive
adomani May 14, 2024
1a25287
silence tests!
adomani May 14, 2024
f16f928
chore: more cdots
adomani May 14, 2024
17ba5da
Merge branch 'adomani/use_cdot_7' into adomani/lint_multiple_goals
adomani May 14, 2024
06b9532
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani May 14, 2024
3d992a3
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani May 20, 2024
c0caa4d
two more files
adomani May 24, 2024
2ecd134
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani May 24, 2024
7517f38
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Jun 19, 2024
77235bb
Merge branch 'master' into adomani/lint_multiple_goals
grunweg Jul 23, 2024
5f42b76
Move to Tactic/Linter
grunweg Jul 23, 2024
732c040
Rename test to Multigoal.lean
grunweg Jul 23, 2024
ece35d7
Last fix; one proof is rather tricky.
grunweg Jul 21, 2024
4226f06
Further fixes.
grunweg Jul 23, 2024
90aefb0
Fix noshake file
grunweg Jul 23, 2024
4de1d51
Tweak test: add no-op; consolidate and document the guard_msg bug.
grunweg Jul 23, 2024
33d5ee1
Tweak documentation, reorder list of allowed tactics.
grunweg Jul 23, 2024
b92fc1e
Add another test.
grunweg Jul 23, 2024
ca0ae70
Rename variables for clarity.
grunweg Jul 23, 2024
130941f
Merge branch 'master' into adomani/lint_multiple_goals
grunweg Aug 17, 2024
05d6947
Fix recent violations.
grunweg Aug 18, 2024
16d319e
Merge branch 'master' into adomani/lint_multiple_goals
grunweg Aug 18, 2024
18a70d9
chore: fix violations in BinaryEntropy
grunweg Aug 30, 2024
beb1374
Merge branch 'master' into adomani/lint_multiple_goals
grunweg Aug 30, 2024
16d7500
Flag current violations.
grunweg Aug 30, 2024
369da82
chore: move option to linter.style.multiGoal
grunweg Sep 18, 2024
11a3ebe
Inline getLinterHash; disable by default (but enable in mathlib).
grunweg Sep 18, 2024
3811f56
Fix test
grunweg Sep 18, 2024
7cf0b72
Merge branch 'master' into adomani/lint_multiple_goals
grunweg Sep 18, 2024
70b9a7f
chore: also import in Mathlib.Init
grunweg Sep 18, 2024
9327f61
Fix typo, oops
grunweg Sep 18, 2024
c0c117a
chore: use HashSet.ofList instead of manually inserting often
grunweg Sep 18, 2024
ca1ede0
chore: remove obsolete code, left over from a previous version
grunweg Sep 18, 2024
5e8896b
Some fixes for the multi-goal linter: not complete.
grunweg Sep 18, 2024
2943eef
Fix the last warnings/silence the linter.
grunweg Sep 18, 2024
a53f763
Merge branch 'master' into adomani/lint_multiple_goals
grunweg Sep 18, 2024
44b65ce
Remove import from Tactic.Basic, and noshake change.
grunweg Sep 18, 2024
f894176
Don't lint on sleepHeartbeats either.
grunweg Sep 23, 2024
586382b
Merge branch 'master' into adomani/lint_multiple_goals
adomani Sep 30, 2024
e13dcbe
more dots
adomani Sep 30, 2024
cf2f11e
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Sep 30, 2024
a929633
remove set_options
adomani Sep 30, 2024
9fab516
chore: prepare for multi-goal linter
adomani Sep 30, 2024
1def34b
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 1, 2024
bfef0a9
Merge remote-tracking branch 'origin/master' into adomani/multi_goal_…
adomani Oct 1, 2024
f7a8f88
chore: add a focusing dot
adomani Oct 1, 2024
1f13179
Laurent
adomani Oct 1, 2024
801bd98
Merge branch 'adomani/add_a_focusing_dot' into adomani/lint_multiple_…
adomani Oct 1, 2024
4a6adaa
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 2, 2024
bfa207e
Kim' suggestions
adomani Oct 2, 2024
47e3889
Merge branch 'adomani/multi_goal_indents4' into adomani/lint_multiple…
adomani Oct 2, 2024
a07749b
Merge branch 'master' into adomani/lint_multiple_goals
adomani Oct 2, 2024
1240d41
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 2, 2024
d82b7a7
Merge branch 'adomani/lint_multiple_goals' of github.com:leanprover-c…
adomani Oct 2, 2024
574aac0
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 2, 2024
ce49d09
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 3, 2024
58f7936
remove the unexpected todo
adomani Oct 3, 2024
2634afb
two cdots
adomani Oct 3, 2024
f4e1bc9
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 3, 2024
44bf842
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 3, 2024
b2fd434
another cdot
adomani Oct 3, 2024
068780b
Merge branch 'master' into adomani/lint_multiple_goals
adomani Oct 4, 2024
598d773
two more cdots
adomani Oct 4, 2024
02fe311
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 4, 2024
1b34cd8
another cdot
adomani Oct 4, 2024
4df8d9b
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 4, 2024
eadeffd
cdot
adomani Oct 5, 2024
31e8639
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 5, 2024
437e3b2
Merge branch 'master' into adomani/lint_multiple_goals
adomani Oct 7, 2024
28a1344
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 9, 2024
4932699
one cdot
adomani Oct 9, 2024
67a79f8
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 17, 2024
7717ed0
Merge branch 'adomani/lint_multiple_goals' of github.com:leanprover-c…
adomani Oct 17, 2024
0883c70
chore: more fixes for multigoal linter
adomani Oct 20, 2024
88cb49d
Merge branch 'adomani/multi_goal_fixes' into adomani/lint_multiple_goals
adomani Oct 20, 2024
48de0f7
improve docs
adomani Oct 20, 2024
653155a
Adjust error message
adomani Oct 20, 2024
45a2a7c
another fix
adomani Oct 20, 2024
78c6e27
Merge branch 'adomani/multi_goal_fixes' into adomani/lint_multiple_goals
adomani Oct 20, 2024
37e059f
one more
adomani Oct 20, 2024
1ab13f9
adaptations to review comments
adomani Oct 21, 2024
18e9f5b
Merge remote-tracking branch 'origin/master' into adomani/lint_multip…
adomani Oct 21, 2024
dd46de6
Update lakefile.lean
adomani Oct 21, 2024
3e9d3e2
Merge branch 'adomani/lint_multiple_goals' of github.com:leanprover-c…
adomani Oct 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Mathlib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -4397,6 +4397,7 @@ import Mathlib.Tactic.Linter.HaveLetLinter
import Mathlib.Tactic.Linter.Header
import Mathlib.Tactic.Linter.Lint
import Mathlib.Tactic.Linter.MinImports
import Mathlib.Tactic.Linter.Multigoal
import Mathlib.Tactic.Linter.OldObtain
import Mathlib.Tactic.Linter.PPRoundtrip
import Mathlib.Tactic.Linter.RefineLinter
Expand Down
1 change: 1 addition & 0 deletions Mathlib/Analysis/Analytic/Within.lean
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ result for `AnalyticOn`, as this requires a bit more work to show that local ext
be stitched together.
-/

set_option linter.style.multiGoal false in
/-- `f` has power series `p` at `x` iff some local extension of `f` has that series -/
lemma hasFPowerSeriesWithinOnBall_iff_exists_hasFPowerSeriesOnBall [CompleteSpace F] {f : E → F}
{p : FormalMultilinearSeries 𝕜 E F} {s : Set E} {x : E} {r : ℝ≥0∞} :
Expand Down
1 change: 1 addition & 0 deletions Mathlib/Init.lean
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Mathlib.Tactic.Linter.GlobalAttributeIn
import Mathlib.Tactic.Linter.Header
-- This file imports Batteries.Tactic.Lint, where the `env_linter` attribute is defined.
import Mathlib.Tactic.Linter.Lint
import Mathlib.Tactic.Linter.Multigoal
import Mathlib.Tactic.Linter.OldObtain
import Mathlib.Tactic.Linter.RefineLinter
import Mathlib.Tactic.Linter.UnusedTactic
Expand Down
2 changes: 1 addition & 1 deletion Mathlib/LinearAlgebra/Eigenspace/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ lemma isNilpotent_restrict_unifEigenspace_top [IsNoetherian R M] (f : End R M) (
mapsTo_unifEigenspace_of_comm (Algebra.mul_sub_algebraMap_commutes f μ) μ _) :
IsNilpotent ((f - μ • 1).restrict h) := by
apply isNilpotent_restrict_of_le
swap; apply isNilpotent_restrict_unifEigenspace_nat f μ (maxUnifEigenspaceIndex f μ)
on_goal 2 => apply isNilpotent_restrict_unifEigenspace_nat f μ (maxUnifEigenspaceIndex f μ)
rw [unifEigenspace_top_eq_maxUnifEigenspaceIndex]

/-- The submodule `eigenspace f μ` for a linear map `f` and a scalar `μ` consists of all vectors `x`
Expand Down
1 change: 1 addition & 0 deletions Mathlib/Tactic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ import Mathlib.Tactic.Linter.HaveLetLinter
import Mathlib.Tactic.Linter.Header
import Mathlib.Tactic.Linter.Lint
import Mathlib.Tactic.Linter.MinImports
import Mathlib.Tactic.Linter.Multigoal
import Mathlib.Tactic.Linter.OldObtain
import Mathlib.Tactic.Linter.PPRoundtrip
import Mathlib.Tactic.Linter.RefineLinter
Expand Down
165 changes: 165 additions & 0 deletions Mathlib/Tactic/Linter/Multigoal.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/-
Copyright (c) 2024 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa
-/
import Lean.Elab.Command

/-!
# The "multiGoal" linter

The "multiGoal" linter emits a warning where there is more than a single goal in scope.
adomani marked this conversation as resolved.
Show resolved Hide resolved
There is an exception: a tactic that closes *all* remaining goals is allowed.

There are a few tactics, such as `skip`, `swap` or the `try` combinator, that are intended to work
specifically in such a situation.
Otherwise, the mathlib style guide ask that goals be be focused until there is only one active goal
at a time.
If this focusing does not happen, the linter emits a warning.
Typically, the focusing is achieved by the `cdot`: `·`, but, e.g., `focus` or `on_goal x`
also serve a similar purpose.

TODO:
grunweg marked this conversation as resolved.
Show resolved Hide resolved
* Should the linter flag unnecessary scoping as well?
For instance, should
```lean
example : True := by
· · exact .intro
```
raise a warning?
* Custom support for "accumulating side-goals", so that once they are all in scope
they can be solved in bulk via `all_goals` or a similar tactic.
* In development, `on_goal` has been partly used as a way of silencing the linter
grunweg marked this conversation as resolved.
Show resolved Hide resolved
precisely to allow the accumulation referred to in the previous item.
Maybe revisit usages of `on_goal` and also nested `induction` and `cases`.
-/

open Lean Elab

namespace Mathlib.Linter

/-- The "multiGoal" linter emits a warning when there are multiple active goals. -/
register_option linter.style.multiGoal : Bool := {
defValue := false
descr := "enable the multiGoal linter"
}

namespace Style.multiGoal

/-- The `SyntaxNodeKind`s in `exclusions` correspond to tactics that the linter allows,
even though there are multiple active goals.
Reasons for admitting a kind in `exclusions` include
* the tactic focuses on one goal, e.g. `·`, `focus`, `on_goal i =>`, ...;
* the tactic is reordering the goals, e.g. `swap`, `rotate_left`, ...;
* the tactic is structuring a proof, e.g. `skip`, `<;>`, ...;
* the tactic is creating new goals, e.g. `constructor`, `cases`, `induction`, ....

There is some overlap in scope between `ignoreBranch` and `exclusions`.

Tactic combinators like `repeat` or `try` are a mix of both.
grunweg marked this conversation as resolved.
Show resolved Hide resolved
-/
abbrev exclusions : Std.HashSet SyntaxNodeKind := .ofList [
-- structuring a proof
``Lean.Parser.Term.cdot,
``cdot,
``cdotTk,
``Lean.Parser.Tactic.tacticSeqBracketed,
`«;»,
`«<;>»,
``Lean.Parser.Tactic.«tactic_<;>_»,
`«{»,
`«]»,
`null,
`then,
`else,
``Lean.Parser.Tactic.«tacticNext_=>_»,
``Lean.Parser.Tactic.tacticSeq1Indented,
``Lean.Parser.Tactic.tacticSeq,
-- re-ordering goals
`Batteries.Tactic.tacticSwap,
``Lean.Parser.Tactic.rotateLeft,
``Lean.Parser.Tactic.rotateRight,
``Lean.Parser.Tactic.skip,
`Batteries.Tactic.«tacticOn_goal-_=>_»,
`Mathlib.Tactic.«tacticSwap_var__,,»,
-- tactic combinators
``Lean.Parser.Tactic.tacticRepeat_,
``Lean.Parser.Tactic.tacticTry_,
-- creating new goals
``Lean.Parser.Tactic.paren,
``Lean.Parser.Tactic.case,
``Lean.Parser.Tactic.constructor,
`Mathlib.Tactic.tacticAssumption',
``Lean.Parser.Tactic.induction,
``Lean.Parser.Tactic.cases,
``Lean.Parser.Tactic.intros,
``Lean.Parser.Tactic.injections,
``Lean.Parser.Tactic.substVars,
`Batteries.Tactic.«tacticPick_goal-_»,
``Lean.Parser.Tactic.case',
`«tactic#adaptation_note_»,
`tacticSleep_heartbeats_
]

/-- The `SyntaxNodeKind`s in `ignoreBranch` correspond to tactics that disable the linter from
their first application until the corresponding proof branch is closed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you; this is much clearer! I'm stumbling a bit over the term "proof branch" - I don't know a better term though. (Do you?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really: I view proofs as (syntax) "trees" and this is a "branch" of the "tree"...

Reasons for ignoring these tactics include
* the linter gets confused by the proof management, e.g. `conv`;
* the tactics are *intended* to act on multiple goals, e.g. `repeat`, `any_goals`, `all_goals`, ...

There is some overlap in scope between `exclusions` and `ignoreBranch`.
-/
abbrev ignoreBranch : Std.HashSet SyntaxNodeKind := .ofList [
``Lean.Parser.Tactic.Conv.conv,
`Mathlib.Tactic.Conv.convLHS,
`Mathlib.Tactic.Conv.convRHS,
``Lean.Parser.Tactic.first,
``Lean.Parser.Tactic.repeat',
``Lean.Parser.Tactic.tacticIterate____,
``Lean.Parser.Tactic.anyGoals,
``Lean.Parser.Tactic.allGoals,
``Lean.Parser.Tactic.focus
]

/-- `getManyGoals t` returns the syntax nodes of the `InfoTree` `t` corresponding to tactic calls
adomani marked this conversation as resolved.
Show resolved Hide resolved
which
* leave at least one goal that was not present before it ran;
* are not excluded through `exclusions` or `ignoreBranch`;

together with the total number of goals
-/
partial
def getManyGoals : InfoTree → Array (Syntax × Nat)
| .node info args =>
let kargs := (args.map getManyGoals).toArray.flatten
if let .ofTacticInfo info := info then
if ignoreBranch.contains info.stx.getKind then #[] else
adomani marked this conversation as resolved.
Show resolved Hide resolved
if let .original .. := info.stx.getHeadInfo then
let newGoals := info.goalsAfter.filter (info.goalsBefore.contains ·)
if newGoals.length != 0 && !exclusions.contains info.stx.getKind then
kargs.push (info.stx, newGoals.length)
else kargs
else kargs
else kargs
| .context _ t => getManyGoals t
| _ => default

@[inherit_doc Mathlib.Linter.linter.style.multiGoal]
def multiGoalLinter : Linter where run := withSetOptionIn fun _stx ↦ do
unless Linter.getLinterValue linter.style.multiGoal (← getOptions) do
return
if (← get).messages.hasErrors then
return
let trees ← getInfoTrees
for t in trees.toArray do
for (s, n) in getManyGoals t do
Linter.logLint linter.style.multiGoal s
m!"There are {n+1} unclosed goals before '{s}' and \
at least one remaining goal afterwards.\n\
Please focus on the current goal, for instance using `·` (typed as \"\\.\")."

initialize addLinter multiGoalLinter

end Style.multiGoal

end Mathlib.Linter
1 change: 1 addition & 0 deletions lakefile.lean
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ abbrev mathlibOnlyLinters : Array LeanOption := #[
⟨`linter.style.longLine, true⟩,
⟨`linter.style.longFile, .ofNat 1500⟩,
⟨`linter.style.missingEnd, true⟩,
⟨`linter.style.multiGoal, true⟩,
⟨`linter.style.setOption, true⟩
]

Expand Down
123 changes: 123 additions & 0 deletions test/Multigoal.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import Mathlib.Tactic.Basic
import Mathlib.Tactic.Conv
import Mathlib.Tactic.Linter.Multigoal
import Mathlib.Util.SleepHeartbeats

-- The warning generated by `linter.style.multiGoal` is not suppressed by `#guard_msgs`,
-- because the linter is run on `#guard_msgs` itself. This is a known issue, see e.g.
-- https://leanprover.zulipchat.com/#narrow/stream/348111-batteries/topic/unreachableTactic.20linter.20not.20suppressed.20by.20.60.23guard_msgs.60
-- We jump through an extra hoop here to silence the warning.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get this linter in before the Lean bump :-)

set_option linter.style.multiGoal false

-- A deactivated linter does nothing.
example : True := by
by_cases 0 = 0
exact .intro
exact .intro

#guard_msgs(drop warning) in
set_option linter.style.multiGoal true in
/--
warning: There are 2 unclosed goals before 'exact .intro' and at least one remaining goal afterwards.
Please focus on the current goal, for instance using `·` (typed as "\.").
note: this linter can be disabled with `set_option linter.style.multiGoal false`
-/
#guard_msgs in
example : True := by
by_cases 0 = 0
adomani marked this conversation as resolved.
Show resolved Hide resolved
exact .intro
exact .intro

#guard_msgs(drop warning) in
set_option linter.style.multiGoal true in
/--
warning: There are 2 unclosed goals before 'assumption' and at least one remaining goal afterwards.
Please focus on the current goal, for instance using `·` (typed as "\.").
note: this linter can be disabled with `set_option linter.style.multiGoal false`
-/
#guard_msgs in
example {n : Nat} (hn : n = 0) : n + 0 = 0 := by
conv =>
congr
rw [← Nat.add_zero 0]
conv_lhs =>
congr
rw [← Nat.add_zero n]
rfl
conv_rhs =>
rw [← Nat.add_zero 0]
congr
rfl
rfl
by_cases 0 = 0
assumption
assumption

set_option linter.unusedTactic false in
#guard_msgs(drop warning) in
set_option linter.style.multiGoal true in
/--
warning: There are 2 unclosed goals before 'rfl' and at least one remaining goal afterwards.
Please focus on the current goal, for instance using `·` (typed as "\.").
note: this linter can be disabled with `set_option linter.style.multiGoal false`
-/
#guard_msgs in
example (p : Prop) (hp : p) : (0 = 0 ∧ p) ∨ 0 = 0 := by
iterate left; decide
repeat' left; decide
refine Or.inl ⟨?_, ?_⟩
rfl
assumption

#guard_msgs(drop warning) in
set_option linter.style.multiGoal true in
/--
warning: There are 3 unclosed goals before 'rfl' and at least one remaining goal afterwards.
Please focus on the current goal, for instance using `·` (typed as "\.").
note: this linter can be disabled with `set_option linter.style.multiGoal false`
---
warning: There are 2 unclosed goals before 'trivial' and at least one remaining goal afterwards.
Please focus on the current goal, for instance using `·` (typed as "\.").
note: this linter can be disabled with `set_option linter.style.multiGoal false`
-/
#guard_msgs in
example : 0 = 0 ∧ 0 = 0 ∧ 0 = 0 := by
refine ⟨?_, ?_, ?_⟩
rfl
trivial
rfl

example (p : Bool) : 0 = 0 := by
cases p
case' false => rfl
case' true => rfl

#guard_msgs in
-- `assumption'` is allowed, as it is useful precisely when there are multiple active goals
grunweg marked this conversation as resolved.
Show resolved Hide resolved
example (p : Bool) (f : False) {h : 0 = 0} : 0 = 0 ∧ 0 = 1 := by
cases p <;>
constructor
assumption'
any_goals cases f

#guard_msgs in
-- `focus` is ignored
grunweg marked this conversation as resolved.
Show resolved Hide resolved
example : True ∧ True := by
constructor
focus
exact .intro
focus
exact .intro

set_option linter.unusedTactic false in
example : 1 = 1 := by
sleep_heartbeats 1000
rfl

-- we test that a tactic closing all remaining goals does not trigger the linter
macro "bi_trivial" : tactic => `(tactic| (trivial; trivial))

set_option linter.style.multiGoal true in
example : True ∧ True := by
constructor
bi_trivial
Loading