[clang] b5b2aec - [analyzer] Add UnarySymExpr
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Thu May 26 05:17:14 PDT 2022
Author: Gabor Marton
Date: 2022-05-26T14:00:27+02:00
New Revision: b5b2aec1ff51197343dede9741ea377f8314d41b
URL: https://github.com/llvm/llvm-project/commit/b5b2aec1ff51197343dede9741ea377f8314d41b
DIFF: https://github.com/llvm/llvm-project/commit/b5b2aec1ff51197343dede9741ea377f8314d41b.diff
LOG: [analyzer] Add UnarySymExpr
This patch adds a new descendant to the SymExpr hierarchy. This way, now
we can assign constraints to symbolic unary expressions. Only the unary
minus and bitwise negation are handled.
Differential Revision: https://reviews.llvm.org/D125318
Added:
clang/test/Analysis/unary-sym-expr.c
Modified:
clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
clang/test/Analysis/explain-svals.cpp
clang/test/Analysis/expr-inspection.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
index ad64cfc742dca..4c9c8239113e3 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
+++ b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
@@ -135,6 +135,11 @@ class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
" (" + Visit(S->getRHS()) + ")";
}
+ std::string VisitUnarySymExpr(const UnarySymExpr *S) {
+ return std::string(UnaryOperator::getOpcodeStr(S->getOpcode())) + " (" +
+ Visit(S->getOperand()) + ")";
+ }
+
// TODO: SymbolCast doesn't appear in practice.
// Add the relevant code once it does.
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
index d0bbf1750d7e5..2154a1dedeeeb 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -153,6 +153,9 @@ class SValBuilder {
SVal makeSymExprValNN(BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs, QualType resultTy);
+ SVal evalUnaryOp(ProgramStateRef state, UnaryOperator::Opcode opc,
+ SVal operand, QualType type);
+
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type);
@@ -350,6 +353,9 @@ class SValBuilder {
nonloc::SymbolVal makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
const SymExpr *rhs, QualType type);
+ NonLoc makeNonLoc(const SymExpr *operand, UnaryOperator::Opcode op,
+ QualType type);
+
/// Create a NonLoc value for cast.
nonloc::SymbolVal makeNonLoc(const SymExpr *operand, QualType fromTy,
QualType toTy);
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
index c71cb88f5574c..3ba3aa5c01566 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h
@@ -309,6 +309,55 @@ class SymbolCast : public SymExpr {
}
};
+/// Represents a symbolic expression involving a unary operator.
+class UnarySymExpr : public SymExpr {
+ const SymExpr *Operand;
+ UnaryOperator::Opcode Op;
+ QualType T;
+
+public:
+ UnarySymExpr(const SymExpr *In, UnaryOperator::Opcode Op, QualType T)
+ : SymExpr(UnarySymExprKind), Operand(In), Op(Op), T(T) {
+ // Note, some unary operators are modeled as a binary operator. E.g. ++x is
+ // modeled as x + 1.
+ assert((Op == UO_Minus || Op == UO_Not) && "non-supported unary expression");
+ // Unary expressions are results of arithmetic. Pointer arithmetic is not
+ // handled by unary expressions, but it is instead handled by applying
+ // sub-regions to regions.
+ assert(isValidTypeForSymbol(T) && "non-valid type for unary symbol");
+ assert(!Loc::isLocType(T) && "unary symbol should be nonloc");
+ }
+
+ unsigned computeComplexity() const override {
+ if (Complexity == 0)
+ Complexity = 1 + Operand->computeComplexity();
+ return Complexity;
+ }
+
+ const SymExpr *getOperand() const { return Operand; }
+ UnaryOperator::Opcode getOpcode() const { return Op; }
+ QualType getType() const override { return T; }
+
+ void dumpToStream(raw_ostream &os) const override;
+
+ static void Profile(llvm::FoldingSetNodeID &ID, const SymExpr *In,
+ UnaryOperator::Opcode Op, QualType T) {
+ ID.AddInteger((unsigned)UnarySymExprKind);
+ ID.AddPointer(In);
+ ID.AddInteger(Op);
+ ID.Add(T);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) override {
+ Profile(ID, Operand, Op, T);
+ }
+
+ // Implement isa<T> support.
+ static bool classof(const SymExpr *SE) {
+ return SE->getKind() == UnarySymExprKind;
+ }
+};
+
/// Represents a symbolic expression involving a binary operator
class BinarySymExpr : public SymExpr {
BinaryOperator::Opcode Op;
@@ -486,6 +535,9 @@ class SymbolManager {
const SymSymExpr *getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op,
const SymExpr *rhs, QualType t);
+ const UnarySymExpr *getUnarySymExpr(const SymExpr *operand,
+ UnaryOperator::Opcode op, QualType t);
+
QualType getType(const SymExpr *SE) const {
return SE->getType();
}
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
index 7163a16263ab8..b93f8e2501559 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def
@@ -33,6 +33,8 @@
#define SYMBOL_RANGE(Id, First, Last)
#endif
+SYMBOL(UnarySymExpr, SymExpr)
+
ABSTRACT_SYMBOL(BinarySymExpr, SymExpr)
SYMBOL(IntSymExpr, BinarySymExpr)
SYMBOL(SymIntExpr, BinarySymExpr)
diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index ea72d1dbb317c..1c33648b2b321 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -474,6 +474,14 @@ class SymbolExpressor
return None;
}
+ Optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) {
+ if (Optional<std::string> Str = lookup(S))
+ return Str;
+ if (Optional<std::string> Str = Visit(S->getOperand()))
+ return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str();
+ return None;
+ }
+
Optional<std::string> VisitSymbolCast(const SymbolCast *S) {
if (Optional<std::string> Str = lookup(S))
return Str;
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index ea66bb3780f61..ae590a3b73387 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -102,6 +102,13 @@ nonloc::SymbolVal SValBuilder::makeNonLoc(const SymExpr *lhs,
return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
}
+NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, UnaryOperator::Opcode op,
+ QualType type) {
+ assert(operand);
+ assert(!Loc::isLocType(type));
+ return nonloc::SymbolVal(SymMgr.getUnarySymExpr(operand, op, type));
+}
+
nonloc::SymbolVal SValBuilder::makeNonLoc(const SymExpr *operand,
QualType fromTy, QualType toTy) {
assert(operand);
@@ -434,6 +441,19 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
return UnknownVal();
}
+SVal SValBuilder::evalUnaryOp(ProgramStateRef state, UnaryOperator::Opcode opc,
+ SVal operand, QualType type) {
+ auto OpN = operand.getAs<NonLoc>();
+ if (!OpN)
+ return UnknownVal();
+
+ if (opc == UO_Minus)
+ return evalMinus(*OpN);
+ if (opc == UO_Not)
+ return evalComplement(*OpN);
+ llvm_unreachable("Unexpected unary operator");
+}
+
SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type) {
if (lhs.isUndef() || rhs.isUndef())
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 03a751845fe1e..cfc8816a0ecba 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -90,6 +90,9 @@ SVal SimpleSValBuilder::evalMinus(NonLoc val) {
switch (val.getSubKind()) {
case nonloc::ConcreteIntKind:
return val.castAs<nonloc::ConcreteInt>().evalMinus(*this);
+ case nonloc::SymbolValKind:
+ return makeNonLoc(val.castAs<nonloc::SymbolVal>().getSymbol(), UO_Minus,
+ val.getType(Context));
default:
return UnknownVal();
}
@@ -99,6 +102,9 @@ SVal SimpleSValBuilder::evalComplement(NonLoc X) {
switch (X.getSubKind()) {
case nonloc::ConcreteIntKind:
return X.castAs<nonloc::ConcreteInt>().evalComplement(*this);
+ case nonloc::SymbolValKind:
+ return makeNonLoc(X.castAs<nonloc::SymbolVal>().getSymbol(), UO_Not,
+ X.getType(Context));
default:
return UnknownVal();
}
@@ -1343,6 +1349,20 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) {
S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
}
+ // FIXME add VisitSymbolCast
+
+ SVal VisitUnarySymExpr(const UnarySymExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+ SVal Op = getConstOrVisit(S->getOperand());
+ if (isUnchanged(S->getOperand(), Op))
+ return skip(S);
+
+ return cache(
+ S, SVB.evalUnaryOp(State, S->getOpcode(), Op, S->getType()));
+ }
+
SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }
SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); }
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 59964ad5ab3b7..2227bd324adc4 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -70,6 +70,16 @@ void SymbolCast::dumpToStream(raw_ostream &os) const {
os << ')';
}
+void UnarySymExpr::dumpToStream(raw_ostream &os) const {
+ os << UnaryOperator::getOpcodeStr(Op);
+ bool Binary = isa<BinarySymExpr>(Operand);
+ if (Binary)
+ os << '(';
+ Operand->dumpToStream(os);
+ if (Binary)
+ os << ')';
+}
+
void SymbolConjured::dumpToStream(raw_ostream &os) const {
os << getKindStr() << getSymbolID() << '{' << T << ", LC" << LCtx->getID();
if (S)
@@ -134,6 +144,9 @@ void SymExpr::symbol_iterator::expand() {
case SymExpr::SymbolCastKind:
itr.push_back(cast<SymbolCast>(SE)->getOperand());
return;
+ case SymExpr::UnarySymExprKind:
+ itr.push_back(cast<UnarySymExpr>(SE)->getOperand());
+ return;
case SymExpr::SymIntExprKind:
itr.push_back(cast<SymIntExpr>(SE)->getLHS());
return;
@@ -306,6 +319,22 @@ const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs,
return cast<SymSymExpr>(data);
}
+const UnarySymExpr *SymbolManager::getUnarySymExpr(const SymExpr *Operand,
+ UnaryOperator::Opcode Opc,
+ QualType T) {
+ llvm::FoldingSetNodeID ID;
+ UnarySymExpr::Profile(ID, Operand, Opc, T);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+ if (!data) {
+ data = (UnarySymExpr *)BPAlloc.Allocate<UnarySymExpr>();
+ new (data) UnarySymExpr(Operand, Opc, T);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<UnarySymExpr>(data);
+}
+
QualType SymbolConjured::getType() const {
return T;
}
@@ -465,6 +494,9 @@ bool SymbolReaper::isLive(SymbolRef sym) {
case SymExpr::SymbolCastKind:
KnownLive = isLive(cast<SymbolCast>(sym)->getOperand());
break;
+ case SymExpr::UnarySymExprKind:
+ KnownLive = isLive(cast<UnarySymExpr>(sym)->getOperand());
+ break;
}
if (KnownLive)
diff --git a/clang/test/Analysis/explain-svals.cpp b/clang/test/Analysis/explain-svals.cpp
index 44cd66b491ea7..30368b6976cc2 100644
--- a/clang/test/Analysis/explain-svals.cpp
+++ b/clang/test/Analysis/explain-svals.cpp
@@ -72,6 +72,7 @@ void test_3(S s) {
void test_4(int x, int y) {
int z;
static int stat;
+ clang_analyzer_explain(-x); // expected-warning-re{{{{^\- \(argument 'x'\)$}}}}
clang_analyzer_explain(x + 1); // expected-warning-re{{{{^\(argument 'x'\) \+ 1$}}}}
clang_analyzer_explain(1 + y); // expected-warning-re{{{{^\(argument 'y'\) \+ 1$}}}}
clang_analyzer_explain(x + y); // expected-warning-re{{{{^\(argument 'x'\) \+ \(argument 'y'\)$}}}}
diff --git a/clang/test/Analysis/expr-inspection.cpp b/clang/test/Analysis/expr-inspection.cpp
index bf8ce5f7681ed..3e65305032927 100644
--- a/clang/test/Analysis/expr-inspection.cpp
+++ b/clang/test/Analysis/expr-inspection.cpp
@@ -18,6 +18,8 @@ void foo(int x, unsigned y) {
clang_analyzer_express(x); // expected-warning{{Unable to express}}
clang_analyzer_denote(x, "$x");
+ clang_analyzer_express(-x); // expected-warning{{-$x}}
+
clang_analyzer_denote(y, "$y");
clang_analyzer_express(x + y); // expected-warning{{$x + $y}}
diff --git a/clang/test/Analysis/unary-sym-expr.c b/clang/test/Analysis/unary-sym-expr.c
new file mode 100644
index 0000000000000..14b3219de3723
--- /dev/null
+++ b/clang/test/Analysis/unary-sym-expr.c
@@ -0,0 +1,38 @@
+// RUN: %clang_analyze_cc1 %s \
+// RUN: -analyzer-checker=core,debug.ExprInspection \
+// RUN: -analyzer-config eagerly-assume=false \
+// RUN: -verify
+
+void clang_analyzer_eval(int);
+void clang_analyzer_dump(int);
+
+int test(int x, int y) {
+
+ clang_analyzer_dump(-x); // expected-warning{{-reg_$0<int x>}}
+ clang_analyzer_dump(~x); // expected-warning{{~reg_$0<int x>}}
+ int z = x + y;
+ clang_analyzer_dump(-z); // expected-warning{{-((reg_$0<int x>) + (reg_$1<int y>))}}
+ clang_analyzer_dump(-(x + y)); // expected-warning{{-((reg_$0<int x>) + (reg_$1<int y>))}}
+ clang_analyzer_dump(-x + y); // expected-warning{{(-reg_$0<int x>) + (reg_$1<int y>)}}
+
+ if (-x == 0) {
+ clang_analyzer_eval(-x == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(-x > 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(-x < 0); // expected-warning{{FALSE}}
+ }
+ if (~y == 0) {
+ clang_analyzer_eval(~y == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(~y > 0); // expected-warning{{FALSE}}
+ clang_analyzer_eval(~y < 0); // expected-warning{{FALSE}}
+ }
+ (void)(x);
+ return 42;
+}
+
+void test_svalbuilder_simplification(int x, int y) {
+ if (x + y != 3)
+ return;
+ clang_analyzer_eval(-(x + y) == -3); // expected-warning{{TRUE}}
+ // FIXME Commutativity is not supported yet.
+ clang_analyzer_eval(-(y + x) == -3); // expected-warning{{UNKNOWN}}
+}
More information about the cfe-commits
mailing list