diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -349,6 +349,9 @@ NonLoc 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 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 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -309,6 +309,52 @@ } }; +/// 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) { + assert(classof(this)); + // 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) && !Loc::isLocType(T)); + } + + 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 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 +532,9 @@ 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 --- 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/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -99,6 +99,13 @@ 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 SValBuilder::makeNonLoc(const SymExpr *operand, QualType fromTy, QualType toTy) { assert(operand); diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -90,6 +90,9 @@ switch (val.getSubKind()) { case nonloc::ConcreteIntKind: return val.castAs().evalMinus(*this); + case nonloc::SymbolValKind: + return makeNonLoc(val.castAs().getSymbol(), UO_Minus, + val.getType(Context)); default: return UnknownVal(); } diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp --- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -70,6 +70,11 @@ os << ')'; } +void UnarySymExpr::dumpToStream(raw_ostream &os) const { + os << UnaryOperator::getOpcodeStr(Op); + Operand->dumpToStream(os); +} + void SymbolConjured::dumpToStream(raw_ostream &os) const { os << getKindStr() << getSymbolID() << '{' << T << ", LC" << LCtx->getID(); if (S) @@ -134,6 +139,9 @@ case SymExpr::SymbolCastKind: itr.push_back(cast(SE)->getOperand()); return; + case SymExpr::UnarySymExprKind: + itr.push_back(cast(SE)->getOperand()); + return; case SymExpr::SymIntExprKind: itr.push_back(cast(SE)->getLHS()); return; @@ -306,6 +314,22 @@ return cast(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(); + new (data) UnarySymExpr(Operand, Opc, T); + DataSet.InsertNode(data, InsertPos); + } + + return cast(data); +} + QualType SymbolConjured::getType() const { return T; } @@ -465,6 +489,9 @@ case SymExpr::SymbolCastKind: KnownLive = isLive(cast(sym)->getOperand()); break; + case SymExpr::UnarySymExprKind: + KnownLive = isLive(cast(sym)->getOperand()); + break; } if (KnownLive) diff --git a/clang/test/Analysis/unary-sym-expr.c b/clang/test/Analysis/unary-sym-expr.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/unary-sym-expr.c @@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -verify + +void clang_analyzer_eval(int); + +int test(int flag) { + if (-flag == 0) { + clang_analyzer_eval(-flag == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(-flag > 0); // expected-warning{{FALSE}} + clang_analyzer_eval(-flag < 0); // expected-warning{{FALSE}} + } + (void)(flag); + return 42; +}