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);
                            }
                        }
                    }
Example #4
0
 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);
                        }
                    }
                });
            });
        }
Example #6
0
            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);
            }
Example #9
0
            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);
                }
            }