Skip to content

[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

Merged
merged 1 commit into from
Jun 8, 2023

Conversation

rintaro
Copy link
Member

@rintaro rintaro commented Jun 2, 2023

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 expansions are not supported at this point.

@rintaro rintaro force-pushed the sourcekit-syntactic-macroexpansion branch 4 times, most recently from ee434b3 to 8294db0 Compare June 5, 2023 16:35
@rintaro
Copy link
Member Author

rintaro commented Jun 5, 2023

@swift-ci Please smoke test

@rintaro rintaro marked this pull request as ready for review June 5, 2023 16:37
@rintaro
Copy link
Member Author

rintaro commented Jun 5, 2023

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 {
Copy link
Contributor

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 😅

Comment on lines +152 to +155
for (auto attr : macro->getAttrs().getAttributes<MacroRoleAttr>()) {
roles -= attr->getMacroRole();
}
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.
Copy link
Contributor

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?

Copy link
Member Author

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.

Comment on lines +109 to +86
// Note that this finds the generated source file that was created in the
// previous expansion requests.
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

@rintaro
Copy link
Member Author

rintaro commented Jun 5, 2023

@swift-ci Please smoke test

@rintaro rintaro force-pushed the sourcekit-syntactic-macroexpansion branch 2 times, most recently from 49c08e9 to 6f315f8 Compare June 7, 2023 15:52
@rintaro
Copy link
Member Author

rintaro commented Jun 7, 2023

@swift-ci Please smoke test

Comment on lines 468 to 472
bool hasError = false;
for (const auto &expansion : expansions) {
hasError |= getExpansion(SF, expansion, consumer);
}
return hasError;
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Member Author

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_");
Copy link
Contributor

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_?

@@ -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'),
Copy link
Contributor

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?

Comment on lines +248 to +252
RawCharSourceRange range;
unsigned parameterIndex;
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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

@rintaro rintaro force-pushed the sourcekit-syntactic-macroexpansion branch from 6f315f8 to f6dd5d1 Compare June 7, 2023 20:21
@rintaro
Copy link
Member Author

rintaro commented Jun 7, 2023

@swift-ci Please smoke test

@rintaro rintaro force-pushed the sourcekit-syntactic-macroexpansion branch from f6dd5d1 to de41f45 Compare June 7, 2023 20:38
@rintaro
Copy link
Member Author

rintaro commented Jun 7, 2023

@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.
@rintaro rintaro force-pushed the sourcekit-syntactic-macroexpansion branch from de41f45 to c3d1304 Compare June 7, 2023 21:26
@rintaro
Copy link
Member Author

rintaro commented Jun 7, 2023

@swift-ci Please smoke test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants