From 9db5a11428bb28988c7e868e4d9d5c603f0b9e26 Mon Sep 17 00:00:00 2001 From: Cedric St-Jean Date: Mon, 25 Dec 2023 06:38:08 +0900 Subject: [PATCH] Define @qq --- docs/src/utilities.md | 1 + src/utils.jl | 23 +++++++++++++++++++++++ test/utils.jl | 16 +++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/src/utilities.md b/docs/src/utilities.md index 58dc89c..6ac3243 100644 --- a/docs/src/utilities.md +++ b/docs/src/utilities.md @@ -43,6 +43,7 @@ MacroTools.combinearg ```@docs MacroTools.@q +MacroTools.@qq MacroTools.isexpr MacroTools.rmlines MacroTools.unblock diff --git a/src/utils.jl b/src/utils.jl index 4d1aca2..9b7a48b 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -36,6 +36,29 @@ macro q(ex) esc(Expr(:quote, striplines(ex))) end +struct Flag end + +to_flag(ex) = MacroTools.prewalk(x->x isa LineNumberNode ? Flag() : x, ex) +to_line(l::LineNumberNode, ex) = MacroTools.prewalk(x->x isa Flag ? l : x, ex) + +""" + @qq [expression] + +Like the `quote` keyword but replace construction site line numbers with __source__. +Line numbers of interpolated expressions are preserved. The result is that line numbers will be +attributed to the macro usage site, instead of the macro source code. + +Only works inside of a macro definition. + +See also: [`@q`](@ref) +""" +macro qq(ex) + # This was a difficult macro to write; the round-trip to Flag appears to be necessary. + # Crucially, we want interpolated expressions to preserve their line numbers. + esc(:($MacroTools.to_line(__source__, $(Expr(:quote, to_flag(ex)))))) +end + + """ isexpr(x, ts...) diff --git a/test/utils.jl b/test/utils.jl index 84e962c..f468845 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,4 +1,4 @@ -using MacroTools: isdef, flatten, striplines +using MacroTools: isdef, flatten, striplines, @qq @testset "utils" begin ex1 = :(function foo(a) return a; end) @@ -45,3 +45,17 @@ end @test flatten(ex) |> striplines == ex |> striplines end end + +## Test for @qq + +macro my_fff_def(a) + @qq function fff() $a end +end + +@my_fff_def begin # line where fff() is defined + function g() # line where fff()() is defined + 22 + end +end + +@test which(fff,()).line == which(fff(),()).line - 1