@@ -2078,39 +2078,114 @@ Parser::parseDocumentationAttribute(SourceLoc AtLoc, SourceLoc Loc) {
2078
2078
return makeParserResult (new (Context) DocumentationAttr (Loc, range, FinalMetadata, Visibility, false ));
2079
2079
}
2080
2080
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
+
2081
2156
// / Processes a parsed option name by attempting to match it to a list of
2082
2157
// / alternative name/value pairs provided by a chain of \c when() calls, ending
2083
2158
// / in either \c whenOmitted() if omitting the option is allowed, or
2084
2159
// / \c diagnoseWhenOmitted() if the option is mandatory.
2085
2160
template <typename T, typename R = T>
2086
2161
class LLVM_NODISCARD AttrOptionSwitch {
2087
2162
// Inputs:
2088
- Optional< StringRef> parsedName; // None: parse error, empty: omitted
2163
+ StringRef parsedName; // empty: omitted or parse error
2089
2164
Parser &P;
2090
2165
SourceLoc loc;
2091
- StringRef attrName; // empty: error already diagnosed
2092
- bool isDeclModifier;
2166
+ StringRef attrName;
2167
+ SingleAttrOptionParseStatus parseStatus;
2168
+ DeclAttrKind attrKind;
2093
2169
2094
2170
// State:
2095
2171
StringRef exampleName; // empty: when() was never called
2096
2172
Optional<R> result; // None: no when() has matched
2097
2173
2098
2174
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 .
2103
2179
// / \param P The parser used to diagnose errors concerning this attribute
2104
2180
// / option.
2105
2181
// / \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)
2112
2187
: parsedName(parsedName), P(P), loc(loc), attrName(attrName),
2113
- isDeclModifier (isDeclModifier ) { }
2188
+ parseStatus (parseStatus), attrKind(attrKind ) { }
2114
2189
2115
2190
// / If the option has the identifier \p name, give it value \p value.
2116
2191
AttrOptionSwitch<R, T> &when (StringLiteral name, T value) {
@@ -2119,7 +2194,8 @@ class LLVM_NODISCARD AttrOptionSwitch {
2119
2194
exampleName = name;
2120
2195
2121
2196
// Does this string match?
2122
- if (parsedName && *parsedName == name) {
2197
+ if (parseStatus == SingleAttrOptionParseStatus::Success
2198
+ && parsedName == name) {
2123
2199
assert (!result && " overlapping AttrOptionSwitch::when()s?" );
2124
2200
result = std::move (value);
2125
2201
}
@@ -2132,22 +2208,16 @@ class LLVM_NODISCARD AttrOptionSwitch {
2132
2208
Optional<R> diagnoseWhenOmitted () {
2133
2209
assert (!exampleName.empty () && " No AttrOptionSwitch::when() calls" );
2134
2210
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 });
2150
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);
2151
2221
2152
2222
return result;
2153
2223
}
@@ -2157,6 +2227,10 @@ class LLVM_NODISCARD AttrOptionSwitch {
2157
2227
// / \returns \c None if an error was diagnosed; \p value if the option was
2158
2228
// / omitted; the value the option was matched to otherwise.
2159
2229
Optional<R> whenOmitted (T value) {
2230
+ // Treat an ExpectedLParen as successful.
2231
+ if (parseStatus == SingleAttrOptionParseStatus::ExpectedLParen)
2232
+ parseStatus = SingleAttrOptionParseStatus::Success;
2233
+
2160
2234
return when (" " , value).diagnoseWhenOmitted ();
2161
2235
}
2162
2236
};
@@ -2180,36 +2254,36 @@ template<typename R>
2180
2254
static AttrOptionSwitch<R>
2181
2255
parseSingleAttrOption (Parser &P, SourceLoc Loc, SourceRange &AttrRange,
2182
2256
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
+ }
2204
2261
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;
2209
2284
}
2210
2285
2211
- AttrRange = SourceRange (Loc, P.PreviousLoc );
2212
- return AttrOptionSwitch<R>(parsedName, P, Loc, AttrName, isModifier);
2286
+ return P.Context .getIdentifier (parsedName);
2213
2287
}
2214
2288
2215
2289
bool Parser::parseNewDeclAttribute (DeclAttributes &Attributes, SourceLoc AtLoc,
@@ -2632,29 +2706,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
2632
2706
}
2633
2707
2634
2708
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)
2638
2713
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 );
2648
2714
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,
2658
2716
AtLoc, range, /* implicit*/ false ));
2659
2717
break ;
2660
2718
}
@@ -2895,30 +2953,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
2895
2953
break ;
2896
2954
}
2897
2955
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)
2901
2960
return false ;
2902
- }
2903
2961
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,
2922
2963
/* implicit*/ false ));
2923
2964
break ;
2924
2965
}
@@ -3063,30 +3104,14 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
3063
3104
}
3064
3105
3065
3106
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)
3069
3111
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
- }
3087
3112
3088
3113
Attributes.add (new (Context) ProjectedValuePropertyAttr (
3089
- name, AtLoc, range, /* implicit*/ false ));
3114
+ * name, AtLoc, range, /* implicit*/ false ));
3090
3115
break ;
3091
3116
}
3092
3117
case DAK_TypeSequence: {
0 commit comments