예제 #1
0
            public override SyntaxNode VisitArgument(ArgumentSyntax node)
            {
                _cancellationToken.ThrowIfCancellationRequested();

                var newArgument = (ArgumentSyntax)base.VisitArgument(node);

                var argumentType = _semanticModel.GetTypeInfo(node.Expression).ConvertedType;

                if (argumentType != null &&
                    !IsPassedToDelegateCreationExpression(node, argumentType))
                {
                    var specAnalyzer             = new SpeculationAnalyzer(node.Expression, newArgument.Expression, _semanticModel, _cancellationToken);
                    var speculativeSemanticModel = specAnalyzer.SpeculativeSemanticModel;
                    var speculatedExpession      = specAnalyzer.ReplacedExpression;

                    bool wasCastAdded;
                    var  newArgumentExpression = speculatedExpession.CastIfPossible(argumentType, speculatedExpession.SpanStart, speculativeSemanticModel, out wasCastAdded);
                    if (wasCastAdded)
                    {
                        return(newArgument.WithExpression(newArgumentExpression));
                    }
                }

                return(newArgument);
            }
        private static bool CanRemoveTypeFromParameter(
            SyntaxNode node,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            // We reduce any the parameters that are contained inside ParameterList
            if (node != null && node.IsParentKind(SyntaxKind.ParameterList) && node.Parent.IsParentKind(SyntaxKind.ParenthesizedLambdaExpression))
            {
                var parameterSyntax = (ParameterSyntax)node;
                if (parameterSyntax.Type != null)
                {
                    var annotation = new SyntaxAnnotation();
                    var newParameterSyntax = parameterSyntax.WithType(null).WithAdditionalAnnotations(annotation);

                    var oldLambda = node.FirstAncestorOrSelf<ParenthesizedLambdaExpressionSyntax>();
                    var newLambda = oldLambda.ReplaceNode(parameterSyntax, newParameterSyntax);
                    var speculationAnalyzer = new SpeculationAnalyzer(oldLambda, newLambda, semanticModel, cancellationToken);
                    newParameterSyntax = (ParameterSyntax)speculationAnalyzer.ReplacedExpression.GetAnnotatedNodesAndTokens(annotation).First();

                    var oldSymbol = semanticModel.GetDeclaredSymbol(parameterSyntax, cancellationToken);
                    var newSymbol = speculationAnalyzer.SpeculativeSemanticModel.GetDeclaredSymbol(newParameterSyntax, cancellationToken);
                    if (oldSymbol != null &&
                        newSymbol != null &&
                        oldSymbol.Type == newSymbol.Type)
                    {
                        return !speculationAnalyzer.ReplacementChangesSemantics();
                    }
                }
            }

            return false;
        }
        private static bool CanRemoveTypeFromParameter(
            SyntaxNode node,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            // We reduce any the parameters that are contained inside ParameterList
            if (node != null && node.IsParentKind(SyntaxKind.ParameterList) && node.Parent.IsParentKind(SyntaxKind.ParenthesizedLambdaExpression))
            {
                var parameterSyntax = (ParameterSyntax)node;
                if (parameterSyntax.Type != null)
                {
                    var annotation         = new SyntaxAnnotation();
                    var newParameterSyntax = parameterSyntax.WithType(null).WithAdditionalAnnotations(annotation);

                    var oldLambda           = node.FirstAncestorOrSelf <ParenthesizedLambdaExpressionSyntax>();
                    var newLambda           = oldLambda.ReplaceNode(parameterSyntax, newParameterSyntax);
                    var speculationAnalyzer = new SpeculationAnalyzer(oldLambda, newLambda, semanticModel, cancellationToken);
                    newParameterSyntax = (ParameterSyntax)speculationAnalyzer.ReplacedExpression.GetAnnotatedNodesAndTokens(annotation).First();

                    var oldSymbol = semanticModel.GetDeclaredSymbol(parameterSyntax, cancellationToken);
                    var newSymbol = speculationAnalyzer.SpeculativeSemanticModel.GetDeclaredSymbol(newParameterSyntax, cancellationToken);
                    if (oldSymbol != null &&
                        newSymbol != null &&
                        oldSymbol.Type == newSymbol.Type)
                    {
                        return(!speculationAnalyzer.ReplacementChangesSemantics());
                    }
                }
            }

            return(false);
        }
        internal override IMethodSymbol GetDelegatingConstructor(
            SemanticDocument document,
            ObjectCreationExpressionSyntax objectCreation,
            INamedTypeSymbol namedType,
            ISet <IMethodSymbol> candidates,
            CancellationToken cancellationToken)
        {
            var model = document.SemanticModel;

            var oldNode = objectCreation
                          .AncestorsAndSelf(ascendOutOfTrivia: false)
                          .Where(node => SpeculationAnalyzer.CanSpeculateOnNode(node))
                          .LastOrDefault();

            var typeNameToReplace = objectCreation.Type;
            var newTypeName       = namedType.GenerateTypeSyntax();
            var newObjectCreation = objectCreation.WithType(newTypeName).WithAdditionalAnnotations(s_annotation);
            var newNode           = oldNode.ReplaceNode(objectCreation, newObjectCreation);

            var speculativeModel = SpeculationAnalyzer.CreateSpeculativeSemanticModelForNode(oldNode, newNode, model);

            if (speculativeModel != null)
            {
                newObjectCreation = (ObjectCreationExpressionSyntax)newNode.GetAnnotatedNodes(s_annotation).Single();
                var symbolInfo     = speculativeModel.GetSymbolInfo(newObjectCreation, cancellationToken);
                var parameterTypes = newObjectCreation.ArgumentList.Arguments.Select(
                    a => a.DetermineParameterType(speculativeModel, cancellationToken)).ToList();

                return(GenerateConstructorHelpers.GetDelegatingConstructor(
                           document, symbolInfo, candidates, namedType, parameterTypes));
            }

            return(null);
        }
        protected override SemanticModel GetSpeculativeSemanticModel(ref SyntaxNode nodeToSpeculate, SemanticModel originalSemanticModel, SyntaxNode originalNode)
        {
            var syntaxNodeToSpeculate = nodeToSpeculate;

            Contract.ThrowIfNull(syntaxNodeToSpeculate);
            Contract.ThrowIfFalse(SpeculationAnalyzer.CanSpeculateOnNode(nodeToSpeculate));
            return(SpeculationAnalyzer.CreateSpeculativeSemanticModelForNode(originalNode, syntaxNodeToSpeculate, (SemanticModel)originalSemanticModel));
        }
예제 #6
0
        private static bool CanReplaceWithDefaultLiteralSlow(DefaultExpressionSyntax defaultExpression, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(
                defaultExpression, s_defaultLiteralExpression, semanticModel,
                cancellationToken,
                skipVerificationForReplacedNode: false,
                failOnOverloadResolutionFailuresInOriginalCode: true);

            return(!speculationAnalyzer.ReplacementChangesSemantics());
        }
예제 #7
0
            public override SyntaxNode Visit(SyntaxNode node)
            {
                if (node == null)
                {
                    return(node);
                }

                if (_isNodeOrTokenOutsideSimplifySpans(node))
                {
                    if (_simplifyAllDescendants)
                    {
                        // One of the ancestor node is within a simplification span, but this node is outside all simplification spans.
                        // Add DontSimplifyAnnotation to node to ensure it doesn't get simplified.
                        return(node.WithAdditionalAnnotations(
                                   SimplificationHelpers.DontSimplifyAnnotation
                                   ));
                    }
                    else
                    {
                        return(node);
                    }
                }

                var savedSimplifyAllDescendants = _simplifyAllDescendants;

                _simplifyAllDescendants =
                    _simplifyAllDescendants || node.HasAnnotation(Simplifier.Annotation);

                if (!_insideSpeculatedNode && SpeculationAnalyzer.CanSpeculateOnNode(node))
                {
                    if (
                        _simplifyAllDescendants ||
                        node.DescendantNodesAndTokens(
                            s_containsAnnotations,
                            descendIntoTrivia: true
                            )
                        .Any(s_hasSimplifierAnnotation)
                        )
                    {
                        _insideSpeculatedNode = true;
                        var rewrittenNode = base.Visit(node);
                        _nodesAndTokensToReduce.Add(
                            new NodeOrTokenToReduce(rewrittenNode, _simplifyAllDescendants, node)
                            );
                        _insideSpeculatedNode = false;
                    }
                }
                else if (node.ContainsAnnotations || savedSimplifyAllDescendants)
                {
                    node = base.Visit(node);
                }

                _simplifyAllDescendants = savedSimplifyAllDescendants;
                return(node);
            }
        public static bool CanReplaceWithReducedName(this NameSyntax name, TypeSyntax reducedName, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(name, reducedName, semanticModel, cancellationToken);

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return(false);
            }

            return(CanReplaceWithReducedNameInContext(name, reducedName, semanticModel, cancellationToken));
        }
예제 #9
0
        /// <summary>
        /// Adds to <paramref name="targetType"/> if it does not contain an anonymous
        /// type and binds to the same type at the given <paramref name="position"/>.
        /// </summary>
        public static ExpressionSyntax CastIfPossible(
            this ExpressionSyntax expression,
            ITypeSymbol targetType,
            int position,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            if (targetType.ContainsAnonymousType())
            {
                return(expression);
            }

            if (targetType.IsSystemVoid())
            {
                return(expression);
            }

            if (targetType.Kind == SymbolKind.DynamicType)
            {
                targetType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Object);
            }

            var typeSyntax = targetType.GenerateTypeSyntax();
            var type       = semanticModel.GetSpeculativeTypeInfo(
                position,
                typeSyntax,
                SpeculativeBindingOption.BindAsTypeOrNamespace).Type;

            if (!targetType.Equals(type))
            {
                return(expression);
            }

            var castExpression = expression.Cast(targetType);

            // Ensure that inserting the cast doesn't change the semantics.
            var specAnalyzer             = new SpeculationAnalyzer(expression, castExpression, semanticModel, cancellationToken);
            var speculativeSemanticModel = specAnalyzer.SpeculativeSemanticModel;

            if (speculativeSemanticModel == null)
            {
                return(expression);
            }

            var speculatedCastExpression = (CastExpressionSyntax)specAnalyzer.ReplacedExpression;

            if (!CastSimplifier.IsUnnecessaryCast(speculatedCastExpression, speculativeSemanticModel, cancellationToken))
            {
                return(expression);
            }

            return(castExpression);
        }
        public static bool CanReplaceWithReducedName(
            this MemberAccessExpressionSyntax memberAccess,
            ExpressionSyntax reducedName,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            if (!IsThisOrTypeOrNamespace(memberAccess, semanticModel))
            {
                return(false);
            }

            var speculationAnalyzer = new SpeculationAnalyzer(memberAccess, reducedName, semanticModel, cancellationToken);

            if (!speculationAnalyzer.SymbolsForOriginalAndReplacedNodesAreCompatible() ||
                speculationAnalyzer.ReplacementChangesSemantics())
            {
                return(false);
            }

            if (WillConflictWithExistingLocal(memberAccess, reducedName))
            {
                return(false);
            }

            if (IsMemberAccessADynamicInvocation(memberAccess, semanticModel))
            {
                return(false);
            }

            if (memberAccess.AccessMethodWithDynamicArgumentInsideStructConstructor(semanticModel))
            {
                return(false);
            }

            if (memberAccess.Expression.Kind() == SyntaxKind.BaseExpression)
            {
                var enclosingNamedType = semanticModel.GetEnclosingNamedType(memberAccess.SpanStart, cancellationToken);
                var symbol             = semanticModel.GetSymbolInfo(memberAccess.Name).Symbol;
                if (enclosingNamedType != null &&
                    !enclosingNamedType.IsSealed &&
                    symbol != null &&
                    symbol.IsOverridable())
                {
                    return(false);
                }
            }

            var invalidTransformation1 = ParserWouldTreatExpressionAsCast(reducedName, memberAccess);

            return(!invalidTransformation1);
        }
        private static Conversion GetSpeculatedExpressionToOuterTypeConversion(ExpressionSyntax speculatedExpression, SpeculationAnalyzer speculationAnalyzer, CancellationToken cancellationToken)
        {
            var typeInfo   = speculationAnalyzer.SpeculativeSemanticModel.GetTypeInfo(speculatedExpression, cancellationToken);
            var conversion = speculationAnalyzer.SpeculativeSemanticModel.GetConversion(speculatedExpression, cancellationToken);

            if (!conversion.IsIdentity)
            {
                return(conversion);
            }

            var speculatedExpressionOuterType = GetOuterCastType(speculatedExpression, speculationAnalyzer.SpeculativeSemanticModel, out var discarded) ?? typeInfo.ConvertedType;

            if (speculatedExpressionOuterType == null)
            {
                return(default);
예제 #12
0
        private SyntaxToken SimplifyIdentifierToken(
            SyntaxToken token,
            SemanticModel semanticModel,
            OptionSet optionSet,
            CancellationToken cancellationToken)
        {
            var unescapedIdentifier = token.ValueText;

            var enclosingXmlNameAttr = token.GetAncestors(n => n is XmlNameAttributeSyntax).FirstOrDefault();

            // always escape keywords
            if (SyntaxFacts.GetKeywordKind(unescapedIdentifier) != SyntaxKind.None && enclosingXmlNameAttr == null)
            {
                return CreateNewIdentifierTokenFromToken(token, escape: true);
            }

            // Escape the Await Identifier if within the Single Line Lambda & Multi Line Context
            // and async method

            var parent = token.Parent;

            if (SyntaxFacts.GetContextualKeywordKind(unescapedIdentifier) == SyntaxKind.AwaitKeyword)
            {
                var enclosingLambdaExpression = parent.GetAncestorsOrThis(n => (n is SimpleLambdaExpressionSyntax || n is ParenthesizedLambdaExpressionSyntax)).FirstOrDefault();
                if (enclosingLambdaExpression != null)
                {
                    if (enclosingLambdaExpression is SimpleLambdaExpressionSyntax)
                    {
                        if (((SimpleLambdaExpressionSyntax)enclosingLambdaExpression).AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword)
                        {
                            return token;
                        }
                    }

                    if (enclosingLambdaExpression is ParenthesizedLambdaExpressionSyntax)
                    {
                        if (((ParenthesizedLambdaExpressionSyntax)enclosingLambdaExpression).AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword)
                        {
                            return token;
                        }
                    }
                }

                var enclosingMethodBlock = parent.GetAncestorsOrThis(n => n is MethodDeclarationSyntax).FirstOrDefault();

                if (enclosingMethodBlock != null && ((MethodDeclarationSyntax)enclosingMethodBlock).Modifiers.Any(n => n.Kind() == SyntaxKind.AsyncKeyword))
                {
                    return token;
                }
            }

            // within a query all contextual query keywords need to be escaped, even if they appear in a non query context.
            if (token.GetAncestors(n => n is QueryExpressionSyntax).Any())
            {
                switch (SyntaxFacts.GetContextualKeywordKind(unescapedIdentifier))
                {
                    case SyntaxKind.FromKeyword:
                    case SyntaxKind.WhereKeyword:
                    case SyntaxKind.SelectKeyword:
                    case SyntaxKind.GroupKeyword:
                    case SyntaxKind.IntoKeyword:
                    case SyntaxKind.OrderByKeyword:
                    case SyntaxKind.JoinKeyword:
                    case SyntaxKind.LetKeyword:
                    case SyntaxKind.InKeyword:
                    case SyntaxKind.OnKeyword:
                    case SyntaxKind.EqualsKeyword:
                    case SyntaxKind.ByKeyword:
                    case SyntaxKind.AscendingKeyword:
                    case SyntaxKind.DescendingKeyword:
                        return CreateNewIdentifierTokenFromToken(token, escape: true);
                }
            }

            var result = token.Kind() == SyntaxKind.IdentifierToken ? CreateNewIdentifierTokenFromToken(token, escape: false) : token;

            // we can't remove the escaping if this would change the semantic. This can happen in cases
            // where there are two attribute declarations one with and and one without the attribute
            // suffix.
            if (SyntaxFacts.IsAttributeName(parent))
            {
                var expression = (SimpleNameSyntax)parent;
                var newExpression = expression.WithIdentifier(result);
                var speculationAnalyzer = new SpeculationAnalyzer(expression, newExpression, semanticModel, cancellationToken);
                if (speculationAnalyzer.ReplacementChangesSemantics())
                {
                    return CreateNewIdentifierTokenFromToken(token, escape: true);
                }
            }

            // TODO: handle crefs and param names of xml doc comments.
            // crefs have the same escaping rules than csharp, param names do not allow escaping in Dev11, but 
            // we may want to change that for Roslyn (Bug 17984, " Could treat '@' specially in <param>, <typeparam>, etc")

            return result;
        }
        private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol?expressionType)
        {
            var cancellationToken = context.CancellationToken;
            var semanticModel     = context.SemanticModel;
            var syntaxTree        = semanticModel.SyntaxTree;

            var preference = context.GetCSharpAnalyzerOptions().PreferMethodGroupConversion;

            if (preference.Notification.Severity == ReportDiagnostic.Suppress)
            {
                // User doesn't care about this rule.
                return;
            }

            var anonymousFunction = (AnonymousFunctionExpressionSyntax)context.Node;

            // Syntax checks first.

            // Don't simplify static lambdas.  The user made them explicitly static to make it clear it must only cause
            // a single allocation for the cached delegate. If we get rid of the lambda (and thus the static-keyword) it
            // won't be clear anymore if the member-group-conversion allocation is cached or not.
            if (anonymousFunction.Modifiers.Any(SyntaxKind.StaticKeyword))
            {
                return;
            }

            if (!TryGetAnonymousFunctionInvocation(anonymousFunction, out var invocation, out var wasAwaited))
            {
                return;
            }

            // If we had an async function, but we didn't await the expression inside then we can't convert this. The
            // underlying value was wrapped into a task, and that won't work if directly referencing the function.
            if (wasAwaited != anonymousFunction.Modifiers.Any(SyntaxKind.AsyncKeyword))
            {
                return;
            }

            // We have to have an invocation in the lambda like `() => X()` or `() => expr.X()`.
            var invokedExpression = invocation.Expression;

            if (invokedExpression is not SimpleNameSyntax and not MemberAccessExpressionSyntax)
            {
                return;
            }

            // lambda and invocation have to agree on number of parameters.
            var parameters = GetParameters(anonymousFunction);

            if (parameters.Count != invocation.ArgumentList.Arguments.Count)
            {
                return;
            }

            // parameters must be passed 1:1 from lambda to invocation.
            for (int i = 0, n = parameters.Count; i < n; i++)
            {
                var parameter = parameters[i];
                var argument  = invocation.ArgumentList.Arguments[i];

                if (argument.Expression is not IdentifierNameSyntax argumentIdentifier)
                {
                    return;
                }

                if (parameter.Identifier.ValueText != argumentIdentifier.Identifier.ValueText)
                {
                    return;
                }
            }

            // if we have `() => new C().X()` then converting to `new C().X` very much changes the meaning.
            if (MayHaveSideEffects(invokedExpression))
            {
                return;
            }

            // Looks like a reasonable candidate to simplify.  Now switch to semantics to check for sure.

            if (CSharpSemanticFacts.Instance.IsInExpressionTree(semanticModel, anonymousFunction, expressionType, cancellationToken))
            {
                return;
            }

            // If we have `object obj = x => Goo(x);` we don't want to simplify.  The compiler warns if you write
            // `object obj = Goo;` because of the conversion to a non-delegate type. While we could insert a cast here
            // to make this work, that goes against the spirit of this analyzer/fixer just removing code.
            var lambdaTypeInfo = semanticModel.GetTypeInfo(anonymousFunction, cancellationToken);

            if (lambdaTypeInfo.ConvertedType == null || lambdaTypeInfo.ConvertedType.SpecialType is SpecialType.System_Object)
            {
                return;
            }

            var lambdaSymbolInfo = semanticModel.GetSymbolInfo(anonymousFunction, cancellationToken);

            if (lambdaSymbolInfo.Symbol is not IMethodSymbol lambdaMethod)
            {
                return;
            }

            var invokedSymbolInfo = semanticModel.GetSymbolInfo(invokedExpression, cancellationToken);

            if (invokedSymbolInfo.Symbol is not IMethodSymbol invokedMethod)
            {
                return;
            }

            // If we're calling a generic method, we have to have supplied type arguments.  They cannot be inferred once
            // we remove the arguments during simplification.
            var invokedTypeArguments = invokedExpression.GetRightmostName() is GenericNameSyntax genericName
                ? genericName.TypeArgumentList.Arguments
                : default;

            if (invokedMethod.TypeArguments.Length != invokedTypeArguments.Count)
            {
                return;
            }

            // Methods have to be complimentary.  That means the same number of parameters, with proper
            // co-contravariance for the parameters and return type.
            if (lambdaMethod.Parameters.Length != invokedMethod.Parameters.Length)
            {
                return;
            }

            var compilation = semanticModel.Compilation;

            // Must be able to convert the invoked method return type to the lambda's return type.
            if (!IsIdentityOrImplicitConversion(compilation, invokedMethod.ReturnType, lambdaMethod.ReturnType))
            {
                return;
            }

            for (int i = 0, n = lambdaMethod.Parameters.Length; i < n; i++)
            {
                var lambdaParameter  = lambdaMethod.Parameters[i];
                var invokedParameter = invokedMethod.Parameters[i];

                if (lambdaParameter.RefKind != invokedParameter.RefKind)
                {
                    return;
                }

                // All the lambda parameters must be convertible to the invoked method parameters.
                if (!IsIdentityOrImplicitConversion(compilation, lambdaParameter.Type, invokedParameter.Type))
                {
                    return;
                }
            }

            // Semantically, this looks good to go.  Now, do an actual speculative replacement to ensure that the
            // non-invoked method reference refers to the same method symbol, and that it converts to the same type that
            // the lambda was.
            var analyzer = new SpeculationAnalyzer(anonymousFunction, invokedExpression, semanticModel, cancellationToken);

            var rewrittenExpression    = analyzer.ReplacedExpression;
            var rewrittenSemanticModel = analyzer.SpeculativeSemanticModel;

            var rewrittenSymbolInfo = rewrittenSemanticModel.GetSymbolInfo(rewrittenExpression, cancellationToken);

            if (rewrittenSymbolInfo.Symbol is not IMethodSymbol rewrittenMethod ||
                !invokedMethod.Equals(rewrittenMethod))
            {
                return;
            }

            var rewrittenConvertedType = rewrittenSemanticModel.GetTypeInfo(rewrittenExpression, cancellationToken).ConvertedType;

            if (!lambdaTypeInfo.ConvertedType.Equals(rewrittenConvertedType))
            {
                return;
            }

            if (OverloadsChanged(
                    semanticModel, anonymousFunction.GetRequiredParent(),
                    rewrittenSemanticModel, rewrittenExpression.GetRequiredParent(), cancellationToken))
            {
                return;
            }

            var startReportSpan = TextSpan.FromBounds(anonymousFunction.SpanStart, invokedExpression.SpanStart);
            var endReportSpan   = TextSpan.FromBounds(invokedExpression.Span.End, anonymousFunction.Span.End);

            context.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags(
                                         Descriptor,
                                         syntaxTree.GetLocation(startReportSpan),
                                         preference.Notification.Severity,
                                         additionalLocations: ImmutableArray.Create(anonymousFunction.GetLocation()),
                                         additionalUnnecessaryLocations: ImmutableArray.Create(
                                             syntaxTree.GetLocation(startReportSpan),
                                             syntaxTree.GetLocation(endReportSpan))));
        }
            public override SyntaxNode VisitArgument(ArgumentSyntax node)
            {
                _cancellationToken.ThrowIfCancellationRequested();

                var newArgument = (ArgumentSyntax)base.VisitArgument(node);

                var argumentType = _semanticModel.GetTypeInfo(node.Expression).ConvertedType;
                if (argumentType != null &&
                    !IsPassedToDelegateCreationExpression(node, argumentType))
                {
                    var specAnalyzer = new SpeculationAnalyzer(node.Expression, newArgument.Expression, _semanticModel, _cancellationToken);
                    var speculativeSemanticModel = specAnalyzer.SpeculativeSemanticModel;
                    var speculatedExpession = specAnalyzer.ReplacedExpression;

                    bool wasCastAdded;
                    var newArgumentExpression = speculatedExpession.CastIfPossible(argumentType, speculatedExpession.SpanStart, speculativeSemanticModel, out wasCastAdded);
                    if (wasCastAdded)
                    {
                        return newArgument.WithExpression(newArgumentExpression);
                    }
                }

                return newArgument;
            }
예제 #15
0
        private static bool IsUnnecessaryCast(
            ExpressionSyntax castNode, ExpressionSyntax castedExpressionNode,
            SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(castNode,
                                                              castedExpressionNode, semanticModel, cancellationToken,
                                                              skipVerificationForReplacedNode: true, failOnOverloadResolutionFailuresInOriginalCode: true);

            // First, check to see if the node ultimately parenting this cast has any
            // syntax errors. If so, we bail.
            if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics)
            {
                return(false);
            }

            var castTypeInfo = semanticModel.GetTypeInfo(castNode, cancellationToken);
            var castType     = castTypeInfo.Type;

            // Case:
            // 1 . Console.WriteLine(await (dynamic)task); Any Dynamic Cast will not be removed.
            if (castType == null || castType.Kind == SymbolKind.DynamicType || castType.IsErrorType())
            {
                return(false);
            }

            var expressionTypeInfo = semanticModel.GetTypeInfo(castedExpressionNode, cancellationToken);
            var expressionType     = expressionTypeInfo.Type;

            if (EnumCastDefinitelyCantBeRemoved(castNode, expressionType, castType))
            {
                return(false);
            }

            // We do not remove any cast on
            // 1. Dynamic Expressions
            // 2. If there is any other argument which is dynamic
            // 3. Dynamic Invocation
            // 4. Assignment to dynamic
            if ((expressionType != null &&
                 (expressionType.IsErrorType() ||
                  expressionType.Kind == SymbolKind.DynamicType)) ||
                IsDynamicInvocation(castNode, semanticModel, cancellationToken) ||
                IsDynamicAssignment(castNode, semanticModel, cancellationToken))
            {
                return(false);
            }

            if (PointerCastDefinitelyCantBeRemoved(castNode, castedExpressionNode))
            {
                return(false);
            }

            if (CastPassedToParamsArrayDefinitelyCantBeRemoved(castNode, castType, semanticModel, cancellationToken))
            {
                return(false);
            }

            // A casts to object can always be removed from an expression inside of an interpolation, since it'll be converted to object
            // in order to call string.Format(...) anyway.
            if (castType?.SpecialType == SpecialType.System_Object &&
                castNode.WalkUpParentheses().IsParentKind(SyntaxKind.Interpolation))
            {
                return(true);
            }

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return(false);
            }

            var expressionToCastType = semanticModel.ClassifyConversion(castNode.SpanStart, castedExpressionNode, castType, isExplicitInSource: true);
            var outerType            = GetOuterCastType(castNode, semanticModel, out var parentIsOrAsExpression) ?? castTypeInfo.ConvertedType;

            // Simple case: If the conversion from the inner expression to the cast type is identity,
            // the cast can be removed.
            if (expressionToCastType.IsIdentity)
            {
                // Simple case: Is this an identity cast to another cast? If so, we're safe to remove it.
                if (castedExpressionNode.WalkDownParentheses().IsKind(SyntaxKind.CastExpression))
                {
                    return(true);
                }

                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      (object)x == "Hi!"
                if (IsRequiredCastForReferenceEqualityComparison(outerType, castNode, semanticModel, out var other))
                {
                    var otherToOuterType = semanticModel.ClassifyConversion(other, outerType);
                    if (otherToOuterType.IsImplicit && otherToOuterType.IsReference)
                    {
                        return(false);
                    }
                }

                if (SameSizedFloatingPointCastMustBePreserved(
                        semanticModel, castNode, castedExpressionNode,
                        expressionType, castType, cancellationToken))
                {
                    return(false);
                }

                return(true);
            }

            Debug.Assert(!expressionToCastType.IsIdentity);
            if (expressionToCastType.IsExplicit)
            {
                // Explicit reference conversions can cause an exception or data loss, hence can never be removed.
                if (expressionToCastType.IsReference)
                {
                    return(false);
                }

                // Unboxing conversions can cause a null ref exception, hence can never be removed.
                if (expressionToCastType.IsUnboxing)
                {
                    return(false);
                }

                // Don't remove any explicit numeric casts.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                if (expressionToCastType.IsNumeric)
                {
                    return(false);
                }
            }

            if (expressionToCastType.IsPointer || expressionToCastType.IsIntPtr)
            {
                // Don't remove any non-identity pointer or IntPtr conversions.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                return(expressionType != null && expressionType.Equals(outerType));
            }

            if (expressionToCastType.IsInterpolatedString)
            {
                // interpolation casts are necessary to preserve semantics if our destination type is not itself
                // FormattableString or some interface of FormattableString.

                return(castType.Equals(castTypeInfo.ConvertedType) ||
                       ImmutableArray <ITypeSymbol> .CastUp(castType.AllInterfaces).Contains(castTypeInfo.ConvertedType));
            }

            if (castedExpressionNode.WalkDownParentheses().IsKind(SyntaxKind.DefaultLiteralExpression) &&
                !castType.Equals(outerType) &&
                outerType.IsNullable())
            {
                // We have a cast like `(T?)(X)default`. We can't remove the inner cast as it effects what value
                // 'default' means in this context.
                return(false);
            }

            if (parentIsOrAsExpression)
            {
                // Note: speculationAnalyzer.ReplacementChangesSemantics() ensures that the parenting is or as expression are not broken.
                // Here we just need to ensure that the original cast expression doesn't invoke a user defined operator.
                return(!expressionToCastType.IsUserDefined);
            }

            if (outerType != null)
            {
                var castToOuterType       = semanticModel.ClassifyConversion(castNode.SpanStart, castNode, outerType);
                var expressionToOuterType = GetSpeculatedExpressionToOuterTypeConversion(speculationAnalyzer.ReplacedExpression, speculationAnalyzer, cancellationToken);

                // if the conversion to the outer type doesn't exist, then we shouldn't offer, except for anonymous functions which can't be reasoned about the same way (see below)
                if (!expressionToOuterType.Exists && !expressionToOuterType.IsAnonymousFunction)
                {
                    return(false);
                }

                // CONSIDER: Anonymous function conversions cannot be compared from different semantic models as lambda symbol comparison requires syntax tree equality. Should this be a compiler bug?
                // For now, just revert back to computing expressionToOuterType using the original semantic model.
                if (expressionToOuterType.IsAnonymousFunction)
                {
                    expressionToOuterType = semanticModel.ClassifyConversion(castNode.SpanStart, castedExpressionNode, outerType);
                }

                // If there is an user-defined conversion from the expression to the cast type or the cast
                // to the outer type, we need to make sure that the same user-defined conversion will be
                // called if the cast is removed.
                if (castToOuterType.IsUserDefined || expressionToCastType.IsUserDefined)
                {
                    return(!expressionToOuterType.IsExplicit &&
                           (HaveSameUserDefinedConversion(expressionToCastType, expressionToOuterType) ||
                            HaveSameUserDefinedConversion(castToOuterType, expressionToOuterType)) &&
                           UserDefinedConversionIsAllowed(castNode, semanticModel));
                }
                else if (expressionToOuterType.IsUserDefined)
                {
                    return(false);
                }

                if (expressionToCastType.IsExplicit &&
                    expressionToOuterType.IsExplicit)
                {
                    return(false);
                }
                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      x == (object)"Hi!"
                if (expressionToCastType.IsImplicit && expressionToCastType.IsReference &&
                    castToOuterType.IsIdentity &&
                    IsRequiredCastForReferenceEqualityComparison(outerType, castNode, semanticModel, out var other))
                {
                    return(false);
                }

                // If the conversion from the expression to the cast type is implicit numeric or constant
                // and the conversion from the expression to the outer type is identity, we'll go ahead
                // and remove the cast.
                if (expressionToOuterType.IsIdentity &&
                    expressionToCastType.IsImplicit &&
                    (expressionToCastType.IsNumeric || expressionToCastType.IsConstantExpression))
                {
                    // Some implicit numeric conversions can cause loss of precision and must not be removed.
                    return(!IsRequiredImplicitNumericConversion(expressionType, castType));
                }

                if (!castToOuterType.IsBoxing &&
                    castToOuterType == expressionToOuterType)
                {
                    if (castToOuterType.IsNullable)
                    {
                        // Even though both the nullable conversions (castToOuterType and expressionToOuterType) are equal, we can guarantee no data loss only if there is an
                        // implicit conversion from expression type to cast type and expression type is non-nullable. For example, consider the cast removal "(float?)" for below:

                        // Console.WriteLine((int)(float?)(int?)2147483647); // Prints -2147483648

                        // castToOuterType:         ExplicitNullable
                        // expressionToOuterType:   ExplicitNullable
                        // expressionToCastType:    ImplicitNullable

                        // We should not remove the cast to "float?".
                        // However, cast to "int?" is unnecessary and should be removable.
                        return(expressionToCastType.IsImplicit && !expressionType.IsNullable());
                    }
                    else if (expressionToCastType.IsImplicit && expressionToCastType.IsNumeric && !castToOuterType.IsIdentity)
                    {
                        // Some implicit numeric conversions can cause loss of precision and must not be removed.
                        return(!IsRequiredImplicitNumericConversion(expressionType, castType));
                    }

                    return(true);
                }

                if (castToOuterType.IsIdentity &&
                    !expressionToCastType.IsUnboxing &&
                    expressionToCastType == expressionToOuterType)
                {
                    return(true);
                }

                // Special case: It's possible to have useless casts inside delegate creation expressions.
                // For example: new Func<string, bool>((Predicate<object>)(y => true)).
                if (IsInDelegateCreationExpression(castNode, semanticModel))
                {
                    if (expressionToCastType.IsAnonymousFunction && expressionToOuterType.IsAnonymousFunction)
                    {
                        return(!speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(castedExpressionNode, speculationAnalyzer.ReplacedExpression));
                    }

                    if (expressionToCastType.IsMethodGroup && expressionToOuterType.IsMethodGroup)
                    {
                        return(true);
                    }
                }

                // Case :
                // 1. IList<object> y = (IList<dynamic>)new List<object>()
                if (expressionToCastType.IsExplicit && castToOuterType.IsExplicit && expressionToOuterType.IsImplicit)
                {
                    // If both expressionToCastType and castToOuterType are numeric, then this is a required cast as one of the conversions leads to loss of precision.
                    // Cast removal can change program behavior.
                    return(!(expressionToCastType.IsNumeric && castToOuterType.IsNumeric));
                }

                // Case :
                // 2. object y = (ValueType)1;
                if (expressionToCastType.IsBoxing && expressionToOuterType.IsBoxing && castToOuterType.IsImplicit)
                {
                    return(true);
                }

                // Case :
                // 3. object y = (NullableValueType)null;
                if ((!castToOuterType.IsBoxing || expressionToCastType.IsNullLiteral) &&
                    castToOuterType.IsImplicit &&
                    expressionToCastType.IsImplicit &&
                    expressionToOuterType.IsImplicit)
                {
                    if (expressionToOuterType.IsAnonymousFunction)
                    {
                        return(expressionToCastType.IsAnonymousFunction &&
                               !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(castedExpressionNode, speculationAnalyzer.ReplacedExpression));
                    }

                    return(true);
                }
            }

            return(false);
        }
예제 #16
0
        private static SyntaxToken SimplifyIdentifierToken(
            SyntaxToken token,
            SemanticModel semanticModel,
            OptionSet optionSet,
            CancellationToken cancellationToken)
        {
            var unescapedIdentifier = token.ValueText;

            var enclosingXmlNameAttr = token.GetAncestors(n => n is XmlNameAttributeSyntax).FirstOrDefault();

            // always escape keywords
            if (SyntaxFacts.GetKeywordKind(unescapedIdentifier) != SyntaxKind.None && enclosingXmlNameAttr == null)
            {
                return(CreateNewIdentifierTokenFromToken(token, escape: true));
            }

            // Escape the Await Identifier if within the Single Line Lambda & Multi Line Context
            // and async method

            var parent = token.Parent;

            if (SyntaxFacts.GetContextualKeywordKind(unescapedIdentifier) == SyntaxKind.AwaitKeyword)
            {
                var enclosingLambdaExpression = parent.GetAncestorsOrThis(n => (n is SimpleLambdaExpressionSyntax || n is ParenthesizedLambdaExpressionSyntax)).FirstOrDefault();
                if (enclosingLambdaExpression != null)
                {
                    if (enclosingLambdaExpression is SimpleLambdaExpressionSyntax simpleLambda)
                    {
                        if (simpleLambda.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword)
                        {
                            return(token);
                        }
                    }

                    if (enclosingLambdaExpression is ParenthesizedLambdaExpressionSyntax parenLamdba)
                    {
                        if (parenLamdba.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword)
                        {
                            return(token);
                        }
                    }
                }

                var enclosingMethodBlock = parent.GetAncestorsOrThis(n => n is MethodDeclarationSyntax).FirstOrDefault();

                if (enclosingMethodBlock != null && ((MethodDeclarationSyntax)enclosingMethodBlock).Modifiers.Any(n => n.Kind() == SyntaxKind.AsyncKeyword))
                {
                    return(token);
                }
            }

            // within a query all contextual query keywords need to be escaped, even if they appear in a non query context.
            if (token.GetAncestors(n => n is QueryExpressionSyntax).Any())
            {
                switch (SyntaxFacts.GetContextualKeywordKind(unescapedIdentifier))
                {
                case SyntaxKind.FromKeyword:
                case SyntaxKind.WhereKeyword:
                case SyntaxKind.SelectKeyword:
                case SyntaxKind.GroupKeyword:
                case SyntaxKind.IntoKeyword:
                case SyntaxKind.OrderByKeyword:
                case SyntaxKind.JoinKeyword:
                case SyntaxKind.LetKeyword:
                case SyntaxKind.InKeyword:
                case SyntaxKind.OnKeyword:
                case SyntaxKind.EqualsKeyword:
                case SyntaxKind.ByKeyword:
                case SyntaxKind.AscendingKeyword:
                case SyntaxKind.DescendingKeyword:
                    return(CreateNewIdentifierTokenFromToken(token, escape: true));
                }
            }

            var result = token.Kind() == SyntaxKind.IdentifierToken ? CreateNewIdentifierTokenFromToken(token, escape: false) : token;

            // we can't remove the escaping if this would change the semantic. This can happen in cases
            // where there are two attribute declarations: one with and one without the attribute
            // suffix.
            if (SyntaxFacts.IsAttributeName(parent))
            {
                var expression          = (SimpleNameSyntax)parent;
                var newExpression       = expression.WithIdentifier(result);
                var speculationAnalyzer = new SpeculationAnalyzer(expression, newExpression, semanticModel, cancellationToken);
                if (speculationAnalyzer.ReplacementChangesSemantics())
                {
                    return(CreateNewIdentifierTokenFromToken(token, escape: true));
                }
            }

            // TODO: handle crefs and param names of xml doc comments.
            // crefs have the same escaping rules than csharp, param names do not allow escaping in Dev11, but
            // we may want to change that for Roslyn (Bug 17984, " Could treat '@' specially in <param>, <typeparam>, etc")

            return(result);
        }
        public static bool IsUnnecessaryCast(this CastExpressionSyntax cast, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(cast,
                                                              cast.Expression, semanticModel, cancellationToken,
                                                              skipVerificationForReplacedNode: true, failOnOverloadResolutionFailuresInOriginalCode: true);

            // First, check to see if the node ultimately parenting this cast has any
            // syntax errors. If so, we bail.
            if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics)
            {
                return(false);
            }

            var castTypeInfo = semanticModel.GetTypeInfo(cast, cancellationToken);
            var castType     = castTypeInfo.Type;

            // Case:
            // 1 . Console.WriteLine(await (dynamic)task); Any Dynamic Cast will not be removed.
            if (castType == null || castType.Kind == SymbolKind.DynamicType || castType.IsErrorType())
            {
                return(false);
            }

            var expressionTypeInfo = semanticModel.GetTypeInfo(cast.Expression, cancellationToken);
            var expressionType     = expressionTypeInfo.Type;

            // We do not remove any cast on
            // 1. Dynamic Expressions
            // 2. If there is any other argument which is dynamic
            // 3. Dynamic Invocation
            if ((expressionType != null &&
                 (expressionType.IsErrorType() ||
                  expressionType.Kind == SymbolKind.DynamicType)) ||
                IsDynamicInvocation(cast, semanticModel, cancellationToken))
            {
                return(false);
            }

            if (PointerCastDefinitelyCantBeRemoved(cast))
            {
                return(false);
            }

            if (CastPassedToParamsArrayDefinitelyCantBeRemoved(cast, castType, semanticModel, cancellationToken))
            {
                return(false);
            }

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return(false);
            }

            var expressionToCastType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, castType, isExplicitInSource: true);

            bool parentIsOrAsExpression;
            var  outerType = GetOuterCastType(cast, semanticModel, out parentIsOrAsExpression) ?? castTypeInfo.ConvertedType;

            // Simple case: If the conversion from the inner expression to the cast type is identity,
            // the cast can be removed.
            if (expressionToCastType.IsIdentity)
            {
                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      (object)x == "Hi!"
                ExpressionSyntax other;
                if (IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    var otherToOuterType = semanticModel.ClassifyConversion(other, outerType);
                    if (otherToOuterType.IsImplicit && otherToOuterType.IsReference)
                    {
                        return(false);
                    }
                }

                return(true);
            }

            if (parentIsOrAsExpression)
            {
                // Note: speculationAnalyzer.ReplacementChangesSemantics() ensures that the parenting is or as expression are not broken.
                // Here we just need to ensure that the original cast expression doesn't invoke a user defined operator.
                return(!expressionToCastType.IsUserDefined);
            }

            if (outerType != null)
            {
                var castToOuterType       = semanticModel.ClassifyConversion(cast.SpanStart, cast, outerType);
                var expressionToOuterType = GetSpeculatedExpressionToOuterTypeConversion(speculationAnalyzer.ReplacedExpression, speculationAnalyzer, cancellationToken);

                // CONSIDER: Anonymous function conversions cannot be compared from different semantic models as lambda symbol comparison requires syntax tree equality. Should this be a compiler bug?
                // For now, just revert back to computing expressionToOuterType using the original semantic model.
                if (expressionToOuterType.IsAnonymousFunction)
                {
                    expressionToOuterType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, outerType);
                }

                // If there is an user-defined conversion from the expression to the cast type or the cast
                // to the outer type, we need to make sure that the same user-defined conversion will be
                // called if the cast is removed.
                if (castToOuterType.IsUserDefined || expressionToCastType.IsUserDefined)
                {
                    return(!expressionToOuterType.IsExplicit &&
                           (HaveSameUserDefinedConversion(expressionToCastType, expressionToOuterType) ||
                            HaveSameUserDefinedConversion(castToOuterType, expressionToOuterType)) &&
                           UserDefinedConversionIsAllowed(cast, semanticModel));
                }
                else if (expressionToOuterType.IsUserDefined)
                {
                    return(false);
                }

                if (expressionToCastType.IsExplicit &&
                    expressionToOuterType.IsExplicit)
                {
                    return(false);
                }

                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      x == (object)"Hi!"
                ExpressionSyntax other;
                if (expressionToCastType.IsImplicit && expressionToCastType.IsReference &&
                    castToOuterType.IsIdentity &&
                    IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    return(false);
                }

                // If the conversion from the expression to the cast type is implicit numeric or constant
                // and the conversion from the expression to the outer type is identity, we'll go ahead
                // and remove the cast.
                if (expressionToOuterType.IsIdentity &&
                    expressionToCastType.IsImplicit &&
                    (expressionToCastType.IsNumeric || expressionToCastType.IsConstantExpression))
                {
                    return(true);
                }

                if (!castToOuterType.IsBoxing &&
                    castToOuterType == expressionToOuterType)
                {
                    if (castToOuterType.IsNullable)
                    {
                        // Even though both the nullable conversions (castToOuterType and expressionToOuterType) are equal, we can guarantee no data loss only if there is an
                        // implicit conversion from expression type to cast type and expression type is non-nullable. For example, consider the cast removal "(float?)" for below:

                        // Console.WriteLine((int)(float?)(int?)2147483647); // Prints -2147483648

                        // castToOuterType:         ExplicitNullable
                        // expressionToOuterType:   ExplicitNullable
                        // expressionToCastType:    ImplicitNullable

                        // We should not remove the cast to "float?".
                        // However, cast to "int?" is unnecessary and should be removable.
                        return(expressionToCastType.IsImplicit && ((ITypeSymbol)expressionType.OriginalDefinition).SpecialType != SpecialType.System_Nullable_T);
                    }
                    else if (expressionToCastType.IsImplicit && expressionToCastType.IsNumeric && !castToOuterType.IsIdentity)
                    {
                        // Some implicit numeric conversions can cause loss of precision and must not be removed.
                        return(!IsRequiredImplicitNumericConversion(expressionType, castType));
                    }

                    return(true);
                }

                if (castToOuterType.IsIdentity &&
                    !expressionToCastType.IsUnboxing &&
                    expressionToCastType == expressionToOuterType)
                {
                    return(true);
                }

                // Special case: It's possible to have useless casts inside delegate creation expressions.
                // For example: new Func<string, bool>((Predicate<object>)(y => true)).
                if (IsInDelegateCreationExpression(cast, semanticModel))
                {
                    if (expressionToCastType.IsAnonymousFunction && expressionToOuterType.IsAnonymousFunction)
                    {
                        return(!speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression));
                    }

                    if (expressionToCastType.IsMethodGroup && expressionToOuterType.IsMethodGroup)
                    {
                        return(true);
                    }
                }

                // Case :
                // 1. IList<object> y = (IList<dynamic>)new List<object>()
                if (expressionToCastType.IsExplicit && castToOuterType.IsExplicit && expressionToOuterType.IsImplicit)
                {
                    return(true);
                }

                // Case :
                // 2. object y = (ValueType)1;
                if (expressionToCastType.IsBoxing && expressionToOuterType.IsBoxing && castToOuterType.IsImplicit)
                {
                    return(true);
                }

                // Case :
                // 3. object y = (NullableValueType)null;
                if ((!castToOuterType.IsBoxing || expressionToCastType.IsNullLiteral) &&
                    castToOuterType.IsImplicit &&
                    expressionToCastType.IsImplicit &&
                    expressionToOuterType.IsImplicit)
                {
                    if (expressionToOuterType.IsAnonymousFunction)
                    {
                        return(expressionToCastType.IsAnonymousFunction &&
                               !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression));
                    }

                    return(true);
                }

                // case :
                // 4. baseType x;
                //    baseType y = (DerivedType)x;
                if (expressionToOuterType.IsIdentity &&
                    castToOuterType.IsImplicit &&
                    castToOuterType.IsReference)
                {
                    return(true);
                }
            }

            return(false);
        }
        private static Conversion GetSpeculatedExpressionToOuterTypeConversion(ExpressionSyntax speculatedExpression, SpeculationAnalyzer speculationAnalyzer, CancellationToken cancellationToken)
        {
            var typeInfo = speculationAnalyzer.SpeculativeSemanticModel.GetTypeInfo(speculatedExpression, cancellationToken);
            var conversion = speculationAnalyzer.SpeculativeSemanticModel.GetConversion(speculatedExpression, cancellationToken);

            if (!conversion.IsIdentity)
            {
                return conversion;
            }

            bool discarded;
            var speculatedExpressionOuterType = GetOuterCastType(speculatedExpression, speculationAnalyzer.SpeculativeSemanticModel, out discarded) ?? typeInfo.ConvertedType;
            if (speculatedExpressionOuterType == null)
            {
                return default(Conversion);
            }

            return speculationAnalyzer.SpeculativeSemanticModel.ClassifyConversion(speculatedExpression, speculatedExpressionOuterType);
        }
        public static bool IsUnnecessaryCast(this CastExpressionSyntax cast, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(cast,
                cast.Expression, semanticModel, cancellationToken,
                skipVerificationForReplacedNode: true, failOnOverloadResolutionFailuresInOriginalCode: true);

            // First, check to see if the node ultimately parenting this cast has any
            // syntax errors. If so, we bail.
            if (speculationAnalyzer.SemanticRootOfOriginalExpression.ContainsDiagnostics)
            {
                return false;
            }

            var castTypeInfo = semanticModel.GetTypeInfo(cast, cancellationToken);
            var castType = castTypeInfo.Type;

            // Case:
            // 1 . Console.WriteLine(await (dynamic)task); Any Dynamic Cast will not be removed.
            if (castType == null || castType.Kind == SymbolKind.DynamicType || castType.IsErrorType())
            {
                return false;
            }

            var expressionTypeInfo = semanticModel.GetTypeInfo(cast.Expression, cancellationToken);
            var expressionType = expressionTypeInfo.Type;

            // We do not remove any cast on 
            // 1. Dynamic Expressions
            // 2. If there is any other argument which is dynamic
            // 3. Dynamic Invocation
            if ((expressionType != null &&
                (expressionType.IsErrorType() ||
                 expressionType.Kind == SymbolKind.DynamicType)) ||
                IsDynamicInvocation(cast, semanticModel, cancellationToken))
            {
                return false;
            }

            if (PointerCastDefinitelyCantBeRemoved(cast))
            {
                return false;
            }

            if (CastPassedToParamsArrayDefinitelyCantBeRemoved(cast, castType, semanticModel, cancellationToken))
            {
                return false;
            }

            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return false;
            }

            var expressionToCastType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, castType, isExplicitInSource: true);

            bool parentIsOrAsExpression;
            var outerType = GetOuterCastType(cast, semanticModel, out parentIsOrAsExpression) ?? castTypeInfo.ConvertedType;

            // Simple case: If the conversion from the inner expression to the cast type is identity,
            // the cast can be removed.
            if (expressionToCastType.IsIdentity)
            {
                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      (object)x == "Hi!"
                ExpressionSyntax other;
                if (IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    var otherToOuterType = semanticModel.ClassifyConversion(other, outerType);
                    if (otherToOuterType.IsImplicit && otherToOuterType.IsReference)
                    {
                        return false;
                    }
                }

                return true;
            }
            else if (expressionToCastType.IsExplicit && expressionToCastType.IsReference)
            {
                // Explicit reference conversions can cause an exception or data loss, hence can never be removed.
                return false;
            }
            else if (expressionToCastType.IsExplicit && expressionToCastType.IsNumeric && IsInExplicitCheckedOrUncheckedContext(cast))
            {
                // Don't remove any explicit numeric casts in explicit checked/unchecked context.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                return false;
            }
            else if (expressionToCastType.IsPointer)
            {
                // Don't remove any non-identity pointer conversions.
                // https://github.com/dotnet/roslyn/issues/2987 tracks improving on this conservative approach.
                return expressionType != null && expressionType.Equals(outerType);
            }

            if (parentIsOrAsExpression)
            {
                // Note: speculationAnalyzer.ReplacementChangesSemantics() ensures that the parenting is or as expression are not broken.
                // Here we just need to ensure that the original cast expression doesn't invoke a user defined operator.
                return !expressionToCastType.IsUserDefined;
            }

            if (outerType != null)
            {
                var castToOuterType = semanticModel.ClassifyConversion(cast.SpanStart, cast, outerType);
                var expressionToOuterType = GetSpeculatedExpressionToOuterTypeConversion(speculationAnalyzer.ReplacedExpression, speculationAnalyzer, cancellationToken);

                // CONSIDER: Anonymous function conversions cannot be compared from different semantic models as lambda symbol comparison requires syntax tree equality. Should this be a compiler bug?
                // For now, just revert back to computing expressionToOuterType using the original semantic model.
                if (expressionToOuterType.IsAnonymousFunction)
                {
                    expressionToOuterType = semanticModel.ClassifyConversion(cast.SpanStart, cast.Expression, outerType);
                }

                // If there is an user-defined conversion from the expression to the cast type or the cast
                // to the outer type, we need to make sure that the same user-defined conversion will be 
                // called if the cast is removed.
                if (castToOuterType.IsUserDefined || expressionToCastType.IsUserDefined)
                {
                    return !expressionToOuterType.IsExplicit &&
                        (HaveSameUserDefinedConversion(expressionToCastType, expressionToOuterType) ||
                         HaveSameUserDefinedConversion(castToOuterType, expressionToOuterType)) &&
                         UserDefinedConversionIsAllowed(cast, semanticModel);
                }
                else if (expressionToOuterType.IsUserDefined)
                {
                    return false;
                }

                if (expressionToCastType.IsExplicit &&
                    expressionToOuterType.IsExplicit)
                {
                    return false;
                }

                // Required explicit cast for reference comparison.
                // Cast removal causes warning CS0252 (Possible unintended reference comparison).
                //      object x = string.Intern("Hi!");
                //      x == (object)"Hi!"
                ExpressionSyntax other;
                if (expressionToCastType.IsImplicit && expressionToCastType.IsReference &&
                    castToOuterType.IsIdentity &&
                    IsRequiredCastForReferenceEqualityComparison(outerType, cast, semanticModel, out other))
                {
                    return false;
                }

                // If the conversion from the expression to the cast type is implicit numeric or constant
                // and the conversion from the expression to the outer type is identity, we'll go ahead
                // and remove the cast.
                if (expressionToOuterType.IsIdentity &&
                    expressionToCastType.IsImplicit &&
                    (expressionToCastType.IsNumeric || expressionToCastType.IsConstantExpression))
                {
                    return true;
                }

                if (!castToOuterType.IsBoxing &&
                    castToOuterType == expressionToOuterType)
                {
                    if (castToOuterType.IsNullable)
                    {
                        // Even though both the nullable conversions (castToOuterType and expressionToOuterType) are equal, we can guarantee no data loss only if there is an
                        // implicit conversion from expression type to cast type and expression type is non-nullable. For example, consider the cast removal "(float?)" for below:

                        // Console.WriteLine((int)(float?)(int?)2147483647); // Prints -2147483648

                        // castToOuterType:         ExplicitNullable
                        // expressionToOuterType:   ExplicitNullable
                        // expressionToCastType:    ImplicitNullable

                        // We should not remove the cast to "float?".
                        // However, cast to "int?" is unnecessary and should be removable.
                        return expressionToCastType.IsImplicit && !((ITypeSymbol)expressionType).IsNullable();
                    }
                    else if (expressionToCastType.IsImplicit && expressionToCastType.IsNumeric && !castToOuterType.IsIdentity)
                    {
                        // Some implicit numeric conversions can cause loss of precision and must not be removed.
                        return !IsRequiredImplicitNumericConversion(expressionType, castType);
                    }

                    return true;
                }

                if (castToOuterType.IsIdentity &&
                    !expressionToCastType.IsUnboxing &&
                    expressionToCastType == expressionToOuterType)
                {
                    return true;
                }

                // Special case: It's possible to have useless casts inside delegate creation expressions.
                // For example: new Func<string, bool>((Predicate<object>)(y => true)).
                if (IsInDelegateCreationExpression(cast, semanticModel))
                {
                    if (expressionToCastType.IsAnonymousFunction && expressionToOuterType.IsAnonymousFunction)
                    {
                        return !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression);
                    }

                    if (expressionToCastType.IsMethodGroup && expressionToOuterType.IsMethodGroup)
                    {
                        return true;
                    }
                }

                // Case :
                // 1. IList<object> y = (IList<dynamic>)new List<object>()
                if (expressionToCastType.IsExplicit && castToOuterType.IsExplicit && expressionToOuterType.IsImplicit)
                {
                    // If both expressionToCastType and castToOuterType are numeric, then this is a required cast as one of the conversions leads to loss of precision.
                    // Cast removal can change program behavior.                    
                    return !(expressionToCastType.IsNumeric && castToOuterType.IsNumeric);
                }

                // Case :
                // 2. object y = (ValueType)1;
                if (expressionToCastType.IsBoxing && expressionToOuterType.IsBoxing && castToOuterType.IsImplicit)
                {
                    return true;
                }

                // Case :
                // 3. object y = (NullableValueType)null;
                if ((!castToOuterType.IsBoxing || expressionToCastType.IsNullLiteral) &&
                    castToOuterType.IsImplicit &&
                    expressionToCastType.IsImplicit &&
                    expressionToOuterType.IsImplicit)
                {
                    if (expressionToOuterType.IsAnonymousFunction)
                    {
                        return expressionToCastType.IsAnonymousFunction &&
                            !speculationAnalyzer.ReplacementChangesSemanticsOfUnchangedLambda(cast.Expression, speculationAnalyzer.ReplacedExpression);
                    }

                    return true;
                }
            }

            return false;
        }
        private static bool CanReplaceWithReducedName(this NameSyntax name, TypeSyntax reducedName, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var speculationAnalyzer = new SpeculationAnalyzer(name, reducedName, semanticModel, cancellationToken);
            if (speculationAnalyzer.ReplacementChangesSemantics())
            {
                return false;
            }

            return CanReplaceWithReducedNameInContext(name, reducedName, semanticModel, cancellationToken);
        }
 private static bool ReplacementChangesSemantics(ExpressionSyntax originalExpression, ExpressionSyntax replacedExpression, SemanticModel semanticModel)
 {
     var speculationAnalyzer = new SpeculationAnalyzer(originalExpression, replacedExpression, semanticModel, CancellationToken.None);
     return speculationAnalyzer.ReplacementChangesSemantics();
 }
        protected override async Task <Document> AddImportAsync(
            SyntaxNode contextNode,
            INamespaceOrTypeSymbol namespaceSymbol,
            Document document,
            bool placeSystemNamespaceFirst,
            CancellationToken cancellationToken)
        {
            var root          = GetCompilationUnitSyntaxNode(contextNode, cancellationToken);
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var simpleUsingDirective      = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: false);
            var externAliasUsingDirective = GetExternAliasUsingDirective(root, namespaceSymbol, semanticModel);

            if (externAliasUsingDirective != null)
            {
                root = root.AddExterns(
                    externAliasUsingDirective
                    .WithAdditionalAnnotations(Formatter.Annotation));
            }

            if (simpleUsingDirective != null)
            {
                // Because of the way usings can be nested inside of namespace declarations,
                // we need to check if the usings must be fully qualified so as not to be
                // ambiguous with the containing namespace.
                if (UsingsAreContainedInNamespace(contextNode))
                {
                    // When we add usings we try and place them, as best we can, where the user
                    // wants them according to their settings.  This means we can't just add the fully-
                    // qualified usings and expect the simplifier to take care of it, the usings have to be
                    // simplified before we attempt to add them to the document.
                    // You might be tempted to think that we could call
                    //   AddUsings -> Simplifier -> SortUsings
                    // But this will clobber the users using settings without asking.  Instead we create a new
                    // Document and check if our using can be simplified.  Worst case we need to back out the
                    // fully qualified change and reapply with the simple name.
                    var        fullyQualifiedUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: true);
                    SyntaxNode newRoot = root.AddUsingDirective(
                        fullyQualifiedUsingDirective, contextNode, placeSystemNamespaceFirst,
                        Formatter.Annotation);
                    var newDocument      = document.WithSyntaxRoot(newRoot);
                    var newSemanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                    newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                    var newUsing = newRoot
                                   .DescendantNodes().OfType <UsingDirectiveSyntax>().Where(uds => uds.IsEquivalentTo(fullyQualifiedUsingDirective, topLevel: true)).Single();
                    var speculationAnalyzer = new SpeculationAnalyzer(newUsing.Name, simpleUsingDirective.Name, newSemanticModel, cancellationToken);
                    if (speculationAnalyzer.ReplacementChangesSemantics())
                    {
                        // Not fully qualifying the using causes to refer to a different namespace so we need to keep it as is.
                        return(newDocument);
                    }
                    else
                    {
                        // It does not matter if it is fully qualified or simple so lets return the simple name.
                        return(document.WithSyntaxRoot(root.AddUsingDirective(
                                                           simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
                                                           Formatter.Annotation)));
                    }
                }
                else
                {
                    // simple form
                    return(document.WithSyntaxRoot(root.AddUsingDirective(
                                                       simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
                                                       Formatter.Annotation)));
                }
            }

            return(document.WithSyntaxRoot(root));
        }
        private static bool CanReplaceWithReducedName(
            this MemberAccessExpressionSyntax memberAccess,
            ExpressionSyntax reducedName,
            SemanticModel semanticModel,
            CancellationToken cancellationToken)
        {
            if (!IsThisOrTypeOrNamespace(memberAccess, semanticModel))
            {
                return false;
            }

            var speculationAnalyzer = new SpeculationAnalyzer(memberAccess, reducedName, semanticModel, cancellationToken);
            if (!speculationAnalyzer.SymbolsForOriginalAndReplacedNodesAreCompatible() ||
                speculationAnalyzer.ReplacementChangesSemantics())
            {
                return false;
            }

            if (WillConflictWithExistingLocal(memberAccess, reducedName))
            {
                return false;
            }

            if (IsMemberAccessADynamicInvocation(memberAccess, semanticModel))
            {
                return false;
            }

            if (memberAccess.AccessMethodWithDynamicArgumentInsideStructConstructor(semanticModel))
            {
                return false;
            }

            if (memberAccess.Expression.Kind() == SyntaxKind.BaseExpression)
            {
                var enclosingNamedType = semanticModel.GetEnclosingNamedType(memberAccess.SpanStart, cancellationToken);
                var symbol = semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken).Symbol;
                if (enclosingNamedType != null &&
                    !enclosingNamedType.IsSealed &&
                    symbol != null &&
                    symbol.IsOverridable())
                {
                    return false;
                }
            }

            var invalidTransformation1 = ParserWouldTreatExpressionAsCast(reducedName, memberAccess);

            return !invalidTransformation1;
        }
        private static async Task <Document> DetectSemanticConflicts(
            Document inlinedDocument,
            SemanticModel newSemanticModelForInlinedDocument,
            SemanticModel semanticModelBeforeInline,
            SymbolInfo originalInitializerSymbolInfo,
            CancellationToken cancellationToken)
        {
            // In this method we detect if inlining the expression introduced the following semantic change:
            // The symbol info associated with any of the inlined expressions does not match the symbol info for original initializer expression prior to inline.

            // If any semantic changes were introduced by inlining, we update the document with conflict annotations.
            // Otherwise we return the given inlined document without any changes.

            var syntaxRootBeforeInline = await semanticModelBeforeInline.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            // Get all the identifier nodes which were replaced with inlined expression.
            var originalIdentifierNodes = FindReferenceAnnotatedNodes(syntaxRootBeforeInline);

            if (originalIdentifierNodes.IsEmpty())
            {
                // No conflicts
                return(inlinedDocument);
            }

            // Get all the inlined expression nodes.
            var syntaxRootAfterInline = await inlinedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var inlinedExprNodes = syntaxRootAfterInline.GetAnnotatedNodesAndTokens(ExpressionToInlineAnnotation);

            Debug.Assert(originalIdentifierNodes.Count() == inlinedExprNodes.Count());

            var originalNodesEnum = originalIdentifierNodes.GetEnumerator();
            var inlinedNodesEnum  = inlinedExprNodes.GetEnumerator();
            Dictionary <SyntaxNode, SyntaxNode> replacementNodesWithChangedSemantics = null;

            while (originalNodesEnum.MoveNext())
            {
                inlinedNodesEnum.MoveNext();
                var originalNode = originalNodesEnum.Current;

                // expressionToInline is Parenthesized prior to replacement, so get the parenting parenthesized expression.
                var inlinedNode = (ExpressionSyntax)inlinedNodesEnum.Current.AsNode().Parent;
                Debug.Assert(inlinedNode.IsKind(SyntaxKind.ParenthesizedExpression));

                // inlinedNode is the expanded form of the actual initializer expression in the original document.
                // We have annotated the inner initializer with a special syntax annotation "InitializerAnnotation".
                // Get this annotated node and compute the symbol info for this node in the inlined document.
                var innerInitializerInInlineNode = (ExpressionSyntax)inlinedNode.GetAnnotatedNodesAndTokens(InitializerAnnotation).First().AsNode();
                var newInializerSymbolInfo       = newSemanticModelForInlinedDocument.GetSymbolInfo(innerInitializerInInlineNode, cancellationToken);

                // Verification: The symbol info associated with any of the inlined expressions does not match the symbol info for original initializer expression prior to inline.
                if (!SpeculationAnalyzer.SymbolInfosAreCompatible(originalInitializerSymbolInfo, newInializerSymbolInfo, performEquivalenceCheck: true))
                {
                    newInializerSymbolInfo = newSemanticModelForInlinedDocument.GetSymbolInfo(inlinedNode, cancellationToken);
                    if (!SpeculationAnalyzer.SymbolInfosAreCompatible(originalInitializerSymbolInfo, newInializerSymbolInfo, performEquivalenceCheck: true))
                    {
                        if (replacementNodesWithChangedSemantics == null)
                        {
                            replacementNodesWithChangedSemantics = new Dictionary <SyntaxNode, SyntaxNode>();
                        }

                        replacementNodesWithChangedSemantics.Add(inlinedNode, originalNode);
                    }
                }
            }

            if (replacementNodesWithChangedSemantics == null)
            {
                // No conflicts.
                return(inlinedDocument);
            }

            // Replace the conflicting inlined nodes with the original nodes annotated with conflict annotation.
            Func <SyntaxNode, SyntaxNode, SyntaxNode> conflictAnnotationAdder =
                (SyntaxNode oldNode, SyntaxNode newNode) =>
                newNode.WithAdditionalAnnotations(ConflictAnnotation.Create(CSharpFeaturesResources.ConflictsDetected));

            return(await inlinedDocument.ReplaceNodesAsync(replacementNodesWithChangedSemantics.Keys, conflictAnnotationAdder, cancellationToken).ConfigureAwait(false));
        }
        /// <summary>
        /// Adds to <paramref name="targetType"/> if it does not contain an anonymous
        /// type and binds to the same type at the given <paramref name="position"/>.
        /// </summary>
        public static ExpressionSyntax CastIfPossible(
            this ExpressionSyntax expression,
            ITypeSymbol targetType,
            int position,
            SemanticModel semanticModel,
            out bool wasCastAdded)
        {
            wasCastAdded = false;

            if (targetType.ContainsAnonymousType())
            {
                return expression;
            }

            if (targetType.Kind == SymbolKind.DynamicType)
            {
                targetType = semanticModel.Compilation.GetSpecialType(SpecialType.System_Object);
            }

            var typeSyntax = targetType.GenerateTypeSyntax();
            var type = semanticModel.GetSpeculativeTypeInfo(
                position,
                typeSyntax,
                SpeculativeBindingOption.BindAsTypeOrNamespace).Type;

            if (!targetType.Equals(type))
            {
                return expression;
            }

            var castExpression = expression.Cast(targetType);

            // Ensure that inserting the cast doesn't change the semantics.
            var specAnalyzer = new SpeculationAnalyzer(expression, castExpression, semanticModel, CancellationToken.None);
            var speculativeSemanticModel = specAnalyzer.SpeculativeSemanticModel;
            if (speculativeSemanticModel == null)
            {
                return expression;
            }

            var speculatedCastExpression = (CastExpressionSyntax)specAnalyzer.ReplacedExpression;
            if (!speculatedCastExpression.IsUnnecessaryCast(speculativeSemanticModel, CancellationToken.None))
            {
                return expression;
            }

            wasCastAdded = true;
            return castExpression;
        }
예제 #26
0
 protected static bool ReplacementChangesSemantics(ExpressionSyntax originalExpression, ExpressionSyntax replacedExpression, SemanticModel semanticModel)
 {
     var speculationAnalyzer = new SpeculationAnalyzer(originalExpression, replacedExpression, semanticModel, CancellationToken.None);
     return speculationAnalyzer.ReplacementChangesSemantics();
 }
예제 #27
0
        internal override IMethodSymbol GetDelegatingConstructor(
            State state,
            SemanticDocument document,
            int argumentCount,
            INamedTypeSymbol namedType,
            ISet <IMethodSymbol> candidates,
            CancellationToken cancellationToken)
        {
            var oldToken  = state.Token;
            var tokenKind = oldToken.Kind();

            if (state.IsConstructorInitializerGeneration)
            {
                SyntaxToken thisOrBaseKeyword;
                SyntaxKind  newCtorInitializerKind;
                if (tokenKind != SyntaxKind.BaseKeyword && state.TypeToGenerateIn == namedType)
                {
                    thisOrBaseKeyword      = SyntaxFactory.Token(SyntaxKind.ThisKeyword);
                    newCtorInitializerKind = SyntaxKind.ThisConstructorInitializer;
                }
                else
                {
                    thisOrBaseKeyword      = SyntaxFactory.Token(SyntaxKind.BaseKeyword);
                    newCtorInitializerKind = SyntaxKind.BaseConstructorInitializer;
                }

                var ctorInitializer = (ConstructorInitializerSyntax)oldToken.Parent;
                var oldArgumentList = ctorInitializer.ArgumentList;
                var newArgumentList = GetNewArgumentList(oldArgumentList, argumentCount);

                var           newCtorInitializer = SyntaxFactory.ConstructorInitializer(newCtorInitializerKind, ctorInitializer.ColonToken, thisOrBaseKeyword, newArgumentList);
                SemanticModel speculativeModel;
                if (document.SemanticModel.TryGetSpeculativeSemanticModel(ctorInitializer.Span.Start, newCtorInitializer, out speculativeModel))
                {
                    var symbolInfo = speculativeModel.GetSymbolInfo(newCtorInitializer, cancellationToken);
                    return(GenerateConstructorHelpers.GetDelegatingConstructor(
                               document, symbolInfo, candidates, namedType, state.ParameterTypes));
                }
            }
            else
            {
                var oldNode = oldToken.Parent
                              .AncestorsAndSelf(ascendOutOfTrivia: false)
                              .Where(node => SpeculationAnalyzer.CanSpeculateOnNode(node))
                              .LastOrDefault();

                var        typeNameToReplace = (TypeSyntax)oldToken.Parent;
                TypeSyntax newTypeName;
                if (namedType != state.TypeToGenerateIn)
                {
                    while (true)
                    {
                        var parentType = typeNameToReplace.Parent as TypeSyntax;
                        if (parentType == null)
                        {
                            break;
                        }

                        typeNameToReplace = parentType;
                    }

                    newTypeName = namedType.GenerateTypeSyntax().WithAdditionalAnnotations(s_annotation);
                }
                else
                {
                    newTypeName = typeNameToReplace.WithAdditionalAnnotations(s_annotation);
                }

                var newNode = oldNode.ReplaceNode(typeNameToReplace, newTypeName);
                newTypeName = (TypeSyntax)newNode.GetAnnotatedNodes(s_annotation).Single();

                var oldArgumentList = (ArgumentListSyntax)newTypeName.Parent.ChildNodes().FirstOrDefault(n => n is ArgumentListSyntax);
                if (oldArgumentList != null)
                {
                    var newArgumentList = GetNewArgumentList(oldArgumentList, argumentCount);
                    if (newArgumentList != oldArgumentList)
                    {
                        newNode     = newNode.ReplaceNode(oldArgumentList, newArgumentList);
                        newTypeName = (TypeSyntax)newNode.GetAnnotatedNodes(s_annotation).Single();
                    }
                }

                var speculativeModel = SpeculationAnalyzer.CreateSpeculativeSemanticModelForNode(oldNode, newNode, document.SemanticModel);
                if (speculativeModel != null)
                {
                    var symbolInfo = speculativeModel.GetSymbolInfo(newTypeName.Parent, cancellationToken);
                    return(GenerateConstructorHelpers.GetDelegatingConstructor(
                               document, symbolInfo, candidates, namedType, state.ParameterTypes));
                }
            }

            return(null);
        }
        protected override async Task<Document> AddImportAsync(
            SyntaxNode contextNode,
            INamespaceOrTypeSymbol namespaceSymbol,
            Document document,
            bool placeSystemNamespaceFirst,
            CancellationToken cancellationToken)
        {
            var root = GetCompilationUnitSyntaxNode(contextNode, cancellationToken);
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
            var simpleUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: false);
            var externAliasUsingDirective = GetExternAliasUsingDirective(root, namespaceSymbol, semanticModel);
            if (externAliasUsingDirective != null)
            {
                root = root.AddExterns(
                    externAliasUsingDirective
                        .WithAdditionalAnnotations(Formatter.Annotation));
            }

            if (simpleUsingDirective != null)
            {
                // Because of the way usings can be nested inside of namespace declarations,
                // we need to check if the usings must be fully qualified so as not to be
                // ambiguous with the containing namespace. 
                if (UsingsAreContainedInNamespace(contextNode))
                {
                    // When we add usings we try and place them, as best we can, where the user
                    // wants them according to their settings.  This means we can't just add the fully-
                    // qualified usings and expect the simplifier to take care of it, the usings have to be
                    // simplified before we attempt to add them to the document.
                    // You might be tempted to think that we could call 
                    //   AddUsings -> Simplifier -> SortUsings
                    // But this will clobber the users using settings without asking.  Instead we create a new
                    // Document and check if our using can be simplified.  Worst case we need to back out the 
                    // fully qualified change and reapply with the simple name.
                    var fullyQualifiedUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: true);
                    SyntaxNode newRoot = root.AddUsingDirective(
                                    fullyQualifiedUsingDirective, contextNode, placeSystemNamespaceFirst,
                                    Formatter.Annotation);
                    var newDocument = document.WithSyntaxRoot(newRoot);
                    var newSemanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
                    newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                    var newUsing = newRoot
                        .DescendantNodes().OfType<UsingDirectiveSyntax>().Where(uds => uds.IsEquivalentTo(fullyQualifiedUsingDirective, topLevel: true)).Single();
                    var speculationAnalyzer = new SpeculationAnalyzer(newUsing.Name, simpleUsingDirective.Name, newSemanticModel, cancellationToken);
                    if (speculationAnalyzer.ReplacementChangesSemantics())
                    {
                        // Not fully qualifying the using causes to refer to a different namespace so we need to keep it as is.
                        return newDocument;
                    }
                    else
                    {
                        // It does not matter if it is fully qualified or simple so lets return the simple name.
                        return document.WithSyntaxRoot(root.AddUsingDirective(
                            simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
                            Formatter.Annotation));
                    }
                }
                else
                {
                    // simple form
                    return document.WithSyntaxRoot(root.AddUsingDirective(
                            simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
                            Formatter.Annotation));
                }
            }

            return document.WithSyntaxRoot(root);
        }