Skip to content

Commit 9b92a14

Browse files
committed
[Macros] 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` * Reject modifiers and 'static' on `MacroExpansionDecl` for now * Copy attributes on `MacroExpansionDecl` to expanded Decls * Fix `OrigDeclAttributes` to handle modifiers (use `getLocation()` instead of `AtLoc`.) rdar://107386648
1 parent d37b6bc commit 9b92a14

File tree

10 files changed

+73
-22
lines changed

10 files changed

+73
-22
lines changed

include/swift/Parse/Parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,9 @@ class Parser {
905905
/// Returns true if the parser is at the start of a SIL decl.
906906
bool isStartOfSILDecl();
907907

908+
/// Returns true if the parser is at a freestanding macro expansion.
909+
bool isStartOfFreestandingMacroExpansion();
910+
908911
/// Parse the top-level Swift items into the provided vector.
909912
///
910913
/// 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: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,13 +4828,21 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes) {
48284828
(Tok.is(tok::pound_endif) && !allowPoundIfAttributes))
48294829
return true;
48304830

4831+
// Force parsing '#<identifier>' after attributes as a macro expansion decl.
4832+
if (isStartOfFreestandingMacroExpansion())
4833+
return true;
4834+
48314835
return isStartOfSwiftDecl(allowPoundIfAttributes);
48324836
}
48334837

4834-
if (Tok.is(tok::pound) && peekToken().is(tok::identifier)) {
4835-
// Macro expansions at the top level are declarations.
4836-
return !isInSILMode() && SF.Kind != SourceFileKind::Interface &&
4837-
CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4838+
if (Tok.is(tok::pound)) {
4839+
if (isStartOfFreestandingMacroExpansion()) {
4840+
// Macro expansions at the top level of non-script file are declarations.
4841+
return CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4842+
}
4843+
4844+
// Otherwise, prefer parsing it as an expression.
4845+
return false;
48384846
}
48394847

48404848
// Skip a #if that contains only attributes in all branches. These will be
@@ -4997,6 +5005,21 @@ bool Parser::isStartOfSILDecl() {
49975005
llvm_unreachable("Unhandled case in switch");
49985006
}
49995007

5008+
bool Parser::isStartOfFreestandingMacroExpansion() {
5009+
// Check if "'#' <identifier>" without any whitespace between them.
5010+
if (!Tok.is(tok::pound))
5011+
return false;
5012+
if (Tok.getLoc().getAdvancedLoc(1) != peekToken().getLoc())
5013+
return false;
5014+
if (!peekToken().isAny(tok::identifier, tok::code_complete) &&
5015+
// allow keywords right after '#' so we can diagnose it when parsing.
5016+
!peekToken().isKeyword())
5017+
return false;
5018+
if (isInSILMode() || SF.Kind == SourceFileKind::Interface)
5019+
return false;
5020+
return true;
5021+
}
5022+
50005023
void Parser::consumeDecl(ParserPosition BeginParserPosition,
50015024
ParseDeclOptions Flags,
50025025
bool IsTopLevel) {
@@ -5249,9 +5272,15 @@ Parser::parseDecl(ParseDeclOptions Flags,
52495272
// Handled below.
52505273
break;
52515274
case tok::pound:
5252-
if (Tok.isAtStartOfLine() &&
5253-
peekToken().is(tok::code_complete) &&
5254-
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) {
5275+
if (!isStartOfFreestandingMacroExpansion()) {
5276+
consumeToken(tok::pound);
5277+
diagnose(Tok.getLoc(),
5278+
diag::macro_expansion_decl_expected_macro_identifier);
5279+
DeclResult = makeParserError();
5280+
break;
5281+
}
5282+
5283+
if (peekToken().is(tok::code_complete)) {
52555284
consumeToken();
52565285
if (CodeCompletionCallbacks) {
52575286
CodeCompletionCallbacks->completeAfterPoundDirective();
@@ -9796,10 +9825,10 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags,
97969825
}
97979826
}
97989827

9799-
return makeParserResult(
9800-
status,
9801-
new (Context) MacroExpansionDecl(
9802-
CurDeclContext, poundLoc, macroNameRef, macroNameLoc,
9803-
leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc,
9804-
argList));
9828+
auto *med = new (Context) MacroExpansionDecl(
9829+
CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc,
9830+
Context.AllocateCopy(genericArgs), rightAngleLoc, argList);
9831+
med->getAttrs() = attributes;
9832+
9833+
return makeParserResult(status, med);
98059834
}

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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,15 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20582058
}
20592059

20602060
void visitMacroExpansionDecl(MacroExpansionDecl *MED) {
2061+
// Attributes will be attached to expanded decls and type checked later.
2062+
// Don't perform "type check attribute" here.
2063+
2064+
// Decline decl modifiers. I.e. we don't support 'public #funcFoo'.
2065+
for (auto *attr : MED->getAttrs()) {
2066+
if (attr->isValid() && attr->isDeclModifier()) {
2067+
diagnoseAndRemoveAttr(MED, attr, diag::invalid_decl_modifier, attr);
2068+
}
2069+
}
20612070
// Assign a discriminator.
20622071
(void)MED->getDiscriminator();
20632072
// Decls in expansion already visited as auxiliary decls.

lib/Sema/TypeCheckMacros.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,8 +1049,10 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) {
10491049

10501050
auto topLevelItems = macroSourceFile->getTopLevelItems();
10511051
for (auto item : topLevelItems) {
1052-
if (auto *decl = item.dyn_cast<Decl *>())
1052+
if (auto *decl = item.dyn_cast<Decl *>()) {
1053+
decl->getAttrs().add(med->getAttrs());
10531054
decl->setDeclContext(dc);
1055+
}
10541056
}
10551057
return macroBufferID;
10561058
}
@@ -1549,6 +1551,9 @@ ConcreteDeclRef ResolveMacroRequest::evaluate(Evaluator &evaluator,
15491551
macroRef.getArgs(), roles);
15501552
}
15511553

1554+
// FIXME: Type checking should reflect the attributes on the expansion.
1555+
// e.g. '@available(macOS 999) #Future { newAPIFrom999() }'.
1556+
15521557
Expr *result = macroExpansion;
15531558
TypeChecker::typeCheckExpression(
15541559
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/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/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)