-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SourceKit] Add request to expand macros syntactically #66295
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
[SourceKit] Add request to expand macros syntactically #66295
Conversation
ee434b3
to
8294db0
Compare
@swift-ci Please smoke test |
I will add nested macros and "expanded" macros test cases, either in this PR or follow ups |
@@ -220,6 +220,60 @@ struct RawCharSourceRange { | |||
unsigned Length; | |||
}; | |||
|
|||
enum class MacroRole : uint8_t { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunate we need another of these 😅
for (auto attr : macro->getAttrs().getAttributes<MacroRoleAttr>()) { | ||
roles -= attr->getMacroRole(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would we want to add roles after we've already synthesized the macro?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the client didn't send the all roles in previous requests.
I don't think it happens practically, but different modules may declare the macro
decl differently for the same macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, this could still result in weird behavior:
- First the client sends the macro with an attached member role -> we expand it as a member
- Now the client sends the macro with only an attached conformance role -> we expand it as both a member and a conformance macro
So, there is state being carried between the requests. I think we should either
- Set the macro roles to only those that are declared in the current request or
- Not re-use a MacroDecl if it has different roles than in a previous invocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I removed caching, we don't carry any state between requests.
The only possible case this hits is that the client sends inconsistent roles within a single request. Which... is not great, but I think it's not worth to handle it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. I thought that MacroDecls
was cached more persistently.
|
||
StableHasher hasher = StableHasher::defaultHasher(); | ||
|
||
// Macro name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be the typename in the underlying module instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
module/typename of the macro still doesn't make the expansion unique
@MyMacro("foo") @MyMacro("bar") func fn()
These two MyMacro
expansions should have unique discriminators. So I decided to combine 2 locations for attached macros (target decl location and the attribute location), instead of the macro name or macro implementation name.
// Note that this finds the generated source file that was created in the | ||
// previous expansion requests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC this will only work up until another request is received that has different arguments. Maybe that's okay, but it does mean that all requests have to be sequential for now, ie. you couldn't request for expansions in different files concurrently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Different files still usually share the same compiler arguments as long as they are in the same module.
But yes, if compiler arguments are different, concurrent requests for nested expansion may not work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we care about any arguments other than the plugin search ones? Could we just filter to that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently use -module-name
too as far as I remember. I don't think it's a good idea to "sanitize" compiler arguments here. Affecting options might change overtime, but I'm skeptical about anyone changing the compiler options or CompilerInvocation correctly updates this request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is less sanitizing and more picking the ones we care about.
anyone changing the compiler options or CompilerInvocation correctly updates this request
Unless it impacts it, they shouldn't need to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They don't know if their change affects this. Like, a new plugin search option might be added in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the caching logic from this PR for now.
@swift-ci Please smoke test |
49c08e9
to
6f315f8
Compare
@swift-ci Please smoke test |
bool hasError = false; | ||
for (const auto &expansion : expansions) { | ||
hasError |= getExpansion(SF, expansion, consumer); | ||
} | ||
return hasError; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't do anything with this error at the moment, so I'm not sure it's worth having. If we wanted to keep it I'd prefer it was an llvm::Error
, though right now the only error is if we fail to find the expansion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, could both getExpansions
and getExpansion
be something like expandAll
and expand
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the return value. Renamed the methods
std::vector<unsigned> bufferIDs; | ||
|
||
SmallString<32> discriminator; | ||
discriminator.append("macro_"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a little more unique for these? Eg. __syntactic_macro_
?
utils/gyb_sourcekit_support/UIDs.py
Outdated
@@ -272,6 +275,8 @@ def __init__(self, internal_name, external_name): | |||
REQUEST('Diagnostics', 'source.request.diagnostics'), | |||
REQUEST('Compile', 'source.request.compile'), | |||
REQUEST('CompileClose', 'source.request.compile.close'), | |||
REQUEST('ExpandMacroSyntactically', | |||
'source.request.expand.macro.syntactically'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are... quite inconsistent with these. How about source.request.syntactic_macro_expansion
?
RawCharSourceRange range; | ||
unsigned parameterIndex; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose that a Replacement
works as follows.
range
references some part of code in the expanded macro definition (which is the part after the =
). The source code of range
will be replaced by the parameterIndex
-th argument of the macro.
If that’s correct, could you add documentation that describes it?
Also, does this work if the macro defines variadic parameters or parameter packs? Because in those cases a single parameter might have multiple arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your understanding is correct. I will add some comment about it.
As for variadics, I am not sure tbh. This just follows the compiler's implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, since "expanded" is not fully supported for attached macro, I don't think we can fully support it.
That's why I didn't mention it in the description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there is some issue with variadic macros defined in terms of other macros in general. Filed rdar://110419352
6f315f8
to
f6dd5d1
Compare
@swift-ci Please smoke test |
f6dd5d1
to
de41f45
Compare
@swift-ci Please smoke test |
Expand macros in the specified source file syntactically (without any module imports, nor typechecking). Request would look like: ``` { key.compilerargs: [...] key.sourcefile: <file name> key.sourcetext: <source text> (optional) key.expansions: [<expansion specifier>...] } ``` `key.compilerargs` are used for getting plugins search paths. If `key.sourcetext` is not specified, it's loaded from the file system. Each `<expansion sepecifier>` is ``` { key.offset: <offset> key.modulename: <plugin module name> key.typename: <macro typename> key.macro_roles: [<macro role UID>...] } ``` Clients have to provide the module and type names because that's semantic. Response is a `CategorizedEdits` just like (semantic) "ExpandMacro" refactoring. But without `key.buffer_name`. Nested expnasions are not supported at this point.
de41f45
to
c3d1304
Compare
@swift-ci Please smoke test |
Expand macros in the specified source file syntactically (without any module imports, nor typechecking).
Request would look like:
key.compilerargs
are used for getting plugins search paths. Ifkey.sourcetext
is not specified, it's loaded from the file system.Each
<expansion sepecifier>
isClients have to provide the module and type names because that's semantic.
Response is a
CategorizedEdits
just like (semantic) "ExpandMacro" refactoring. But withoutkey.buffer_name
. Nested expansions are not supported at this point.