Skip to content

Commit 9017dea

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 9017dea

File tree

11 files changed

+74
-24
lines changed

11 files changed

+74
-24
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: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,13 +4828,24 @@ 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+
if (isInSILMode() || SF.Kind == SourceFileKind::Interface)
4841+
return false;
4842+
4843+
// Macro expansions at the top level of non-script file are declarations.
4844+
return CurDeclContext->isModuleScopeContext() && !allowTopLevelCode();
4845+
}
4846+
4847+
// Otherwise, prefer parsing it as an expression.
4848+
return false;
48384849
}
48394850

48404851
// Skip a #if that contains only attributes in all branches. These will be
@@ -4997,6 +5008,19 @@ bool Parser::isStartOfSILDecl() {
49975008
llvm_unreachable("Unhandled case in switch");
49985009
}
49995010

5011+
bool Parser::isStartOfFreestandingMacroExpansion() {
5012+
// Check if "'#' <identifier>" without any whitespace between them.
5013+
if (!Tok.is(tok::pound))
5014+
return false;
5015+
if (Tok.getLoc().getAdvancedLoc(1) != peekToken().getLoc())
5016+
return false;
5017+
if (!peekToken().isAny(tok::identifier, tok::code_complete) &&
5018+
// allow keywords right after '#' so we can diagnose it when parsing.
5019+
!peekToken().isKeyword())
5020+
return false;
5021+
return true;
5022+
}
5023+
50005024
void Parser::consumeDecl(ParserPosition BeginParserPosition,
50015025
ParseDeclOptions Flags,
50025026
bool IsTopLevel) {
@@ -5249,9 +5273,15 @@ Parser::parseDecl(ParseDeclOptions Flags,
52495273
// Handled below.
52505274
break;
52515275
case tok::pound:
5252-
if (Tok.isAtStartOfLine() &&
5253-
peekToken().is(tok::code_complete) &&
5254-
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) {
5276+
if (!isStartOfFreestandingMacroExpansion()) {
5277+
consumeToken(tok::pound);
5278+
diagnose(Tok.getLoc(),
5279+
diag::macro_expansion_decl_expected_macro_identifier);
5280+
DeclResult = makeParserError();
5281+
break;
5282+
}
5283+
5284+
if (peekToken().is(tok::code_complete)) {
52555285
consumeToken();
52565286
if (CodeCompletionCallbacks) {
52575287
CodeCompletionCallbacks->completeAfterPoundDirective();
@@ -9796,10 +9826,10 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags,
97969826
}
97979827
}
97989828

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

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/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)