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

Document renumbering lines in macros #40955

Closed
wants to merge 7 commits into from
Closed
Changes from all commits
Commits
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
56 changes: 56 additions & 0 deletions doc/src/manual/metaprogramming.md
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,62 @@ This allows macros to look up contextual information, such as existing bindings,
or to insert the value as an extra argument to a runtime function call doing self-reflection
in the current module.

### Renumbering lines in macros

You will likely want to renumber new lines of code introduced in macros
Consider the following trivial macro:

```jldoctest macro_line_numbers
julia> macro adds_more_lines_1()
quote
"code"
end
end
@adds_more_lines_1 (macro with 1 method)
```

Let's take a closer look at the new code introduced by the macro:

```jldoctest macro_line_numbers
julia> numbered = quote
"code"
end;

julia> numbered.head
:block

julia> typeof.(numbered.args)
2-element Vector{DataType}:
LineNumberNode
String
```

When a macro is invoked, Julia will introduce a new `LineNumberNode` that points to inside the macro.
You likely want line numbering to point to the user's call site, rather than the definition expression.
This is for the following two reasons:

- If an error occurs in code introduced in a macro, you likely want stack-traces to point to the user's call site, rather than the macro's internals.
- If a user calls a macro, you likely want coverage to consider the user's call site as covered, rather than the macro itself.
Copy link
Member

Choose a reason for hiding this comment

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

These argument seem inaccurate to me. The source should already be be getting represented as the place where the macro is being inlined at (as a fake "caller"). Injecting it again into the macro body seems like it could lead to even stranger frames as it tries to guess why this macro is changing back and forth between inlined and local. If the source location is missing in the caller (where we splice the macro to), that seems like just a bug.

There's already Base.remove_linenums! and associated improvements in PR form: #31335 and Meta.replace_sourceloc! from #22623.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the source location is missing in the caller (where we splice the macro to), that seems like just a bug.

Ok, if I understand you right, a bug like this is likely what I'm running into

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I close this and open a new issue?


Thus, consider revising the macro as follows:

```jldoctest macro_line_numbers
julia> macro adds_more_lines_2()
result =
quote
"code"
end
result.args[1] = __source__
result
end
@adds_more_lines_2 (macro with 1 method)
```

`__source__` will contain a `LineNumberNode` pointing to the user's call site
(see [Macro invocation](@ref) for more information about `__source__`).
The macro will now renumber the new line of code to point to the user's call site, rather than inside the macro.
Sometimes users will pass entire lines of code to macros, complete with additional `LineNumberNode`s.
In this case, instead of using `__source__` to renumber lines, you might use these more fine-grained `LineNumberNode`s.

### Building an advanced macro

Expand Down