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()); }
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) {
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)))); }
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); }
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()); }
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); }
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) {
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);
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); }
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); }
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--; } }
/// <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); }
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)); }
/* * 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); }
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)); }