Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -3486,6 +3486,11 @@ static bool isBitwiseOp(Opcode Opc) { return Opc >= BO_And && Opc <= BO_Or; } bool isBitwiseOp() const { return isBitwiseOp(getOpcode()); } + static bool isBitwiseOrShiftOp(Opcode Opc) { + return isBitwiseOp(Opc) || isShiftOp(Opc); + } + bool isBitwiseOrShiftOp() const { return isBitwiseOrShiftOp(getOpcode()); } + static bool isRelationalOp(Opcode Opc) { return Opc >= BO_LT && Opc<=BO_GE; } bool isRelationalOp() const { return isRelationalOp(getOpcode()); } Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -117,6 +117,7 @@ const RangeSet &Other) const; RangeSet Negate(BasicValueFactory &BV, Factory &F) const; + void print() const { print(llvm::errs()); } void print(raw_ostream &os) const; bool operator==(const RangeSet &other) const { Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -443,16 +443,17 @@ /// Apply implicit constraints for bitwise OR- and AND-. /// For unsigned types, bitwise OR with a constant always returns /// a value greater-or-equal than the constant, and bitwise AND -/// returns a value less-or-equal then the constant. +/// returns a value less-or-equal than the constant. +/// Also apply the previously applied constraint range information of the +/// left-hand side operand of \p SIE in order to prevent false assumptions. /// -/// Pattern matches the expression \p Sym against those rule, +/// Pattern matches the expression \p SIE against those rules, /// and applies the required constraints. -/// \p Input Previously established expression range set -static RangeSet applyBitwiseConstraints( - BasicValueFactory &BV, - RangeSet::Factory &F, - RangeSet Input, - const SymIntExpr* SIE) { +/// \p Result Previously established expression range set +static RangeSet applyBitwiseConstraints(ProgramStateRef State, + BasicValueFactory &BV, + RangeSet::Factory &F, RangeSet Result, + const SymIntExpr *SIE) { QualType T = SIE->getType(); bool IsUnsigned = T->isUnsignedIntegerType(); const llvm::APSInt &RHS = SIE->getRHS(); @@ -461,19 +462,34 @@ // For unsigned types, the output of bitwise-or is bigger-or-equal than RHS. if (Operator == BO_Or && IsUnsigned) - return Input.Intersect(BV, F, RHS, BV.getMaxValue(T)); + return Result.Intersect(BV, F, RHS, BV.getMaxValue(T)); // Bitwise-or with a non-zero constant is always non-zero. if (Operator == BO_Or && RHS != Zero) - return assumeNonZero(BV, F, SIE, Input); + return assumeNonZero(BV, F, SIE, Result); // For unsigned types, or positive RHS, // bitwise-and output is always smaller-or-equal than RHS (assuming two's // complement representation of signed types). if (Operator == BO_And && (IsUnsigned || RHS >= Zero)) - return Input.Intersect(BV, F, BV.getMinValue(T), RHS); + Result = Result.Intersect(BV, F, BV.getMinValue(T), RHS); + + // For all of the bitwise operations, + // if they remain in that 'SymIntExpr' form that means we cannot evaluate the + // operation properly and they remain range-based. At this point we would + // introduce a completely new 'RangeSet' with new assumptions and we would + // forget about the previous constraint range information of the left-hand + // side operand of 'SIE'. Here we apply the previously applied range to the + // current range by intersecting them to prevent false assumptions. + ConstraintRangeTy Constraints = State->get(); + if (const RangeSet *PreviousRS = Constraints.lookup(SIE->getLHS())) { + if (!PreviousRS->isEmpty()) { + RangeSet::iterator I = PreviousRS->begin(); + Result = Result.Intersect(BV, F, I->From(), I->To()); + } + } - return Input; + return Result; } RangeSet RangeConstraintManager::getRange(ProgramStateRef State, @@ -506,8 +522,9 @@ return assumeNonZero(BV, F, Sym, Result); // Known constraints on ranges of bitwise expressions. - if (const SymIntExpr* SIE = dyn_cast(Sym)) - return applyBitwiseConstraints(BV, F, Result, SIE); + if (const SymIntExpr *SIE = dyn_cast(Sym)) + if (BinaryOperator::isBitwiseOrShiftOp(SIE->getOpcode())) + return applyBitwiseConstraints(State, BV, F, Result, SIE); return Result; } Index: clang/test/Analysis/bitwise-nullability.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/bitwise-nullability.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +// expected-no-diagnostics: +// We do not support evaluating bitwise operations, so that when we check for +// their results being null previously we did a state-split because it is a +// fresh new symbol and it could be null and non-null as well. The problem was +// the left-hand side operand of the operation already has constraint range +// informations which could contradicts with the current new assumption. +// From now we apply the constraint range informations of the left-hand side +// operand in order to prevent false assumptions. + +typedef unsigned long long uint64_t; + +void test_narrowing_down_range(uint64_t Magic) { + uint64_t MaskedMagic = Magic & (0xffffffffffffffffULL >> 13); + + if (Magic) { + // no-warning: 'Assuming 'Magic' is 0' was here. + return; + } + + if (MaskedMagic) { + // no-warning: 'Assuming 'MaskedMagic' is not equal to 0' was here. + (void)(1 / Magic); + } +}