private void SetValueFromPredicate( AnalysisEntity key, NullAbstractValue value, NullAnalysisData negatedCurrentAnalysisData, bool equals, bool inferInCurrentAnalysisData, bool inferInNegatedCurrentAnalysisData, ref PredicateValueKind predicateValueKind) { var negatedValue = NegatePredicateValue(value); if (CurrentAnalysisData.TryGetValue(key, out NullAbstractValue existingValue) && IsValidValueForPredicateAnalysis(existingValue) && (existingValue == NullAbstractValue.Null || value == NullAbstractValue.Null)) { if (value == existingValue && equals || negatedValue == existingValue && !equals) { predicateValueKind = PredicateValueKind.AlwaysTrue; negatedValue = NullAbstractValue.Invalid; inferInCurrentAnalysisData = false; } if (negatedValue == existingValue && equals || value == existingValue && !equals) { predicateValueKind = PredicateValueKind.AlwaysFalse; value = NullAbstractValue.Invalid; inferInNegatedCurrentAnalysisData = false; } } if (!equals) { if (value != NullAbstractValue.Invalid && negatedValue != NullAbstractValue.Invalid) { var temp = value; value = negatedValue; negatedValue = temp; } } if (inferInCurrentAnalysisData) { // Set value for the CurrentAnalysisData. SetAbstractValue(CurrentAnalysisData, key, value); } if (inferInNegatedCurrentAnalysisData) { // Set negated value for the NegatedCurrentAnalysisData. SetAbstractValue(negatedCurrentAnalysisData, key, negatedValue); } }
private void SetValueForComparisonOperator(IOperation target, IOperation assignedValue, bool equals, ref PredicateValueKind predicateValueKind, ValueContentAnalysisData targetAnalysisData) { ValueContentAbstractValue currentAssignedValue = GetCachedAbstractValue(assignedValue); if (currentAssignedValue.IsLiteralState && AnalysisEntityFactory.TryCreate(target, out AnalysisEntity targetEntity)) { if (CurrentAnalysisData.TryGetValue(targetEntity, out ValueContentAbstractValue existingTargetValue) && existingTargetValue.IsLiteralState) { var newValue = currentAssignedValue.IntersectLiteralValues(existingTargetValue); if (newValue.NonLiteralState == ValueContainsNonLiteralState.Invalid) { predicateValueKind = equals ? PredicateValueKind.AlwaysFalse : PredicateValueKind.AlwaysTrue; } else if (predicateValueKind != PredicateValueKind.AlwaysFalse && newValue.IsLiteralState && newValue.LiteralValues.Count == 1 && currentAssignedValue.LiteralValues.Count == 1 && existingTargetValue.LiteralValues.Count == 1) { predicateValueKind = equals ? PredicateValueKind.AlwaysTrue : PredicateValueKind.AlwaysFalse; } currentAssignedValue = newValue; } if (equals) { CopyAbstractValue copyValue = GetCopyAbstractValue(target); if (copyValue.Kind.IsKnown()) { // https://github.com/dotnet/roslyn-analyzers/issues/2106 tracks enabling the below assert. //Debug.Assert(copyValue.AnalysisEntities.Contains(targetEntity)); foreach (var analysisEntity in copyValue.AnalysisEntities) { SetAbstractValue(targetAnalysisData, analysisEntity, currentAssignedValue); } } else { SetAbstractValue(targetAnalysisData, targetEntity, currentAssignedValue); } } } }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { compilationContext.RegisterOperationBlockAction(operationBlockContext => { var owningSymbol = operationBlockContext.OwningSymbol; if (operationBlockContext.Options.IsConfiguredToSkipAnalysis(AlwaysTrueFalseOrNullRule, owningSymbol, operationBlockContext.Compilation, operationBlockContext.CancellationToken)) { return; } var processedOperationRoots = new HashSet <IOperation>(); foreach (var operationRoot in operationBlockContext.OperationBlocks) { static bool ShouldAnalyze(IOperation op) => (op as IBinaryOperation)?.IsComparisonOperator() == true || (op as IInvocationOperation)?.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean || op.Kind == OperationKind.Coalesce || op.Kind == OperationKind.ConditionalAccess || op.Kind == OperationKind.IsNull || op.Kind == OperationKind.IsPattern; if (operationRoot.HasAnyOperationDescendant(ShouldAnalyze)) { // Skip duplicate analysis from operation blocks for constructor initializer and body. if (!processedOperationRoots.Add(operationRoot.GetRoot())) { // Already processed. continue; } var cfg = operationBlockContext.GetControlFlowGraph(operationRoot); var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(operationBlockContext.Compilation); var valueContentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult(cfg, owningSymbol, wellKnownTypeProvider, operationBlockContext.Options, AlwaysTrueFalseOrNullRule, PointsToAnalysisKind.Complete, operationBlockContext.CancellationToken, out var copyAnalysisResultOpt, out var pointsToAnalysisResult); if (valueContentAnalysisResult == null || pointsToAnalysisResult == null) { continue; } foreach (var operation in cfg.DescendantOperations()) { // Skip implicit operations. // However, 'IsNull' operations are compiler generated operations corresponding to // non-implicit conditional access operations, so we should not skip them. if (operation.IsImplicit && operation.Kind != OperationKind.IsNull) { continue; } switch (operation.Kind) { case OperationKind.BinaryOperator: var binaryOperation = (IBinaryOperation)operation; PredicateValueKind predicateKind = GetPredicateKind(binaryOperation); if (predicateKind != PredicateValueKind.Unknown && (binaryOperation.LeftOperand is not IBinaryOperation leftBinary || GetPredicateKind(leftBinary) == PredicateValueKind.Unknown) && (binaryOperation.RightOperand is not IBinaryOperation rightBinary || GetPredicateKind(rightBinary) == PredicateValueKind.Unknown)) { ReportAlwaysTrueFalseOrNullDiagnostic(operation, predicateKind); } break; case OperationKind.Invocation: case OperationKind.IsPattern: predicateKind = GetPredicateKind(operation); if (predicateKind != PredicateValueKind.Unknown) { ReportAlwaysTrueFalseOrNullDiagnostic(operation, predicateKind); } break; case OperationKind.IsNull: // '{0}' is always/never '{1}'. Remove or refactor the condition(s) to avoid dead code. predicateKind = GetPredicateKind(operation); DiagnosticDescriptor rule; switch (predicateKind) { case PredicateValueKind.AlwaysTrue: rule = AlwaysTrueFalseOrNullRule; break; case PredicateValueKind.AlwaysFalse: rule = NeverNullRule; break; default: continue; } var originalOperation = operationRoot.SemanticModel.GetOperation(operation.Syntax, operationBlockContext.CancellationToken); if (originalOperation is IAssignmentOperation or IVariableDeclaratorOperation) { // Skip compiler generated IsNull operation for assignment/variable declaration within a using. continue; } var arg1 = operation.Syntax.ToString(); var arg2 = operation.Language == LanguageNames.VisualBasic ? "Nothing" : "null"; var diagnostic = operation.CreateDiagnostic(rule, arg1, arg2); operationBlockContext.ReportDiagnostic(diagnostic); break; } } PredicateValueKind GetPredicateKind(IOperation operation) { Debug.Assert(operation.Kind is OperationKind.BinaryOperator or OperationKind.Invocation or OperationKind.IsNull or OperationKind.IsPattern); RoslynDebug.Assert(pointsToAnalysisResult != null); RoslynDebug.Assert(valueContentAnalysisResult != null); if (operation is IBinaryOperation binaryOperation && binaryOperation.IsComparisonOperator() || operation.Type?.SpecialType == SpecialType.System_Boolean) { PredicateValueKind predicateKind = pointsToAnalysisResult.GetPredicateKind(operation); if (predicateKind != PredicateValueKind.Unknown) { return(predicateKind); } if (copyAnalysisResultOpt != null) { predicateKind = copyAnalysisResultOpt.GetPredicateKind(operation); if (predicateKind != PredicateValueKind.Unknown) { return(predicateKind); } } predicateKind = valueContentAnalysisResult.GetPredicateKind(operation); if (predicateKind != PredicateValueKind.Unknown) { return(predicateKind); } } return(PredicateValueKind.Unknown); } void ReportAlwaysTrueFalseOrNullDiagnostic(IOperation operation, PredicateValueKind predicateKind) { Debug.Assert(predicateKind != PredicateValueKind.Unknown); // '{0}' is always '{1}'. Remove or refactor the condition(s) to avoid dead code. var arg1 = operation.Syntax.ToString(); var arg2 = predicateKind == PredicateValueKind.AlwaysTrue ? (operation.Language == LanguageNames.VisualBasic ? "True" : "true") : (operation.Language == LanguageNames.VisualBasic ? "False" : "false"); var diagnostic = operation.CreateDiagnostic(AlwaysTrueFalseOrNullRule, arg1, arg2); operationBlockContext.ReportDiagnostic(diagnostic); } } }
protected override void SetPredicateValueKind(IOperation operation, TAnalysisData analysisData, PredicateValueKind predicateValueKind) { base.SetPredicateValueKind(operation, analysisData, predicateValueKind); if (predicateValueKind == PredicateValueKind.AlwaysFalse) { (analysisData as AnalysisEntityBasedPredicateAnalysisData <TAbstractAnalysisValue>).IsReachableBlockData = false; } }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext => { if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod)) { return; } foreach (var operationRoot in operationBlockStartContext.OperationBlocks) { IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock(); if (topmostBlock != null && topmostBlock.HasAnyOperationDescendant(op => (op as IBinaryOperation)?.IsComparisonOperator() == true || op.Kind == OperationKind.Coalesce || op.Kind == OperationKind.ConditionalAccess)) { var cfg = ControlFlowGraph.Create(topmostBlock); var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(operationBlockStartContext.Compilation); var nullAnalysisResult = NullAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider); var pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, nullAnalysisResult); var copyAnalysisResult = CopyAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, nullAnalysisResultOpt: nullAnalysisResult, pointsToAnalysisResultOpt: pointsToAnalysisResult); // Do another null analysis pass to improve the results from PointsTo and Copy analysis. nullAnalysisResult = NullAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, copyAnalysisResult, pointsToAnalysisResultOpt: pointsToAnalysisResult); var stringContentAnalysisResult = StringContentAnalysis.GetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider, copyAnalysisResult, nullAnalysisResult, pointsToAnalysisResult); operationBlockStartContext.RegisterOperationAction(operationContext => { PredicateValueKind GetPredicateKind(IBinaryOperation operation) { if (operation.IsComparisonOperator()) { PredicateValueKind binaryPredicateKind = nullAnalysisResult.GetPredicateKind(operation); if (binaryPredicateKind != PredicateValueKind.Unknown) { return(binaryPredicateKind); } binaryPredicateKind = copyAnalysisResult.GetPredicateKind(operation); if (binaryPredicateKind != PredicateValueKind.Unknown) { return(binaryPredicateKind); } binaryPredicateKind = stringContentAnalysisResult.GetPredicateKind(operation); if (binaryPredicateKind != PredicateValueKind.Unknown) { return(binaryPredicateKind); } ; } return(PredicateValueKind.Unknown); } var binaryOperation = (IBinaryOperation)operationContext.Operation; PredicateValueKind predicateKind = GetPredicateKind(binaryOperation); if (predicateKind != PredicateValueKind.Unknown && (!(binaryOperation.LeftOperand is IBinaryOperation leftBinary) || GetPredicateKind(leftBinary) == PredicateValueKind.Unknown) && (!(binaryOperation.RightOperand is IBinaryOperation rightBinary) || GetPredicateKind(rightBinary) == PredicateValueKind.Unknown)) { // '{0}' is always '{1}'. Remove or refactor the condition(s) to avoid dead code. var arg1 = binaryOperation.Syntax.ToString(); var arg2 = predicateKind == PredicateValueKind.AlwaysTrue ? (binaryOperation.Language == LanguageNames.VisualBasic ? "True" : "true") : (binaryOperation.Language == LanguageNames.VisualBasic ? "False" : "false"); var diagnostic = binaryOperation.CreateDiagnostic(AlwaysTrueFalseOrNullRule, arg1, arg2); operationContext.ReportDiagnostic(diagnostic); } }, OperationKind.BinaryOperator); operationBlockStartContext.RegisterOperationAction(operationContext => { IOperation nullCheckedOperation = operationContext.Operation.Kind == OperationKind.Coalesce ? ((ICoalesceOperation)operationContext.Operation).Value : ((IConditionalAccessOperation)operationContext.Operation).Operation; // '{0}' is always/never '{1}'. Remove or refactor the condition(s) to avoid dead code. DiagnosticDescriptor rule; switch (nullAnalysisResult[nullCheckedOperation]) { case NullAbstractValue.Null: rule = AlwaysTrueFalseOrNullRule; break; case NullAbstractValue.NotNull: rule = NeverNullRule; break; default: return; } var arg1 = nullCheckedOperation.Syntax.ToString(); var arg2 = nullCheckedOperation.Language == LanguageNames.VisualBasic ? "Nothing" : "null"; var diagnostic = nullCheckedOperation.CreateDiagnostic(rule, arg1, arg2); operationContext.ReportDiagnostic(diagnostic); }, OperationKind.Coalesce, OperationKind.ConditionalAccess); } } }); }); }
private void SetValueForComparisonOperator(IOperation target, IOperation assignedValue, StringContentAnalysisData negatedCurrentAnalysisData, bool equals, ref PredicateValueKind predicateValueKind) { var analysisData = equals ? CurrentAnalysisData : negatedCurrentAnalysisData; StringContentAbstractValue stringContentValue = GetCachedAbstractValue(assignedValue); if (stringContentValue.IsLiteralState && AnalysisEntityFactory.TryCreate(target, out AnalysisEntity targetEntity)) { if (analysisData.TryGetValue(targetEntity, out StringContentAbstractValue existingValue) && existingValue.IsLiteralState) { var newStringContentValue = stringContentValue.IntersectLiteralValues(existingValue); if (newStringContentValue.NonLiteralState == StringContainsNonLiteralState.Invalid) { predicateValueKind = equals ? PredicateValueKind.AlwaysFalse : PredicateValueKind.AlwaysTrue; } else if (predicateValueKind != PredicateValueKind.AlwaysFalse && newStringContentValue.IsLiteralState && newStringContentValue.LiteralValues.Count == 1 && stringContentValue.LiteralValues.Count == 1 && existingValue.LiteralValues.Count == 1) { predicateValueKind = equals ? PredicateValueKind.AlwaysTrue : PredicateValueKind.AlwaysFalse; } stringContentValue = newStringContentValue; } CopyAbstractValue copyValue = GetCopyAbstractValue(target); if (copyValue.Kind == CopyAbstractValueKind.Known) { Debug.Assert(copyValue.AnalysisEntities.Contains(targetEntity)); foreach (var analysisEntity in copyValue.AnalysisEntities) { SetAbstractValue(analysisData, analysisEntity, stringContentValue); } } else { SetAbstractValue(analysisData, targetEntity, stringContentValue); } } }
protected override void SetPredicateValueKind(IOperation operation, TAnalysisData analysisData, PredicateValueKind predicateValueKind) { base.SetPredicateValueKind(operation, analysisData, predicateValueKind); if (predicateValueKind == PredicateValueKind.AlwaysFalse) { analysisData.IsReachableBlockData = false; } }
private bool SetValueForComparisonOperator(IOperation target, IOperation assignedValue, NullAnalysisData negatedCurrentAnalysisData, bool equals, ref PredicateValueKind predicateValueKind) { NullAbstractValue nullValue = GetNullAbstractValue(assignedValue); if (IsValidValueForPredicateAnalysis(nullValue) && AnalysisEntityFactory.TryCreate(target, out AnalysisEntity targetEntity)) { bool inferInCurrentAnalysisData = true; bool inferInNegatedCurrentAnalysisData = true; if (nullValue == NullAbstractValue.NotNull) { // Comparison with a non-null value guarantees that we can infer result in only one of the branches. // For example, predicate "a == c", where we know 'c' is non-null, guarantees 'a' is non-null in CurrentAnalysisData, // but we cannot infer anything about nullness of 'a' in NegatedCurrentAnalysisData. if (equals) { inferInNegatedCurrentAnalysisData = false; } else { inferInCurrentAnalysisData = false; } } CopyAbstractValue copyValue = GetCopyAbstractValue(target); if (copyValue.Kind == CopyAbstractValueKind.Known) { Debug.Assert(copyValue.AnalysisEntities.Contains(targetEntity)); foreach (var analysisEntity in copyValue.AnalysisEntities) { SetValueFromPredicate(analysisEntity, nullValue, negatedCurrentAnalysisData, equals, inferInCurrentAnalysisData, inferInNegatedCurrentAnalysisData, ref predicateValueKind); } } else { SetValueFromPredicate(targetEntity, nullValue, negatedCurrentAnalysisData, equals, inferInCurrentAnalysisData, inferInNegatedCurrentAnalysisData, ref predicateValueKind); } return(true); } return(false); }
private void SetValueFromPredicate( AnalysisEntity key, NullAbstractValue value, PointsToAnalysisData negatedCurrentAnalysisData, bool equals, bool inferInCurrentAnalysisData, bool inferInNegatedCurrentAnalysisData, IOperation target, ref PredicateValueKind predicateValueKind) { // Compute the negated value. NullAbstractValue negatedValue = NegatePredicateValue(value); // Check if the key already has an existing "Null" or "NotNull" NullState that would make the condition always true or false. // If so, set the predicateValueKind to always true/false, set the value in branch that can never be taken to NullAbstractValue.Invalid // and turn off value inference in one of the branch. if (CurrentAnalysisData.TryGetValue(key, out PointsToAbstractValue existingPointsToValue)) { NullAbstractValue existingNullValue = existingPointsToValue.NullState; if (IsValidValueForPredicateAnalysis(existingNullValue) && (existingNullValue == NullAbstractValue.Null || value == NullAbstractValue.Null)) { if (value == existingNullValue && equals || negatedValue == existingNullValue && !equals) { predicateValueKind = PredicateValueKind.AlwaysTrue; negatedValue = NullAbstractValue.Invalid; inferInCurrentAnalysisData = false; } if (negatedValue == existingNullValue && equals || value == existingNullValue && !equals) { predicateValueKind = PredicateValueKind.AlwaysFalse; value = NullAbstractValue.Invalid; inferInNegatedCurrentAnalysisData = false; } } } // Swap value and negatedValue if we are processing not-equals operator. if (!equals) { if (value != NullAbstractValue.Invalid && negatedValue != NullAbstractValue.Invalid) { var temp = value; value = negatedValue; negatedValue = temp; } } if (inferInCurrentAnalysisData) { // Set value for the CurrentAnalysisData. SetAbstractValueFromPredicate(CurrentAnalysisData, key, target, value); } if (inferInNegatedCurrentAnalysisData) { // Set negated value for the NegatedCurrentAnalysisData. SetAbstractValueFromPredicate(negatedCurrentAnalysisData, key, target, negatedValue); } }