Skip to content

Commit e95f2d8

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 (cherry picked from commit 9fc1521)
1 parent b0c2dd8 commit e95f2d8

File tree

11 files changed

+112
-35
lines changed

11 files changed

+112
-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
@@ -856,7 +856,7 @@ OrigDeclAttrFilter::operator()(const DeclAttribute *Attr) const {
856856
auto declLoc = decl->getStartLoc();
857857
auto *mod = decl->getModuleContext();
858858
auto *declFile = mod->getSourceFileContainingLocation(declLoc);
859-
auto *attrFile = mod->getSourceFileContainingLocation(Attr->AtLoc);
859+
auto *attrFile = mod->getSourceFileContainingLocation(Attr->getLocation());
860860
if (!declFile || !attrFile)
861861
return Attr;
862862

lib/Parse/ParseDecl.cpp

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4768,7 +4768,8 @@ static void skipAttribute(Parser &P) {
47684768
}
47694769
}
47704770

4771-
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
4771+
bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
4772+
bool hadAttrsOrModifiers) {
47724773
if (Tok.is(tok::at_sign) && peekToken().is(tok::kw_rethrows)) {
47734774
// @rethrows does not follow the general rule of @<identifier> so
47744775
// it is needed to short circuit this else there will be an infinite
@@ -4817,22 +4818,40 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48174818
(Tok.is(tok::pound_endif) && !allowPoundIfAttributes))
48184819
return true;
48194820

4820-
return isStartOfSwiftDecl(allowPoundIfAttributes);
4821+
return isStartOfSwiftDecl(allowPoundIfAttributes,
4822+
/*hadAttrsOrModifiers=*/true);
48214823
}
48224824

4823-
if (Tok.is(tok::pound) && peekToken().is(tok::identifier)) {
4824-
// Macro expansions at the top level are declarations.
4825-
return !isInSILMode() && SF.Kind != SourceFileKind::Interface &&
4826-
CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4825+
if (Tok.is(tok::pound)) {
4826+
if (isStartOfFreestandingMacroExpansion()) {
4827+
if (isInSILMode() || SF.Kind == SourceFileKind::Interface)
4828+
return false;
4829+
4830+
// Parse '#<identifier>' after attrs/modifiers as a macro expansion decl.
4831+
if (hadAttrsOrModifiers)
4832+
return true;
4833+
4834+
// Macro expansions at the top level of non-script file are declarations.
4835+
return CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4836+
}
4837+
4838+
// Otherwise, prefer parsing it as an expression.
4839+
return false;
48274840
}
48284841

48294842
// Skip a #if that contains only attributes in all branches. These will be
48304843
// parsed as attributes of a declaration, not as separate declarations.
48314844
if (Tok.is(tok::pound_if) && allowPoundIfAttributes) {
48324845
BacktrackingScope backtrack(*this);
48334846
bool sawAnyAttributes = false;
4834-
return skipIfConfigOfAttributes(sawAnyAttributes) &&
4835-
(Tok.is(tok::eof) || (sawAnyAttributes && isStartOfSwiftDecl()));
4847+
if (!skipIfConfigOfAttributes(sawAnyAttributes))
4848+
return false;
4849+
if (Tok.is(tok::eof))
4850+
return true;
4851+
if (!sawAnyAttributes)
4852+
return false;
4853+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/true,
4854+
/*hadAttrsOrModifiers=*/true);
48364855
}
48374856

48384857
// If we have a decl modifying keyword, check if the next token is a valid
@@ -4854,13 +4873,15 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48544873
// If we found the start of a decl while trying to skip over the
48554874
// paren, then we have something incomplete like 'private('. Return
48564875
// true for better recovery.
4857-
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false))
4876+
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4877+
/*hadAttrsOrModifiers=*/true))
48584878
return true;
48594879

48604880
skipSingle();
48614881
}
48624882
}
4863-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4883+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4884+
/*hadAttrsOrModifiers=*/true);
48644885
}
48654886
}
48664887

@@ -4887,7 +4908,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48874908
consumeToken(tok::l_paren);
48884909
consumeToken(tok::identifier);
48894910
consumeToken(tok::r_paren);
4890-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4911+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4912+
/*hadAttrsOrModifiers=*/true);
48914913
}
48924914

48934915
if (Tok.isContextualKeyword("actor")) {
@@ -4899,7 +4921,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48994921
// it's an actor declaration, otherwise, it isn't.
49004922
do {
49014923
consumeToken();
4902-
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false));
4924+
} while (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4925+
/*hadAttrsOrModifiers=*/true));
49034926
return Tok.is(tok::identifier);
49044927
}
49054928

@@ -4942,12 +4965,14 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
49424965
// If we found the start of a decl while trying to skip over the
49434966
// paren, then we have something incomplete like 'package('. Return
49444967
// true for better recovery.
4945-
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false))
4968+
if (isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4969+
/*hadAttrsOrModifiers=*/true))
49464970
return true;
49474971
skipSingle();
49484972
}
49494973
}
4950-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4974+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4975+
/*hadAttrsOrModifiers=*/true);
49514976
}
49524977
}
49534978

@@ -4958,7 +4983,8 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
49584983
// Otherwise, do a recursive parse.
49594984
Parser::BacktrackingScope Backtrack(*this);
49604985
consumeToken(tok::identifier);
4961-
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false);
4986+
return isStartOfSwiftDecl(/*allowPoundIfAttributes=*/false,
4987+
/*hadAttrsOrModifiers=*/true);
49624988
}
49634989

49644990
bool Parser::isStartOfSILDecl() {
@@ -4986,6 +5012,19 @@ bool Parser::isStartOfSILDecl() {
49865012
llvm_unreachable("Unhandled case in switch");
49875013
}
49885014

5015+
bool Parser::isStartOfFreestandingMacroExpansion() {
5016+
// Check if "'#' <identifier>" without any whitespace between them.
5017+
if (!Tok.is(tok::pound))
5018+
return false;
5019+
if (Tok.getRange().getEnd() != peekToken().getLoc())
5020+
return false;
5021+
if (!peekToken().isAny(tok::identifier, tok::code_complete) &&
5022+
// allow keywords right after '#' so we can diagnose it when parsing.
5023+
!peekToken().isKeyword())
5024+
return false;
5025+
return true;
5026+
}
5027+
49895028
void Parser::consumeDecl(ParserPosition BeginParserPosition,
49905029
ParseDeclOptions Flags,
49915030
bool IsTopLevel) {
@@ -5238,9 +5277,15 @@ Parser::parseDecl(ParseDeclOptions Flags,
52385277
// Handled below.
52395278
break;
52405279
case tok::pound:
5241-
if (Tok.isAtStartOfLine() &&
5242-
peekToken().is(tok::code_complete) &&
5243-
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) {
5280+
if (!isStartOfFreestandingMacroExpansion()) {
5281+
consumeToken(tok::pound);
5282+
diagnose(Tok.getLoc(),
5283+
diag::macro_expansion_decl_expected_macro_identifier);
5284+
DeclResult = makeParserError();
5285+
break;
5286+
}
5287+
5288+
if (peekToken().is(tok::code_complete)) {
52445289
consumeToken();
52455290
if (CodeCompletionCallbacks) {
52465291
CodeCompletionCallbacks->completeAfterPoundDirective();
@@ -5252,6 +5297,7 @@ Parser::parseDecl(ParseDeclOptions Flags,
52525297

52535298
// Parse as a macro expansion.
52545299
DeclResult = parseDeclMacroExpansion(Flags, Attributes);
5300+
StaticLoc = SourceLoc(); // Ignore 'static' on macro expansion
52555301
break;
52565302

52575303
case tok::pound_if:
@@ -9785,10 +9831,10 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags,
97859831
}
97869832
}
97879833

9788-
return makeParserResult(
9789-
status,
9790-
new (Context) MacroExpansionDecl(
9791-
CurDeclContext, poundLoc, macroNameRef, macroNameLoc,
9792-
leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc,
9793-
argList));
9834+
auto *med = new (Context) MacroExpansionDecl(
9835+
CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc,
9836+
Context.AllocateCopy(genericArgs), rightAngleLoc, argList);
9837+
med->getAttrs() = attributes;
9838+
9839+
return makeParserResult(status, med);
97949840
}

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.

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

0 commit comments

Comments
 (0)