Skip to content

Commit 74ddec5

Browse files
committed
[Macros/Parser] Attributes on MacroExpansionDecl
* Parse `#<identifier>` attribute list as a `MacroExpansionDecl` regardless of the position * Diagnose whitespaces between `#` and the macro name. * Correctly attach attributes to `MacroExpansionDecl` * Fix `OrigDeclAttributes` to handle modifiers (use `getLocation()` instead of `AtLoc`.) Type checking is a TODO rdar://107386648
1 parent 4d8dab0 commit 74ddec5

12 files changed

+115
-35
lines changed

include/swift/Parse/Parser.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,11 +900,15 @@ class Parser {
900900
// Decl Parsing
901901

902902
/// Returns true if parser is at the start of a Swift decl or decl-import.
903-
bool isStartOfSwiftDecl(bool allowPoundIfAttributes = true);
903+
bool isStartOfSwiftDecl(bool allowPoundIfAttributes = true,
904+
bool hadAttrsOrModifiers = false);
904905

905906
/// Returns true if the parser is at the start of a SIL decl.
906907
bool isStartOfSILDecl();
907908

909+
/// Returns true if the parser is at a freestanding macro expansion.
910+
bool isStartOfFreestandingMacroExpansion();
911+
908912
/// Parse the top-level Swift items into the provided vector.
909913
///
910914
/// Each item will be a declaration, statement, or expression.

lib/AST/Attr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ OrigDeclAttrFilter::operator()(const DeclAttribute *Attr) const {
855855
auto declLoc = decl->getStartLoc();
856856
auto *mod = decl->getModuleContext();
857857
auto *declFile = mod->getSourceFileContainingLocation(declLoc);
858-
auto *attrFile = mod->getSourceFileContainingLocation(Attr->AtLoc);
858+
auto *attrFile = mod->getSourceFileContainingLocation(Attr->getLocation());
859859
if (!declFile || !attrFile)
860860
return Attr;
861861

lib/Parse/ParseDecl.cpp

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4786,7 +4786,8 @@ static void skipAttribute(Parser &P) {
47864786
}
47874787
}
47884788

4789-
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
4789+
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
4790+
bool hadAttrsOrModifiers) {
47904791
if (Tok.is(tok::at_sign) && peekToken().is(tok::kw_rethrows)) {
47914792
// @rethrows does not follow the general rule of @<identifier> so
47924793
// it is needed to short circuit this else there will be an infinite
@@ -4835,22 +4836,40 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48354836
(Tok.is(tok::pound_endif) && !allowPoundIfAttributes))
48364837
return true;
48374838

4838-
return isStartOfSwiftDecl(allowPoundIfAttributes);
4839+
return isStartOfSwiftDecl(allowPoundIfAttributes,
4840+
/*hadAttrsOrModifiers=*/true);
48394841
}
48404842

4841-
if (Tok.is(tok::pound) && peekToken().is(tok::identifier)) {
4842-
// Macro expansions at the top level are declarations.
4843-
return !isInSILMode() && SF.Kind != SourceFileKind::Interface &&
4844-
CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4843+
if (Tok.is(tok::pound)) {
4844+
if (isStartOfFreestandingMacroExpansion()) {
4845+
if (isInSILMode() || SF.Kind == SourceFileKind::Interface)
4846+
return false;
4847+
4848+
// Parse '#<identifier>' after attrs/modifiers as a macro expansion decl.
4849+
if (hadAttrsOrModifiers)
4850+
return true;
4851+
4852+
// Macro expansions at the top level of non-script file are declarations.
4853+
return CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4854+
}
4855+
4856+
// Otherwise, prefer parsing it as an expression.
4857+
return false;
48454858
}
48464859

48474860
// Skip a #if that contains only attributes in all branches. These will be
48484861
// parsed as attributes of a declaration, not as separate declarations.
48494862
if (Tok.is(tok::pound_if) && allowPoundIfAttributes) {
48504863
BacktrackingScope backtrack(*this);
48514864
bool sawAnyAttributes = false;
4852-
return skipIfConfigOfAttributes(sawAnyAttributes) &&
4853-
(Tok.is(tok::eof) || (sawAnyAttributes && isStartOfSwiftDecl()));
4865+
if (!skipIfConfigOfAttributes(sawAnyAttributes))
4866+
return false;
4867+
if (Tok.is(tok::eof))
4868+
return true;
4869+
if (!sawAnyAttributes)
4870+
return false;
4871+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/true,
4872+
/*hadAttrsOrModifiers=*/true);
48544873
}
48554874

48564875
// If we have a decl modifying keyword, check if the next token is a valid
@@ -4872,13 +4891,15 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48724891
// If we found the start of a decl while trying to skip over the
48734892
// paren, then we have something incomplete like 'private('. Return
48744893
// true for better recovery.
4875-
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false))
4894+
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4895+
/*hadAttrsOrModifiers=*/true))
48764896
return true;
48774897

48784898
skipSingle();
48794899
}
48804900
}
4881-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4901+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4902+
/*hadAttrsOrModifiers=*/true);
48824903
}
48834904
}
48844905

@@ -4905,7 +4926,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
49054926
consumeToken(tok::l_paren);
49064927
consumeToken(tok::identifier);
49074928
consumeToken(tok::r_paren);
4908-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4929+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4930+
/*hadAttrsOrModifiers=*/true);
49094931
}
49104932

49114933
if (Tok.isContextualKeyword("actor")) {
@@ -4917,7 +4939,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
49174939
// it's an actor declaration, otherwise, it isn't.
49184940
do {
49194941
consumeToken();
4920-
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false));
4942+
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4943+
/*hadAttrsOrModifiers=*/true));
49214944
return Tok.is(tok::identifier);
49224945
}
49234946

@@ -4960,12 +4983,14 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
49604983
// If we found the start of a decl while trying to skip over the
49614984
// paren, then we have something incomplete like 'package('. Return
49624985
// true for better recovery.
4963-
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false))
4986+
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4987+
/*hadAttrsOrModifiers=*/true))
49644988
return true;
49654989
skipSingle();
49664990
}
49674991
}
4968-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4992+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4993+
/*hadAttrsOrModifiers=*/true);
49694994
}
49704995
}
49714996

@@ -4976,7 +5001,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
49765001
// Otherwise, do a recursive parse.
49775002
Parser::BacktrackingScope Backtrack(*this);
49785003
consumeToken(tok::identifier);
4979-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
5004+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
5005+
/*hadAttrsOrModifiers=*/true);
49805006
}
49815007

49825008
bool Parser::isStartOfSILDecl() {
@@ -5004,6 +5030,19 @@ bool Parser::isStartOfSILDecl() {
50045030
llvm_unreachable("Unhandled case in switch");
50055031
}
50065032

5033+
bool Parser::isStartOfFreestandingMacroExpansion() {
5034+
// Check if "'#' <identifier>" without any whitespace between them.
5035+
if (!Tok.is(tok::pound))
5036+
return false;
5037+
if (Tok.getRange().getEnd() != peekToken().getLoc())
5038+
return false;
5039+
if (!peekToken().isAny(tok::identifier, tok::code_complete) &&
5040+
// allow keywords right after '#' so we can diagnose it when parsing.
5041+
!peekToken().isKeyword())
5042+
return false;
5043+
return true;
5044+
}
5045+
50075046
void Parser::consumeDecl(ParserPosition BeginParserPosition,
50085047
ParseDeclOptions Flags,
50095048
bool IsTopLevel) {
@@ -5256,9 +5295,15 @@ Parser::parseDecl(ParseDeclOptions Flags,
52565295
// Handled below.
52575296
break;
52585297
case tok::pound:
5259-
if (Tok.isAtStartOfLine() &&
5260-
peekToken().is(tok::code_complete) &&
5261-
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) {
5298+
if (!isStartOfFreestandingMacroExpansion()) {
5299+
consumeToken(tok::pound);
5300+
diagnose(Tok.getLoc(),
5301+
diag::macro_expansion_decl_expected_macro_identifier);
5302+
DeclResult = makeParserError();
5303+
break;
5304+
}
5305+
5306+
if (peekToken().is(tok::code_complete)) {
52625307
consumeToken();
52635308
if (CodeCompletionCallbacks) {
52645309
CodeCompletionCallbacks->completeAfterPoundDirective();
@@ -5270,6 +5315,7 @@ Parser::parseDecl(ParseDeclOptions Flags,
52705315

52715316
// Parse as a macro expansion.
52725317
DeclResult = parseDeclMacroExpansion(Flags, Attributes);
5318+
StaticLoc = SourceLoc(); // Ignore 'static' on macro expansion
52735319
break;
52745320

52755321
case tok::pound_if:
@@ -9803,10 +9849,10 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags,
98039849
}
98049850
}
98059851

9806-
return makeParserResult(
9807-
status,
9808-
new (Context) MacroExpansionDecl(
9809-
CurDeclContext, poundLoc, macroNameRef, macroNameLoc,
9810-
leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc,
9811-
argList));
9852+
auto *med = new (Context) MacroExpansionDecl(
9853+
CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc,
9854+
Context.AllocateCopy(genericArgs), rightAngleLoc, argList);
9855+
med->getAttrs() = attributes;
9856+
9857+
return makeParserResult(status, med);
98129858
}

lib/Parse/ParseExpr.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,6 +1841,12 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
18411841
}
18421842

18431843
case tok::pound:
1844+
if (!isStartOfFreestandingMacroExpansion()) {
1845+
consumeToken(tok::pound);
1846+
diagnose(Tok.getLoc(),
1847+
diag::macro_expansion_expr_expected_macro_identifier);
1848+
return makeParserError();
1849+
}
18441850
if (peekToken().is(tok::code_complete) &&
18451851
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) {
18461852
return parseExprPoundCodeCompletion(/*ParentKind*/None);

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20582058
}
20592059

20602060
void visitMacroExpansionDecl(MacroExpansionDecl *MED) {
2061+
// TODO: Type check attributes.
2062+
// Type checking arguments should reflect the attributes.
2063+
// e.g. '@available(macOS 999) #Future { newAPIFrom999() }'.
2064+
20612065
// Assign a discriminator.
20622066
(void)MED->getDiscriminator();
20632067
// Decls in expansion already visited as auxiliary decls.

lib/Sema/TypeCheckMacros.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,9 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator,
15491549
macroRef.getArgs(), roles);
15501550
}
15511551

1552+
// FIXME: Type checking should reflect the attributes on the expansion.
1553+
// e.g. '@available(macOS 999) #Future { newAPIFrom999() }'.
1554+
15521555
Expr *result = macroExpansion;
15531556
TypeChecker::typeCheckExpression(
15541557
result, dc, {}, TypeCheckExprFlags::DisableMacroExpansions);

test/Parse/line-directive.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public struct S {
3030
// expected-error@+5{{operators must have one or two arguments}}
3131
// expected-error@+4{{member operator '/()' must have at least one argument of type 'S'}}
3232
// expected-error@+3{{expected '{' in body of function declaration}}
33-
// expected-error@+2 2 {{consecutive declarations on a line must be separated by ';}}
34-
// expected-error@+1 2 {{expected a macro identifier}}
33+
// expected-error@+2 {{consecutive declarations on a line must be separated by ';}}
34+
// expected-error@+1 {{expected a macro identifier}}
3535
/ ###line 25 "line-directive.swift"
3636
}
3737
// expected-error@+1{{#line directive was renamed to #sourceLocation}}

test/Parse/macro_decl.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ extension String {
1616

1717
#memberwiseInit(flavor: .chocolate, haha: true) { "abc" }
1818

19+
@available(macOS 999, *)
20+
public #memberwiseInit()
21+
22+
static internal #memberwiseInit
23+
1924
struct Foo {
2025
#memberwiseInit
2126

@@ -26,3 +31,18 @@ extension String {
2631
// expected-error @+1 {{expected a macro identifier for a pound literal declaration}}
2732
#()
2833
}
34+
35+
@RandomAttr #someFunc
36+
37+
public #someFunc
38+
39+
#someFunc
40+
41+
func test() {
42+
@discardableResult #someFunc
43+
44+
dynamic #someFunc
45+
46+
@CustomAttr
47+
isolated #someFunc
48+
}

test/Parse/object_literals.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ let _ = #notAPound // expected-error {{no macro named 'notAPound'}}
44
let _ = #notAPound(1, 2) // expected-error {{no macro named 'notAPound'}}
55
let _ = #Color // expected-error {{no macro named 'Color'}}
66

7-
let _ = [##] // expected-error 2 {{expected a macro identifier}} {{none}}
8-
// expected-error @-1 {{consecutive statements on a line must be separated by ';'}}
7+
let _ = [##] // expected-error {{expected a macro identifier}} {{none}}

test/Parse/operator_decl.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ infix operator aa--: A // expected-error {{'aa' is considered an identifier and
5252
infix operator <<$$@< // expected-error {{'$$' is considered an identifier and must not appear within an operator name}}
5353
infix operator !!@aa // expected-error {{'@' is not allowed in operator names}}
5454
infix operator #++= // expected-error {{'#' is not allowed in operator names}}
55-
// expected-error@-1 {{expected a macro identifier}}
5655
infix operator ++=# // expected-error {{'#' is not allowed in operator names}}
5756
infix operator -># // expected-error {{'#' is not allowed in operator names}}
5857

@@ -61,7 +60,6 @@ infix operator -># // expected-error {{'#' is not allowed in operator names}}
6160
infix operator =#=
6261
// expected-error@-1 {{'#' is not allowed in operator names}}
6362
// expected-error@-2 {{'=' must have consistent whitespace on both sides}}
64-
// expected-error@-3 {{expected a macro identifier}}
6563

6664
infix operator +++=
6765
infix operator *** : A

test/Parse/raw_string_errors.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ let _ = #"\##("invalid")"#
1212
let _ = ###"""invalid"######
1313
// expected-error@-1{{too many '#' characters in closing delimiter}}{{26-29=}}
1414
// expected-error@-2{{consecutive statements on a line must be separated by ';'}}
15-
// expected-error@-3 3 {{expected a macro identifier}}
15+
// expected-error@-3{{expected a macro identifier}}
1616

1717
let _ = ####"invalid"###
1818
// expected-error@-1{{unterminated string literal}}
1919

2020
let _ = ###"invalid"######
2121
// expected-error@-1{{too many '#' characters in closing delimiter}}{{24-27=}}
2222
// expected-error@-2{{consecutive statements on a line must be separated by ';'}}
23-
// expected-error@-3 3 {{expected a macro identifier}}
23+
// expected-error@-3{{expected a macro identifier}}
2424

2525
let _ = ##"""aa
2626
foobar

test/StringProcessing/Frontend/disable-flag.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ _ = /x/
99
// expected-error@-2 {{cannot find 'x' in scope}}
1010
// expected-error@-3 {{'/' is not a postfix unary operator}}
1111

12-
_ = #/x/# // expected-error 2 {{expected a macro identifier}}
12+
_ = #/x/# // expected-error {{expected a macro identifier}}
1313

1414
func foo(_ x: Regex<Substring>) {} // expected-error {{cannot find type 'Regex' in scope}}

0 commit comments

Comments
 (0)