protected override async Task <ImmutableArray <CodeAction> > GetRefactoringsForSingleParameterAsync(
            Document document, IParameterSymbol parameter, SyntaxNode functionDeclaration, IMethodSymbol methodSymbol,
            IBlockOperation blockStatementOpt, CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // Only should provide null-checks for reference types and nullable types.
            if (!ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            // Great.  There was no null check.  Offer to add one.
            var result = ArrayBuilder <CodeAction> .GetInstance();

            result.Add(new MyCodeAction(
                           FeaturesResources.Add_null_check,
                           c => AddNullCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, c)));

            // Also, if this was a string, offer to add the special checks to
            // string.IsNullOrEmpty and string.IsNullOrWhitespace.
            if (parameter.Type.SpecialType == SpecialType.System_String)
            {
                result.Add(new MyCodeAction(
                               FeaturesResources.Add_string_IsNullOrEmpty_check,
                               c => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), c)));

                result.Add(new MyCodeAction(
                               FeaturesResources.Add_string_IsNullOrWhiteSpace_check,
                               c => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrWhiteSpace), c)));
            }

            return(result.ToImmutableAndFree());
        }
Пример #2
0
        private bool CheckBlock(IBlockOperation blockOperation, IExpressionStatementOperation expressionOperation, IInvocationOperation invocation)
        {
            if (blockOperation.Parent is IUsingOperation || blockOperation.Parent is ICatchClauseOperation)
            {
                return(false);
            }

            if (blockOperation.Operations.Length == 1)
            {
                return(true);
            }

            int index = blockOperation.Operations.IndexOf(expressionOperation);

            if (index != blockOperation.Operations.Length - 1)
            {
                return(false);
            }

            // the assert.fail is the last operation.  Check if they are ending the loop with an if(blah) continue; fail;

            IOperation previous = blockOperation.Operations[index - 1];

            if (previous is IConditionalOperation conditional && conditional.WhenFalse is null)
            {
Пример #3
0
        public override void VisitBlock(IBlockOperation operation)
        {
            Assert.Equal(OperationKind.Block, operation.Kind);
            VisitLocals(operation.Locals);

            AssertEx.Equal(operation.Operations, operation.Children);
        }
        protected override async Task <ImmutableArray <CodeAction> > GetRefactoringsForAllParametersAsync(
            Document document, SyntaxNode functionDeclaration, IMethodSymbol methodSymbol,
            IBlockOperation blockStatementOpt, ImmutableArray <SyntaxNode> listOfParameterNodes, TextSpan parameterSpan, CancellationToken cancellationToken)
        {
            // List to keep track of the valid parameters
            var listOfParametersOrdinals = new List <int>();
            var semanticModel            = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            foreach (var parameterNode in listOfParameterNodes)
            {
                var parameter = (IParameterSymbol)semanticModel.GetDeclaredSymbol(parameterNode, cancellationToken);
                if (ParameterValidForNullCheck(document, parameter, semanticModel, blockStatementOpt, cancellationToken))
                {
                    listOfParametersOrdinals.Add(parameter.Ordinal);
                }
            }

            // Min 2 parameters to offer the refactoring
            if (listOfParametersOrdinals.Count < 2)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            // Great.  The list has parameters that need null checks. Offer to add null checks for all.
            return(ImmutableArray.Create <CodeAction>(new MyCodeAction(
                                                          FeaturesResources.Add_null_checks_for_all_parameters,
                                                          c => UpdateDocumentForRefactoringAsync(document, blockStatementOpt, listOfParametersOrdinals, parameterSpan, c))));
        }
Пример #5
0
        public bool TryGetOrComputeResult(
            ImmutableArray <IOperation> operationBlocks,
            IMethodSymbol containingMethod,
            bool trackInstanceFields,
            out DataFlowAnalysisResult <DisposeBlockAnalysisResult, DisposeAbstractValue> disposeAnalysisResult,
            out DataFlowAnalysisResult <PointsToBlockAnalysisResult, PointsToAbstractValue> pointsToAnalysisResult,
            out ImmutableDictionary <IFieldSymbol, PointsToAbstractValue> trackedInstanceFieldPointsToMap)
        {
            foreach (var operationRoot in operationBlocks)
            {
                IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock();
                if (topmostBlock != null)
                {
                    var cfg = ControlFlowGraph.Create(topmostBlock);

                    // Invoking an instance method may likely invalidate all the instance field analysis state, i.e.
                    // reference type fields might be re-assigned to point to different objects in the called method.
                    // An optimistic points to analysis assumes that the points to values of instance fields don't change on invoking an instance method.
                    // A pessimistic points to analysis resets all the instance state and assumes the instance field might point to any object, hence has unknown state.
                    // For dispose analysis, we want to perform an optimistic points to analysis as we assume a disposable field is not likely to be re-assigned to a separate object in helper method invocations in Dispose.
                    pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider, pessimisticAnalysis: false);
                    disposeAnalysisResult  = DisposeAnalysis.GetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider, _disposeOwnershipTransferLikelyTypes, pointsToAnalysisResult,
                                                                                trackInstanceFields, out trackedInstanceFieldPointsToMap);
                    return(true);
                }
            }

            disposeAnalysisResult           = null;
            pointsToAnalysisResult          = null;
            trackedInstanceFieldPointsToMap = null;
            return(false);
        }
Пример #6
0
        public bool TryGetOrComputeResult(
            ImmutableArray <IOperation> operationBlocks,
            IMethodSymbol containingMethod,
            AnalyzerOptions analyzerOptions,
            DiagnosticDescriptor rule,
            bool trackInstanceFields,
            bool trackExceptionPaths,
            CancellationToken cancellationToken,
            out DisposeAnalysisResult disposeAnalysisResult,
            out PointsToAnalysisResult pointsToAnalysisResult,
            InterproceduralAnalysisPredicate interproceduralAnalysisPredicateOpt = null,
            bool defaultDisposeOwnershipTransferAtConstructor = false)
        {
            foreach (var operationRoot in operationBlocks)
            {
                IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock();
                if (topmostBlock != null)
                {
                    var cfg = topmostBlock.GetEnclosingControlFlowGraph();

                    disposeAnalysisResult = DisposeAnalysis.GetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider,
                                                                               analyzerOptions, rule, _disposeOwnershipTransferLikelyTypes, trackInstanceFields,
                                                                               trackExceptionPaths, cancellationToken, out pointsToAnalysisResult,
                                                                               interproceduralAnalysisPredicateOpt: interproceduralAnalysisPredicateOpt,
                                                                               defaultDisposeOwnershipTransferAtConstructor: defaultDisposeOwnershipTransferAtConstructor);
                    return(true);
                }
            }

            disposeAnalysisResult  = null;
            pointsToAnalysisResult = null;
            return(false);
        }
        protected override async Task <ImmutableArray <CodeAction> > GetRefactoringsAsync(
            Document document, IParameterSymbol parameter, TMemberDeclarationSyntax memberDeclaration,
            IBlockOperation blockStatementOpt, CancellationToken cancellationToken)
        {
            // Only should provide null-checks for reference types and nullable types.
            if (!parameter.Type.IsReferenceType &&
                !parameter.Type.IsNullable())
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            var syntaxFacts   = document.GetLanguageService <ISyntaxFactsService>();
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            // Look for an existing "if (p == null)" statement, or "p ?? throw" check.  If we already
            // have one, we don't want to offer to generate a new null check.
            //
            // Note: we only check the top level statements of the block.  I think that's sufficient
            // as this will catch the 90% case, while not being that bad an experience even when
            // people do strange things in their constructors.
            if (blockStatementOpt != null)
            {
                foreach (var statement in blockStatementOpt.Operations)
                {
                    if (IsIfNullCheck(statement, parameter))
                    {
                        return(ImmutableArray <CodeAction> .Empty);
                    }

                    if (ContainsNullCoalesceCheck(
                            syntaxFacts, semanticModel, statement,
                            parameter, cancellationToken))
                    {
                        return(ImmutableArray <CodeAction> .Empty);
                    }
                }
            }

            // Great.  There was no null check.  Offer to add one.
            var result = ArrayBuilder <CodeAction> .GetInstance();

            result.Add(new MyCodeAction(
                           FeaturesResources.Add_null_check,
                           c => AddNullCheckAsync(document, parameter, memberDeclaration, blockStatementOpt, c)));

            // Also, if this was a string, offer to add the special checks to
            // string.IsNullOrEmpty and string.IsNullOrWhitespace.
            if (parameter.Type.SpecialType == SpecialType.System_String)
            {
                result.Add(new MyCodeAction(
                               FeaturesResources.Add_string_IsNullOrEmpty_check,
                               c => AddStringCheckAsync(document, parameter, memberDeclaration, blockStatementOpt, nameof(string.IsNullOrEmpty), c)));

                result.Add(new MyCodeAction(
                               FeaturesResources.Add_string_IsNullOrWhiteSpace_check,
                               c => AddStringCheckAsync(document, parameter, memberDeclaration, blockStatementOpt, nameof(string.IsNullOrWhiteSpace), c)));
            }

            return(result.ToImmutableAndFree());
        }
Пример #8
0
        private static ImmutableDictionary <IParameterSymbol, SyntaxNode> GetOrComputeHazardousParameterUsages(
            IBlockOperation topmostBlock,
            Compilation compilation,
            ISymbol owningSymbol,
            InterproceduralAnalysisConfiguration interproceduralAnalysisConfig,
            bool pessimisticAnalysis = true)
        {
            Debug.Assert(topmostBlock != null);

            var cfg = topmostBlock.GetEnclosingControlFlowGraph();
            var wellKnownTypeProvider  = WellKnownTypeProvider.GetOrCreate(compilation);
            var pointsToAnalysisResult = PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(cfg, owningSymbol, wellKnownTypeProvider,
                                                                                                 interproceduralAnalysisConfig, interproceduralAnalysisPredicateOpt: null, pessimisticAnalysis);

            if (pointsToAnalysisResult != null)
            {
                var result = TryGetOrComputeResult(cfg, owningSymbol, wellKnownTypeProvider,
                                                   interproceduralAnalysisConfig, pessimisticAnalysis, pointsToAnalysisResult);
                if (result != null)
                {
                    return(result.HazardousParameterUsages);
                }
            }

            return(ImmutableDictionary <IParameterSymbol, SyntaxNode> .Empty);
        }
        private static ImmutableDictionary <IParameterSymbol, SyntaxNode> GetOrComputeHazardousParameterUsages(
            IBlockOperation topmostBlock,
            Compilation compilation,
            ISymbol owningSymbol,
            AnalyzerOptions analyzerOptions,
            SymbolNamesWithValueOption <Unit> nullCheckValidationMethods,
            InterproceduralAnalysisConfiguration interproceduralAnalysisConfig,
            bool performCopyAnalysis,
            bool pessimisticAnalysis = true)
        {
            var cfg = topmostBlock.GetEnclosingControlFlowGraph();

            if (cfg == null)
            {
                return(ImmutableDictionary <IParameterSymbol, SyntaxNode> .Empty);
            }

            var wellKnownTypeProvider  = WellKnownTypeProvider.GetOrCreate(compilation);
            var pointsToAnalysisResult = PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider,
                                                                                                 interproceduralAnalysisConfig, interproceduralAnalysisPredicateOpt: null, pessimisticAnalysis, performCopyAnalysis);

            if (pointsToAnalysisResult != null)
            {
                var result = TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider,
                                                   nullCheckValidationMethods, interproceduralAnalysisConfig, pessimisticAnalysis, pointsToAnalysisResult);
                if (result != null)
                {
                    return(result.HazardousParameterUsages);
                }
            }

            return(ImmutableDictionary <IParameterSymbol, SyntaxNode> .Empty);
        }
Пример #10
0
        public static ImmutableDictionary <IParameterSymbol, SyntaxNode> GetOrComputeHazardousParameterUsages(
            IBlockOperation topmostBlock,
            Compilation compilation,
            ISymbol owningSymbol,
            AnalyzerOptions analyzerOptions,
            DiagnosticDescriptor rule,
            CancellationToken cancellationToken,
            PointsToAnalysisKind defaultPointsToAnalysisKind        = PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties,
            InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.ContextSensitive,
            uint defaultMaxInterproceduralMethodCallChain           = 1, // By default, we only want to track method calls one level down.
            bool pessimisticAnalysis = false)
        {
            Debug.Assert(!analyzerOptions.IsConfiguredToSkipAnalysis(rule, owningSymbol, compilation, cancellationToken));

            var cfg = topmostBlock.GetEnclosingControlFlowGraph();

            if (cfg == null)
            {
                return(ImmutableDictionary <IParameterSymbol, SyntaxNode> .Empty);
            }

            var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create(
                analyzerOptions, rule, cfg, compilation, interproceduralAnalysisKind, cancellationToken, defaultMaxInterproceduralMethodCallChain);
            var performCopyAnalysis        = analyzerOptions.GetCopyAnalysisOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, defaultValue: false, cancellationToken);
            var nullCheckValidationMethods = analyzerOptions.GetNullCheckValidationMethodsOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, cancellationToken);
            var pointsToAnalysisKind       = analyzerOptions.GetPointsToAnalysisKindOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, defaultPointsToAnalysisKind, cancellationToken);

            return(GetOrComputeHazardousParameterUsages(cfg, compilation, owningSymbol, analyzerOptions,
                                                        nullCheckValidationMethods, pointsToAnalysisKind, interproceduralAnalysisConfig, performCopyAnalysis, pessimisticAnalysis));
        }
 static bool IsSingleStatementBody(IBlockOperation body)
 {
     return(body.Operations.Length == 1 ||
            (body.Operations.Length == 3 && body.Syntax.Language == LanguageNames.VisualBasic &&
             body.Operations[1] is ILabeledOperation labeledOp && labeledOp.IsImplicit &&
             body.Operations[2] is IReturnOperation returnOp && returnOp.IsImplicit));
 }
        private static bool ShouldExcludeOperationBlock(ImmutableArray <IOperation> operationBlocks)
        {
            if (operationBlocks != null && operationBlocks.Length == 1)
            {
                IBlockOperation block = operationBlocks[0] as IBlockOperation;

                // Analyze IBlockOperation blocks.
                if (block == null)
                {
                    return(true);
                }

                var operations = block.Operations.GetOperations();

                if (operations.Length == 0 ||
                    (operations.Length == 1 &&
                     operations[0].Kind == OperationKind.Throw))
                {
                    // Empty body OR body that just throws.
                    return(true);
                }

                // Expression-bodied can be an implicit return and conversion on top of the throw operation
                if (operations.Length == 1 &&
                    operations[0] is IReturnOperation returnOp &&
                    returnOp.IsImplicit &&
                    returnOp.ReturnedValue is IConversionOperation conversionOp &&
                    conversionOp.IsImplicit && conversionOp.Operand.Kind == OperationKind.Throw)
                {
                    return(true);
                }
            }

            return(false);
        }
        public static bool IsMethodNotImplementedOrSupported(this OperationBlockAnalysisContext context)
        {
            // Note that VB method bodies with 1 action have 3 operations.
            // The first is the actual operation, the second is a label statement, and the third is a return
            // statement. The last two are implicit in these scenarios.

            var operationBlocks = context.OperationBlocks.WhereAsArray(operation => !operation.IsOperationNoneRoot());

            IBlockOperation methodBlock = null;

            if (operationBlocks.Length == 1 && operationBlocks[0].Kind == OperationKind.Block)
            {
                methodBlock = (IBlockOperation)operationBlocks[0];
            }
            else if (operationBlocks.Length > 1)
            {
                foreach (var block in operationBlocks)
                {
                    if (block.Kind == OperationKind.Block)
                    {
                        methodBlock = (IBlockOperation)block;
                        break;
                    }
                }
            }

            if (methodBlock != null)
            {
Пример #14
0
 protected abstract Task <ImmutableArray <CodeAction> > GetRefactoringsForSingleParameterAsync(
     Document document,
     IParameterSymbol parameter,
     SyntaxNode functionDeclaration,
     IMethodSymbol methodSymbol,
     IBlockOperation blockStatementOpt,
     CancellationToken cancellationToken);
 protected abstract Task <ImmutableArray <CodeAction> > GetRefactoringsForAllParametersAsync(
     Document document,
     SyntaxNode functionDeclaration,
     IMethodSymbol method,
     IBlockOperation blockStatementOpt,
     ImmutableArray <SyntaxNode> listOfParameterNodes,
     TextSpan parameterSpan,
     CancellationToken cancellationToken);
Пример #16
0
        public override void VisitBlock(IBlockOperation operation)
        {
            foreach (var local in operation.Locals)
            {
                // empty loop body, just want to make sure it won't crash.
            }

            base.VisitBlock(operation);
        }
Пример #17
0
        public override void VisitBlock(IBlockOperation operation)
        {
            Assert.Equal(OperationKind.Block, operation.Kind);

            foreach (var local in operation.Locals)
            {
                Assert.NotNull(local);
            }

            AssertEx.Equal(operation.Operations, operation.Children);
        }
Пример #18
0
        private static IMethodSymbol?FindOwningSymbol(IBlockOperation block, ISymbol containingSymbol)
        {
            return(block.Parent switch
            {
                ILocalFunctionOperation localFunction => localFunction.Symbol,
                IAnonymousFunctionOperation anonymousFunction => anonymousFunction.Symbol,

                // Block parent is the method declaration, for vbnet this means a null parent but for C# it's a IMethodBodyOperation
                null => containingSymbol as IMethodSymbol,
                IMethodBodyOperation _ => containingSymbol as IMethodSymbol,

                _ => null,
            });
        public static ControlFlowGraph GetControlFlowGraph(this ImmutableArray <IOperation> operationBlocks)
        {
            foreach (var operationRoot in operationBlocks)
            {
                IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock();
                if (topmostBlock != null)
                {
                    return(topmostBlock.GetEnclosingControlFlowGraph());
                }
            }

            return(null);
        }
        private bool TryFindAssignmentExpression(
            IBlockOperation containingBlock,
            IConditionalOperation ifOperation,
            ISymbol localOrParameter,
            out IExpressionStatementOperation expressionStatement,
            out IAssignmentOperation assignmentExpression
            )
        {
            var ifOperationIndex = containingBlock.Operations.IndexOf(ifOperation);

            // walk forward until we find an assignment of this local/parameter into
            // something else.
            for (var i = ifOperationIndex + 1; i < containingBlock.Operations.Length; i++)
            {
                expressionStatement =
                    containingBlock.Operations[i] as IExpressionStatementOperation;
                if (expressionStatement == null)
                {
                    continue;
                }

                assignmentExpression = expressionStatement.Operation as IAssignmentOperation;
                if (assignmentExpression == null)
                {
                    continue;
                }

                if (
                    !TryGetLocalOrParameterSymbol(
                        assignmentExpression.Value,
                        out var assignmentValue
                        )
                    )
                {
                    continue;
                }

                if (!Equals(localOrParameter, assignmentValue))
                {
                    continue;
                }

                return(true);
            }

            expressionStatement  = null;
            assignmentExpression = null;
            return(false);
        }
            public IdentifierGenerator(SemanticModel model, IBlockOperation block)
            {
                _nextIdentifier = FindFirstUnusedIdentifierIndex(model, block.Syntax.SpanStart, "ptr");
                HashSet <string> localNames = new HashSet <string>(block.Locals.Select(x => x.Name));
                string?          identifier = NextIdentifier();

                while (identifier is not null && localNames.Contains(identifier))
                {
                    identifier = NextIdentifier();
                }
                if (identifier is not null)
                {
                    // The last identifier was not in use, so go back one to use it the next call.
                    _nextIdentifier--;
                }
            }
Пример #22
0
        /// <summary>
        /// Returns the topmost <see cref="IBlockOperation"/> containing the given <paramref name="operation"/>.
        /// </summary>
        public static IBlockOperation GetTopmostParentBlock(this IOperation operation)
        {
            IOperation      currentOperation      = operation;
            IBlockOperation topmostBlockOperation = null;

            while (currentOperation != null)
            {
                if (currentOperation is IBlockOperation blockOperation)
                {
                    topmostBlockOperation = blockOperation;
                }

                currentOperation = currentOperation.Parent;
            }

            return(topmostBlockOperation);
        }
Пример #23
0
        public static ImmutableDictionary <IParameterSymbol, SyntaxNode> GetOrComputeHazardousParameterUsages(
            IBlockOperation topmostBlock,
            Compilation compilation,
            ISymbol owningSymbol,
            AnalyzerOptions analyzerOptions,
            DiagnosticDescriptor rule,
            CancellationToken cancellationToken,
            InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.ContextSensitive,
            uint defaultMaxInterproceduralMethodCallChain           = 1, // By default, we only want to track method calls one level down.
            bool pessimisticAnalysis = true)
        {
            var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create(
                analyzerOptions, rule, interproceduralAnalysisKind, cancellationToken, defaultMaxInterproceduralMethodCallChain);

            return(GetOrComputeHazardousParameterUsages(topmostBlock, compilation, owningSymbol,
                                                        interproceduralAnalysisConfig, pessimisticAnalysis));
        }
Пример #24
0
        /*
         * Microsoft.CodeAnalysis.Operations. IAddressOfOperation
         * Microsoft.CodeAnalysis.Operations. IAnonymousFunctionOperation
         * Microsoft.CodeAnalysis.Operations. IAnonymousObjectCreationOperation
         * Microsoft.CodeAnalysis.Operations. IArgumentOperation
         * Microsoft.CodeAnalysis.Operations. IArrayCreationOperation
         * Microsoft.CodeAnalysis.Operations. IArrayElementReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IArrayInitializerOperation
         * - Microsoft.CodeAnalysis.Operations. IAssignmentOperation
         * Microsoft.CodeAnalysis.Operations. IAwaitOperation
         * Microsoft.CodeAnalysis.Operations. IBinaryOperation
         * - Microsoft.CodeAnalysis.Operations. IBlockOperation
         * Microsoft.CodeAnalysis.Operations. IBranchOperation
         * Microsoft.CodeAnalysis.Operations. ICaseClauseOperation
         * Microsoft.CodeAnalysis.Operations. ICatchClauseOperation
         * Microsoft.CodeAnalysis.Operations. ICoalesceOperation
         * Microsoft.CodeAnalysis.Operations. ICollectionElementInitializerOperation
         * Microsoft.CodeAnalysis.Operations. ICompoundAssignmentOperation
         * Microsoft.CodeAnalysis.Operations. IConditionalAccessInstanceOperation
         * Microsoft.CodeAnalysis.Operations. IConditionalAccessOperation
         * Microsoft.CodeAnalysis.Operations. IConditionalOperation
         * Microsoft.CodeAnalysis.Operations. IConstantPatternOperation
         * Microsoft.CodeAnalysis.Operations. IConversionOperation
         * Microsoft.CodeAnalysis.Operations. IDeclarationExpressionOperation
         * Microsoft.CodeAnalysis.Operations. IDeclarationPatternOperation
         * Microsoft.CodeAnalysis.Operations. IDeconstructionAssignmentOperation
         * Microsoft.CodeAnalysis.Operations. IDefaultCaseClauseOperation
         * Microsoft.CodeAnalysis.Operations. IDefaultValueOperation
         * Microsoft.CodeAnalysis.Operations. IDelegateCreationOperation
         * Microsoft.CodeAnalysis.Operations. IDynamicIndexerAccessOperation
         * Microsoft.CodeAnalysis.Operations. IDynamicInvocationOperation
         * Microsoft.CodeAnalysis.Operations. IDynamicMemberReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IDynamicObjectCreationOperation
         * Microsoft.CodeAnalysis.Operations. IEmptyOperation
         * Microsoft.CodeAnalysis.Operations. IEndOperation
         * Microsoft.CodeAnalysis.Operations. IEventAssignmentOperation
         * Microsoft.CodeAnalysis.Operations. IEventReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IExpressionStatementOperation
         * Microsoft.CodeAnalysis.Operations. IFieldInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IFieldReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IFixedOperation
         * Microsoft.CodeAnalysis.Operations. IForEachLoopOperation
         * Microsoft.CodeAnalysis.Operations. IForLoopOperation
         * Microsoft.CodeAnalysis.Operations. IForToLoopOperation
         * Microsoft.CodeAnalysis.Operations. IIncrementOrDecrementOperation
         * Microsoft.CodeAnalysis.Operations. IInstanceReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IInterpolatedStringContentOperation
         * Microsoft.CodeAnalysis.Operations. IInterpolatedStringOperation
         * Microsoft.CodeAnalysis.Operations. IInterpolatedStringTextOperation
         * Microsoft.CodeAnalysis.Operations. IInterpolationOperation
         * Microsoft.CodeAnalysis.Operations. IInvalidOperation
         * Microsoft.CodeAnalysis.Operations. IInvocationOperation
         * Microsoft.CodeAnalysis.Operations. IIsPatternOperation
         * Microsoft.CodeAnalysis.Operations. IIsTypeOperation
         * Microsoft.CodeAnalysis.Operations. ILabeledOperation
         * Microsoft.CodeAnalysis.Operations. ILiteralOperation
         * Microsoft.CodeAnalysis.Operations. ILocalFunctionOperation
         * Microsoft.CodeAnalysis.Operations. ILocalReferenceOperation
         * Microsoft.CodeAnalysis.Operations. ILockOperation
         * Microsoft.CodeAnalysis.Operations. ILoopOperation
         * Microsoft.CodeAnalysis.Operations. IMemberInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IMemberReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IMethodReferenceOperation
         * Microsoft.CodeAnalysis.Operations. INameOfOperation
         * Microsoft.CodeAnalysis.Operations. IObjectCreationOperation
         * Microsoft.CodeAnalysis.Operations. IObjectOrCollectionInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IOmittedArgumentOperation
         * Microsoft.CodeAnalysis.Operations. IParameterInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IParameterReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IParenthesizedOperation
         * Microsoft.CodeAnalysis.Operations. IPatternCaseClauseOperation
         * Microsoft.CodeAnalysis.Operations. IPatternOperation
         * Microsoft.CodeAnalysis.Operations. IPlaceholderOperation
         * Microsoft.CodeAnalysis.Operations. IPointerIndirectionReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IPropertyInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IPropertyReferenceOperation
         * Microsoft.CodeAnalysis.Operations. IRaiseEventOperation
         * Microsoft.CodeAnalysis.Operations. IRangeCaseClauseOperation
         * Microsoft.CodeAnalysis.Operations. IRelationalCaseClauseOperation
         * Microsoft.CodeAnalysis.Operations. IReturnOperation
         * Microsoft.CodeAnalysis.Operations. ISimpleAssignmentOperation
         * Microsoft.CodeAnalysis.Operations. ISingleValueCaseClauseOperation
         * Microsoft.CodeAnalysis.Operations. ISizeOfOperation
         * Microsoft.CodeAnalysis.Operations. IStopOperation
         * Microsoft.CodeAnalysis.Operations. ISwitchCaseOperation
         * Microsoft.CodeAnalysis.Operations. ISwitchOperation
         * Microsoft.CodeAnalysis.Operations. ISymbolInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IThrowOperation
         * Microsoft.CodeAnalysis.Operations. ITranslatedQueryOperation
         * Microsoft.CodeAnalysis.Operations. ITryOperation
         * Microsoft.CodeAnalysis.Operations. ITupleOperation
         * Microsoft.CodeAnalysis.Operations. ITypeOfOperation
         * Microsoft.CodeAnalysis.Operations. ITypeParameterObjectCreationOperation
         * Microsoft.CodeAnalysis.Operations. IUnaryOperation
         * Microsoft.CodeAnalysis.Operations. IUsingOperation
         * Microsoft.CodeAnalysis.Operations. IVariableDeclarationGroupOperation
         * Microsoft.CodeAnalysis.Operations. IVariableDeclarationOperation
         * Microsoft.CodeAnalysis.Operations. IVariableDeclaratorOperation
         * Microsoft.CodeAnalysis.Operations. IVariableInitializerOperation
         * Microsoft.CodeAnalysis.Operations. IWhileLoopOperation
         * Microsoft.CodeAnalysis.Operations. IWithOperation
         */

        private static AbstractStatement ReadBlock(IBlockOperation op)
        {
            var context = CodeReaderContext.GetContextOrThrow();
            var result  = new BlockStatement();

            using (context.PushState(new BlockContext(result)))
            {
                foreach (var childOp in op.Operations)
                {
                    if (!IsRedundantStatement(childOp))
                    {
                        result.Statements.Add(ReadStatement(childOp));
                    }
                }
            }

            return(result);
        }
Пример #25
0
        private async Task <Document> UpdateDocumentForRefactoringAsync(
            Document document,
            SyntaxNode functionDeclaration,
            IBlockOperation blockStatementOpt,
            List <int> listOfParametersOrdinals,
            int position,
            CancellationToken cancellationToken)
        {
            foreach (var index in listOfParametersOrdinals)
            {
                // Updates functionDeclaration and uses it to get the first valid ParameterNode using the ordinals (index).
                var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                var token = root.FindToken(position);
                var firstparameterNode = GetParameterNode(token, position);
                functionDeclaration = firstparameterNode.FirstAncestorOrSelf <SyntaxNode>(IsFunctionDeclaration);
                var generator      = SyntaxGenerator.GetGenerator(document);
                var parameterNodes = generator.GetParameters(functionDeclaration);
                var semanticModel  = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                var parameter   = GetParameterAtOrdinal(index, parameterNodes, semanticModel, cancellationToken);
                var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>();

                if (!CanOfferRefactoring(functionDeclaration, semanticModel, syntaxFacts, cancellationToken, out blockStatementOpt))
                {
                    continue;
                }

                // If parameter is a string, default check would be IsNullOrEmpty. This is because IsNullOrEmpty is more commonly used in this regard according to telemetry and UX testing.
                if (parameter.Type.SpecialType == SpecialType.System_String)
                {
                    document = await AddStringCheckAsync(document, parameter, functionDeclaration, (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), cancellationToken).ConfigureAwait(false);

                    continue;
                }

                // For all other parameters, add null check - updates document
                document = await AddNullCheckAsync(document, parameter, functionDeclaration,
                                                   (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, cancellationToken).ConfigureAwait(false);
            }

            return(document);
        }
        public static ImmutableDictionary<IParameterSymbol, SyntaxNode> GetOrComputeHazardousParameterUsages(
            IBlockOperation topmostBlock,
            Compilation compilation,
            ISymbol owningSymbol,
            AnalyzerOptions analyzerOptions,
            DiagnosticDescriptor rule,
            CancellationToken cancellationToken,
            InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.ContextSensitive,
            uint defaultMaxInterproceduralMethodCallChain = 1, // By default, we only want to track method calls one level down.
            bool pessimisticAnalysis = true)
        {
            Debug.Assert(!owningSymbol.IsConfiguredToSkipAnalysis(analyzerOptions, rule, compilation, cancellationToken));

            var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create(
                   analyzerOptions, rule, topmostBlock.Syntax.SyntaxTree, compilation, interproceduralAnalysisKind, cancellationToken, defaultMaxInterproceduralMethodCallChain);
            var performCopyAnalysis = analyzerOptions.GetCopyAnalysisOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, defaultValue: false, cancellationToken);
            var nullCheckValidationMethods = analyzerOptions.GetNullCheckValidationMethodsOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, cancellationToken);
            return GetOrComputeHazardousParameterUsages(topmostBlock, compilation, owningSymbol, analyzerOptions,
                nullCheckValidationMethods, interproceduralAnalysisConfig, performCopyAnalysis, pessimisticAnalysis);
        }
        private static bool ValueIsAccessed(
            SemanticModel semanticModel,
            IConditionalOperation ifOperation,
            IBlockOperation containingBlock,
            ISymbol localOrParameter,
            IExpressionStatementOperation expressionStatement,
            IAssignmentOperation assignmentExpression
            )
        {
            var statements               = containingBlock.Operations;
            var ifOperationIndex         = statements.IndexOf(ifOperation);
            var expressionStatementIndex = statements.IndexOf(expressionStatement);

            if (expressionStatementIndex > ifOperationIndex + 1)
            {
                // There are intermediary statements between the check and the assignment.
                // Make sure they don't try to access the local.
                var dataFlow = semanticModel.AnalyzeDataFlow(
                    statements[ifOperationIndex + 1].Syntax,
                    statements[expressionStatementIndex - 1].Syntax
                    );

                if (
                    dataFlow.ReadInside.Contains(localOrParameter) ||
                    dataFlow.WrittenInside.Contains(localOrParameter)
                    )
                {
                    return(true);
                }
            }

            // Also, have to make sure there is no read/write of the local/parameter on the left
            // of the assignment.  For example: map[val.Id] = val;
            var exprDataFlow = semanticModel.AnalyzeDataFlow(assignmentExpression.Target.Syntax);

            return(exprDataFlow.ReadInside.Contains(localOrParameter) ||
                   exprDataFlow.WrittenInside.Contains(localOrParameter));
        }
        protected override async Task <ImmutableArray <CodeAction> > GetRefactoringsAsync(
            Document document, IParameterSymbol parameter, SyntaxNode functionDeclaration, IMethodSymbol method,
            IBlockOperation blockStatementOpt, CancellationToken cancellationToken)
        {
            // Only supported for constructor parameters.
            if (method.MethodKind != MethodKind.Constructor)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            var assignmentStatement = TryFindFieldOrPropertyAssignmentStatement(
                parameter, blockStatementOpt);

            if (assignmentStatement != null)
            {
                // We're already assigning this parameter to a field/property in this type.
                // So there's nothing more for us to do.
                return(ImmutableArray <CodeAction> .Empty);
            }

            // Haven't initialized any fields/properties with this parameter.  Offer to assign
            // to an existing matching field/prop if we can find one, or add a new field/prop
            // if we can't.

            var fieldOrProperty = await TryFindMatchingUninitializedFieldOrPropertySymbolAsync(
                document, parameter, blockStatementOpt, cancellationToken).ConfigureAwait(false);

            if (fieldOrProperty != null)
            {
                // Found a field/property that this parameter should be assigned to.
                // Just offer the simple assignment to it.

                var resource = fieldOrProperty.Kind == SymbolKind.Field
                    ? FeaturesResources.Initialize_field_0
                    : FeaturesResources.Initialize_property_0;

                var title = string.Format(resource, fieldOrProperty.Name);

                return(ImmutableArray.Create <CodeAction>(new MyCodeAction(
                                                              title,
                                                              c => AddSymbolInitializationAsync(
                                                                  document, parameter, functionDeclaration, method, blockStatementOpt, fieldOrProperty, c))));
            }
            else
            {
                // Didn't find a field/prop that this parameter could be assigned to.
                // Offer to create new one and assign to that.
                var codeGenService = document.GetLanguageService <ICodeGenerationService>();

                // Get the parts of the parameter name and the appropriate naming rules so
                // that we can name the field/property accordingly.
                var parameterNameParts = this.GetParameterWordParts(parameter);
                var rules = await document.GetNamingRulesAsync(FallbackNamingRules.RefactoringMatchLookupRules, cancellationToken).ConfigureAwait(false);

                var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

                var requireAccessibilityModifiers = options.GetOption(CodeStyleOptions.RequireAccessibilityModifiers);

                var field    = CreateField(requireAccessibilityModifiers, parameter, rules, parameterNameParts);
                var property = CreateProperty(requireAccessibilityModifiers, parameter, rules, parameterNameParts);

                // Offer to generate either a property or a field.  Currently we place the property
                // suggestion first (to help users with the immutable object+property pattern). But
                // we could consider swapping this if people prefer creating private fields more.
                return(ImmutableArray.Create <CodeAction>(
                           new MyCodeAction(string.Format(FeaturesResources.Create_and_initialize_property_0, property.Name),
                                            c => AddSymbolInitializationAsync(document, parameter, functionDeclaration, method, blockStatementOpt, property, c)),
                           new MyCodeAction(string.Format(FeaturesResources.Create_and_initialize_field_0, field.Name),
                                            c => AddSymbolInitializationAsync(document, parameter, functionDeclaration, method, blockStatementOpt, field, c))));
            }
        }
 protected abstract SyntaxNode TryGetLastStatement(IBlockOperation blockStatementOpt);
 protected override Task <ImmutableArray <CodeAction> > GetRefactoringsForAllParametersAsync(Document document, SyntaxNode functionDeclaration, IMethodSymbol method, IBlockOperation blockStatementOpt, ImmutableArray <SyntaxNode> listOfParameterNodes, int position, CancellationToken cancellationToken)
 {
     return(Task.FromResult(ImmutableArray <CodeAction> .Empty));
 }