-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Macros/Parse] Attributes on MacroExpansionDecl #65815
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4786,7 +4786,8 @@ static void skipAttribute(Parser &P) { | |
} | ||
} | ||
|
||
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | ||
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes, | ||
bool hadAttrsOrModifiers) { | ||
if (Tok.is(tok::at_sign) && peekToken().is(tok::kw_rethrows)) { | ||
// @rethrows does not follow the general rule of @<identifier> so | ||
// it is needed to short circuit this else there will be an infinite | ||
|
@@ -4835,22 +4836,40 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | |
(Tok.is(tok::pound_endif) && !allowPoundIfAttributes)) | ||
return true; | ||
|
||
return isStartOfSwiftDecl(allowPoundIfAttributes); | ||
return isStartOfSwiftDecl(allowPoundIfAttributes, | ||
/*hadAttrsOrModifiers=*/true); | ||
} | ||
|
||
if (Tok.is(tok::pound) && peekToken().is(tok::identifier)) { | ||
// Macro expansions at the top level are declarations. | ||
return !isInSILMode() && SF.Kind != SourceFileKind::Interface && | ||
CurDeclContext->isModuleScopeContext() && !allowTopLevelCode(); | ||
if (Tok.is(tok::pound)) { | ||
if (isStartOfFreestandingMacroExpansion()) { | ||
if (isInSILMode() || SF.Kind == SourceFileKind::Interface) | ||
return false; | ||
|
||
// Parse '#<identifier>' after attrs/modifiers as a macro expansion decl. | ||
if (hadAttrsOrModifiers) | ||
return true; | ||
|
||
// Macro expansions at the top level of non-script file are declarations. | ||
return CurDeclContext->isModuleScopeContext() && !allowTopLevelCode(); | ||
} | ||
|
||
// Otherwise, prefer parsing it as an expression. | ||
return false; | ||
} | ||
|
||
// Skip a #if that contains only attributes in all branches. These will be | ||
// parsed as attributes of a declaration, not as separate declarations. | ||
if (Tok.is(tok::pound_if) && allowPoundIfAttributes) { | ||
BacktrackingScope backtrack(*this); | ||
bool sawAnyAttributes = false; | ||
return skipIfConfigOfAttributes(sawAnyAttributes) && | ||
(Tok.is(tok::eof) || (sawAnyAttributes && isStartOfSwiftDecl())); | ||
if (!skipIfConfigOfAttributes(sawAnyAttributes)) | ||
return false; | ||
if (Tok.is(tok::eof)) | ||
return true; | ||
if (!sawAnyAttributes) | ||
return false; | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/true, | ||
/*hadAttrsOrModifiers=*/true); | ||
} | ||
|
||
// If we have a decl modifying keyword, check if the next token is a valid | ||
|
@@ -4872,13 +4891,15 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | |
// If we found the start of a decl while trying to skip over the | ||
// paren, then we have something incomplete like 'private('. Return | ||
// true for better recovery. | ||
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)) | ||
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true)) | ||
return true; | ||
|
||
skipSingle(); | ||
} | ||
} | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true); | ||
} | ||
} | ||
|
||
|
@@ -4905,7 +4926,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | |
consumeToken(tok::l_paren); | ||
consumeToken(tok::identifier); | ||
consumeToken(tok::r_paren); | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true); | ||
} | ||
|
||
if (Tok.isContextualKeyword("actor")) { | ||
|
@@ -4917,7 +4939,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | |
// it's an actor declaration, otherwise, it isn't. | ||
do { | ||
consumeToken(); | ||
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)); | ||
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true)); | ||
return Tok.is(tok::identifier); | ||
} | ||
|
||
|
@@ -4960,12 +4983,14 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | |
// If we found the start of a decl while trying to skip over the | ||
// paren, then we have something incomplete like 'package('. Return | ||
// true for better recovery. | ||
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false)) | ||
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true)) | ||
return true; | ||
skipSingle(); | ||
} | ||
} | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true); | ||
} | ||
} | ||
|
||
|
@@ -4976,7 +5001,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) { | |
// Otherwise, do a recursive parse. | ||
Parser::BacktrackingScope Backtrack(*this); | ||
consumeToken(tok::identifier); | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false); | ||
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false, | ||
/*hadAttrsOrModifiers=*/true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What’s the attribute or modifier that has been consumed here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We don’t know that it’s a contextual keyword, right? We just continue pretending that it might be, but really, it could be any identifier, right? Or am I missing something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's
above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, OK. I just didn’t read far enough back. It’s what you get from large functions that implicitly retain state from other early exits… Thanks for the explanation. Good with me 👍🏽 |
||
} | ||
|
||
bool Parser::isStartOfSILDecl() { | ||
|
@@ -5004,6 +5030,19 @@ bool Parser::isStartOfSILDecl() { | |
llvm_unreachable("Unhandled case in switch"); | ||
} | ||
|
||
bool Parser::isStartOfFreestandingMacroExpansion() { | ||
// Check if "'#' <identifier>" without any whitespace between them. | ||
if (!Tok.is(tok::pound)) | ||
return false; | ||
if (Tok.getRange().getEnd() != peekToken().getLoc()) | ||
return false; | ||
if (!peekToken().isAny(tok::identifier, tok::code_complete) && | ||
// allow keywords right after '#' so we can diagnose it when parsing. | ||
!peekToken().isKeyword()) | ||
return false; | ||
return true; | ||
} | ||
|
||
void Parser::consumeDecl(ParserPosition BeginParserPosition, | ||
ParseDeclOptions Flags, | ||
bool IsTopLevel) { | ||
|
@@ -5256,9 +5295,15 @@ Parser::parseDecl(ParseDeclOptions Flags, | |
// Handled below. | ||
break; | ||
case tok::pound: | ||
if (Tok.isAtStartOfLine() && | ||
peekToken().is(tok::code_complete) && | ||
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) { | ||
if (!isStartOfFreestandingMacroExpansion()) { | ||
consumeToken(tok::pound); | ||
diagnose(Tok.getLoc(), | ||
diag::macro_expansion_decl_expected_macro_identifier); | ||
DeclResult = makeParserError(); | ||
break; | ||
} | ||
|
||
if (peekToken().is(tok::code_complete)) { | ||
consumeToken(); | ||
if (CodeCompletionCallbacks) { | ||
CodeCompletionCallbacks->completeAfterPoundDirective(); | ||
|
@@ -5270,6 +5315,7 @@ Parser::parseDecl(ParseDeclOptions Flags, | |
|
||
// Parse as a macro expansion. | ||
DeclResult = parseDeclMacroExpansion(Flags, Attributes); | ||
StaticLoc = SourceLoc(); // Ignore 'static' on macro expansion | ||
break; | ||
|
||
case tok::pound_if: | ||
|
@@ -9803,10 +9849,10 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags, | |
} | ||
} | ||
|
||
return makeParserResult( | ||
status, | ||
new (Context) MacroExpansionDecl( | ||
CurDeclContext, poundLoc, macroNameRef, macroNameLoc, | ||
leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc, | ||
argList)); | ||
auto *med = new (Context) MacroExpansionDecl( | ||
CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc, | ||
Context.AllocateCopy(genericArgs), rightAngleLoc, argList); | ||
med->getAttrs() = attributes; | ||
|
||
return makeParserResult(status, med); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1841,6 +1841,12 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { | |
} | ||
|
||
case tok::pound: | ||
if (!isStartOfFreestandingMacroExpansion()) { | ||
consumeToken(tok::pound); | ||
diagnose(Tok.getLoc(), | ||
diag::macro_expansion_expr_expected_macro_identifier); | ||
return makeParserError(); | ||
} | ||
Comment on lines
+1844
to
+1849
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weren’t we diagnosing this before? Out of curiosity: Is there a test case that triggers this? |
||
if (peekToken().is(tok::code_complete) && | ||
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) { | ||
return parseExprPoundCodeCompletion(/*ParentKind*/None); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,8 +30,8 @@ public struct S { | |
// expected-error@+5{{operators must have one or two arguments}} | ||
// expected-error@+4{{member operator '/()' must have at least one argument of type 'S'}} | ||
// expected-error@+3{{expected '{' in body of function declaration}} | ||
// expected-error@+2 2 {{consecutive declarations on a line must be separated by ';}} | ||
// expected-error@+1 2 {{expected a macro identifier}} | ||
// expected-error@+2 {{consecutive declarations on a line must be separated by ';}} | ||
// expected-error@+1 {{expected a macro identifier}} | ||
/ ###line 25 "line-directive.swift" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is improved because previously There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, thank you! |
||
} | ||
// expected-error@+1{{#line directive was renamed to #sourceLocation}} | ||
|
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.
Can
actor
be a modifier here?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.
actor
is still declared as a (deprecated) modifier, and it's diagnosed as a error inparseDeclModifierList()
. The diagnostic message is not great, though.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, OK. Makes sense then. Just wanted to double-check