public static bool CanBeReplacedWithAnyName(this NameSyntax nameSyntax) { if (nameSyntax.IsParentKind(SyntaxKind.AliasQualifiedName) || nameSyntax.IsParentKind(SyntaxKind.NameColon) || nameSyntax.IsParentKind(SyntaxKind.NameEquals) || nameSyntax.IsParentKind(SyntaxKind.TypeParameterConstraintClause)) { return false; } if (nameSyntax.CheckParent<QualifiedNameSyntax>(q => q.Right == nameSyntax) || nameSyntax.CheckParent<MemberAccessExpressionSyntax>(m => m.Name == nameSyntax)) { return false; } // TODO(cyrusn): Add more cases as the language changes. return true; }
public static bool CanReplaceWithLValue( this ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken) { if (expression.IsKind(SyntaxKind.StackAllocArrayCreationExpression)) { // Stack alloc is very interesting. While it appears to be an expression, it is only // such so it can appear in a variable decl. It is not a normal expression that can // go anywhere. return false; } if (expression.IsKind(SyntaxKind.BaseExpression) || expression.IsKind(SyntaxKind.CollectionInitializerExpression) || expression.IsKind(SyntaxKind.ObjectInitializerExpression) || expression.IsKind(SyntaxKind.ComplexElementInitializerExpression)) { return false; } // literal can be always replaced. if (expression is LiteralExpressionSyntax && !expression.IsParentKind(SyntaxKind.UnaryMinusExpression)) { return true; } if (!(expression is ObjectCreationExpressionSyntax) && !(expression is AnonymousObjectCreationExpressionSyntax)) { var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken); if (!symbolInfo.GetBestOrAllSymbols().All(CanReplace)) { // If the expression is actually a reference to a type, then it can't be replaced // with an arbitrary expression. return false; } } // If we are a conditional access expression: // case (1) : obj?.Method(), obj1.obj2?.Property // case (2) : obj?.GetAnotherObj()?.Length, obj?.AnotherObj?.Length // in case (1), the entire expression forms the conditional access expression, which can be replaced with an LValue. // in case (2), the nested conditional access expression is ".GetAnotherObj()?.Length" or ".AnotherObj()?.Length" // essentially, the first expression (before the operator) in a nested conditional access expression // is some form of member binding expression and they cannot be replaced with an LValue. if (expression.IsKind(SyntaxKind.ConditionalAccessExpression)) { return expression.Parent.Kind() != SyntaxKind.ConditionalAccessExpression; } switch (expression.Parent.Kind()) { case SyntaxKind.InvocationExpression: // Technically, you could introduce an LValue for "Foo" in "Foo()" even if "Foo" binds // to a method. (i.e. by assigning to a Func<...> type). However, this is so contrived // and none of the features that use this extension consider this replaceable. if (expression.IsKind(SyntaxKind.IdentifierName) || expression is MemberAccessExpressionSyntax) { // If it looks like a method then we don't allow it to be replaced if it is a // method (or if it doesn't bind). var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken); return symbolInfo.GetBestOrAllSymbols().Any() && !symbolInfo.GetBestOrAllSymbols().Any(s => s is IMethodSymbol); } else { // It doesn't look like a method, we allow this to be replaced. return true; } // If the parent is a conditional access expression, we could introduce an LValue // for the given expression, unless it is itself a MemberBindingExpression or starts with one. // Case (1) : The WhenNotNull clause always starts with a memberbindingexpression. // expression '.Method()' in a?.Method() // Case (2) : The Expression clause always starts with a memberbindingexpression if // the grandparent is a conditional access expression. // expression '.Method' in a?.Method()?.Length // Case (3) : The child Conditional access expression always starts with a memberbindingexpression if // the parent is a conditional access expression. This case is already covered before the parent kind switch case SyntaxKind.ConditionalAccessExpression: var parentConditionalAccessExpression = (ConditionalAccessExpressionSyntax)expression.Parent; return expression != parentConditionalAccessExpression.WhenNotNull && !parentConditionalAccessExpression.Parent.IsKind(SyntaxKind.ConditionalAccessExpression); case SyntaxKind.IsExpression: case SyntaxKind.AsExpression: // Can't introduce a variable for the type portion of an is/as check. var isOrAsExpression = (BinaryExpressionSyntax)expression.Parent; return expression == isOrAsExpression.Left; case SyntaxKind.EqualsValueClause: case SyntaxKind.ExpressionStatement: case SyntaxKind.ArrayInitializerExpression: case SyntaxKind.CollectionInitializerExpression: case SyntaxKind.Argument: case SyntaxKind.AttributeArgument: case SyntaxKind.AnonymousObjectMemberDeclarator: case SyntaxKind.ArrowExpressionClause: case SyntaxKind.AwaitExpression: case SyntaxKind.ReturnStatement: case SyntaxKind.YieldReturnStatement: case SyntaxKind.ParenthesizedLambdaExpression: case SyntaxKind.SimpleLambdaExpression: case SyntaxKind.ParenthesizedExpression: case SyntaxKind.ArrayRankSpecifier: case SyntaxKind.ConditionalExpression: case SyntaxKind.IfStatement: case SyntaxKind.CatchFilterClause: case SyntaxKind.WhileStatement: case SyntaxKind.DoStatement: case SyntaxKind.ThrowStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.InterpolatedStringExpression: case SyntaxKind.ComplexElementInitializerExpression: case SyntaxKind.Interpolation: // Direct parent kind checks. return true; } if (expression.Parent is PrefixUnaryExpressionSyntax) { if (!(expression is LiteralExpressionSyntax && expression.IsParentKind(SyntaxKind.UnaryMinusExpression))) { return true; } } var parentNonExpression = expression.GetAncestors().SkipWhile(n => n is ExpressionSyntax).FirstOrDefault(); var topExpression = expression; while (topExpression.Parent is TypeSyntax) { topExpression = (TypeSyntax)topExpression.Parent; } if (parentNonExpression != null && parentNonExpression.IsKind(SyntaxKind.FromClause) && topExpression != null && ((FromClauseSyntax)parentNonExpression).Type == topExpression) { return false; } // Parent type checks. if (expression.Parent is PostfixUnaryExpressionSyntax || expression.Parent is BinaryExpressionSyntax || expression.Parent is AssignmentExpressionSyntax || expression.Parent is QueryClauseSyntax || expression.Parent is SelectOrGroupClauseSyntax || expression.Parent is CheckedExpressionSyntax) { return true; } // Specific child checks. if (expression.CheckParent<ForEachStatementSyntax>(f => f.Expression == expression) || expression.CheckParent<MemberAccessExpressionSyntax>(m => m.Expression == expression) || expression.CheckParent<CastExpressionSyntax>(c => c.Expression == expression)) { return true; } // Misc checks. if ((expression.IsParentKind(SyntaxKind.NameEquals) && expression.Parent.IsParentKind(SyntaxKind.AttributeArgument)) || expression.IsLeftSideOfAnyAssignExpression()) { return true; } return false; }
public static bool CanReplaceWithLValue( this ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken) { if (expression.IsKind(SyntaxKind.StackAllocArrayCreationExpression)) { // Stack alloc is very interesting. While it appears to be an expression, it is only // such so it can appear in a variable decl. It is not a normal expression that can // go anywhere. return false; } if (expression.IsKind(SyntaxKind.BaseExpression) || expression.IsKind(SyntaxKind.CollectionInitializerExpression) || expression.IsKind(SyntaxKind.ObjectInitializerExpression) || expression.IsKind(SyntaxKind.ComplexElementInitializerExpression)) { return false; } // literal can be always replaced. if (expression is LiteralExpressionSyntax && !expression.IsParentKind(SyntaxKind.UnaryMinusExpression)) { return true; } if (!(expression is ObjectCreationExpressionSyntax) && !(expression is AnonymousObjectCreationExpressionSyntax)) { var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken); if (!symbolInfo.GetBestOrAllSymbols().All(CanReplace)) { // If the expression is actually a reference to a type, then it can't be replaced // with an arbitrary expression. return false; } } // Technically, you could introduce an LValue for "Foo" in "Foo()" even if "Foo" binds // to a method. (i.e. by assigning to a Func<...> type). However, this is so contrived // and none of the features that use this extension consider this replaceable. if (expression.Parent is InvocationExpressionSyntax) { if (expression.IsKind(SyntaxKind.IdentifierName) || expression is MemberAccessExpressionSyntax) { // If it looks like a method then we don't allow it to be replaced if it is a // method (or if it doesn't bind). var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken); return symbolInfo.GetBestOrAllSymbols().Any() && !symbolInfo.GetBestOrAllSymbols().Any(s => s is IMethodSymbol); } else { // It doesn't look like a method, we allow this to be replaced. return true; } } // Direct parent kind checks. if (expression.IsParentKind(SyntaxKind.ExpressionStatement) || expression.IsParentKind(SyntaxKind.EqualsValueClause) || expression.IsParentKind(SyntaxKind.ArrayInitializerExpression) || expression.IsParentKind(SyntaxKind.CollectionInitializerExpression) || expression.IsParentKind(SyntaxKind.Argument) || expression.IsParentKind(SyntaxKind.AttributeArgument) || expression.IsParentKind(SyntaxKind.AnonymousObjectMemberDeclarator) || expression.IsParentKind(SyntaxKind.ReturnStatement) || expression.IsParentKind(SyntaxKind.YieldReturnStatement) || expression.IsParentKind(SyntaxKind.ParenthesizedLambdaExpression) || expression.IsParentKind(SyntaxKind.SimpleLambdaExpression) || expression.IsParentKind(SyntaxKind.ParenthesizedExpression) || expression.IsParentKind(SyntaxKind.ArrayRankSpecifier) || expression.IsParentKind(SyntaxKind.ConditionalExpression) || expression.IsParentKind(SyntaxKind.IfStatement) || expression.IsParentKind(SyntaxKind.WhileStatement) || expression.IsParentKind(SyntaxKind.DoStatement) || expression.IsParentKind(SyntaxKind.ThrowStatement) || expression.IsParentKind(SyntaxKind.SwitchStatement)) { return true; } if (expression.IsParentKind(SyntaxKind.IsExpression) || expression.IsParentKind(SyntaxKind.AsExpression)) { // Can't introduce a variable for the type portion of an is/as check. var isOrAsExpression = (BinaryExpressionSyntax)expression.Parent; return expression == isOrAsExpression.Left; } if (expression.Parent is PrefixUnaryExpressionSyntax) { if (!(expression is LiteralExpressionSyntax && expression.IsParentKind(SyntaxKind.UnaryMinusExpression))) { return true; } } var parentNonExpression = expression.GetAncestors().SkipWhile(n => n is ExpressionSyntax).FirstOrDefault(); var topExpression = expression; while (topExpression.Parent is TypeSyntax) { topExpression = (TypeSyntax)topExpression.Parent; } if (parentNonExpression != null && parentNonExpression.IsKind(SyntaxKind.FromClause) && topExpression != null && ((FromClauseSyntax)parentNonExpression).Type == topExpression) { return false; } // Parent type checks. if (expression.Parent is PostfixUnaryExpressionSyntax || expression.Parent is BinaryExpressionSyntax || expression.Parent is QueryClauseSyntax || expression.Parent is SelectOrGroupClauseSyntax || expression.Parent is CheckedExpressionSyntax) { return true; } // Specific child checks. if (expression.CheckParent<ForEachStatementSyntax>(f => f.Expression == expression) || expression.CheckParent<MemberAccessExpressionSyntax>(m => m.Expression == expression) || expression.CheckParent<CastExpressionSyntax>(c => c.Expression == expression)) { return true; } // Misc checks. if ((expression.IsParentKind(SyntaxKind.NameEquals) && expression.Parent.IsParentKind(SyntaxKind.AttributeArgument)) || expression.IsLeftSideOfAnyAssignExpression()) { return true; } return false; }
// public bool IsInvocationExpression(SyntaxNode node) // { // return node is InvocationExpressionSyntax; // } // // public bool IsAnonymousFunction(SyntaxNode node) // { // return node is ParenthesizedLambdaExpressionSyntax || // node is SimpleLambdaExpressionSyntax || // node is AnonymousMethodExpressionSyntax; // } // // public bool IsGenericName(SyntaxNode node) // { // return node is GenericNameSyntax; // } public static bool IsNamedParameter (this SyntaxNode node) { return node.CheckParent<NameColonSyntax> (p => p.Name == node); }