public static bool IsExpressionContext()

in src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs [1907:2444]


        public static bool IsExpressionContext(
            this SyntaxTree syntaxTree,
            int position,
            SyntaxToken tokenOnLeftOfPosition,
            bool attributes,
            CancellationToken cancellationToken,
            SemanticModel semanticModelOpt = null)
        {
            // cases:
            //   var q = |
            //   var q = a|
            // this list is *not* exhaustive.

            var token = tokenOnLeftOfPosition.GetPreviousTokenIfTouchingWord(position);

            if (token.GetAncestor<ConditionalDirectiveTriviaSyntax>() != null)
            {
                return false;
            }

            if (!attributes)
            {
                if (token.GetAncestor<AttributeListSyntax>() != null)
                {
                    return false;
                }
            }

            if (syntaxTree.IsConstantExpressionContext(position, tokenOnLeftOfPosition, cancellationToken))
            {
                return true;
            }

            // no expressions after .   ::   ->
            if (token.IsKind(SyntaxKind.DotToken) ||
                token.IsKind(SyntaxKind.ColonColonToken) ||
                token.IsKind(SyntaxKind.MinusGreaterThanToken))
            {
                return false;
            }

            // Normally you can have any sort of expression after an equals. However, this does not
            // apply to a "using Goo = ..." situation.
            if (token.IsKind(SyntaxKind.EqualsToken))
            {
                if (token.Parent.IsKind(SyntaxKind.NameEquals) &&
                    token.Parent.IsParentKind(SyntaxKind.UsingDirective))
                {
                    return false;
                }
            }

            // q = |
            // q -= |
            // q *= |
            // q += |
            // q /= |
            // q ^= |
            // q %= |
            // q &= |
            // q |= |
            // q <<= |
            // q >>= |
            if (token.IsKind(SyntaxKind.EqualsToken) ||
                token.IsKind(SyntaxKind.MinusEqualsToken) ||
                token.IsKind(SyntaxKind.AsteriskEqualsToken) ||
                token.IsKind(SyntaxKind.PlusEqualsToken) ||
                token.IsKind(SyntaxKind.SlashEqualsToken) ||
                token.IsKind(SyntaxKind.ExclamationEqualsToken) ||
                token.IsKind(SyntaxKind.CaretEqualsToken) ||
                token.IsKind(SyntaxKind.AmpersandEqualsToken) ||
                token.IsKind(SyntaxKind.BarEqualsToken) ||
                token.IsKind(SyntaxKind.PercentEqualsToken) ||
                token.IsKind(SyntaxKind.LessThanLessThanEqualsToken) ||
                token.IsKind(SyntaxKind.GreaterThanGreaterThanEqualsToken))
            {
                return true;
            }

            // ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.Parent.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return true;
            }

            // - |
            // + |
            // ~ |
            // ! |
            if (token.Parent is PrefixUnaryExpressionSyntax)
            {
                var prefix = token.Parent as PrefixUnaryExpressionSyntax;
                return prefix.OperatorToken == token;
            }

            // not sure about these:
            //   ++ |
            //   -- |
#if false
                token.Kind == SyntaxKind.PlusPlusToken ||
                token.Kind == SyntaxKind.DashDashToken)
#endif
            // await |
            if (token.Parent is AwaitExpressionSyntax)
            {
                var awaitExpression = token.Parent as AwaitExpressionSyntax;
                return awaitExpression.AwaitKeyword == token;
            }

            // Check for binary operators.
            // Note:
            //   - We handle < specially as it can be ambiguous with generics.
            //   - We handle * specially because it can be ambiguous with pointer types.

            // a *
            // a /
            // a %
            // a +
            // a -
            // a <<
            // a >>
            // a <
            // a >
            // a &&
            // a ||
            // a &
            // a |
            // a ^
            if (token.Parent is BinaryExpressionSyntax)
            {
                // If the client provided a binding, then check if this is actually generic.  If so,
                // then this is not an expression context. i.e. if we have "Goo < |" then it could
                // be an expression context, or it could be a type context if Goo binds to a type or
                // method.
                if (semanticModelOpt != null && syntaxTree.IsGenericTypeArgumentContext(position, tokenOnLeftOfPosition, cancellationToken, semanticModelOpt))
                {
                    return false;
                }

                var binary = token.Parent as BinaryExpressionSyntax;
                if (binary.OperatorToken == token)
                {
                    // If this is a multiplication expression and a semantic model was passed in,
                    // check to see if the expression to the left is a type name. If it is, treat
                    // this as a pointer type.
                    if (token.IsKind(SyntaxKind.AsteriskToken) && semanticModelOpt != null)
                    {
                        if (binary.Left is TypeSyntax type && type.IsPotentialTypeName(semanticModelOpt, cancellationToken))
                        {
                            return false;
                        }
                    }

                    return true;
                }
            }

            // Special case:
            //    Goo * bar
            //    Goo ? bar
            // This parses as a local decl called bar of type Goo* or Goo?
            if (tokenOnLeftOfPosition.IntersectsWith(position) &&
                tokenOnLeftOfPosition.IsKind(SyntaxKind.IdentifierToken))
            {
                var previousToken = tokenOnLeftOfPosition.GetPreviousToken(includeSkipped: true);
                if (previousToken.IsKind(SyntaxKind.AsteriskToken) ||
                    previousToken.IsKind(SyntaxKind.QuestionToken))
                {
                    if (previousToken.Parent.IsKind(SyntaxKind.PointerType) ||
                        previousToken.Parent.IsKind(SyntaxKind.NullableType))
                    {
                        var type = previousToken.Parent as TypeSyntax;
                        if (type.IsParentKind(SyntaxKind.VariableDeclaration) &&
                            type.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement))
                        {
                            var declStatement = type.Parent.Parent as LocalDeclarationStatementSyntax;

                            // note, this doesn't apply for cases where we know it 
                            // absolutely is not multiplication or a conditional expression.
                            var underlyingType = type is PointerTypeSyntax
                                ? ((PointerTypeSyntax)type).ElementType
                                : ((NullableTypeSyntax)type).ElementType;

                            if (!underlyingType.IsPotentialTypeName(semanticModelOpt, cancellationToken))
                            {
                                return true;
                            }
                        }
                    }
                }
            }

            // new int[|
            // new int[expr, |
            if (token.IsKind(SyntaxKind.OpenBracketToken) ||
                token.IsKind(SyntaxKind.CommaToken))
            {
                if (token.Parent.IsKind(SyntaxKind.ArrayRankSpecifier))
                {
                    return true;
                }
            }

            // goo ? |
            if (token.IsKind(SyntaxKind.QuestionToken) &&
                token.Parent.IsKind(SyntaxKind.ConditionalExpression))
            {
                // If the condition is simply a TypeSyntax that binds to a type, treat this as a nullable type.
                var conditionalExpression = (ConditionalExpressionSyntax)token.Parent;
                var type = conditionalExpression.Condition as TypeSyntax;

                return type == null
                    || !type.IsPotentialTypeName(semanticModelOpt, cancellationToken);
            }

            // goo ? bar : |
            if (token.IsKind(SyntaxKind.ColonToken) &&
                token.Parent.IsKind(SyntaxKind.ConditionalExpression))
            {
                return true;
            }

            // typeof(|
            // default(|
            // sizeof(|
            if (token.IsKind(SyntaxKind.OpenParenToken))
            {
                if (token.Parent.IsKind(SyntaxKind.TypeOfExpression, SyntaxKind.DefaultExpression, SyntaxKind.SizeOfExpression))
                {
                    return false;
                }
            }

            // var(|
            // var(id, |
            // Those are more likely to be deconstruction-declarations being typed than invocations a method "var"
            if (token.IsKind(SyntaxKind.OpenParenToken, SyntaxKind.CommaToken) &&
                token.IsInvocationOfVarExpression())
            {
                return false;
            }

            // Goo(|
            // Goo(expr, |
            // this[|
            // var t = (1, |
            // var t = (| , 2)
            if (token.IsKind(SyntaxKind.OpenParenToken) ||
                token.IsKind(SyntaxKind.OpenBracketToken) ||
                token.IsKind(SyntaxKind.CommaToken))
            {
                if (token.Parent.IsKind(SyntaxKind.ArgumentList, SyntaxKind.BracketedArgumentList, SyntaxKind.TupleExpression))
                {
                    return true;
                }
            }

            // [Goo(|
            // [Goo(expr, |
            if (attributes)
            {
                if (token.IsKind(SyntaxKind.OpenParenToken) ||
                    token.IsKind(SyntaxKind.CommaToken))
                {
                    if (token.Parent.IsKind(SyntaxKind.AttributeArgumentList))
                    {
                        return true;
                    }
                }
            }

            // Goo(ref |
            // Goo(in |
            // Goo(out |
            if (token.IsKind(SyntaxKind.RefKeyword) ||
                token.IsKind(SyntaxKind.InKeyword) ||
                token.IsKind(SyntaxKind.OutKeyword))
            {
                if (token.Parent.IsKind(SyntaxKind.Argument))
                {
                    return true;
                }
            }

            // Goo(bar: |
            if (token.IsKind(SyntaxKind.ColonToken) &&
                token.Parent.IsKind(SyntaxKind.NameColon) &&
                token.Parent.IsParentKind(SyntaxKind.Argument))
            {
                return true;
            }

            // a => |
            if (token.IsKind(SyntaxKind.EqualsGreaterThanToken))
            {
                return true;
            }

            // new List<int> { |
            // new List<int> { expr, |
            if (token.IsKind(SyntaxKind.OpenBraceToken) ||
                token.IsKind(SyntaxKind.CommaToken))
            {
                if (token.Parent is InitializerExpressionSyntax)
                {
                    // The compiler treats the ambiguous case as an object initializer, so we'll say
                    // expressions are legal here
                    if (token.Parent.IsKind(SyntaxKind.ObjectInitializerExpression) && token.IsKind(SyntaxKind.OpenBraceToken))
                    {
                        // In this position { a$$ =, the user is trying to type an object initializer.
                        if (!token.IntersectsWith(position) && token.GetNextToken().GetNextToken().IsKind(SyntaxKind.EqualsToken))
                        {
                            return false;
                        }

                        return true;
                    }

                    // Perform a semantic check to determine whether or not the type being created
                    // can support a collection initializer. If not, this must be an object initializer
                    // and can't be an expression context.
                    if (semanticModelOpt != null &&
                        token.Parent.IsParentKind(SyntaxKind.ObjectCreationExpression))
                    {
                        var objectCreation = (ObjectCreationExpressionSyntax)token.Parent.Parent;
                        var type = semanticModelOpt.GetSymbolInfo(objectCreation.Type, cancellationToken).Symbol as ITypeSymbol;
                        var containingSymbol = semanticModelOpt.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
                        if (type != null && !type.CanSupportCollectionInitializer(containingSymbol))
                        {
                            return false;
                        }
                    }

                    return true;
                }
            }

            // for (; |
            // for (; ; |
            if (token.IsKind(SyntaxKind.SemicolonToken) &&
                token.Parent.IsKind(SyntaxKind.ForStatement))
            {
                var forStatement = (ForStatementSyntax)token.Parent;
                if (token == forStatement.FirstSemicolonToken ||
                    token == forStatement.SecondSemicolonToken)
                {
                    return true;
                }
            }

            // for ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.Parent.IsKind(SyntaxKind.ForStatement))
            {
                var forStatement = (ForStatementSyntax)token.Parent;
                if (token == forStatement.OpenParenToken)
                {
                    return true;
                }
            }

            // for (; ; Goo(), | 
            // for ( Goo(), |
            if (token.IsKind(SyntaxKind.CommaToken) &&
                token.Parent.IsKind(SyntaxKind.ForStatement))
            {
                return true;
            }

            // foreach (var v in |
            // from a in |
            // join b in |
            if (token.IsKind(SyntaxKind.InKeyword))
            {
                if (token.Parent.IsKind(SyntaxKind.ForEachStatement,
                                        SyntaxKind.ForEachVariableStatement,
                                        SyntaxKind.FromClause,
                                        SyntaxKind.JoinClause))
                {
                    return true;
                }
            }

            // join x in y on |
            // join x in y on a equals |
            if (token.IsKind(SyntaxKind.OnKeyword) ||
                token.IsKind(SyntaxKind.EqualsKeyword))
            {
                if (token.Parent.IsKind(SyntaxKind.JoinClause))
                {
                    return true;
                }
            }

            // where |
            if (token.IsKind(SyntaxKind.WhereKeyword) &&
                token.Parent.IsKind(SyntaxKind.WhereClause))
            {
                return true;
            }

            // orderby |
            // orderby a, |
            if (token.IsKind(SyntaxKind.OrderByKeyword) ||
                token.IsKind(SyntaxKind.CommaToken))
            {
                if (token.Parent.IsKind(SyntaxKind.OrderByClause))
                {
                    return true;
                }
            }

            // select |
            if (token.IsKind(SyntaxKind.SelectKeyword) &&
                token.Parent.IsKind(SyntaxKind.SelectClause))
            {
                return true;
            }

            // group |
            // group expr by |
            if (token.IsKind(SyntaxKind.GroupKeyword) ||
                token.IsKind(SyntaxKind.ByKeyword))
            {
                if (token.Parent.IsKind(SyntaxKind.GroupClause))
                {
                    return true;
                }
            }

            // return |
            // yield return |
            // but not: [return |
            if (token.IsKind(SyntaxKind.ReturnKeyword))
            {
                if (token.GetPreviousToken(includeSkipped: true).Kind() != SyntaxKind.OpenBracketToken)
                {
                    return true;
                }
            }

            // throw |
            if (token.IsKind(SyntaxKind.ThrowKeyword))
            {
                return true;
            }

            // while ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.WhileKeyword))
            {
                return true;
            }

            // todo: handle 'for' cases.

            // using ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.UsingKeyword))
            {
                return true;
            }

            // lock ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.LockKeyword))
            {
                return true;
            }

            // lock ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.IfKeyword))
            {
                return true;
            }

            // switch ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.SwitchKeyword))
            {
                return true;
            }

            // checked ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.CheckedKeyword))
            {
                return true;
            }

            // unchecked ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.UncheckedKeyword))
            {
                return true;
            }

            // when ( |
            if (token.IsKind(SyntaxKind.OpenParenToken) &&
                token.GetPreviousToken(includeSkipped: true).IsKind(SyntaxKind.WhenKeyword))
            {
                return true;
            }

            // (SomeType) |
            if (token.IsAfterPossibleCast())
            {
                return true;
            }

            // In anonymous type initializer.
            //
            // new { | We allow new inside of anonymous object member declarators, so that the user
            // can dot into a member afterward. For example:
            //
            // var a = new { new C().Goo };
            if (token.IsKind(SyntaxKind.OpenBraceToken) || token.IsKind(SyntaxKind.CommaToken))
            {
                if (token.Parent.IsKind(SyntaxKind.AnonymousObjectCreationExpression))
                {
                    return true;
                }
            }

            // $"{ |
            // $@"{ |
            // $"{x} { |
            // $@"{x} { |
            if (token.IsKind(SyntaxKind.OpenBraceToken))
            {
                return token.Parent.IsKind(SyntaxKind.Interpolation)
                    && ((InterpolationSyntax)token.Parent).OpenBraceToken == token;
            }

            return false;
        }