Skip to content

Commit 22a54b5

Browse files
committed
[NFC] Add parsing helpers for @attr(<identifier>)
Refactors `parseSingleAttrOption()` to create a helper that can parse a single arbitrary `Identifier`. This simplifies the handling of `SwiftNativeObjCRuntimeBaseAttr`, `ObjCRuntimeNameAttr`, and `ProjectedValuePropertyAttr`.
1 parent dfbb604 commit 22a54b5

File tree

4 files changed

+151
-132
lines changed

4 files changed

+151
-132
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,9 @@ ERROR(attr_expected_integer_literal,none,
14691469
ERROR(attr_expected_option_such_as,none,
14701470
"expected '%0' option such as '%1'", (StringRef, StringRef))
14711471

1472+
ERROR(attr_expected_option_identifier,none,
1473+
"expected '%0' option to be an identifier", (StringRef))
1474+
14721475
ERROR(attr_unknown_option,none,
14731476
"unknown option '%0' for attribute '%1'", (StringRef, StringRef))
14741477

@@ -1482,18 +1485,9 @@ ERROR(attr_expected_colon_after_label,none,
14821485
ERROR(alignment_must_be_positive_integer,none,
14831486
"alignment value must be a positive integer literal", ())
14841487

1485-
ERROR(swift_native_objc_runtime_base_must_be_identifier,none,
1486-
"@_swift_native_objc_runtime_base class name must be an identifier", ())
1487-
1488-
ERROR(objc_runtime_name_must_be_identifier,none,
1489-
"@_objcRuntimeName name must be an identifier", ())
1490-
14911488
ERROR(attr_only_at_non_local_scope, none,
14921489
"attribute '%0' can only be used in a non-local scope", (StringRef))
14931490

1494-
ERROR(projection_value_property_not_identifier,none,
1495-
"@_projectedValueProperty name must be an identifier", ())
1496-
14971491
// Access control
14981492
ERROR(attr_access_expected_set,none,
14991493
"expected 'set' as subject of '%0' modifier", (StringRef))

include/swift/Parse/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ class Parser {
896896

897897
/// Parse the specified expected token and return its location on success. On failure, emit the specified
898898
/// error diagnostic, a note at the specified note location, and return the location of the previous token.
899-
bool parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag,
899+
bool parseMatchingToken(tok K, SourceLoc &TokLoc, Diagnostic ErrorDiag,
900900
SourceLoc OtherLoc);
901901

902902
/// Returns the proper location for a missing right brace, parenthesis, etc.

lib/Parse/ParseDecl.cpp

Lines changed: 146 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,39 +2078,114 @@ Parser::parseDocumentationAttribute(SourceLoc AtLoc, SourceLoc Loc) {
20782078
return makeParserResult(new (Context) DocumentationAttr(Loc, range, FinalMetadata, Visibility, false));
20792079
}
20802080

2081+
enum class SingleAttrOptionParseStatus : uint8_t {
2082+
Success,
2083+
ExpectedLParen,
2084+
ExpectedIdentifier,
2085+
ExpectedRParen
2086+
};
2087+
2088+
void diagnoseSingleAttrOptionParseStatus(
2089+
Parser &P, SingleAttrOptionParseStatus status,
2090+
SourceLoc Loc, StringRef AttrName,
2091+
DeclAttrKind DK, Diagnostic nonIdentifierDiagnostic) {
2092+
bool isDeclModifier = DeclAttribute::isDeclModifier(DK);
2093+
2094+
switch (status) {
2095+
case SingleAttrOptionParseStatus::Success:
2096+
break;
2097+
2098+
case SingleAttrOptionParseStatus::ExpectedLParen:
2099+
P.diagnose(Loc, diag::attr_expected_lparen, AttrName, isDeclModifier);
2100+
break;
2101+
2102+
case SingleAttrOptionParseStatus::ExpectedIdentifier:
2103+
P.diagnose(Loc, nonIdentifierDiagnostic);
2104+
break;
2105+
2106+
case SingleAttrOptionParseStatus::ExpectedRParen:
2107+
P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isDeclModifier);
2108+
break;
2109+
}
2110+
}
2111+
2112+
/// Parses an attribute argument list that allows a single identifier with a
2113+
/// known set of permitted options:
2114+
///
2115+
/// \verbatim
2116+
/// '(' identifier ')'
2117+
/// \endverbatim
2118+
///
2119+
/// Returns an object of type \c AttrOptionSwitch, a type loosely inspired by
2120+
/// \c llvm::StringSwitch which can be used in a fluent style to map each
2121+
/// permitted identifier to a value. Together, they will automatically
2122+
/// diagnose \c diag::attr_expected_lparen,
2123+
/// \c diag::attr_expected_option_such_as, \c diag::attr_unknown_option, and
2124+
/// \c diag::attr_expected_rparen when needed.
2125+
///
2126+
/// \seealso AttrOptionSwitch
2127+
static SingleAttrOptionParseStatus
2128+
parseSingleAttrOptionImpl(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
2129+
DeclAttrKind DK, StringRef &Result) {
2130+
Result = "";
2131+
SWIFT_DEFER {
2132+
AttrRange = SourceRange(Loc, P.PreviousLoc);
2133+
};
2134+
2135+
if (!P.Tok.is(tok::l_paren))
2136+
return SingleAttrOptionParseStatus::ExpectedLParen;
2137+
2138+
llvm::Optional<SyntaxParsingContext> ModDetailContext;
2139+
if (DK == DAK_ReferenceOwnership) {
2140+
ModDetailContext.emplace(P.SyntaxContext, SyntaxKind::DeclModifierDetail);
2141+
}
2142+
2143+
P.consumeToken(tok::l_paren);
2144+
2145+
StringRef parsedName = P.Tok.getText();
2146+
if (!P.consumeIf(tok::identifier))
2147+
return SingleAttrOptionParseStatus::ExpectedIdentifier;
2148+
2149+
if (!P.consumeIf(tok::r_paren))
2150+
return SingleAttrOptionParseStatus::ExpectedRParen;
2151+
2152+
Result = parsedName;
2153+
return SingleAttrOptionParseStatus::Success;
2154+
}
2155+
20812156
/// Processes a parsed option name by attempting to match it to a list of
20822157
/// alternative name/value pairs provided by a chain of \c when() calls, ending
20832158
/// in either \c whenOmitted() if omitting the option is allowed, or
20842159
/// \c diagnoseWhenOmitted() if the option is mandatory.
20852160
template<typename T, typename R = T>
20862161
class LLVM_NODISCARD AttrOptionSwitch {
20872162
// Inputs:
2088-
Optional<StringRef> parsedName; // None: parse error, empty: omitted
2163+
StringRef parsedName; // empty: omitted or parse error
20892164
Parser &P;
20902165
SourceLoc loc;
2091-
StringRef attrName; // empty: error already diagnosed
2092-
bool isDeclModifier;
2166+
StringRef attrName;
2167+
SingleAttrOptionParseStatus parseStatus;
2168+
DeclAttrKind attrKind;
20932169

20942170
// State:
20952171
StringRef exampleName; // empty: when() was never called
20962172
Optional<R> result; // None: no when() has matched
20972173

20982174
public:
2099-
/// \param parsedName The name of the option parsed out of the source code. If
2100-
/// \c None, there was some sort of parse error; this will normally be
2101-
/// diagnosed as \c diag::attr_expected_option_such_as using the name
2102-
/// from the first \c when() call as an example.
2175+
/// \param parseStatus The status of the option parse (whether it succeeded or expeienced
2176+
/// some error). If
2177+
/// \param parsedName The name of the option parsed out of the source code. If \p parseStatus is
2178+
/// an error, this should be empty.
21032179
/// \param P The parser used to diagnose errors concerning this attribute
21042180
/// option.
21052181
/// \param loc The source location to diagnose errors at.
2106-
/// \param attrName The name of the attribute, used in diagnostics. If empty,
2107-
/// an error has already been diagnosed and the AttrOptionSwitch should
2108-
/// just fall through.
2109-
/// \param isDeclModifier Are we parsing an attribute or a modifier?
2110-
AttrOptionSwitch(Optional<StringRef> parsedName, Parser &P, SourceLoc loc,
2111-
StringRef attrName, bool isDeclModifier)
2182+
/// \param attrName The name of the attribute, used in diagnostics.
2183+
/// \param attrKind The kind of the attribute, used in diagnostics.
2184+
AttrOptionSwitch(SingleAttrOptionParseStatus parseStatus,
2185+
StringRef parsedName, Parser &P, SourceLoc loc,
2186+
StringRef attrName, DeclAttrKind attrKind)
21122187
: parsedName(parsedName), P(P), loc(loc), attrName(attrName),
2113-
isDeclModifier(isDeclModifier) { }
2188+
parseStatus(parseStatus), attrKind(attrKind) { }
21142189

21152190
/// If the option has the identifier \p name, give it value \p value.
21162191
AttrOptionSwitch<R, T> &when(StringLiteral name, T value) {
@@ -2119,7 +2194,8 @@ class LLVM_NODISCARD AttrOptionSwitch {
21192194
exampleName = name;
21202195

21212196
// Does this string match?
2122-
if (parsedName && *parsedName == name) {
2197+
if (parseStatus == SingleAttrOptionParseStatus::Success
2198+
&& parsedName == name) {
21232199
assert(!result && "overlapping AttrOptionSwitch::when()s?");
21242200
result = std::move(value);
21252201
}
@@ -2132,22 +2208,16 @@ class LLVM_NODISCARD AttrOptionSwitch {
21322208
Optional<R> diagnoseWhenOmitted() {
21332209
assert(!exampleName.empty() && "No AttrOptionSwitch::when() calls");
21342210

2135-
if (attrName.empty())
2136-
// An error has already been diagnosed; nothing to do.
2137-
return None;
2138-
2139-
if (!result) {
2140-
if (!parsedName)
2141-
// We parsed a non-identifier; diagnose it with `exampleName`.
2142-
P.diagnose(loc, diag::attr_expected_option_such_as, attrName,
2143-
exampleName);
2144-
else if (*parsedName == "")
2145-
// Option list was omitted; apparently this attr doesn't allow that.
2146-
P.diagnose(loc, diag::attr_expected_lparen, attrName, isDeclModifier);
2147-
else
2148-
// The identifier didn't match any of the when() calls.
2149-
P.diagnose(loc, diag::attr_unknown_option, *parsedName, attrName);
2211+
if (parseStatus != SingleAttrOptionParseStatus::Success) {
2212+
// Any sort of parse error (other than an ExpectedLParen permitted by
2213+
// whenOmitted().)
2214+
diagnoseSingleAttrOptionParseStatus(
2215+
P, parseStatus, loc, attrName, attrKind,
2216+
{ diag::attr_expected_option_such_as, attrName, exampleName });
21502217
}
2218+
else if (!result)
2219+
// We parsed an identifier, but it didn't match any of the when() calls.
2220+
P.diagnose(loc, diag::attr_unknown_option, parsedName, attrName);
21512221

21522222
return result;
21532223
}
@@ -2157,6 +2227,10 @@ class LLVM_NODISCARD AttrOptionSwitch {
21572227
/// \returns \c None if an error was diagnosed; \p value if the option was
21582228
/// omitted; the value the option was matched to otherwise.
21592229
Optional<R> whenOmitted(T value) {
2230+
// Treat an ExpectedLParen as successful.
2231+
if (parseStatus == SingleAttrOptionParseStatus::ExpectedLParen)
2232+
parseStatus = SingleAttrOptionParseStatus::Success;
2233+
21602234
return when("", value).diagnoseWhenOmitted();
21612235
}
21622236
};
@@ -2180,36 +2254,36 @@ template<typename R>
21802254
static AttrOptionSwitch<R>
21812255
parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
21822256
StringRef AttrName, DeclAttrKind DK) {
2183-
bool isModifier = DeclAttribute::isDeclModifier(DK);
2184-
if (!P.Tok.is(tok::l_paren)) {
2185-
AttrRange = SourceRange(Loc);
2186-
// Create an AttrOptionSwitch with an empty value. The calls on it will
2187-
// decide whether or not that's valid.
2188-
return AttrOptionSwitch<R>(StringRef(), P, Loc, AttrName, isModifier);
2189-
}
2190-
2191-
llvm::Optional<SyntaxParsingContext> ModDetailContext;
2192-
if (DK == DAK_ReferenceOwnership) {
2193-
ModDetailContext.emplace(P.SyntaxContext, SyntaxKind::DeclModifierDetail);
2194-
}
2195-
2196-
P.consumeToken(tok::l_paren);
2197-
2198-
StringRef parsedName = P.Tok.getText();
2199-
if (!P.consumeIf(tok::identifier)) {
2200-
// Once we have an example of a valid option, diagnose this with
2201-
// diag::attr_expected_option_such_as.
2202-
return AttrOptionSwitch<R>(None, P, Loc, AttrName, isModifier);
2203-
}
2257+
StringRef parsedName;
2258+
auto status = parseSingleAttrOptionImpl(P, Loc, AttrRange, DK, parsedName);
2259+
return AttrOptionSwitch<R>(status, parsedName, P, Loc, AttrName, DK);
2260+
}
22042261

2205-
if (!P.consumeIf(tok::r_paren)) {
2206-
P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isModifier);
2207-
// Pass through the switch without diagnosing anything.
2208-
return AttrOptionSwitch<R>(None, P, Loc, "", isModifier);
2262+
static Optional<Identifier>
2263+
parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc,
2264+
SourceRange &AttrRange, StringRef AttrName,
2265+
DeclAttrKind DK, bool allowOmitted = false) {
2266+
StringRef parsedName;
2267+
auto status = parseSingleAttrOptionImpl(P, Loc, AttrRange, DK, parsedName);
2268+
2269+
switch (status) {
2270+
case SingleAttrOptionParseStatus::Success:
2271+
break;
2272+
2273+
case SingleAttrOptionParseStatus::ExpectedLParen:
2274+
if (allowOmitted)
2275+
break;
2276+
LLVM_FALLTHROUGH;
2277+
2278+
case SingleAttrOptionParseStatus::ExpectedRParen:
2279+
case SingleAttrOptionParseStatus::ExpectedIdentifier:
2280+
diagnoseSingleAttrOptionParseStatus(
2281+
P, status, Loc, AttrName, DK,
2282+
{ diag::attr_expected_option_identifier, AttrName });
2283+
return None;
22092284
}
22102285

2211-
AttrRange = SourceRange(Loc, P.PreviousLoc);
2212-
return AttrOptionSwitch<R>(parsedName, P, Loc, AttrName, isModifier);
2286+
return P.Context.getIdentifier(parsedName);
22132287
}
22142288

22152289
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
@@ -2632,29 +2706,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
26322706
}
26332707

26342708
case DAK_SwiftNativeObjCRuntimeBase: {
2635-
if (!consumeIf(tok::l_paren)) {
2636-
diagnose(Loc, diag::attr_expected_lparen, AttrName,
2637-
DeclAttribute::isDeclModifier(DK));
2709+
SourceRange range;
2710+
auto name = parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName,
2711+
DK);
2712+
if (!name)
26382713
return false;
2639-
}
2640-
2641-
if (Tok.isNot(tok::identifier)) {
2642-
diagnose(Loc, diag::swift_native_objc_runtime_base_must_be_identifier);
2643-
return false;
2644-
}
2645-
2646-
Identifier name;
2647-
consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);
26482714

2649-
auto range = SourceRange(Loc, Tok.getRange().getStart());
2650-
2651-
if (!consumeIf(tok::r_paren)) {
2652-
diagnose(Loc, diag::attr_expected_rparen, AttrName,
2653-
DeclAttribute::isDeclModifier(DK));
2654-
return false;
2655-
}
2656-
2657-
Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(name,
2715+
Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(*name,
26582716
AtLoc, range, /*implicit*/ false));
26592717
break;
26602718
}
@@ -2895,30 +2953,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
28952953
break;
28962954
}
28972955
case DAK_ObjCRuntimeName: {
2898-
if (!consumeIf(tok::l_paren)) {
2899-
diagnose(Loc, diag::attr_expected_lparen, AttrName,
2900-
DeclAttribute::isDeclModifier(DK));
2956+
SourceRange range;
2957+
auto name =
2958+
parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK);
2959+
if (!name)
29012960
return false;
2902-
}
29032961

2904-
if (Tok.isNot(tok::identifier)) {
2905-
diagnose(Loc, diag::objc_runtime_name_must_be_identifier);
2906-
return false;
2907-
}
2908-
2909-
auto name = Tok.getText();
2910-
2911-
consumeToken(tok::identifier);
2912-
2913-
auto range = SourceRange(Loc, Tok.getRange().getStart());
2914-
2915-
if (!consumeIf(tok::r_paren)) {
2916-
diagnose(Loc, diag::attr_expected_rparen, AttrName,
2917-
DeclAttribute::isDeclModifier(DK));
2918-
return false;
2919-
}
2920-
2921-
Attributes.add(new (Context) ObjCRuntimeNameAttr(name, AtLoc, range,
2962+
Attributes.add(new (Context) ObjCRuntimeNameAttr(name->str(), AtLoc, range,
29222963
/*implicit*/ false));
29232964
break;
29242965
}
@@ -3063,30 +3104,14 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
30633104
}
30643105

30653106
case DAK_ProjectedValueProperty: {
3066-
if (!consumeIf(tok::l_paren)) {
3067-
diagnose(Loc, diag::attr_expected_lparen, AttrName,
3068-
DeclAttribute::isDeclModifier(DK));
3107+
SourceRange range;
3108+
auto name =
3109+
parseSingleAttrOptionIdentifier(*this, Loc, range, AttrName, DK);
3110+
if (!name)
30693111
return false;
3070-
}
3071-
3072-
if (Tok.isNot(tok::identifier)) {
3073-
diagnose(Loc, diag::projection_value_property_not_identifier);
3074-
return false;
3075-
}
3076-
3077-
Identifier name;
3078-
consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);
3079-
3080-
auto range = SourceRange(Loc, Tok.getRange().getStart());
3081-
3082-
if (!consumeIf(tok::r_paren)) {
3083-
diagnose(Loc, diag::attr_expected_rparen, AttrName,
3084-
DeclAttribute::isDeclModifier(DK));
3085-
return false;
3086-
}
30873112

30883113
Attributes.add(new (Context) ProjectedValuePropertyAttr(
3089-
name, AtLoc, range, /*implicit*/ false));
3114+
*name, AtLoc, range, /*implicit*/ false));
30903115
break;
30913116
}
30923117
case DAK_TypeSequence: {

lib/Parse/Parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,7 @@ bool Parser::parseToken(tok K, SourceLoc &TokLoc, const Diagnostic &D) {
975975
return true;
976976
}
977977

978-
bool Parser::parseMatchingToken(tok K, SourceLoc &TokLoc, Diag<> ErrorDiag,
978+
bool Parser::parseMatchingToken(tok K, SourceLoc &TokLoc, Diagnostic ErrorDiag,
979979
SourceLoc OtherLoc) {
980980
Diag<> OtherNote;
981981
switch (K) {

0 commit comments

Comments
 (0)