Zulip Chat Archive

Stream: mathlib4

Topic: Special-casing the unusedTactic linter


Heather Macbeth (Aug 26 2024 at 20:55):

(For some reason the unusedTactic linter objects to one version's test file but not the other, has anyone debugged this kind of unpredictability before?)

Damiano Testa (Aug 26 2024 at 20:58):

Just glancing at the code (I am not at a computer), I think that the linter is complaining about the "config" skip that the linter considers to be unused.

Heather Macbeth (Aug 26 2024 at 20:58):

Right, but there is an identical file in which no complaints occur.

Damiano Testa (Aug 26 2024 at 20:59):

Oh, on which file is the linter silent?

Heather Macbeth (Aug 26 2024 at 21:01):

It's noisy on test/linear_combination' and silent on test/linear_combination :)
https://github.com/leanprover-community/mathlib4/actions/runs/10566534579/job/29273542575

Damiano Testa (Aug 26 2024 at 21:05):

Ah, the docs of the linter hold the answer:

* The tactic does not check the discharger for `linear_combination`,
  but checks `linear_combination` itself.
  The main reason is that `skip` is a common discharger tactic and the linter would
  then always fail whenever the user explicitly chose to pass `skip` as a discharger tactic.

Damiano Testa (Aug 26 2024 at 21:06):

So, I am guessing that linear_combination' should be special-cased in the same way.

Heather Macbeth (Aug 26 2024 at 21:06):

Aha! Thanks!

Damiano Testa (Aug 26 2024 at 21:06):

I can do that tomorrow, but if you want to take care of it now, it should be pretty straightforward as a change.

Heather Macbeth (Aug 26 2024 at 21:09):

I made a guess on how to fix it, and if I guessed correctly, there will be no need to bother you.

Notification Bot (Aug 26 2024 at 21:10):

10 messages were moved here from #mathlib4 > Narrowing the scope of linear_combination by Heather Macbeth.

Michael Rothgang (Aug 26 2024 at 21:11):

I think the guess is almost right; it should be

``|>.insert `Mathlib.Tactic.LinearCombination'.linearCombination'``

Michael Rothgang (Aug 26 2024 at 21:11):

(The namespace name also has a prime.)

Mario Carneiro (Aug 26 2024 at 21:11):

@Damiano Testa that sounds like another instance of that principle from before: skip when used as a tactic (rather than a tacticSeq), or as the lone tactic in a tacticSeq, shouldn't be linted because you can't syntactically delete it

Damiano Testa (Aug 27 2024 at 07:22):

Where is the information of whether the syntax is a tactic or a tacticSeq? I imagine somewhere in the infotree, but I cannot seem to find it.

Mario Carneiro (Aug 27 2024 at 07:45):

it's in the syntax itself

Mario Carneiro (Aug 27 2024 at 07:45):

although in general it's not super easy to get a hold of

Mario Carneiro (Aug 27 2024 at 07:48):

the parent syntax will have some expectation for what its child is

Damiano Testa (Aug 27 2024 at 07:49):

Stored in... preresolved?

Damiano Testa (Aug 27 2024 at 07:49):

Or the SourceInfo?

Mario Carneiro (Aug 27 2024 at 07:49):

no, just the structure of the tree and the node names

Damiano Testa (Aug 27 2024 at 07:49):

I am looking at what I thought was the "whole" syntax tree and I cannot find it.

Mario Carneiro (Aug 27 2024 at 07:49):

in the case of tacticSeq, you can more or less what the hard code the possible parents are

Damiano Testa (Aug 27 2024 at 07:49):

Ah, like a missing null node somewhere?

Mario Carneiro (Aug 27 2024 at 07:50):

I mean if the parent is a tacticSeqIndented node and under that is a null node and under that is a bunch of syntax nodes, then all those syntax nodes are in the tactic category because that's what tacticSeqIndented expects

Damiano Testa (Aug 27 2024 at 07:51):

Got it: it is a "usual" skip that appears inside something that was parsed as a tacticSeq or not.

Mario Carneiro (Aug 27 2024 at 07:52):

If it is a skip node and its parent is a null node of length 1 (whose parent is a tacticSeqIndented or tacticSeqBracketed node), then that means it's alone in its tacticSeq

Mario Carneiro (Aug 27 2024 at 07:52):

if it has a parent that isn't a null node then it is being used as a tactic directly in some other syntax

Damiano Testa (Aug 27 2024 at 07:54):

Indeed: this is what a skip with usual tactic parsing looks like

|-node linearCombination, none
|   |-atom original: ⟨⟩⟨ -- 'linear_combination'
|   |-node null, none
|   |   |-node normStx, none
|   |   |   |-atom original: ⟨⟩⟨⟩-- '('
|   |   |   |-atom original: ⟨⟩⟨ -- 'norm'
|   |   |   |-atom original: ⟨⟩⟨ -- ':='
|   |   |   |-node Lean.Parser.Tactic.skip, none
|   |   |   |   |-atom original: ⟨⟩⟨⟩-- 'skip'
|   |   |   |-atom original: ⟨⟩⟨⏎  --rfl⏎⏎⟩-- ')'
|   |-node null, none

and this is what it looks like when you modify linear_combination to use a tacticSeq as discharger:

|-node linearCombination, none
|   |-atom original: ⟨⟩⟨ -- 'linear_combination'
|   |-node null, none
|   |   |-node normStx, none
|   |   |   |-atom original: ⟨⟩⟨⟩-- '('
|   |   |   |-atom original: ⟨⟩⟨ -- 'norm'
|   |   |   |-atom original: ⟨⟩⟨ -- ':='
|   |   |   |-node Lean.Parser.Tactic.tacticSeq, none
|   |   |   |   |-node Lean.Parser.Tactic.tacticSeq1Indented, none
|   |   |   |   |   |-node null, none
|   |   |   |   |   |   |-node Lean.Parser.Tactic.skip, none
|   |   |   |   |   |   |   |-atom original: ⟨⟩⟨⟩-- 'skip'
|   |   |   |-atom original: ⟨⟩⟨⏎  --rfl⏎⏎⟩-- ')'

Damiano Testa (Aug 27 2024 at 07:55):

After the := I can now see that the syntax either places directly the node for the tactic or starts the dance for a tacticSeq.

Mario Carneiro (Aug 27 2024 at 07:56):

in this case, you want to lint only when it has a parent that is a tacticSeq with more than 1 element

Mario Carneiro (Aug 27 2024 at 07:56):

so both of these cases would be rejected

Damiano Testa (Aug 27 2024 at 07:56):

Yes, agreed.

Damiano Testa (Aug 27 2024 at 07:56):

"Rejected" as in "allowed by the linter"! :upside_down:

Mario Carneiro (Aug 27 2024 at 07:58):

although if the tactic is anything other than skip you still want to lint it and suggest to replace it by skip in these edge cases

Damiano Testa (Aug 27 2024 at 08:01):

Yes, I am not sure that I will have time to work on this this week, but I agree: a tactic (that is not part of a tacticSeq!) should not be linted and a tacticSeq should be linted only if it contains more than one tactic.

Damiano Testa (Aug 27 2024 at 08:21):

From an "efficiency" point of view, does it make sense to make the linter not inspect tactics that are required to modify something? E.g., should the linter not inspect simp in all its forms or is it not worth it?

Mario Carneiro (Aug 27 2024 at 22:11):

simp can have the failIfUnchanged := false option, in which case it may not fail even if it does nothing


Last updated: May 02 2025 at 03:31 UTC