Skip to content

Commit ef078f4

Browse files
authored
Merge pull request #65967 from rintaro/macroexpanson-name-sameline
[Parse] Improve freestanding macro expansion parsing
2 parents 31032b9 + e3a0f24 commit ef078f4

File tree

9 files changed

+198
-86
lines changed

9 files changed

+198
-86
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,8 @@ ERROR(macro_expansion_expr_expected_macro_identifier,PointsToFirstBadToken,
20572057
"expected a macro identifier for a pound literal expression", ())
20582058
ERROR(macro_expansion_decl_expected_macro_identifier,PointsToFirstBadToken,
20592059
"expected a macro identifier for a pound literal declaration", ())
2060+
ERROR(extra_whitespace_macro_expansion_identifier,PointsToFirstBadToken,
2061+
"extraneous whitespace between '#' and macro name is not permitted", ())
20602062

20612063
ERROR(macro_role_attr_expected_kind,PointsToFirstBadToken,
20622064
"expected %select{a freestanding|an attached}0 macro role such as "

include/swift/Parse/Parser.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,16 @@ class Parser {
17511751
DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag,
17521752
DeclNameOptions flags);
17531753

1754+
/// Parse macro expansion.
1755+
///
1756+
/// macro-expansion:
1757+
/// '#' identifier generic-arguments? expr-paren? trailing-closure?
1758+
ParserStatus parseFreestandingMacroExpansion(
1759+
SourceLoc &poundLoc, DeclNameLoc &macroNameLoc, DeclNameRef &macroNameRef,
1760+
SourceLoc &leftAngleLoc, SmallVectorImpl<TypeRepr *> &genericArgs,
1761+
SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic,
1762+
const Diagnostic &diag);
1763+
17541764
ParserResult<Expr> parseExprIdentifier();
17551765
Expr *parseExprEditorPlaceholder(Token PlaceholderTok,
17561766
Identifier PlaceholderId);

lib/Parse/ParseDecl.cpp

Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4945,8 +4945,9 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
49454945
}
49464946

49474947
// 'macro' name
4948-
if (Tok.isContextualKeyword("macro") && Tok2.is(tok::identifier))
4949-
return true;
4948+
if (Tok.isContextualKeyword("macro")) {
4949+
return Tok2.is(tok::identifier);
4950+
}
49504951

49514952
if (Tok.isContextualKeyword("package")) {
49524953
// If `case` is the next token after `return package` statement,
@@ -5031,16 +5032,20 @@ bool Parser::isStartOfSILDecl() {
50315032
}
50325033

50335034
bool Parser::isStartOfFreestandingMacroExpansion() {
5034-
// Check if "'#' <identifier>" without any whitespace between them.
5035+
// Check if "'#' <identifier>" where the identifier is on the sameline.
50355036
if (!Tok.is(tok::pound))
50365037
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())
5038+
const Token &Tok2 = peekToken();
5039+
if (Tok2.isAtStartOfLine())
50425040
return false;
5043-
return true;
5041+
5042+
if (Tok2.isAny(tok::identifier, tok::code_complete))
5043+
return true;
5044+
if (Tok2.isKeyword()) {
5045+
// allow keywords right after '#' so we can diagnose it when parsing.
5046+
return Tok.getRange().getEnd() == Tok2.getLoc();
5047+
}
5048+
return false;
50445049
}
50455050

50465051
void Parser::consumeDecl(ParserPosition BeginParserPosition,
@@ -9814,47 +9819,18 @@ ParserResult<MacroDecl> Parser::parseDeclMacro(DeclAttributes &attributes) {
98149819
ParserResult<MacroExpansionDecl>
98159820
Parser::parseDeclMacroExpansion(ParseDeclOptions flags,
98169821
DeclAttributes &attributes) {
9817-
SourceLoc poundLoc = consumeToken(tok::pound);
9822+
SourceLoc poundLoc;
98189823
DeclNameLoc macroNameLoc;
9819-
DeclNameRef macroNameRef = parseDeclNameRef(
9820-
macroNameLoc, diag::macro_expansion_decl_expected_macro_identifier,
9821-
DeclNameOptions());
9822-
if (!macroNameRef)
9823-
return makeParserError();
9824-
9825-
ParserStatus status;
9824+
DeclNameRef macroNameRef;
98269825
SourceLoc leftAngleLoc, rightAngleLoc;
9827-
SmallVector<TypeRepr *, 8> genericArgs;
9828-
if (canParseAsGenericArgumentList()) {
9829-
auto genericArgsStatus = parseGenericArguments(
9830-
genericArgs, leftAngleLoc, rightAngleLoc);
9831-
status |= genericArgsStatus;
9832-
if (genericArgsStatus.isErrorOrHasCompletion())
9833-
diagnose(leftAngleLoc, diag::while_parsing_as_left_angle_bracket);
9834-
}
9835-
9826+
SmallVector<TypeRepr *, 4> genericArgs;
98369827
ArgumentList *argList = nullptr;
9837-
if (Tok.isFollowingLParen()) {
9838-
auto result = parseArgumentList(tok::l_paren, tok::r_paren,
9839-
/*isExprBasic*/ false,
9840-
/*allowTrailingClosure*/ true);
9841-
status |= result;
9842-
if (result.hasCodeCompletion())
9843-
return makeParserCodeCompletionResult<MacroExpansionDecl>();
9844-
argList = result.getPtrOrNull();
9845-
} else if (Tok.is(tok::l_brace)) {
9846-
SmallVector<Argument, 2> trailingClosures;
9847-
auto closuresStatus = parseTrailingClosures(/*isExprBasic*/ false,
9848-
macroNameLoc.getSourceRange(),
9849-
trailingClosures);
9850-
status |= closuresStatus;
9851-
9852-
if (!trailingClosures.empty()) {
9853-
argList = ArgumentList::createParsed(Context, SourceLoc(),
9854-
trailingClosures, SourceLoc(),
9855-
/*trailingClosureIdx*/ 0);
9856-
}
9857-
}
9828+
ParserStatus status = parseFreestandingMacroExpansion(
9829+
poundLoc, macroNameLoc, macroNameRef, leftAngleLoc, genericArgs,
9830+
rightAngleLoc, argList, /*isExprBasic=*/false,
9831+
diag::macro_expansion_decl_expected_macro_identifier);
9832+
if (!macroNameRef)
9833+
return status;
98589834

98599835
auto *med = new (Context) MacroExpansionDecl(
98609836
CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc,

lib/Parse/ParseExpr.cpp

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,71 @@ DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc,
22692269
return DeclNameRef({ Context, baseName, argumentLabels });
22702270
}
22712271

2272+
ParserStatus Parser::parseFreestandingMacroExpansion(
2273+
SourceLoc &poundLoc, DeclNameLoc &macroNameLoc, DeclNameRef &macroNameRef,
2274+
SourceLoc &leftAngleLoc, SmallVectorImpl<TypeRepr *> &genericArgs,
2275+
SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic,
2276+
const Diagnostic &diag) {
2277+
SourceLoc poundEndLoc = Tok.getRange().getEnd();
2278+
poundLoc = consumeToken(tok::pound);
2279+
2280+
if (Tok.isAtStartOfLine()) {
2281+
// Diagnose and bail out if there's no macro name on the same line.
2282+
diagnose(poundEndLoc, diag);
2283+
return makeParserError();
2284+
}
2285+
2286+
bool hasWhitespaceBeforeName = poundEndLoc != Tok.getLoc();
2287+
2288+
// Diagnose and parse keyword right after `#`.
2289+
if (Tok.isKeyword() && !hasWhitespaceBeforeName) {
2290+
diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText());
2291+
diagnose(Tok, diag::backticks_to_escape)
2292+
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
2293+
2294+
// Let 'parseDeclNameRef' to parse this as an identifier.
2295+
Tok.setKind(tok::identifier);
2296+
}
2297+
macroNameRef = parseDeclNameRef(macroNameLoc, diag, DeclNameOptions());
2298+
if (!macroNameRef)
2299+
return makeParserError();
2300+
2301+
// Diagnose whitespace between '#' and the macro name, and continue parsing.
2302+
if (hasWhitespaceBeforeName) {
2303+
diagnose(poundEndLoc, diag::extra_whitespace_macro_expansion_identifier)
2304+
.fixItRemoveChars(poundEndLoc, macroNameLoc.getStartLoc());
2305+
}
2306+
2307+
ParserStatus status;
2308+
if (canParseAsGenericArgumentList()) {
2309+
auto genericArgsStatus =
2310+
parseGenericArguments(genericArgs, leftAngleLoc, rightAngleLoc);
2311+
status |= genericArgsStatus;
2312+
if (genericArgsStatus.isErrorOrHasCompletion())
2313+
diagnose(leftAngleLoc, diag::while_parsing_as_left_angle_bracket);
2314+
}
2315+
2316+
if (Tok.isFollowingLParen()) {
2317+
auto result = parseArgumentList(tok::l_paren, tok::r_paren, isExprBasic,
2318+
/*allowTrailingClosure*/ true);
2319+
status |= result;
2320+
argList = result.getPtrOrNull();
2321+
} else if (Tok.is(tok::l_brace)) {
2322+
SmallVector<Argument, 2> trailingClosures;
2323+
auto closuresStatus = parseTrailingClosures(
2324+
isExprBasic, macroNameLoc.getSourceRange(), trailingClosures);
2325+
status |= closuresStatus;
2326+
2327+
if (!trailingClosures.empty()) {
2328+
argList = ArgumentList::createParsed(Context, SourceLoc(),
2329+
trailingClosures, SourceLoc(),
2330+
/*trailingClosureIdx*/ 0);
2331+
}
2332+
}
2333+
2334+
return status;
2335+
}
2336+
22722337
/// expr-identifier:
22732338
/// unqualified-decl-name generic-args?
22742339
ParserResult<Expr> Parser::parseExprIdentifier() {
@@ -3376,45 +3441,18 @@ Parser::parseExprCallSuffix(ParserResult<Expr> fn, bool isExprBasic) {
33763441
}
33773442

33783443
ParserResult<Expr> Parser::parseExprMacroExpansion(bool isExprBasic) {
3379-
SourceLoc poundLoc = consumeToken(tok::pound);
3444+
SourceLoc poundLoc;
33803445
DeclNameLoc macroNameLoc;
3381-
DeclNameRef macroNameRef = parseDeclNameRef(
3382-
macroNameLoc, diag::macro_expansion_expr_expected_macro_identifier,
3383-
DeclNameOptions());
3384-
if (!macroNameRef)
3385-
return makeParserError();
3386-
3387-
ParserStatus status;
3446+
DeclNameRef macroNameRef;
33883447
SourceLoc leftAngleLoc, rightAngleLoc;
3389-
SmallVector<TypeRepr *, 8> genericArgs;
3390-
if (canParseAsGenericArgumentList()) {
3391-
auto genericArgsStatus = parseGenericArguments(
3392-
genericArgs, leftAngleLoc, rightAngleLoc);
3393-
status |= genericArgsStatus;
3394-
if (genericArgsStatus.isErrorOrHasCompletion())
3395-
diagnose(leftAngleLoc, diag::while_parsing_as_left_angle_bracket);
3396-
}
3397-
3448+
SmallVector<TypeRepr *, 4> genericArgs;
33983449
ArgumentList *argList = nullptr;
3399-
if (Tok.isFollowingLParen()) {
3400-
auto result = parseArgumentList(tok::l_paren, tok::r_paren, isExprBasic,
3401-
/*allowTrailingClosure*/ true);
3402-
argList = result.getPtrOrNull();
3403-
status |= result;
3404-
} else if (Tok.is(tok::l_brace) &&
3405-
isValidTrailingClosure(isExprBasic, *this)) {
3406-
SmallVector<Argument, 2> trailingClosures;
3407-
auto closureStatus = parseTrailingClosures(isExprBasic,
3408-
macroNameLoc.getSourceRange(),
3409-
trailingClosures);
3410-
status |= closureStatus;
3411-
3412-
if (!trailingClosures.empty()) {
3413-
argList = ArgumentList::createParsed(Context, SourceLoc(),
3414-
trailingClosures, SourceLoc(),
3415-
/*trailingClosureIdx*/ 0);
3416-
}
3417-
}
3450+
ParserStatus status = parseFreestandingMacroExpansion(
3451+
poundLoc, macroNameLoc, macroNameRef, leftAngleLoc, genericArgs,
3452+
rightAngleLoc, argList, isExprBasic,
3453+
diag::macro_expansion_expr_expected_macro_identifier);
3454+
if (!macroNameRef)
3455+
return status;
34183456

34193457
return makeParserResult(
34203458
status,

test/Macros/macro_keywordname.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: mkdir -p %t/src
5+
// RUN: mkdir -p %t/plugins
6+
// RUN: mkdir -p %t/lib
7+
8+
// RUN: split-file %s %t/src
9+
10+
//#-- Prepare the macro dylib plugin.
11+
// RUN: %host-build-swift \
12+
// RUN: -swift-version 5 \
13+
// RUN: -emit-library -o %t/plugins/%target-library-name(MacroDefinition) \
14+
// RUN: -module-name MacroDefinition \
15+
// RUN: %t/src/MacroDefinition.swift \
16+
// RUN: -g -no-toolchain-stdlib-rpath
17+
18+
//#-- Prepare the macro library.
19+
// RUN: %target-swift-frontend \
20+
// RUN: -swift-version 5 \
21+
// RUN: -emit-module -o %t/lib/MacroLib.swiftmodule \
22+
// RUN: -module-name MacroLib \
23+
// RUN: -plugin-path %t/plugins \
24+
// RUN: %t/src/MacroLib.swift
25+
26+
// RUN: %target-swift-frontend \
27+
// RUN: -typecheck -verify \
28+
// RUN: -I %t/lib \
29+
// RUN: -plugin-path %t/plugins \
30+
// RUN: %t/src/test.swift
31+
32+
//--- MacroDefinition.swift
33+
import SwiftSyntax
34+
import SwiftSyntaxMacros
35+
36+
public struct OneMacro: ExpressionMacro {
37+
public static func expansion(
38+
of node: some FreestandingMacroExpansionSyntax,
39+
in context: some MacroExpansionContext
40+
) throws -> ExprSyntax {
41+
return "1"
42+
}
43+
}
44+
45+
46+
//--- MacroLib.swift
47+
@freestanding(expression) public macro `public`() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro")
48+
@freestanding(expression) public macro `escaped`() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro")
49+
@freestanding(expression) public macro normal() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro")
50+
51+
//--- test.swift
52+
import MacroLib
53+
@freestanding(expression) public macro `class`() -> Int = #externalMacro(module: "MacroDefinition", type: "OneMacro")
54+
55+
func test() {
56+
let _: Int = #public() // expected-error {{keyword 'public' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}}
57+
let _: Int = #`public`()
58+
let _: Int = #escaped()
59+
let _: Int = #`class`()
60+
let _: Int = #normal()
61+
}

test/Macros/macro_self.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ func sync() {}
99
macro Self() = #externalMacro(module: "MacroDefinition", type: "InvalidMacro")
1010

1111
func testSelfAsFreestandingMacro() {
12-
_ = #self // expected-error {{expected a macro identifier for a pound literal expression}}
12+
_ = #self // expected-error {{keyword 'self' cannot be used as an identifier here}} expected-note {{use backticks to escape it}}
1313
}
1414

1515
func testCapitalSelfAsFreestandingMacro() {
16-
_ = #Self // expected-error {{expected a macro identifier for a pound literal expression}}
16+
_ = #Self // expected-error {{keyword 'Self' cannot be used as an identifier here}} expected-note {{use backticks to escape it}}
1717
}
1818

1919
func testSelfAsAttachedMacro() {

test/Parse/macro_decl.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ func test() {
4646
@CustomAttr
4747
isolated #someFunc
4848
}
49+
50+
public # someFunc // expected-error {{extraneous whitespace between '#' and macro name is not permitted}} {{9-10=}}
51+
52+
struct S {
53+
# someFunc // expected-error {{extraneous whitespace between '#' and macro name is not permitted}} {{4-5=}}
54+
#class // expected-error {{keyword 'class' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{4-9=`class`}}
55+
# struct Inner {} // expected-error {{expected a macro identifier for a pound literal declaration}} expected-error {{consecutive declarations on a line}}
56+
}

test/Parse/macro_expr.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,17 @@ _ = #another {
3535

3636
// expected-error @+1 {{expected a macro identifier for a pound literal expression}}
3737
_ = #()
38+
39+
do {
40+
_ = # // expected-error {{expected a macro identifier for a pound literal expression}}
41+
name()
42+
}
43+
do {
44+
_ = # macro() // expected-error {{extraneous whitespace between '#' and macro name is not permitted}} {{8-9=}}
45+
}
46+
do {
47+
_ = #public() // expected-error {{keyword 'public' cannot be used as an identifier here}} expected-note {{if this name is unavoidable, use backticks to escape it}} {{8-14=`public`}}
48+
}
49+
do {
50+
_ = # public() // expected-error {{expected a macro identifier for a pound literal expression}}
51+
}

test/decl/import/import.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ func f1(_ a: Swift.Int) -> Swift.Void { print(a) }
3434
import func Swift.print
3535

3636
// rdar://14418336
37-
#import something_nonexistent // expected-error {{expected a macro identifier}} expected-error {{no such module 'something_nonexistent'}}
37+
#import something_nonexistent
38+
// expected-error@-1 {{keyword 'import' cannot be used as an identifier here}} expected-note@-1 {{use backticks to escape it}}
39+
// expected-error@-2 {{no macro named 'import'}}
40+
// expected-error@-3 {{consecutive statements on a line}} expected-error@-3 {{cannot find 'something_nonexistent' in scope}}
3841

3942
// Import specific decls
4043
import typealias Swift.Int

0 commit comments

Comments
 (0)