@@ -906,10 +906,25 @@ private bool IsPossibleAttributeDeclaration()
906
906
907
907
private SyntaxList<AttributeListSyntax> ParseAttributeDeclarations(bool inExpressionContext)
908
908
{
909
- var attributes = _pool.Allocate<AttributeListSyntax>();
910
909
var saveTerm = _termState;
911
910
_termState |= TerminatorState.IsAttributeDeclarationTerminator;
912
911
912
+ // An attribute can never appear *inside* an attribute argument (since a lambda expression cannot be used as
913
+ // a constant argument value). However, during parsing we can end up in a state where we're trying to
914
+ // exactly that, through a path of Attribute->Argument->Expression->Attribute (since attributes can not be
915
+ // on lambda expressions).
916
+ //
917
+ // Worse, when we are in a deeply ambiguous (or gibberish) scenario, where we see lots of code with `... [
918
+ // ... [ ... ] ... ] ...`, we can get into exponential speculative parsing where we try `[ ... ]` both as an
919
+ // attribute *and* a collection expression.
920
+ //
921
+ // Since we cannot ever legally have an attribute within an attribute, we bail out here immediately
922
+ // syntactically. This does mean we won't parse something like: `[X([Y]() => {})]` without errors, but that
923
+ // is not semantically legal anyway.
924
+ if (saveTerm == _termState)
925
+ return default;
926
+
927
+ var attributes = _pool.Allocate<AttributeListSyntax>();
913
928
while (this.IsPossibleAttributeDeclaration())
914
929
{
915
930
var attributeDeclaration = this.TryParseAttributeDeclaration(inExpressionContext);
@@ -11868,7 +11883,18 @@ private bool ScanExplicitlyTypedLambda(Precedence precedence)
11868
11883
// If we have an `=` then parse out a default value. Note: this is not legal, but this allows us to
11869
11884
// to be resilient to the user writing this so we don't go completely off the rails.
11870
11885
if (equalsToken != null)
11886
+ {
11887
+ // Note: we don't do this if we have `=[`. Realistically, this is never going to be a lambda
11888
+ // expression as a `[` can only start an attribute declaration or collection expression, neither of
11889
+ // which can be a default arg. Checking for this helps us from going off the rails in pathological
11890
+ // cases with lots of nested tokens that look like the could be anything.
11891
+ if (this.CurrentToken.Kind == SyntaxKind.OpenBracketToken)
11892
+ {
11893
+ return false;
11894
+ }
11895
+
11871
11896
this.ParseExpressionCore();
11897
+ }
11872
11898
11873
11899
switch (this.CurrentToken.Kind)
11874
11900
{
0 commit comments