Skip to content

Commit acb997b

Browse files
committed
Alternate design
1 parent 22a54b5 commit acb997b

File tree

2 files changed

+122
-204
lines changed

2 files changed

+122
-204
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,18 @@ IDENTIFIER_WITH_NAME(TypeWrapperProperty, "$_storage")
315315
IDENTIFIER(storageKeyPath)
316316
IDENTIFIER(memberwise)
317317

318+
// Attribute options
319+
IDENTIFIER_(_always)
320+
IDENTIFIER_(assumed)
321+
IDENTIFIER(checked)
322+
IDENTIFIER(never)
323+
IDENTIFIER(none)
324+
IDENTIFIER(safe)
325+
IDENTIFIER(size)
326+
IDENTIFIER(speed)
327+
IDENTIFIER(unchecked)
328+
IDENTIFIER(unsafe)
329+
318330
// The singleton instance of TupleTypeDecl in the Builtin module
319331
IDENTIFIER(TheTupleType)
320332

lib/Parse/ParseDecl.cpp

Lines changed: 110 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,62 +2078,37 @@ 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:
2081+
/// Guts of \c parseSingleAttrOption and \c parseSingleAttrOptionIdentifier.
21142082
///
2115-
/// \verbatim
2116-
/// '(' identifier ')'
2117-
/// \endverbatim
2083+
/// \param P The parser object.
2084+
/// \param Loc The location of the attribute name (before the \c tok::l_paren, if any).
2085+
/// \param AttrRange Will be set to the range of the entire attribute, including its option if any.
2086+
/// \param AttrName The spelling of the attribute in the source code. Used in diagnostics.
2087+
/// \param DK The kind of the attribute being parsed.
2088+
/// \param allowOmitted If true, treat a missing argument list as permitted and return
2089+
/// \c Identifier() ; if false, diagnose a missing argument list as an error.
2090+
/// \param nonIdentifierDiagnostic The diagnostic to emit if something other than a
2091+
/// \c tok::identifier is used as an argument.
21182092
///
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
2093+
/// \returns \c None if an error was diagnosed; \c Identifier() if the argument list was permissibly
2094+
/// omitted; the identifier written by the user otherwise.
2095+
static Optional<Identifier>
21282096
parseSingleAttrOptionImpl(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
2129-
DeclAttrKind DK, StringRef &Result) {
2130-
Result = "";
2097+
StringRef AttrName, DeclAttrKind DK,
2098+
bool allowOmitted,
2099+
Diagnostic nonIdentifierDiagnostic) {
21312100
SWIFT_DEFER {
21322101
AttrRange = SourceRange(Loc, P.PreviousLoc);
21332102
};
2134-
2135-
if (!P.Tok.is(tok::l_paren))
2136-
return SingleAttrOptionParseStatus::ExpectedLParen;
2103+
bool isDeclModifier = DeclAttribute::isDeclModifier(DK);
2104+
2105+
if (!P.Tok.is(tok::l_paren)) {
2106+
if (allowOmitted)
2107+
return Identifier();
2108+
2109+
P.diagnose(Loc, diag::attr_expected_lparen, AttrName, isDeclModifier);
2110+
return None;
2111+
}
21372112

21382113
llvm::Optional<SyntaxParsingContext> ModDetailContext;
21392114
if (DK == DAK_ReferenceOwnership) {
@@ -2143,147 +2118,78 @@ parseSingleAttrOptionImpl(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
21432118
P.consumeToken(tok::l_paren);
21442119

21452120
StringRef parsedName = P.Tok.getText();
2146-
if (!P.consumeIf(tok::identifier))
2147-
return SingleAttrOptionParseStatus::ExpectedIdentifier;
2121+
if (!P.consumeIf(tok::identifier)) {
2122+
P.diagnose(Loc, nonIdentifierDiagnostic);
2123+
return None;
2124+
}
21482125

2149-
if (!P.consumeIf(tok::r_paren))
2150-
return SingleAttrOptionParseStatus::ExpectedRParen;
2151-
2152-
Result = parsedName;
2153-
return SingleAttrOptionParseStatus::Success;
2154-
}
2155-
2156-
/// Processes a parsed option name by attempting to match it to a list of
2157-
/// alternative name/value pairs provided by a chain of \c when() calls, ending
2158-
/// in either \c whenOmitted() if omitting the option is allowed, or
2159-
/// \c diagnoseWhenOmitted() if the option is mandatory.
2160-
template<typename T, typename R = T>
2161-
class LLVM_NODISCARD AttrOptionSwitch {
2162-
// Inputs:
2163-
StringRef parsedName; // empty: omitted or parse error
2164-
Parser &P;
2165-
SourceLoc loc;
2166-
StringRef attrName;
2167-
SingleAttrOptionParseStatus parseStatus;
2168-
DeclAttrKind attrKind;
2169-
2170-
// State:
2171-
StringRef exampleName; // empty: when() was never called
2172-
Optional<R> result; // None: no when() has matched
2173-
2174-
public:
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.
2179-
/// \param P The parser used to diagnose errors concerning this attribute
2180-
/// option.
2181-
/// \param loc The source location to diagnose errors at.
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)
2187-
: parsedName(parsedName), P(P), loc(loc), attrName(attrName),
2188-
parseStatus(parseStatus), attrKind(attrKind) { }
2189-
2190-
/// If the option has the identifier \p name, give it value \p value.
2191-
AttrOptionSwitch<R, T> &when(StringLiteral name, T value) {
2192-
// Save this to use in a future diagnostic, if needed.
2193-
if (exampleName.empty() && !name.empty())
2194-
exampleName = name;
2195-
2196-
// Does this string match?
2197-
if (parseStatus == SingleAttrOptionParseStatus::Success
2198-
&& parsedName == name) {
2199-
assert(!result && "overlapping AttrOptionSwitch::when()s?");
2200-
result = std::move(value);
2201-
}
2202-
2203-
return *this;
2204-
}
2205-
2206-
/// Diagnose if the option is missing or was not matched, returning either the
2207-
/// option's value or \c None if an error was diagnosed.
2208-
Optional<R> diagnoseWhenOmitted() {
2209-
assert(!exampleName.empty() && "No AttrOptionSwitch::when() calls");
2210-
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 });
2217-
}
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);
2221-
2222-
return result;
2126+
if (!P.consumeIf(tok::r_paren)) {
2127+
P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isDeclModifier);
2128+
return None;
22232129
}
22242130

2225-
/// Diagnose if the option is missing or not matched, returning:
2226-
///
2227-
/// \returns \c None if an error was diagnosed; \p value if the option was
2228-
/// omitted; the value the option was matched to otherwise.
2229-
Optional<R> whenOmitted(T value) {
2230-
// Treat an ExpectedLParen as successful.
2231-
if (parseStatus == SingleAttrOptionParseStatus::ExpectedLParen)
2232-
parseStatus = SingleAttrOptionParseStatus::Success;
2233-
2234-
return when("", value).diagnoseWhenOmitted();
2235-
}
2236-
};
2131+
return P.Context.getIdentifier(parsedName);
2132+
}
22372133

2238-
/// Parses an attribute argument list that allows a single identifier with a
2239-
/// known set of permitted options:
2134+
/// Parses a (possibly optional) argument for an attribute containing a single, arbitrary identifier.
22402135
///
2241-
/// \verbatim
2242-
/// '(' identifier ')'
2243-
/// \endverbatim
2244-
///
2245-
/// Returns an object of type \c AttrOptionSwitch, a type loosely inspired by
2246-
/// \c llvm::StringSwitch which can be used in a fluent style to map each
2247-
/// permitted identifier to a value. Together, they will automatically
2248-
/// diagnose \c diag::attr_expected_lparen,
2249-
/// \c diag::attr_expected_option_such_as, \c diag::attr_unknown_option, and
2250-
/// \c diag::attr_expected_rparen when needed.
2136+
/// \param P The parser object.
2137+
/// \param Loc The location of the attribute name (before the \c tok::l_paren, if any).
2138+
/// \param AttrRange Will be set to the range of the entire attribute, including its option if any.
2139+
/// \param AttrName The spelling of the attribute in the source code. Used in diagnostics.
2140+
/// \param DK The kind of the attribute being parsed.
2141+
/// \param allowOmitted If true, treat a missing argument list as permitted and return
2142+
/// \c Identifier() ; if false, diagnose a missing argument list as an error.
22512143
///
2252-
/// \seealso AttrOptionSwitch
2253-
template<typename R>
2254-
static AttrOptionSwitch<R>
2255-
parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
2256-
StringRef AttrName, DeclAttrKind DK) {
2257-
StringRef parsedName;
2258-
auto status = parseSingleAttrOptionImpl(P, Loc, AttrRange, DK, parsedName);
2259-
return AttrOptionSwitch<R>(status, parsedName, P, Loc, AttrName, DK);
2260-
}
2261-
2144+
/// \returns \c None if an error was diagnosed; \c Identifier() if the argument list was permissibly
2145+
/// omitted; the identifier written by the user otherwise.
22622146
static Optional<Identifier>
22632147
parseSingleAttrOptionIdentifier(Parser &P, SourceLoc Loc,
22642148
SourceRange &AttrRange, StringRef AttrName,
22652149
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 });
2150+
return parseSingleAttrOptionImpl(
2151+
P, Loc, AttrRange, AttrName, DK, allowOmitted,
2152+
{ diag::attr_expected_option_identifier, AttrName });
2153+
}
2154+
2155+
/// Parses a (possibly optional) argument for an attribute containing a single identifier from a known set of
2156+
/// supported values, mapping it to a domain-specific type.
2157+
///
2158+
/// \param P The parser object.
2159+
/// \param Loc The location of the attribute name (before the \c tok::l_paren, if any).
2160+
/// \param AttrRange Will be set to the range of the entire attribute, including its option if any.
2161+
/// \param AttrName The spelling of the attribute in the source code. Used in diagnostics.
2162+
/// \param DK The kind of the attribute being parsed.
2163+
/// \param options The set of permitted keywords and their corresponding values.
2164+
/// \param valueIfOmitted If present, treat a missing argument list as permitted and return
2165+
/// the provided value; if absent, diagnose a missing argument list as an error.
2166+
///
2167+
/// \returns \c None if an error was diagnosed; the value corresponding to the identifier written by the
2168+
/// user otherwise.
2169+
template<typename R>
2170+
static Optional<R>
2171+
parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
2172+
StringRef AttrName, DeclAttrKind DK,
2173+
ArrayRef<std::tuple<Identifier, R>> options,
2174+
Optional<R> valueIfOmitted = None) {
2175+
auto parsedIdentifier = parseSingleAttrOptionImpl(
2176+
P, Loc, AttrRange,AttrName, DK,
2177+
/*allowOmitted=*/valueIfOmitted.hasValue(),
2178+
Diagnostic(diag::attr_expected_option_such_as, AttrName,
2179+
std::get<0>(options.front()).str()));
2180+
if (!parsedIdentifier)
22832181
return None;
2284-
}
22852182

2286-
return P.Context.getIdentifier(parsedName);
2183+
// If omitted (and omission is permitted), return valueIfOmitted.
2184+
if (parsedIdentifier == Identifier())
2185+
return *valueIfOmitted;
2186+
2187+
for (auto &option : options)
2188+
if (std::get<0>(option) == *parsedIdentifier)
2189+
return std::get<1>(option);
2190+
2191+
P.diagnose(Loc, diag::attr_unknown_option, parsedIdentifier->str(), AttrName);
2192+
return None;
22872193
}
22882194

22892195
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
@@ -2418,11 +2324,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
24182324
}
24192325

24202326
case DAK_Inline: {
2421-
auto kind = parseSingleAttrOption<InlineKind>
2422-
(*this, Loc, AttrRange, AttrName, DK)
2423-
.when("never", InlineKind::Never)
2424-
.when("__always", InlineKind::Always)
2425-
.diagnoseWhenOmitted();
2327+
auto kind = parseSingleAttrOption<InlineKind>(
2328+
*this, Loc, AttrRange, AttrName, DK, {
2329+
{ Context.Id_never, InlineKind::Never },
2330+
{ Context.Id__always, InlineKind::Always }
2331+
});
24262332
if (!kind)
24272333
return false;
24282334

@@ -2433,12 +2339,12 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
24332339
}
24342340

24352341
case DAK_Optimize: {
2436-
auto optMode = parseSingleAttrOption<OptimizationMode>
2437-
(*this, Loc, AttrRange, AttrName, DK)
2438-
.when("speed", OptimizationMode::ForSpeed)
2439-
.when("size", OptimizationMode::ForSize)
2440-
.when("none", OptimizationMode::NoOptimization)
2441-
.diagnoseWhenOmitted();
2342+
auto optMode = parseSingleAttrOption<OptimizationMode>(
2343+
*this, Loc, AttrRange, AttrName, DK, {
2344+
{ Context.Id_speed, OptimizationMode::ForSpeed },
2345+
{ Context.Id_size, OptimizationMode::ForSize },
2346+
{ Context.Id_none, OptimizationMode::NoOptimization }
2347+
});
24422348
if (!optMode)
24432349
return false;
24442350

@@ -2449,11 +2355,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
24492355
}
24502356

24512357
case DAK_Exclusivity: {
2452-
auto mode = parseSingleAttrOption<ExclusivityAttr::Mode>
2453-
(*this, Loc, AttrRange, AttrName, DK)
2454-
.when("checked", ExclusivityAttr::Mode::Checked)
2455-
.when("unchecked", ExclusivityAttr::Mode::Unchecked)
2456-
.diagnoseWhenOmitted();
2358+
auto mode = parseSingleAttrOption<ExclusivityAttr::Mode>(
2359+
*this, Loc, AttrRange, AttrName, DK, {
2360+
{ Context.Id_checked, ExclusivityAttr::Mode::Checked },
2361+
{ Context.Id_unchecked, ExclusivityAttr::Mode::Unchecked }
2362+
});
24572363
if (!mode)
24582364
return false;
24592365

@@ -2470,11 +2376,11 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
24702376

24712377
if (Kind == ReferenceOwnership::Unowned) {
24722378
// Parse an optional specifier after unowned.
2473-
Kind = parseSingleAttrOption<ReferenceOwnership>
2474-
(*this, Loc, AttrRange, AttrName, DK)
2475-
.when("unsafe", ReferenceOwnership::Unmanaged)
2476-
.when("safe", ReferenceOwnership::Unowned)
2477-
.whenOmitted(ReferenceOwnership::Unowned)
2379+
Kind = parseSingleAttrOption<ReferenceOwnership>(
2380+
*this, Loc, AttrRange, AttrName, DK, {
2381+
{ Context.Id_unsafe, ReferenceOwnership::Unmanaged },
2382+
{ Context.Id_safe, ReferenceOwnership::Unowned }
2383+
}, ReferenceOwnership::Unowned)
24782384
// Recover from errors by going back to Unowned.
24792385
.getValueOr(ReferenceOwnership::Unowned);
24802386
}
@@ -2490,10 +2396,10 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
24902396
}
24912397

24922398
case DAK_NonSendable: {
2493-
auto kind = parseSingleAttrOption<NonSendableKind>
2494-
(*this, Loc, AttrRange, AttrName, DK)
2495-
.when("_assumed", NonSendableKind::Assumed)
2496-
.whenOmitted(NonSendableKind::Specific);
2399+
auto kind = parseSingleAttrOption<NonSendableKind>(
2400+
*this, Loc, AttrRange, AttrName, DK, {
2401+
{ Context.Id_assumed, NonSendableKind::Assumed }
2402+
}, NonSendableKind::Specific);
24972403
if (!kind)
24982404
return false;
24992405

0 commit comments

Comments
 (0)