public override void VisitLocalReference(ILocalReferenceOperation operation) { var local = operation.Local; var isDeclaration = operation.IsDeclaration; base.VisitLocalReference(operation); }
private void AnalyzeAssignment(OperationAnalysisContext context) { var assignmentOperation = (ISimpleAssignmentOperation)context.Operation; // Check if there are more then one assignment in a statement if (assignmentOperation.Target is not IMemberReferenceOperation operationTarget) { return; } // This analyzer makes sense only for reference type objects if (operationTarget.Instance?.Type?.IsReferenceType != true) { return; } bool isViolationFound = operationTarget.Instance switch { ILocalReferenceOperation localInstance => AnalyzeAssignmentToMember(assignmentOperation, localInstance, (a, b) => a.Local.Equals(b.Local)), IMemberReferenceOperation memberInstance => AnalyzeAssignmentToMember(assignmentOperation, memberInstance, (a, b) => a.Member.Equals(b.Member) && a.Instance?.Syntax.ToString() == b.Instance?.Syntax.ToString()), IParameterReferenceOperation parameterInstance => AnalyzeAssignmentToMember(assignmentOperation, parameterInstance, (a, b) => a.Parameter.Equals(b.Parameter)), _ => false, }; if (isViolationFound) { var diagnostic = operationTarget.CreateDiagnostic(Rule, operationTarget.Instance.Syntax, operationTarget.Member.Name); context.ReportDiagnostic(diagnostic); } }
// Local functions static bool IsArgumentValueEqual(IOperation targetArg, IOperation valueArg) { // Check if arguments are identical constant/local/parameter reference operations. // 1. Not identical: 'this[i] = this[j]' // 2. Identical: 'this[i] = this[i]', 'this[0] = this[0]' if (targetArg.Kind != valueArg.Kind) { return(false); } if (targetArg.ConstantValue.HasValue != valueArg.ConstantValue.HasValue) { return(false); } if (targetArg.ConstantValue.HasValue) { return(Equals(targetArg.ConstantValue.Value, valueArg.ConstantValue.Value)); } #pragma warning disable IDE0055 // Fix formatting - Does not seem to be handling switch expressions. return(targetArg switch { ILocalReferenceOperation targetLocalReference => Equals(targetLocalReference.Local, ((ILocalReferenceOperation)valueArg).Local), IParameterReferenceOperation targetParameterReference => Equals(targetParameterReference.Parameter, ((IParameterReferenceOperation)valueArg).Parameter), _ => false, });
public override IdentifierInfo VisitLocalReference([NotNull] ILocalReferenceOperation operation, [CanBeNull] object argument) { var name = new IdentifierName(operation.Local.Name, operation.Local.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat)); return(new IdentifierInfo(name, operation.Local.Type, "Variable")); }
public override void VisitLocalReference(ILocalReferenceOperation operation) { Assert.Equal(OperationKind.LocalReference, operation.Kind); Assert.NotNull(operation.Local); var isDeclaration = operation.IsDeclaration; Assert.Empty(operation.Children); }
private EvaluationResult AnalyzeLocalReference([NotNull] ILocalReferenceOperation local) { var assignmentWalker = new VariableAssignmentWalker(local.Local, this); assignmentWalker.VisitMethod(); return(assignmentWalker.Result); }
private static ILocalSymbol TryGetTupleElement([NotNull] IOperation elementOperation) { ILocalReferenceOperation localReference = elementOperation is IDeclarationExpressionOperation declarationExpression ? declarationExpression.Expression as ILocalReferenceOperation : elementOperation as ILocalReferenceOperation; return(localReference != null && localReference.IsDeclaration ? localReference.Local : null); }
internal static ISymbol?GetUnderlyingSymbol(IOperation?operation) { return(operation switch { IParameterReferenceOperation paramRef => paramRef.Parameter, ILocalReferenceOperation localRef => localRef.Local, IMemberReferenceOperation memberRef => memberRef.Member, _ => null, });
public override void VisitLocalReference(ILocalReferenceOperation operation) { if (operation.SemanticModel != null) { FindFromLocalSymbol(operation.SemanticModel, operation.Local); } base.VisitLocalReference(operation); }
protected override ITypeSymbol GetSymbolType( SemanticModel semanticModel, ISymbol symbol ) { var selectionOperation = semanticModel.GetOperation( SelectionResult.GetContainingScope() ); switch (symbol) { case ILocalSymbol localSymbol when localSymbol.NullableAnnotation == NullableAnnotation.Annotated: case IParameterSymbol parameterSymbol when parameterSymbol.NullableAnnotation == NullableAnnotation.Annotated: // For local symbols and parameters, we can check what the flow state // for refences to the symbols are and determine if we can change // the nullability to a less permissive state. var references = selectionOperation .DescendantsAndSelf() .Where(IsSymbolReferencedByOperation); if (AreAllReferencesNotNull(references)) { return(base.GetSymbolType(semanticModel, symbol) .WithNullableAnnotation(NullableAnnotation.NotAnnotated)); } return(base.GetSymbolType(semanticModel, symbol)); default: return(base.GetSymbolType(semanticModel, symbol)); } bool AreAllReferencesNotNull(IEnumerable <IOperation> references) => references.All( r => semanticModel.GetTypeInfo(r.Syntax).Nullability.FlowState == NullableFlowState.NotNull ); bool IsSymbolReferencedByOperation(IOperation operation) => operation switch { ILocalReferenceOperation localReference => localReference.Local.Equals(symbol), IParameterReferenceOperation parameterReference => parameterReference.Parameter.Equals(symbol), IAssignmentOperation assignment => IsSymbolReferencedByOperation(assignment.Target), _ => false }; } }
private static IOperation?GetLocalReferenceDeclaringOperation(ILocalReferenceOperation localReference, SemanticModel semanticModel, CancellationToken token) { var localSymbolDeclaringSyntax = localReference.Local.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(token); if (localSymbolDeclaringSyntax != null) { return(semanticModel.GetOperation(localSymbolDeclaringSyntax)); } return(null); }
public override void VisitLocalReference([NotNull] ILocalReferenceOperation operation) { if (IsInvokingDelegateVariable(operation)) { return; } var assignmentWalker = new VariableAssignmentWalker(operation.Local, operation.Syntax.GetLocation(), owner); assignmentWalker.VisitBlockBody(); Result.CopyFrom(assignmentWalker.Result); }
private Task VisitReferenceAsync(IOperation operation, CancellationToken cancellationToken) { Debug.Assert(operation is ILocalReferenceOperation or IParameterReferenceOperation or IFieldReferenceOperation or IPropertyReferenceOperation); if (IsContainedIn <IArgumentOperation>(operation, out var argumentOperation) && argumentOperation.Parameter is not null) { if (argumentOperation.Parameter.IsRefOrOut()) { // Always add ref or out parameters to track as assignments since the values count as // assignments across method calls for the purposes of value tracking. return(AddOperationAsync(operation, argumentOperation.Parameter, cancellationToken)); } // If the parameter is not a ref or out param, track the reference assignments that count // as input to the argument being passed to the method. return(AddReference(operation, cancellationToken)); } if (IsContainedIn <IReturnOperation>(operation) || IsContainedIn <IAssignmentOperation>(operation)) { // If the reference is part of a return operation or assignment operation we want to track where the values come from // since they contribute to the "output" of the method/assignment and are relavent for value tracking. return(AddReference(operation, cancellationToken)); } return(Task.CompletedTask); Task AddReference(IOperation operation, CancellationToken cancellationToken) => operation switch { IParameterReferenceOperation parameterReference => AddOperationAsync(operation, parameterReference.Parameter, cancellationToken), IFieldReferenceOperation fieldReferenceOperation => AddOperationAsync(operation, fieldReferenceOperation.Member, cancellationToken), IPropertyReferenceOperation propertyReferenceOperation => AddOperationAsync(operation, propertyReferenceOperation.Member, cancellationToken), ILocalReferenceOperation localReferenceOperation => AddOperationAsync(operation, localReferenceOperation.Local, cancellationToken), _ => Task.CompletedTask }; }
private static bool IsReassignedInCatch(ICatchClauseOperation catchClause, ILocalReferenceOperation localReference) { var dataflow = catchClause.Language == LanguageNames.CSharp ? catchClause.SemanticModel.AnalyzeDataFlow(catchClause.Handler.Syntax) : catchClause.SemanticModel.AnalyzeDataFlow(catchClause.Handler.Operations[0].Syntax, catchClause.Handler.Operations[^ 1].Syntax);
public override GlobalFlowStateDictionaryAnalysisValue VisitLocalReference(ILocalReferenceOperation operation, object?argument) { var value = base.VisitLocalReference(operation, argument); return(VisitLocalOrParameter(operation.Local.Type?.OriginalDefinition, operation, value)); }
public virtual void VisitLocalReference(ILocalReferenceOperation operation) { DefaultVisit(operation); }
public override void VisitLocalReference([NotNull] ILocalReferenceOperation operation) { base.VisitLocalReference(operation); }
public override TAbstractAnalysisValue VisitLocalReference(ILocalReferenceOperation operation, object argument) { var value = base.VisitLocalReference(operation, argument); return(ComputeAnalysisValueForReferenceOperation(operation, value)); }
public override TAbstractAnalysisValue VisitLocalReference(ILocalReferenceOperation operation, object argument) { return(GetAbstractValue(operation.Local)); }
public override Scope VisitLocalReference(ILocalReferenceOperation operation, Scope currentScope) => currentScope.Union( new LocalValue(operation.Local) );
public override bool VisitLocalReference([NotNull] ILocalReferenceOperation operation1, [CanBeNull] IOperation argument) { return(argument is ILocalReferenceOperation operation2 && AreBaseOperationsEqual(operation1, operation2) && AreSymbolsEqual(operation1.Local, operation2.Local) && operation1.IsDeclaration == operation2.IsDeclaration); }
public override IOperation VisitLocalReference(ILocalReferenceOperation operation, object argument) { return(new LocalReferenceExpression(operation.Local, operation.IsDeclaration, ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit)); }
public override void VisitLocalReference(ILocalReferenceOperation operation) { Callback(operation.Local.Type); base.VisitLocalReference(operation); }
private static bool IsInvokingDelegateVariable([NotNull] ILocalReferenceOperation operation) { return(operation.Parent is IInvocationOperation); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(context => { if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemString, out INamedTypeSymbol? stringType) || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemChar, out INamedTypeSymbol? charType) || !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemStringComparison, out INamedTypeSymbol? stringComparisonType)) { return; } // First get all the string.IndexOf methods that we are interested in tagging var stringIndexOfMethods = stringType .GetMembers("IndexOf") .OfType <IMethodSymbol>() .WhereAsArray(s => s.Parameters.Length <= 2); var stringArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(stringType)); var charArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType)); var stringAndComparisonTypeArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(stringType), ParameterInfo.GetParameterInfo(stringComparisonType)); var charAndComparisonTypeArgumentIndexOfMethod = stringIndexOfMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType), ParameterInfo.GetParameterInfo(stringComparisonType)); // Check that the contains methods that take 2 parameters exist // string.Contains(char) is also .NETStandard2.1+ var stringContainsMethods = stringType .GetMembers("Contains") .OfType <IMethodSymbol>() .WhereAsArray(s => s.Parameters.Length <= 2); var stringAndComparisonTypeArgumentContainsMethod = stringContainsMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(stringType), ParameterInfo.GetParameterInfo(stringComparisonType)); var charAndComparisonTypeArgumentContainsMethod = stringContainsMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType), ParameterInfo.GetParameterInfo(stringComparisonType)); var charArgumentContainsMethod = stringContainsMethods.GetFirstOrDefaultMemberWithParameterInfos( ParameterInfo.GetParameterInfo(charType)); if (stringAndComparisonTypeArgumentContainsMethod == null || charAndComparisonTypeArgumentContainsMethod == null || charArgumentContainsMethod == null) { return; } // Roslyn doesn't yet support "FindAllReferences" at a file/block level. So instead, find references to local int variables in this block. context.RegisterOperationBlockStartAction(OnOperationBlockStart); return; void OnOperationBlockStart(OperationBlockStartAnalysisContext context) { if (context.OwningSymbol is not IMethodSymbol method) { return; } // Algorithm: // We aim to change string.IndexOf -> string.Contains // 1. We register 1 callback for invocations of IndexOf. // 1a. Check if invocation.Parent is a binary operation we care about (string.IndexOf >= 0 OR string.IndexOf == -1). If so, report a diagnostic and return from the callback. // 1b. Otherwise, check if invocation.Parent is a variable declarator. If so, add the invocation as a potential violation to track into variableNameToOperationsMap. // 2. We register another callback for local references // 2a. If the local reference is not a type int, bail out. // 2b. If the local reference operation's parent is not a binary operation, add it to "localsToBailOut". // 3. In an operation block end, we check if entries in "variableNameToOperationsMap" exist in "localToBailOut". If an entry is NOT present, we report a diagnostic at that invocation. PooledConcurrentSet <ILocalSymbol> localsToBailOut = PooledConcurrentSet <ILocalSymbol> .GetInstance(); PooledConcurrentDictionary <ILocalSymbol, IInvocationOperation> variableNameToOperationsMap = PooledConcurrentDictionary <ILocalSymbol, IInvocationOperation> .GetInstance(); context.RegisterOperationAction(PopulateLocalReferencesSet, OperationKind.LocalReference); context.RegisterOperationAction(AnalyzeInvocationOperation, OperationKind.Invocation); context.RegisterOperationBlockEndAction(OnOperationBlockEnd); return; // Local Functions void PopulateLocalReferencesSet(OperationAnalysisContext context) { ILocalReferenceOperation localReference = (ILocalReferenceOperation)context.Operation; if (localReference.Local.Type.SpecialType != SpecialType.System_Int32) { return; } var parent = localReference.Parent; if (parent is IBinaryOperation binaryOperation) { var otherOperand = binaryOperation.LeftOperand is ILocalReferenceOperation ? binaryOperation.RightOperand : binaryOperation.LeftOperand; if (CheckOperatorKindAndOperand(binaryOperation, otherOperand)) { // Do nothing. This is a valid case to the tagged in the analyzer return; } } localsToBailOut.Add(localReference.Local); } void AnalyzeInvocationOperation(OperationAnalysisContext context) { var invocationOperation = (IInvocationOperation)context.Operation; if (!IsDesiredTargetMethod(invocationOperation.TargetMethod)) { return; } var parent = invocationOperation.Parent; if (parent is IBinaryOperation binaryOperation) { var otherOperand = binaryOperation.LeftOperand is IInvocationOperation ? binaryOperation.RightOperand : binaryOperation.LeftOperand; if (CheckOperatorKindAndOperand(binaryOperation, otherOperand)) { context.ReportDiagnostic(binaryOperation.CreateDiagnostic(Rule)); } } else if (parent is IVariableInitializerOperation variableInitializer) { if (variableInitializer.Parent is IVariableDeclaratorOperation variableDeclaratorOperation) { variableNameToOperationsMap.TryAdd(variableDeclaratorOperation.Symbol, invocationOperation); } else if (variableInitializer.Parent is IVariableDeclarationOperation variableDeclarationOperation && variableDeclarationOperation.Declarators.Length == 1) { variableNameToOperationsMap.TryAdd(variableDeclarationOperation.Declarators[0].Symbol, invocationOperation); } } } static bool CheckOperatorKindAndOperand(IBinaryOperation binaryOperation, IOperation otherOperand) { var operatorKind = binaryOperation.OperatorKind; if (otherOperand.ConstantValue.HasValue && otherOperand.ConstantValue.Value is int intValue) { if ((operatorKind == BinaryOperatorKind.Equals && intValue < 0) || (operatorKind == BinaryOperatorKind.GreaterThanOrEqual && intValue == 0)) { // This is the only case we are targeting in this analyzer return(true); } } return(false); } void OnOperationBlockEnd(OperationBlockAnalysisContext context) { foreach (var variableNameAndLocation in variableNameToOperationsMap) { ILocalSymbol variable = variableNameAndLocation.Key; if (!localsToBailOut.Contains(variable)) { context.ReportDiagnostic(variableNameAndLocation.Value.CreateDiagnostic(Rule)); } } variableNameToOperationsMap.Free(context.CancellationToken); localsToBailOut.Free(context.CancellationToken); } bool IsDesiredTargetMethod(IMethodSymbol targetMethod) => targetMethod.Equals(stringArgumentIndexOfMethod) || targetMethod.Equals(charArgumentIndexOfMethod) || targetMethod.Equals(stringAndComparisonTypeArgumentIndexOfMethod) || targetMethod.Equals(charAndComparisonTypeArgumentIndexOfMethod); } });
/// <summary> /// コンストラクタ /// </summary> /// <param name="operation">IOperationインスタンス</param> /// <param name="container">イベントコンテナ</param> public LocalReference(ILocalReferenceOperation operation, EventContainer container) : base(container) { Expressions.Add(new Expression(operation.Local.Name, Expression.GetSymbolTypeName(operation.Local))); }
public override void VisitLocalReference([NotNull] ILocalReferenceOperation operation) { Result = owner.AnalyzeLocalReference(operation); }