fix: do not discard mutual members on macro use (#10695)

This PR fixes an issue where non-`macro` members of a `mutual` block
were discarded if there was at least one macro present.

Fixes #10687
This commit is contained in:
Sebastian Ullrich 2025-10-07 14:04:04 +02:00 committed by GitHub
parent 13c38f64a5
commit ca7e7c4279
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 10 deletions

View file

@ -264,16 +264,15 @@ def expandMutualElement : Macro := fun stx => do
for elem in stx[1].getArgs do
-- Don't trigger the `expandNamespacedDecl` macro, the namespace is handled by the mutual def
-- elaborator directly instead
if elem.isOfKind ``Parser.Command.declaration then
continue
match (← expandMacro? elem) with
| some elemNew =>
if elemNew.isOfKind nullKind then
elemsNew := elemsNew ++ elemNew.getArgs
else
elemsNew := elemsNew.push elemNew
modified := true
| none => elemsNew := elemsNew.push elem
if !elem.isOfKind ``Parser.Command.declaration then
if let some elemNew ← expandMacro? elem then
if elemNew.isOfKind nullKind then
elemsNew := elemsNew ++ elemNew.getArgs
else
elemsNew := elemsNew.push elemNew
modified := true
continue
elemsNew := elemsNew.push elem
if modified then
return stx.setArg 1 (mkNullNode elemsNew)
else

48
tests/lean/run/10687.lean Normal file
View file

@ -0,0 +1,48 @@
import Lean
/-! Macros in `mutual` should preserve remaining `mutual` items. -/
open Lean Elab Command Lean.PrettyPrinter
private def trace [Repr α] (name : String) (p : IO α) : IO α := do
IO.println s!"tracing: {name}"
p
syntax (name := traced) "#traced " declModifiers "def " ident optDeclSig " := " term : command
@[macro traced] def elabTracedDef : Macro := fun stx => do
match stx with
| `(command| #traced $mods:declModifiers def $name:ident $sig:optDeclSig := $body:term) => do
let nameStr := name.getId.toString
let name_ := mkIdent (name.getId.appendAfter "_")
let args := sig.raw[0].getArgs
let mut argNames := #[]
for arg in args do
argNames := argNames.push (mkIdent (arg[1][0].getId))
-- Generate the first definition: def foo_ : Type := body
let def1 := ← `(command| $mods:declModifiers def $name_ $sig:optDeclSig := $body)
-- Generate the second definition: def foo : Type := trace "foo" foo_
let traceCall := ← `(trace $(quote nameStr) ($name_ $[ $argNames:ident ]*))
let def2 := ← `(command| $mods:declModifiers def $name $sig:optDeclSig := $traceCall)
let res <- `($def1:command
$def2:command)
dbg_trace "{res.raw.prettyPrint}"
return res
| _ => Macro.throwUnsupported
#traced
def foo (x : IO Nat) : IO Nat := x
#eval foo (pure 5)
mutual
--#traced
private partial def baz (x : IO Nat) : IO Nat :=
bar (pure 7)
#traced -- this previously discarded `baz`
private partial def bar (x : IO Nat) : IO Nat :=
baz (pure 5)
end -- mutual