Skip to content

Commit c5747c3

Browse files
committed
[NFC] Refactor name parsing into Parser::parseDeclNameRef()
Gives this functionality a more self-documenting interface.
1 parent b5985e0 commit c5747c3

File tree

2 files changed

+178
-89
lines changed

2 files changed

+178
-89
lines changed

include/swift/Parse/Parser.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,44 @@ class Parser {
14121412
/// \param loc The location of the label (empty if it doesn't exist)
14131413
void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc);
14141414

1415+
enum class DeclNameFlag : uint8_t {
1416+
/// If passed, operator basenames are allowed.
1417+
AllowOperators = 1 << 0,
1418+
1419+
/// If passed, names that coincide with keywords are allowed. Used after a
1420+
/// dot to enable things like '.init' and '.default'.
1421+
AllowKeywords = 1 << 1,
1422+
1423+
/// If passed, 'deinit' and 'subscript' should be parsed as special names,
1424+
/// not ordinary identifiers.
1425+
UseSpecialNamesForDeinitAndSubscript = AllowKeywords | 1 << 2,
1426+
1427+
/// If passed, compound names with argument lists are allowed, unless they
1428+
/// have empty argument lists.
1429+
AllowCompoundNames = 1 << 4,
1430+
1431+
/// If passed, compound names with empty argument lists are allowed.
1432+
AllowZeroArgCompoundNames = AllowCompoundNames | 1 << 5,
1433+
};
1434+
using DeclNameOptions = OptionSet<DeclNameFlag>;
1435+
1436+
friend DeclNameOptions operator|(DeclNameFlag flag1, DeclNameFlag flag2) {
1437+
return DeclNameOptions(flag1) | flag2;
1438+
}
1439+
1440+
/// Without \c DeclNameFlag::AllowCompoundNames, parse an
1441+
/// unqualified-decl-base-name.
1442+
///
1443+
/// unqualified-decl-base-name: identifier
1444+
///
1445+
/// With \c DeclNameFlag::AllowCompoundNames, parse an unqualified-base-name.
1446+
///
1447+
/// unqualified-decl-name:
1448+
/// unqualified-decl-base-name
1449+
/// unqualified-decl-base-name '(' ((identifier | '_') ':') + ')'
1450+
DeclNameRef parseDeclNameRef(DeclNameLoc &loc, const Diagnostic &diag,
1451+
DeclNameOptions flags);
1452+
14151453
/// Parse an unqualified-decl-base-name.
14161454
///
14171455
/// unqualified-decl-name:

lib/Parse/ParseExpr.cpp

Lines changed: 140 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,12 +2078,87 @@ void Parser::parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc) {
20782078
}
20792079
}
20802080

2081-
DeclNameRef Parser::parseUnqualifiedDeclBaseName(
2082-
bool afterDot,
2083-
DeclNameLoc &loc,
2084-
const Diagnostic &diag,
2085-
bool allowOperators,
2086-
bool allowDeinitAndSubscript) {
2081+
static bool tryParseArgLabelList(Parser &P, Parser::DeclNameOptions flags,
2082+
SourceLoc &lparenLoc,
2083+
SmallVectorImpl<Identifier> &argumentLabels,
2084+
SmallVectorImpl<SourceLoc> &argumentLabelLocs,
2085+
SourceLoc &rparenLoc) {
2086+
if (!flags.contains(Parser::DeclNameFlag::AllowCompoundNames))
2087+
return false;
2088+
2089+
// Is the current token a left paren?
2090+
if (!P.Tok.isFollowingLParen())
2091+
return false;
2092+
2093+
// Okay, let's look ahead and see if the next token is something that could
2094+
// be in an arg label list...
2095+
const Token &next = P.peekToken();
2096+
2097+
// A close parenthesis, if empty lists are allowed.
2098+
bool nextIsRParen =
2099+
flags.contains(Parser::DeclNameFlag::AllowZeroArgCompoundNames) &&
2100+
next.is(tok::r_paren);
2101+
// An argument label.
2102+
bool nextIsArgLabel = next.canBeArgumentLabel() || next.is(tok::colon);
2103+
// An editor placeholder.
2104+
bool nextIsPlaceholder = Identifier::isEditorPlaceholder(next.getText());
2105+
2106+
if (!(nextIsRParen || nextIsArgLabel || nextIsPlaceholder))
2107+
return false;
2108+
2109+
// Try to parse a compound name.
2110+
SyntaxParsingContext ArgsCtxt(P.SyntaxContext, SyntaxKind::DeclNameArguments);
2111+
Parser::BacktrackingScope backtrack(P);
2112+
2113+
lparenLoc = P.consumeToken(tok::l_paren);
2114+
while (P.Tok.isNot(tok::r_paren)) {
2115+
SyntaxParsingContext ArgCtxt(P.SyntaxContext, SyntaxKind::DeclNameArgument);
2116+
2117+
// If we see a ':', the user forgot the '_';
2118+
if (P.Tok.is(tok::colon)) {
2119+
P.diagnose(P.Tok, diag::empty_arg_label_underscore)
2120+
.fixItInsert(P.Tok.getLoc(), "_");
2121+
argumentLabels.push_back(Identifier());
2122+
argumentLabelLocs.push_back(P.consumeToken(tok::colon));
2123+
}
2124+
2125+
Identifier argName;
2126+
SourceLoc argLoc;
2127+
P.parseOptionalArgumentLabel(argName, argLoc);
2128+
if (argLoc.isValid()) {
2129+
argumentLabels.push_back(argName);
2130+
argumentLabelLocs.push_back(argLoc);
2131+
continue;
2132+
}
2133+
2134+
// This is not a compound name.
2135+
// FIXME: Could recover better if we "know" it's a compound name.
2136+
ArgCtxt.setBackTracking();
2137+
ArgsCtxt.setBackTracking();
2138+
2139+
return false;
2140+
}
2141+
2142+
// We have a compound name. Cancel backtracking and build that name.
2143+
backtrack.cancelBacktrack();
2144+
2145+
if (argumentLabels.empty() && P.SyntaxContext->isEnabled())
2146+
P.SyntaxContext->addSyntax(
2147+
ParsedSyntaxRecorder::makeBlankDeclNameArgumentList(
2148+
P.leadingTriviaLoc(), *P.SyntaxContext));
2149+
else
2150+
ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList);
2151+
2152+
rparenLoc = P.consumeToken(tok::r_paren);
2153+
2154+
assert(argumentLabels.size() == argumentLabelLocs.size());
2155+
2156+
return true;
2157+
}
2158+
2159+
DeclNameRef Parser::parseDeclNameRef(DeclNameLoc &loc,
2160+
const Diagnostic &diag,
2161+
DeclNameOptions flags) {
20872162
// Consume the base name.
20882163
DeclBaseName baseName;
20892164
SourceLoc baseNameLoc;
@@ -2092,17 +2167,21 @@ DeclNameRef Parser::parseUnqualifiedDeclBaseName(
20922167
baseNameLoc = consumeIdentifier(
20932168
&baseNameId, /*allowDollarIdentifier=*/true);
20942169
baseName = baseNameId;
2095-
} else if (allowOperators && Tok.isAnyOperator()) {
2170+
} else if (flags.contains(DeclNameFlag::AllowOperators) &&
2171+
Tok.isAnyOperator()) {
20962172
baseName = Context.getIdentifier(Tok.getText());
20972173
baseNameLoc = consumeToken();
2098-
} else if (afterDot && Tok.isKeyword()) {
2174+
} else if (flags.contains(DeclNameFlag::AllowKeywords) && Tok.isKeyword()) {
2175+
bool specialDeinitAndSubscript =
2176+
flags.contains(DeclNameFlag::UseSpecialNamesForDeinitAndSubscript);
2177+
20992178
// Syntax highlighting should treat this token as an identifier and
21002179
// not as a keyword.
21012180
if (Tok.is(tok::kw_init))
21022181
baseName = DeclBaseName::createConstructor();
2103-
else if (allowDeinitAndSubscript &&Tok.is(tok::kw_deinit))
2182+
else if (specialDeinitAndSubscript && Tok.is(tok::kw_deinit))
21042183
baseName = DeclBaseName::createDestructor();
2105-
else if (allowDeinitAndSubscript &&Tok.is(tok::kw_subscript))
2184+
else if (specialDeinitAndSubscript && Tok.is(tok::kw_subscript))
21062185
baseName = DeclBaseName::createSubscript();
21072186
else
21082187
baseName = Context.getIdentifier(Tok.getText());
@@ -2115,95 +2194,67 @@ DeclNameRef Parser::parseUnqualifiedDeclBaseName(
21152194
return DeclNameRef();
21162195
}
21172196

2118-
loc = DeclNameLoc(baseNameLoc);
2119-
return DeclNameRef(baseName);
2120-
}
2121-
2122-
2123-
DeclNameRef Parser::parseUnqualifiedDeclName(bool afterDot,
2124-
DeclNameLoc &loc,
2125-
const Diagnostic &diag,
2126-
bool allowOperators,
2127-
bool allowZeroArgCompoundNames,
2128-
bool allowDeinitAndSubscript) {
2129-
// Consume the base name.
2130-
auto baseName = parseUnqualifiedDeclBaseName(afterDot, loc, diag,
2131-
allowOperators,
2132-
allowDeinitAndSubscript);
2133-
2134-
// If the next token isn't a following '(', we don't have a compound name.
2135-
if (!baseName || !Tok.isFollowingLParen())
2136-
return baseName;
2137-
2138-
// If the next token is a ')' then we have a 0-arg compound name. This is
2139-
// explicitly differentiated from "simple" (non-compound) name in DeclName.
2140-
// Unfortunately only some places in the grammar are ok with accepting this
2141-
// kind of name; in other places it's ambiguous with trailing calls.
2142-
if (allowZeroArgCompoundNames && peekToken().is(tok::r_paren)) {
2143-
SyntaxParsingContext ArgsCtxt(SyntaxContext, SyntaxKind::DeclNameArguments);
2144-
consumeToken(tok::l_paren);
2145-
if (SyntaxContext->isEnabled())
2146-
SyntaxContext->addSyntax(
2147-
ParsedSyntaxRecorder::makeBlankDeclNameArgumentList(
2148-
leadingTriviaLoc(), *SyntaxContext));
2149-
consumeToken(tok::r_paren);
2150-
SmallVector<Identifier, 2> argumentLabels;
2151-
return baseName.withArgumentLabels(Context, argumentLabels);
2152-
}
2153-
2154-
// If the token after that isn't an argument label or ':', we don't have a
2155-
// compound name.
2156-
if ((!peekToken().canBeArgumentLabel() && !peekToken().is(tok::colon)) ||
2157-
Identifier::isEditorPlaceholder(peekToken().getText())) {
2158-
return baseName;
2159-
}
2160-
2161-
// Try to parse a compound name.
2162-
SyntaxParsingContext ArgsCtxt(SyntaxContext, SyntaxKind::DeclNameArguments);
2163-
BacktrackingScope backtrack(*this);
2164-
2197+
// Parse an argument list, if the flags allow it and it's present.
21652198
SmallVector<Identifier, 2> argumentLabels;
21662199
SmallVector<SourceLoc, 2> argumentLabelLocs;
2167-
SourceLoc lparenLoc = consumeToken(tok::l_paren);
2200+
SourceLoc lparenLoc;
21682201
SourceLoc rparenLoc;
2169-
while (Tok.isNot(tok::r_paren)) {
2170-
SyntaxParsingContext ArgCtxt(SyntaxContext, SyntaxKind::DeclNameArgument);
21712202

2172-
// If we see a ':', the user forgot the '_';
2173-
if (Tok.is(tok::colon)) {
2174-
diagnose(Tok, diag::empty_arg_label_underscore)
2175-
.fixItInsert(Tok.getLoc(), "_");
2176-
argumentLabels.push_back(Identifier());
2177-
argumentLabelLocs.push_back(consumeToken(tok::colon));
2178-
}
2203+
bool hadArgList = tryParseArgLabelList(*this, flags, lparenLoc,
2204+
argumentLabels, argumentLabelLocs,
2205+
rparenLoc);
21792206

2180-
Identifier argName;
2181-
SourceLoc argLoc;
2182-
parseOptionalArgumentLabel(argName, argLoc);
2183-
if (argLoc.isValid()) {
2184-
argumentLabels.push_back(argName);
2185-
argumentLabelLocs.push_back(argLoc);
2186-
continue;
2187-
}
2207+
if (argumentLabelLocs.empty() || !hadArgList)
2208+
loc = DeclNameLoc(baseNameLoc);
2209+
else
2210+
loc = DeclNameLoc(Context, baseNameLoc, lparenLoc, argumentLabelLocs,
2211+
rparenLoc);
21882212

2189-
// This is not a compound name.
2190-
// FIXME: Could recover better if we "know" it's a compound name.
2191-
ArgCtxt.setBackTracking();
2192-
ArgsCtxt.setBackTracking();
2193-
return baseName;
2213+
if (!hadArgList)
2214+
return DeclNameRef(baseName);
2215+
2216+
return DeclNameRef({ Context, baseName, argumentLabels });
2217+
}
2218+
2219+
DeclNameRef Parser::parseUnqualifiedDeclBaseName(
2220+
bool afterDot,
2221+
DeclNameLoc &loc,
2222+
const Diagnostic &diag,
2223+
bool allowOperators,
2224+
bool allowDeinitAndSubscript) {
2225+
DeclNameOptions flags = {};
2226+
if (afterDot)
2227+
flags |= DeclNameFlag::AllowKeywords;
2228+
if (allowOperators)
2229+
flags |= DeclNameFlag::AllowOperators;
2230+
if (allowDeinitAndSubscript) {
2231+
assert(afterDot);
2232+
flags |= DeclNameFlag::UseSpecialNamesForDeinitAndSubscript;
21942233
}
2195-
// We have a compound name. Cancel backtracking and build that name.
2196-
backtrack.cancelBacktrack();
21972234

2198-
ArgsCtxt.collectNodesInPlace(SyntaxKind::DeclNameArgumentList);
2199-
rparenLoc = consumeToken(tok::r_paren);
2235+
return parseDeclNameRef(loc, diag, flags);
2236+
}
22002237

2201-
assert(!argumentLabels.empty() && "Logic above should prevent this");
2202-
assert(argumentLabels.size() == argumentLabelLocs.size());
22032238

2204-
loc = DeclNameLoc(Context, loc.getBaseNameLoc(), lparenLoc, argumentLabelLocs,
2205-
rparenLoc);
2206-
return baseName.withArgumentLabels(Context, argumentLabels);
2239+
DeclNameRef Parser::parseUnqualifiedDeclName(bool afterDot,
2240+
DeclNameLoc &loc,
2241+
const Diagnostic &diag,
2242+
bool allowOperators,
2243+
bool allowZeroArgCompoundNames,
2244+
bool allowDeinitAndSubscript) {
2245+
DeclNameOptions flags = DeclNameFlag::AllowCompoundNames;
2246+
if (afterDot)
2247+
flags |= DeclNameFlag::AllowKeywords;
2248+
if (allowOperators)
2249+
flags |= DeclNameFlag::AllowOperators;
2250+
if (allowDeinitAndSubscript) {
2251+
assert(afterDot);
2252+
flags |= DeclNameFlag::UseSpecialNamesForDeinitAndSubscript;
2253+
}
2254+
if (allowZeroArgCompoundNames)
2255+
flags |= DeclNameFlag::AllowZeroArgCompoundNames;
2256+
2257+
return parseDeclNameRef(loc, diag, flags);
22072258
}
22082259

22092260
/// expr-identifier:

0 commit comments

Comments
 (0)