Skip to content

Commit 5f8dca0

Browse files
author
Gabor Marton
committed
[Analyzer] Extend ConstraintAssignor to handle remainder op
Summary: `a % b != 0` implies that `a != 0` for any `a` and `b`. This patch extends the ConstraintAssignor to do just that. In fact, we could do something similar with division and in case of multiplications we could have some other inferences, but I'd like to keep these for future patches. Fixes https://bugs.llvm.org/show_bug.cgi?id=51940 Reviewers: noq, vsavchenko, steakhal, szelethus, asdenyspetrov Subscribers: Differential Revision: https://reviews.llvm.org/D110357
1 parent e2a2c83 commit 5f8dca0

File tree

3 files changed

+99
-3
lines changed

3 files changed

+99
-3
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ class RangeSet {
282282
/// where N = size(this)
283283
bool contains(llvm::APSInt Point) const { return containsImpl(Point); }
284284

285+
bool containsZero() const {
286+
APSIntType T{getMinValue()};
287+
return contains(T.getZeroValue());
288+
}
289+
285290
void dump(raw_ostream &OS) const;
286291
void dump() const;
287292

clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,28 @@ class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> {
16101610
return Assignor.assign(CoS, NewConstraint);
16111611
}
16121612

1613+
/// Handle expressions like: a % b != 0.
1614+
template <typename SymT>
1615+
bool handleRemainderOp(const SymT *Sym, RangeSet Constraint) {
1616+
if (Sym->getOpcode() != BO_Rem)
1617+
return true;
1618+
const SymbolRef LHS = Sym->getLHS();
1619+
const llvm::APSInt &Zero =
1620+
Builder.getBasicValueFactory().getValue(0, Sym->getType());
1621+
// a % b != 0 implies that a != 0.
1622+
if (!Constraint.containsZero()) {
1623+
State = RCM.assumeSymNE(State, LHS, Zero, Zero);
1624+
if (!State)
1625+
return false;
1626+
}
1627+
return true;
1628+
}
1629+
16131630
inline bool assignSymExprToConst(const SymExpr *Sym, Const Constraint);
1631+
inline bool assignSymIntExprToRangeSet(const SymIntExpr *Sym,
1632+
RangeSet Constraint) {
1633+
return handleRemainderOp(Sym, Constraint);
1634+
}
16141635
inline bool assignSymSymExprToRangeSet(const SymSymExpr *Sym,
16151636
RangeSet Constraint);
16161637

@@ -1688,9 +1709,7 @@ class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> {
16881709
if (Constraint.getConcreteValue())
16891710
return !Constraint.getConcreteValue()->isZero();
16901711

1691-
APSIntType T{Constraint.getMinValue()};
1692-
Const Zero = T.getZeroValue();
1693-
if (!Constraint.contains(Zero))
1712+
if (!Constraint.containsZero())
16941713
return true;
16951714

16961715
return llvm::None;
@@ -1734,6 +1753,9 @@ bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
17341753

17351754
bool ConstraintAssignor::assignSymSymExprToRangeSet(const SymSymExpr *Sym,
17361755
RangeSet Constraint) {
1756+
if (!handleRemainderOp(Sym, Constraint))
1757+
return false;
1758+
17371759
Optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
17381760

17391761
if (!ConstraintAsBool)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: %clang_analyze_cc1 %s \
2+
// RUN: -analyzer-checker=core \
3+
// RUN: -analyzer-checker=debug.ExprInspection \
4+
// RUN: -verify
5+
6+
// expected-no-diagnostics
7+
8+
void clang_analyzer_warnIfReached();
9+
10+
void rem_constant_rhs_ne_zero(int x, int y) {
11+
if (x % 3 == 0) // x % 3 != 0 -> x != 0
12+
return;
13+
if (x * y != 0) // x * y == 0
14+
return;
15+
if (y != 1) // y == 1 -> x == 0
16+
return;
17+
clang_analyzer_warnIfReached(); // no-warning
18+
(void)x; // keep the constraints alive.
19+
}
20+
21+
void rem_symbolic_rhs_ne_zero(int x, int y, int z) {
22+
if (x % z == 0) // x % z != 0 -> x != 0
23+
return;
24+
if (x * y != 0) // x * y == 0
25+
return;
26+
if (y != 1) // y == 1 -> x == 0
27+
return;
28+
clang_analyzer_warnIfReached(); // no-warning
29+
(void)x; // keep the constraints alive.
30+
}
31+
32+
void rem_symbolic_rhs_ne_zero_nested(int w, int x, int y, int z) {
33+
if (w % x % z == 0) // w % x % z != 0 -> w % x != 0
34+
return;
35+
if (w % x * y != 0) // w % x * y == 0
36+
return;
37+
if (y != 1) // y == 1 -> w % x == 0
38+
return;
39+
clang_analyzer_warnIfReached(); // no-warning
40+
(void)(w * x); // keep the constraints alive.
41+
}
42+
43+
void rem_constant_rhs_ne_zero_early_contradiction(int x, int y) {
44+
if ((x + y) != 0) // (x + y) == 0
45+
return;
46+
if ((x + y) % 3 == 0) // (x + y) % 3 != 0 -> (x + y) != 0 -> contradiction
47+
return;
48+
clang_analyzer_warnIfReached(); // no-warning
49+
(void)x; // keep the constraints alive.
50+
}
51+
52+
void rem_symbolic_rhs_ne_zero_early_contradiction(int x, int y, int z) {
53+
if ((x + y) != 0) // (x + y) == 0
54+
return;
55+
if ((x + y) % z == 0) // (x + y) % z != 0 -> (x + y) != 0 -> contradiction
56+
return;
57+
clang_analyzer_warnIfReached(); // no-warning
58+
(void)x; // keep the constraints alive.
59+
}
60+
61+
void internal_unsigned_signed_mismatch(unsigned a) {
62+
int d = a;
63+
// Implicit casts are not handled, thus the analyzer models `d % 2` as
64+
// `(reg_$0<unsigned int a>) % 2`
65+
// However, this should not result in internal signedness mismatch error when
66+
// we assign new constraints below.
67+
if (d % 2 != 0)
68+
return;
69+
}

0 commit comments

Comments
 (0)