Index: clang/CMakeLists.txt =================================================================== --- clang/CMakeLists.txt +++ clang/CMakeLists.txt @@ -466,6 +466,10 @@ option(CLANG_ENABLE_ARCMT "Build ARCMT." ON) option(CLANG_ENABLE_STATIC_ANALYZER "Build static analyzer." ON) +if(CLANG_ENABLE_STATIC_ANALYZER) + add_definitions(-DCLANG_ENABLE_STATIC_ANALYZER) +endif() + option(CLANG_ENABLE_PROTO_FUZZER "Build Clang protobuf fuzzer." OFF) if(NOT CLANG_ENABLE_STATIC_ANALYZER AND CLANG_ENABLE_ARCMT) Index: clang/include/clang/AST/ASTTypeTraits.h =================================================================== --- clang/include/clang/AST/ASTTypeTraits.h +++ clang/include/clang/AST/ASTTypeTraits.h @@ -23,6 +23,13 @@ #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/AlignOf.h" +#if CLANG_ENABLE_STATIC_ANALYZER +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#endif // CLANG_ENABLE_STATIC_ANALYZER + namespace llvm { class raw_ostream; @@ -69,6 +76,11 @@ static ASTNodeKind getFromNode(const Stmt &S); static ASTNodeKind getFromNode(const Type &T); static ASTNodeKind getFromNode(const OMPClause &C); +#if CLANG_ENABLE_STATIC_ANALYZER + static ASTNodeKind getFromNode(const ento::SVal &V); + static ASTNodeKind getFromNode(const ento::MemRegion &MR); + static ASTNodeKind getFromNode(const ento::SymExpr &SE); +#endif // CLANG_ENABLE_STATIC_ANALYZER /// \} /// Returns \c true if \c this and \c Other represent the same kind. @@ -121,7 +133,11 @@ /// Check if the given ASTNodeKind identifies a type that offers pointer /// identity. This is useful for the fast path in DynTypedNode. bool hasPointerIdentity() const { +#ifndef CLANG_ENABLE_STATIC_ANALYZER return KindId > NKI_LastKindWithoutPointerIdentity; +#else + return KindId > NKI_LastKindWithoutPointerIdentity && KindId < NKI_SVal; +#endif // CLANG_ENABLE_STATIC_ANALYZER } private: @@ -150,6 +166,24 @@ NKI_OMPClause, #define OMP_CLAUSE_CLASS(Enum, Str, Class) NKI_##Class, #include "llvm/Frontend/OpenMP/OMPKinds.def" + +#if CLANG_ENABLE_STATIC_ANALYZER + NKI_MemRegion, +#define REGION(DERIVED, BASE) NKI_##DERIVED, +#define ABSTRACT_REGION(DERIVED, BASE) REGION(DERIVED, BASE) +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + NKI_SymExpr, +#define SYMBOL(DERIVED, BASE) NKI_##DERIVED, +#define ABSTRACT_SYMBOL(DERIVED, BASE) SYMBOL(DERIVED, BASE) +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + NKI_SVal, +#define BASIC_SVAL(DERIVED, BASE) NKI_##DERIVED, +#define ABSTRACT_SVAL(DERIVED, BASE) BASIC_SVAL(DERIVED, BASE) +#define LOC_SVAL(DERIVED, BASE) NKI_loc_##DERIVED, +#define NONLOC_SVAL(DERIVED, BASE) NKI_nonloc_##DERIVED, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +#endif // CLANG_ENABLE_STATIC_ANALYZER + NKI_NumberOfKinds }; @@ -208,6 +242,36 @@ #include "llvm/Frontend/OpenMP/OMPKinds.def" #undef KIND_TO_KIND_ID +#if CLANG_ENABLE_STATIC_ANALYZER +// Use namespace ento. +#define KIND_TO_KIND_ID(CLASS) \ + template <> struct ASTNodeKind::KindToKindId { \ + static const NodeKindId Id = NKI_##CLASS; \ + }; +KIND_TO_KIND_ID(SVal) +KIND_TO_KIND_ID(MemRegion) +KIND_TO_KIND_ID(SymExpr) +#define REGION(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) +#define ABSTRACT_REGION(DERIVED, BASE) REGION(DERIVED, BASE) +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" +#define SYMBOL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) +#define ABSTRACT_SYMBOL(DERIVED, BASE) SYMBOL(DERIVED, BASE) +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + +// Use namespace ento::loc and ento::nonloc. +#define SVAL_KIND_TO_KIND_ID(NAMESPACE, CLASS) \ + template <> struct ASTNodeKind::KindToKindId { \ + static const NodeKindId Id = NKI_##NAMESPACE##_##CLASS; \ + }; +#define BASIC_SVAL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) +#define ABSTRACT_SVAL(DERIVED, BASE) BASIC_SVAL(DERIVED, BASE) +#define LOC_SVAL(DERIVED, BASE) SVAL_KIND_TO_KIND_ID(loc, DERIVED) +#define NONLOC_SVAL(DERIVED, BASE) SVAL_KIND_TO_KIND_ID(nonloc, DERIVED) +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +#undef KIND_TO_KIND_ID +#undef SVAL_KIND_TO_KIND_ID +#endif // CLANG_ENABLE_STATIC_ANALYZER + inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) { OS << K.asStringRef(); return OS; @@ -314,6 +378,11 @@ NNSLB.getOpaqueData()); } +#if CLANG_ENABLE_STATIC_ANALYZER + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) + return getUnchecked() < Other.getUnchecked(); +#endif // CLANG_ENABLE_STATIC_ANALYZER + assert(getMemoizationData() && Other.getMemoizationData()); return getMemoizationData() < Other.getMemoizationData(); } @@ -334,6 +403,11 @@ return getUnchecked() == Other.getUnchecked(); +#if CLANG_ENABLE_STATIC_ANALYZER + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) + return getUnchecked() == Other.getUnchecked(); +#endif // CLANG_ENABLE_STATIC_ANALYZER + assert(getMemoizationData() && Other.getMemoizationData()); return getMemoizationData() == Other.getMemoizationData(); } @@ -369,6 +443,13 @@ NNSL.getOpaqueData()); } +#if CLANG_ENABLE_STATIC_ANALYZER + if (ASTNodeKind::getFromNodeKind().isBaseOf(Val.NodeKind)) { + auto V = Val.getUnchecked(); + return llvm::hash_combine(V.getRawData(), V.getRawKind()); + } +#endif // CLANG_ENABLE_STATIC_ANALYZER + assert(Val.getMemoizationData()); return llvm::hash_value(Val.getMemoizationData()); } @@ -446,6 +527,27 @@ } }; +#if CLANG_ENABLE_STATIC_ANALYZER + template struct SValConverter { + static const T *get(ASTNodeKind NodeKind, const char Storage[]) { + if (ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)) + return &getUnchecked(NodeKind, Storage); + return nullptr; + } + static const T &getUnchecked(ASTNodeKind NodeKind, + const char Storage[]) { + assert(ASTNodeKind::getFromNodeKind().isBaseOf(NodeKind)); + return *reinterpret_cast(Storage); + } + static DynTypedNode create(const ento::SVal &Node) { + DynTypedNode Result; + Result.NodeKind = ASTNodeKind::getFromNode(Node); + new (Result.Storage.buffer) ento::SVal(Node); + return Result; + } + }; +#endif // CLANG_ENABLE_STATIC_ANALYZER + ASTNodeKind NodeKind; /// Stores the data of the node. @@ -458,7 +560,11 @@ /// pointers and thus need to be stored by value. llvm::AlignedCharArrayUnion Storage; +#if CLANG_ENABLE_STATIC_ANALYZER + ento::SVal, +#endif // CLANG_ENABLE_STATIC_ANALYZER + TypeLoc> + Storage; }; template @@ -510,6 +616,23 @@ struct DynTypedNode::BaseConverter< TypeLoc, void> : public ValueConverter {}; +#if CLANG_ENABLE_STATIC_ANALYZER +template +struct DynTypedNode::BaseConverter< + T, std::enable_if_t::value>> + : public SValConverter {}; + +template +struct DynTypedNode::BaseConverter< + T, std::enable_if_t::value>> + : public DynCastPtrConverter {}; + +template +struct DynTypedNode::BaseConverter< + T, std::enable_if_t::value>> + : public DynCastPtrConverter {}; +#endif // CLANG_ENABLE_STATIC_ANALYZER + // The only operation we allow on unsupported types is \c get. // This allows to conveniently use \c DynTypedNode when having an arbitrary // AST node that is not supported, but prevents misuse - a user cannot create Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -2183,6 +2183,8 @@ /// corresponds to, e.g. "sizeof" or "[pre]++" static StringRef getOpcodeStr(Opcode Op); + StringRef getOpcodeStr() const { return getOpcodeStr(getOpcode()); } + /// Retrieve the unary opcode that corresponds to the given /// overloaded operator. static Opcode getOverloadedOpcode(OverloadedOperatorKind OO, bool Postfix); Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -48,6 +48,10 @@ #include #include +#if CLANG_ENABLE_STATIC_ANALYZER +#include "clang/StaticAnalyzer/Core/PathSensitive/SADVisitor.h" +#endif // CLANG_ENABLE_STATIC_ANALYZER + // The following three macros are used for meta programming. The code // using them is responsible for defining macro OPERATOR(). @@ -73,6 +77,7 @@ namespace clang { +#ifndef TRY_TO // A helper macro to implement short-circuiting when recursing. It // invokes CALL_EXPR, which must be a method call, on the derived // object (s.t. a user of RecursiveASTVisitor can override the method @@ -82,6 +87,7 @@ if (!getDerived().CALL_EXPR) \ return false; \ } while (false) +#endif /// A class that does preorder or postorder /// depth-first traversal on the entire Clang AST and visits each node. @@ -148,7 +154,12 @@ /// By default, this visitor preorder traverses the AST. If postorder traversal /// is needed, the \c shouldTraversePostOrder method needs to be overridden /// to return \c true. -template class RecursiveASTVisitor { +template +class RecursiveASTVisitor +#if CLANG_ENABLE_STATIC_ANALYZER + : public ento::SADVisitor +#endif // CLANG_ENABLE_STATIC_ANALYZER +{ public: /// A queue used for performing data recursion over statements. /// Parameters involving this type are used to implement data Index: clang/include/clang/ASTMatchers/ASTMatchFinder.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchFinder.h +++ clang/include/clang/ASTMatchers/ASTMatchFinder.h @@ -159,6 +159,11 @@ MatchCallback *Action); void addMatcher(const CXXCtorInitializerMatcher &NodeMatch, MatchCallback *Action); +#if CLANG_ENABLE_STATIC_ANALYZER + void addMatcher(const SValMatcher &NodeMatch, MatchCallback *Action); + void addMatcher(const MemRegionMatcher &NodeMatch, MatchCallback *Action); + void addMatcher(const SymExprMatcher &NodeMatch, MatchCallback *Action); +#endif // CLANG_ENABLE_STATIC_ANALYZER /// @} /// Adds a matcher to execute when running over the AST. @@ -201,7 +206,7 @@ /// when it matches. struct MatchersByType { std::vector> - DeclOrStmt; + BaseMatchers; std::vector> Type; std::vector> NestedNameSpecifier; Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -144,6 +144,11 @@ using NestedNameSpecifierMatcher = internal::Matcher; using NestedNameSpecifierLocMatcher = internal::Matcher; using CXXCtorInitializerMatcher = internal::Matcher; +#if CLANG_ENABLE_STATIC_ANALYZER +using SValMatcher = internal::Matcher; +using MemRegionMatcher = internal::Matcher; +using SymExprMatcher = internal::Matcher; +#endif // CLANG_ENABLE_STATIC_ANALYZER /// @} /// Matches any node. @@ -4579,10 +4584,20 @@ /// forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d")))))) /// will trigger a match for each combination of variable declaration /// and reference to that variable declaration within a compound statement. +#ifndef CLANG_ENABLE_STATIC_ANALYZER AST_POLYMORPHIC_MATCHER_P(equalsBoundNode, AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl, Type, QualType), - std::string, ID) { + std::string, ID) +#else +AST_POLYMORPHIC_MATCHER_P(equalsBoundNode, + AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl, Type, + QualType, ento::SVal, + ento::MemRegion, + ento::SymExpr), + std::string, ID) +#endif // CLANG_ENABLE_STATIC_ANALYZER +{ // FIXME: Figure out whether it makes sense to allow this // on any other node types. // For *Loc it probably does not make sense, as those seem @@ -4742,11 +4757,21 @@ .matchesNode(Node); } -AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals, - AST_POLYMORPHIC_SUPPORTED_TYPES(CharacterLiteral, - CXXBoolLiteralExpr, - IntegerLiteral), - unsigned, Value, 1) { +#ifndef CLANG_ENABLE_STATIC_ANALYZER +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + equals, + AST_POLYMORPHIC_SUPPORTED_TYPES(CharacterLiteral, CXXBoolLiteralExpr, + IntegerLiteral), + unsigned, Value, 1) +#else +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + equals, + AST_POLYMORPHIC_SUPPORTED_TYPES(CharacterLiteral, CXXBoolLiteralExpr, + IntegerLiteral, ento::nonloc::ConcreteInt, + ento::loc::ConcreteInt), + unsigned, Value, 1) +#endif // CLANG_ENABLE_STATIC_ANALYZER +{ return internal::ValueEqualsMatcher(Value) .matchesNode(Node); } @@ -4761,6 +4786,17 @@ .matchesNode(Node); } +#if CLANG_ENABLE_STATIC_ANALYZER +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + equals, + AST_POLYMORPHIC_SUPPORTED_TYPES(IntegerLiteral, ento::nonloc::ConcreteInt, + ento::loc::ConcreteInt), + int, Value, 3) { + return internal::ValueEqualsMatcher(Value).matchesNode( + Node); +} +#endif // CLANG_ENABLE_STATIC_ANALYZER + /// Matches the operator Name of operator expressions (binary or /// unary). /// @@ -4768,12 +4804,19 @@ /// \code /// !(a || b) /// \endcode +#ifndef CLANG_ENABLE_STATIC_ANALYZER AST_POLYMORPHIC_MATCHER_P(hasOperatorName, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, UnaryOperator), - std::string, Name) { - return Name == Node.getOpcodeStr(Node.getOpcode()); -} + std::string, Name) +#else +AST_POLYMORPHIC_MATCHER_P(hasOperatorName, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, + UnaryOperator, + ento::BinarySymExpr), + std::string, Name) +#endif // CLANG_ENABLE_STATIC_ANALYZER +{ return Name == Node.getOpcodeStr(); } /// Matches operator expressions (binary or unary) that have any of the /// specified names. @@ -4784,7 +4827,13 @@ extern const internal::VariadicFunction< internal::PolymorphicMatcherWithParam1< internal::HasAnyOperatorNameMatcher, std::vector, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, UnaryOperator)>, +#ifndef CLANG_ENABLE_STATIC_ANALYZER + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, UnaryOperator) +#else + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, UnaryOperator, + ento::BinarySymExpr) +#endif // CLANG_ENABLE_STATIC_ANALYZER + >, StringRef, internal::hasAnyOperatorNameFunc> hasAnyOperatorName; @@ -4865,6 +4914,40 @@ return anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)); } +#ifdef CLANG_ENABLE_STATIC_ANALYZER + +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + hasSymLHS, + AST_POLYMORPHIC_SUPPORTED_TYPES(ento::SymIntExpr, ento::SymSymExpr), + internal::Matcher, InnerMatcher, 0) { + const ento::SymExpr *LHS = Node.getLHS(); + return InnerMatcher.matches(*LHS, Finder, Builder); +} + +AST_MATCHER_P_OVERLOAD(ento::IntSymExpr, hasSymLHS, + internal::Matcher, + InnerMatcher, 1) { + ento::nonloc::ConcreteInt LHS(Node.getLHS()); + return InnerMatcher.matches(LHS, Finder, Builder); +} + +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + hasSymRHS, + AST_POLYMORPHIC_SUPPORTED_TYPES(ento::IntSymExpr, ento::SymSymExpr), + internal::Matcher, InnerMatcher, 0) { + const ento::SymExpr *RHS = Node.getLHS(); + return InnerMatcher.matches(*RHS, Finder, Builder); +} + +AST_MATCHER_P_OVERLOAD(ento::SymIntExpr, hasSymRHS, + internal::Matcher, + InnerMatcher, 1) { + ento::nonloc::ConcreteInt RHS(Node.getRHS()); + return InnerMatcher.matches(RHS, Finder, Builder); +} + +#endif // CLANG_ENABLE_STATIC_ANALYZER + /// Matches if the operand of a unary operator matches. /// /// Example matches true (matcher = hasUnaryOperand( @@ -6440,6 +6523,24 @@ return &Node == Other; } +#if CLANG_ENABLE_STATIC_ANALYZER + +AST_MATCHER_P_OVERLOAD(ento::SVal, equalsNode, ento::SVal, Other, 3) { + return Node == Other; +} + +AST_MATCHER_P_OVERLOAD(ento::MemRegion, equalsNode, const ento::MemRegion *, + Other, 4) { + return &Node == Other; +} + +AST_MATCHER_P_OVERLOAD(ento::SymExpr, equalsNode, const ento::SymExpr *, Other, + 5) { + return &Node == Other; +} + +#endif // CLANG_ENABLE_STATIC_ANALYZER + /// @} /// Matches each case or default statement belonging to the given switch @@ -7128,6 +7229,132 @@ // End OpenMP handling. //----------------------------------------------------------------------------// +#if CLANG_ENABLE_STATIC_ANALYZER + +//===----------------------------------------------------------------------===// +// Static Analyzer Data. +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// MemRegion. +//===----------------------------------------------------------------------===// + +AST_MATCHER_P(ento::MemRegion, hasBaseRegion, + internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(*Node.getBaseRegion(), Finder, Builder); +} + +AST_MATCHER_P(ento::MemRegion, hasSymbolicBase, + internal::Matcher, InnerMatcher) { + const ento::SymbolicRegion *SR = Node.getSymbolicBase(); + return SR && InnerMatcher.matches(*SR, Finder, Builder); +} + +AST_MATCHER_P(ento::MemRegion, hasSuperRegion, + internal::Matcher, InnerMatcher) { + const auto *SR = Node.getAs(); + return SR && InnerMatcher.matches(*SR->getSuperRegion(), Finder, Builder); +} + +AST_MATCHER_P(ento::MemRegion, hasMostDerivedObjectRegion, + internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(*Node.getMostDerivedObjectRegion(), Finder, + Builder); +} + +AST_MATCHER_P(ento::MemRegion, hasMemorySpace, + internal::Matcher, InnerMatcher) { + return InnerMatcher.matches(*Node.getMemorySpace(), Finder, Builder); +} + +AST_MATCHER_P(ento::MemRegion, isSubRegionOf, const ento::MemRegion *, MR) { + return Node.isSubRegionOf(MR); +} + +AST_MATCHER_P(ento::VarRegion, hasDecl, internal::Matcher, + InnerMatcher) { + return InnerMatcher.matches(*Node.getDecl(), Finder, Builder); +} + +extern const internal::VariadicAllOfMatcher sVal; +template +using SValMatch = const internal::VariadicDynCastAllOfMatcher; +extern SValMatch definedSVal; +extern SValMatch definedOrUnknownSVal; +extern SValMatch undefinedVal; +extern SValMatch unknownVal; +extern SValMatch locConcreteInt; +extern SValMatch locGotoLabel; +extern SValMatch locMemRegionVal; +extern SValMatch compoundVal; +extern SValMatch nonLocConcreteInt; +extern SValMatch lazyCompoundVal; +extern SValMatch locAsInteger; +extern SValMatch pointerToMember; +extern SValMatch symbolVal; + +extern const internal::VariadicAllOfMatcher memRegion; +template +using MemRegionMatch = + const internal::VariadicDynCastAllOfMatcher; +extern MemRegionMatch allocaRegion; +extern MemRegionMatch blockCodeRegion; +extern MemRegionMatch blockDataRegion; +extern MemRegionMatch codeTextRegion; +extern MemRegionMatch compoundLiteralRegion; +extern MemRegionMatch cxxBaseObjectRegion; +extern MemRegionMatch cxxDerivedObjectRegion; +extern MemRegionMatch cxxTempObjectRegion; +extern MemRegionMatch cxxThisRegion; +extern MemRegionMatch declRegion; +extern MemRegionMatch elementRegion; +extern MemRegionMatch fieldRegion; +extern MemRegionMatch functionCodeRegion; +extern MemRegionMatch objcIvarRegion; +extern MemRegionMatch objcStringRegion; +extern MemRegionMatch stringRegion; +extern MemRegionMatch subRegion; +extern MemRegionMatch symbolicRegion; +extern MemRegionMatch typedRegion; +extern MemRegionMatch typedValueRegion; +extern MemRegionMatch varRegion; + +extern MemRegionMatch codeSpaceRegion; +extern MemRegionMatch + globalImmutableSpaceRegion; +extern MemRegionMatch + globalInternalSpaceRegion; +extern MemRegionMatch globalSystemSpaceRegion; +extern MemRegionMatch globalsSpaceRegion; +extern MemRegionMatch heapSpaceRegion; +extern MemRegionMatch memSpaceRegion; +extern MemRegionMatch + nonStaticGlobalSpaceRegion; +extern MemRegionMatch stackSpaceRegion; +extern MemRegionMatch + stackArgumentsSpaceRegion; +extern MemRegionMatch stackLocalsSpaceRegion; +extern MemRegionMatch staticGlobalSpaceRegion; +extern MemRegionMatch unknownSpaceRegion; + +extern const internal::VariadicAllOfMatcher symExpr; +template +using SymExprMatch = + const internal::VariadicDynCastAllOfMatcher; +extern SymExprMatch binarySymExpr; +extern SymExprMatch intSymExpr; +extern SymExprMatch symIntExpr; +extern SymExprMatch symSymExpr; +extern SymExprMatch symbolCast; +extern SymExprMatch symbolConjured; +extern SymExprMatch symbolData; +extern SymExprMatch symbolDerived; +extern SymExprMatch symbolExtent; +extern SymExprMatch symbolRegionValue; +extern SymExprMatch symbolMetadata; + +#endif // CLANG_ENABLE_STATIC_ANALYZER + } // namespace ast_matchers } // namespace clang Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -927,6 +927,11 @@ std::is_same::value || std::is_same::value || std::is_same::value || +#if CLANG_ENABLE_STATIC_ANALYZER + std::is_same::value || + std::is_same::value || + std::is_same::value || +#endif // CLANG_ENABLE_STATIC_ANALYZER std::is_same::value; }; template @@ -1000,6 +1005,11 @@ std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || +#if CLANG_ENABLE_STATIC_ANALYZER + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || +#endif // CLANG_ENABLE_STATIC_ANALYZER std::is_base_of::value, "unsupported type for recursive matching"); return matchesChildOf(DynTypedNode::create(Node), getASTContext(), Matcher, @@ -1016,6 +1026,11 @@ std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || +#if CLANG_ENABLE_STATIC_ANALYZER + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || +#endif // CLANG_ENABLE_STATIC_ANALYZER std::is_base_of::value, "unsupported type for recursive matching"); return matchesDescendantOf(DynTypedNode::create(Node), getASTContext(), @@ -1031,6 +1046,11 @@ static_assert(std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || +#if CLANG_ENABLE_STATIC_ANALYZER + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || +#endif // CLANG_ENABLE_STATIC_ANALYZER std::is_base_of::value, "type not allowed for recursive matching"); return matchesAncestorOf(DynTypedNode::create(Node), getASTContext(), @@ -1094,7 +1114,11 @@ /// Useful for matchers like \c anything and \c unless. using AllNodeBaseTypes = TypeList; + Type, TypeLoc, +#if CLANG_ENABLE_STATIC_ANALYZER + ento::SVal, ento::MemRegion, ento::SymExpr, +#endif // CLANG_ENABLE_STATIC_ANALYZER + CXXCtorInitializer>; /// Helper meta-function to extract the argument out of a function of /// type void(Arg). @@ -1540,6 +1564,10 @@ static_assert(std::is_base_of::value || std::is_base_of::value || std::is_base_of::value || +#if CLANG_ENABLE_STATIC_ANALYZER + std::is_base_of::value || + std::is_base_of::value || +#endif // CLANG_ENABLE_STATIC_ANALYZER std::is_base_of::value, "the node must have a getValue method"); @@ -1867,6 +1895,9 @@ template > class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface { static_assert(std::is_same::value || +#if CLANG_ENABLE_STATIC_ANALYZER + std::is_base_of::value || +#endif // CLANG_ENABLE_STATIC_ANALYZER std::is_same::value, "Matcher only supports `BinaryOperator` and `UnaryOperator`"); static_assert(std::is_same>::value, @@ -1877,26 +1908,24 @@ : SingleNodeMatcherInterface(), Names(std::move(Names)) {} bool matchesNode(const T &Node) const override { - StringRef OpName = getOpName(Node); + StringRef OpName = Node.getOpcodeStr(); return llvm::any_of( Names, [&](const std::string &Name) { return Name == OpName; }); } -private: - static StringRef getOpName(const UnaryOperator &Node) { - return Node.getOpcodeStr(Node.getOpcode()); - } - static StringRef getOpName(const BinaryOperator &Node) { - return Node.getOpcodeStr(); - } - const std::vector Names; }; using HasOpNameMatcher = PolymorphicMatcherWithParam1, - void(TypeList)>; +#ifndef CLANG_ENABLE_STATIC_ANALYZER + void(TypeList) +#else + void(TypeList) +#endif // CLANG_ENABLE_STATIC_ANALYZER + >; HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef NameRefs); Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SADVisitor.h =================================================================== --- /dev/null +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SADVisitor.h @@ -0,0 +1,309 @@ +//===--- SADVisitor.h - Static Analyzer Data visitor ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SADVISITOR_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SADVISITOR_H + +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +namespace clang { +namespace ento { + +#ifndef TRY_TO +// A helper macro to implement short-circuiting when recursing. It +// invokes CALL_EXPR, which must be a method call, on the derived +// object (s.t. a user of SADVisitor can override the method +// in CALL_EXPR). +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!getDerived().CALL_EXPR) \ + return false; \ + } while (false) +#endif // TRY_TO + +template class SADVisitor { +public: + /// Return a reference to the derived class. + Derived &getDerived() { return *static_cast(this); } + + /// Traverse a symbolic value. + bool TraverseSVal(SVal V); + + /// Traverse a memory region. + bool TraverseMemRegion(const MemRegion *MR); + + /// Traverse a symbolic expression. + bool TraverseSymExpr(const SymExpr *SE); + + // SVal. + // Declare Traverse*() for all concerete SVal classes. +#define ABSTRACT_SVAL(Id, Parent) +#define BASIC_SVAL(Id, Parent) +#define LOC_SVAL(Id, Parent) bool TraverseLoc##Id(loc::Id V); +#define NONLOC_SVAL(Id, Parent) bool TraverseNonLoc##Id(nonloc::Id V); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + + // Define empty Visit*() for all SVal classes. + bool VisitSVal(SVal V) { return true; } +#define ABSTRACT_SVAL(Id, Parent) \ + bool Visit##Id(Id V) { return true; } +#define BASIC_SVAL(Id, Parent) \ + bool Visit##Id(Id V) { return true; } +#define LOC_SVAL(Id, Parent) \ + bool VisitLoc##Id(loc::Id V) { return true; } +#define NONLOC_SVAL(Id, Parent) \ + bool VisitNonLoc##Id(nonloc::Id V) { return true; } +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + + // MemRegion. + // Declare Traverse*() for all concerete MemRegion classes. +#define ABSTRACT_REGION(Id, Parent) +#define REGION(Id, Parent) bool Traverse##Id(const Id *MR); +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + + // Define empty Visit*() for all MemRegion classes. + bool VisitMemRegion(const MemRegion *MR) { return true; } +#define REGION(Id, Parent) \ + bool Visit##Id(const Id *MR) { return true; } +#define ABSTRACT_REGION(Id, Parent) REGION(Id, Parent) +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + + // SymExpr. + // Declare Traverse*() for all concerete SymExpr classes. +#define ABSTRACT_SYMBOL(Id, Parent) +#define SYMBOL(Id, Parent) bool Traverse##Id(const Id *SE); +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + + // Define empty Visit*() for all SymExpr classes. + bool VisitSymExpr(const SymExpr *SE) { return true; } +#define SYMBOL(Id, Parent) \ + bool Visit##Id(const Id *SE) { return true; } +#define ABSTRACT_SYMBOL(Id, Parent) SYMBOL(Id, Parent) +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" +}; + +//===----------------------------------------------------------------------===// +// SVal. +//===----------------------------------------------------------------------===// + +#define DISPATCH(NAME, CLASS) \ + return getDerived().Traverse##NAME(V.castAs()) + +template bool SADVisitor::TraverseSVal(SVal V) { + // Use namespace loc and nonloc. + switch (V.getBaseKind()) { + case SVal::LocKind: + switch (V.getSubKind()) { +#define LOC_SVAL(Id, Parent) \ + case loc::Id##Kind: \ + DISPATCH(Loc##Id, loc::Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + case SVal::NonLocKind: + switch (V.getSubKind()) { +#define NONLOC_SVAL(Id, Parent) \ + case nonloc::Id##Kind: \ + DISPATCH(NonLoc##Id, nonloc::Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + default: + llvm_unreachable("BASIC_SVAL"); + } + llvm_unreachable("Unhandled SVal"); +} + +#undef DISPATCH + +#define TRUE_SVAL(TRAVERSE) \ + template bool SADVisitor::TRAVERSE { \ + return true; \ + } +#define BASIC_SVAL(Id) TRUE_SVAL(Traverse##Id(Id V)) +#define LOC_SVAL(Id) TRUE_SVAL(TraverseLoc##Id(loc::Id V)) +#define NONLOC_SVAL(Id) TRUE_SVAL(TraverseNonLoc##Id(nonloc::Id V)) +LOC_SVAL(ConcreteInt) +LOC_SVAL(GotoLabel) +NONLOC_SVAL(ConcreteInt) +NONLOC_SVAL(LocAsInteger) +NONLOC_SVAL(PointerToMember) +#undef NONLOC_SVAL +#undef LOC_SVAL +#undef BASIC_SVAL +#undef TRUE_SVAL + +template +bool SADVisitor::TraverseLocMemRegionVal(loc::MemRegionVal V) { + TRY_TO(TraverseMemRegion(V.getRegion())); + return true; +} + +template +bool SADVisitor::TraverseNonLocCompoundVal(nonloc::CompoundVal V) { + /* FIXME: Make it work. + for (const SVal V : llvm::make_range(V.begin(), V.end())) + TRY_TO(TraverseSVal(V));*/ + return true; +} + +template +bool SADVisitor::TraverseNonLocLazyCompoundVal( + nonloc::LazyCompoundVal V) { + /* FIXME: Make it work. + TRY_TO(TraverseMemRegion(V.getRegion()));*/ + return true; +} + +template +bool SADVisitor::TraverseNonLocSymbolVal(nonloc::SymbolVal V) { + TRY_TO(TraverseSymExpr(V.getSymbol())); + return true; +} + +//===----------------------------------------------------------------------===// +// MemRegion. +//===----------------------------------------------------------------------===// + +#define DISPATCH(CLASS) return getDerived().Traverse##CLASS(cast(MR)) + +template +bool SADVisitor::TraverseMemRegion(const MemRegion *MR) { + switch (MR->getKind()) { +#define REGION(Id, Parent) \ + case MemRegion::Id##Kind: \ + DISPATCH(Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + } + llvm_unreachable("Unhandled MemRegion"); +} + +#undef DISPATCH + +#define REGION(CLASS) \ + template \ + bool SADVisitor::Traverse##CLASS(const CLASS *MR) { \ + return true; \ + } +REGION(AllocaRegion) +REGION(BlockCodeRegion) +REGION(BlockDataRegion) +REGION(CompoundLiteralRegion) +REGION(CXXBaseObjectRegion) +REGION(CXXDerivedObjectRegion) +REGION(CXXTempObjectRegion) +REGION(CXXThisRegion) +REGION(ElementRegion) +REGION(FieldRegion) +REGION(FunctionCodeRegion) +REGION(ObjCIvarRegion) +REGION(ObjCStringRegion) +REGION(StringRegion) +REGION(VarRegion) + +REGION(CodeSpaceRegion) +REGION(GlobalImmutableSpaceRegion) +REGION(GlobalInternalSpaceRegion) +REGION(GlobalSystemSpaceRegion) +REGION(HeapSpaceRegion) +REGION(StackArgumentsSpaceRegion) +REGION(StackLocalsSpaceRegion) +REGION(StaticGlobalSpaceRegion) +REGION(UnknownSpaceRegion) + +template +bool SADVisitor::TraverseSymbolicRegion(const SymbolicRegion *MR) { + TRY_TO(TraverseSymExpr(MR->getSymbol())); + return true; +} + +#undef REGION + +//===----------------------------------------------------------------------===// +// SymExpr. +//===----------------------------------------------------------------------===// + +#define DISPATCH(CLASS) return getDerived().Traverse##CLASS(cast(SE)) + +template +bool SADVisitor::TraverseSymExpr(const SymExpr *SE) { + switch (SE->getKind()) { +#define SYMBOL(Id, Parent) \ + case SymExpr::Id##Kind: \ + DISPATCH(Id); +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + } + llvm_unreachable("Unhandled SymExpr"); +} + +#undef DISPATCH + +template +bool SADVisitor::TraverseIntSymExpr(const IntSymExpr *SE) { + TRY_TO(TraverseSVal(nonloc::ConcreteInt(SE->getLHS()))); + TRY_TO(TraverseSymExpr(SE->getRHS())); + return true; +} + +template +bool SADVisitor::TraverseSymIntExpr(const SymIntExpr *SE) { + TRY_TO(TraverseSymExpr(SE->getLHS())); + TRY_TO(TraverseSVal(nonloc::ConcreteInt(SE->getRHS()))); + return true; +} + +template +bool SADVisitor::TraverseSymSymExpr(const SymSymExpr *SE) { + TRY_TO(TraverseSymExpr(SE->getLHS())); + TRY_TO(TraverseSymExpr(SE->getRHS())); + return true; +} + +template +bool SADVisitor::TraverseSymbolCast(const SymbolCast *SE) { + return true; +} + +template +bool SADVisitor::TraverseSymbolConjured(const SymbolConjured *SE) { + return true; +} + +template +bool SADVisitor::TraverseSymbolDerived(const SymbolDerived *SE) { + // FIXME: Match on parent? + TRY_TO(TraverseMemRegion(SE->getRegion())); + return true; +} + +template +bool SADVisitor::TraverseSymbolExtent(const SymbolExtent *SE) { + TRY_TO(TraverseMemRegion(SE->getRegion())); + return true; +} + +template +bool SADVisitor::TraverseSymbolMetadata(const SymbolMetadata *SE) { + TRY_TO(TraverseMemRegion(SE->getRegion())); + return true; +} + +template +bool SADVisitor::TraverseSymbolRegionValue( + const SymbolRegionValue *SE) { + TRY_TO(TraverseMemRegion(SE->getRegion())); + return true; +} + +#undef TRY_TO + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SADVISITOR_H Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -133,6 +133,10 @@ return !(*this == R); } + bool operator<(const SVal &R) const { + return getRawKind() < R.getRawKind() && Data < R.Data; + } + bool isUnknown() const { return getRawKind() == UnknownValKind; } @@ -207,6 +211,8 @@ SymExpr::symbol_iterator symbol_end() const { return SymExpr::symbol_end(); } + + const void *getRawData() const { return Data; } }; inline raw_ostream &operator<<(raw_ostream &os, clang::ento::SVal V) { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -321,6 +321,8 @@ BinaryOperator::Opcode getOpcode() const { return Op; } + StringRef getOpcodeStr() const { return BinaryOperator::getOpcodeStr(Op); }; + // Implement isa support. static bool classof(const SymExpr *SE) { Kind k = SE->getKind(); Index: clang/lib/AST/ASTTypeTraits.cpp =================================================================== --- clang/lib/AST/ASTTypeTraits.cpp +++ clang/lib/AST/ASTTypeTraits.cpp @@ -18,6 +18,12 @@ #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OpenMPClause.h" +#if CLANG_ENABLE_STATIC_ANALYZER +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#endif // CLANG_ENABLE_STATIC_ANALYZER + using namespace clang; const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { @@ -41,6 +47,23 @@ { NKI_None, "OMPClause" }, #define OMP_CLAUSE_CLASS(Enum, Str, Class) {NKI_OMPClause, #Class}, #include "llvm/Frontend/OpenMP/OMPKinds.def" + +#if CLANG_ENABLE_STATIC_ANALYZER + {NKI_None, "MemRegion"}, +#define REGION(DERIVED, BASE) {NKI_##BASE, #DERIVED}, +#define ABSTRACT_REGION(DERIVED, BASE) REGION(DERIVED, BASE) +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + {NKI_None, "SymExpr"}, +#define SYMBOL(DERIVED, BASE) {NKI_##BASE, #DERIVED}, +#define ABSTRACT_SYMBOL(DERIVED, BASE) SYMBOL(DERIVED, BASE) +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + {NKI_None, "SVal"}, +#define BASIC_SVAL(DERIVED, BASE) {NKI_##BASE, #DERIVED}, +#define ABSTRACT_SVAL(DERIVED, BASE) BASIC_SVAL(DERIVED, BASE) +#define LOC_SVAL(DERIVED, BASE) {NKI_##BASE, #DERIVED}, +#define NONLOC_SVAL(DERIVED, BASE) {NKI_##BASE, #DERIVED}, +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +#endif // CLANG_ENABLE_STATIC_ANALYZER }; bool ASTNodeKind::isBaseOf(ASTNodeKind Other, unsigned *Distance) const { @@ -122,6 +145,56 @@ llvm_unreachable("invalid stmt kind"); } +#if CLANG_ENABLE_STATIC_ANALYZER + +ASTNodeKind ASTNodeKind::getFromNode(const ento::SVal &V) { + switch (V.getBaseKind()) { +#define BASIC_SVAL(DERIVED, BASE) \ + case ento::SVal::DERIVED##Kind: \ + return ASTNodeKind(NKI_##DERIVED); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + case ento::SVal::NonLocKind: { + switch (V.getSubKind()) { +#define NONLOC_SVAL(DERIVED, BASE) \ + case ento::nonloc::DERIVED##Kind: \ + return ASTNodeKind(NKI_nonloc_##DERIVED); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + } + case ento::SVal::LocKind: { + switch (V.getSubKind()) { +#define LOC_SVAL(DERIVED, BASE) \ + case ento::loc::DERIVED##Kind: \ + return ASTNodeKind(NKI_loc_##DERIVED); +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" + } + } + } + llvm_unreachable("Unhandled SVal"); +} + +ASTNodeKind ASTNodeKind::getFromNode(const ento::MemRegion &MR) { + switch (MR.getKind()) { +#define REGION(DERIVED, BASE) \ + case ento::MemRegion::DERIVED##Kind: \ + return ASTNodeKind(NKI_##DERIVED); +#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" + } + llvm_unreachable("Unhandled MemRegion"); +} + +ASTNodeKind ASTNodeKind::getFromNode(const ento::SymExpr &SE) { + switch (SE.getKind()) { +#define SYMBOL(DERIVED, BASE) \ + case ento::SymExpr::DERIVED##Kind: \ + return ASTNodeKind(NKI_##DERIVED); +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + } + llvm_unreachable("Unhandled SymExpr"); +} + +#endif // CLANG_ENABLE_STATIC_ANALYZER + void DynTypedNode::print(llvm::raw_ostream &OS, const PrintingPolicy &PP) const { if (const TemplateArgument *TA = get()) @@ -145,6 +218,14 @@ S->printPretty(OS, nullptr, PP); else if (const Type *T = get()) QualType(T, 0).print(OS, PP); +#if CLANG_ENABLE_STATIC_ANALYZER + else if (const auto *V = get()) + OS << V; + else if (const auto *MR = get()) + OS << MR; + else if (const auto *SE = get()) + OS << SE; +#endif // CLANG_ENABLE_STATIC_ANALYZER else OS << "Unable to print values of type " << NodeKind.asStringRef() << "\n"; } @@ -156,6 +237,14 @@ S->dump(OS, SM); else if (const Type *T = get()) T->dump(OS); +#if CLANG_ENABLE_STATIC_ANALYZER + else if (const auto *V = get()) + OS << V; + else if (const auto *MR = get()) + OS << MR; + else if (const auto *SE = get()) + OS << SE; +#endif // CLANG_ENABLE_STATIC_ANALYZER else OS << "Unable to dump values of type " << NodeKind.asStringRef() << "\n"; } Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -26,6 +26,12 @@ #include #include +#if CLANG_ENABLE_STATIC_ANALYZER +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#endif // CLANG_ENABLE_STATIC_ANALYZER + namespace clang { namespace ast_matchers { namespace internal { @@ -120,6 +126,16 @@ traverse(*T); else if (const auto *C = DynNode.get()) traverse(*C); + +#if CLANG_ENABLE_STATIC_ANALYZER + else if (const auto *V = DynNode.get()) + traverse(*V); + else if (const auto *ME = DynNode.get()) + traverse(*ME); + else if (const auto *SE = DynNode.get()) + traverse(*SE); +#endif // CLANG_ENABLE_STATIC_ANALYZER + // FIXME: Add other base types after adding tests. // It's OK to always overwrite the bound nodes, as if there was @@ -255,6 +271,33 @@ bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return true; } +#if CLANG_ENABLE_STATIC_ANALYZER + bool TraverseSVal(ento::SVal V) { + ScopedIncrement ScopedDepth(&CurrentDepth); + if (!match(V)) + return false; + return traverse(V); + } + + bool TraverseMemRegion(const ento::MemRegion *MR) { + if (!MR) + return true; + ScopedIncrement ScopedDepth(&CurrentDepth); + if (!match(*MR)) + return false; + return traverse(*MR); + } + + bool TraverseSymExpr(const ento::SymExpr *SE) { + if (!SE) + return true; + ScopedIncrement ScopedDepth(&CurrentDepth); + if (!match(*SE)) + return false; + return traverse(*SE); + } +#endif // CLANG_ENABLE_STATIC_ANALYZER + private: // Used for updating the depth during traversal. struct ScopedIncrement { @@ -296,6 +339,17 @@ return VisitorBase::TraverseConstructorInitializer( const_cast(&CtorInit)); } +#if CLANG_ENABLE_STATIC_ANALYZER + bool baseTraverse(const ento::SVal &V) { + return VisitorBase::TraverseSVal(V); + } + bool baseTraverse(const ento::MemRegion &MR) { + return VisitorBase::TraverseMemRegion(&MR); + } + bool baseTraverse(const ento::SymExpr &SE) { + return VisitorBase::TraverseSymExpr(&SE); + } +#endif // CLANG_ENABLE_STATIC_ANALYZER // Sets 'Matched' to true if 'Matcher' matches 'Node' and: // 0 < CurrentDepth <= MaxDepth. @@ -439,6 +493,11 @@ bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS); bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit); +#if CLANG_ENABLE_STATIC_ANALYZER + bool TraverseSVal(ento::SVal V); + bool TraverseMemRegion(const ento::MemRegion *MR); + bool TraverseSymExpr(const ento::SymExpr *SE); +#endif // CLANG_ENABLE_STATIC_ANALYZER // Matches children or descendants of 'Node' with 'BaseMatcher'. bool memoizedMatchesRecursively(const DynTypedNode &Node, ASTContext &Ctx, @@ -549,6 +608,14 @@ } else if (auto *N = Node.get()) { match(*N); } +#if CLANG_ENABLE_STATIC_ANALYZER + else if (auto *N = Node.get()) + match(*N); + else if (auto *N = Node.get()) + match(*N); + else if (auto *N = Node.get()) + match(*N); +#endif // CLANG_ENABLE_STATIC_ANALYZER } template void match(const T &Node) { @@ -619,7 +686,7 @@ const bool EnableCheckProfiling = Options.CheckProfiling.hasValue(); TimeBucketRegion Timer; - auto &Matchers = this->Matchers->DeclOrStmt; + auto &Matchers = this->Matchers->BaseMatchers; for (unsigned short I : Filter) { auto &MP = Matchers[I]; if (EnableCheckProfiling) @@ -634,7 +701,7 @@ const std::vector &getFilterForKind(ASTNodeKind Kind) { auto &Filter = MatcherFiltersMap[Kind]; - auto &Matchers = this->Matchers->DeclOrStmt; + auto &Matchers = this->Matchers->BaseMatchers; assert((Matchers.size() < USHRT_MAX) && "Too many matchers."); for (unsigned I = 0, E = Matchers.size(); I != E; ++I) { if (Matchers[I].first.canMatchNodesOfKind(Kind)) { @@ -671,6 +738,17 @@ void matchDispatch(const CXXCtorInitializer *Node) { matchWithoutFilter(*Node, Matchers->CtorInit); } +#if CLANG_ENABLE_STATIC_ANALYZER + void matchDispatch(const ento::SVal *Node) { + matchWithFilter(DynTypedNode::create(*Node)); + } + void matchDispatch(const ento::MemRegion *Node) { + matchWithFilter(DynTypedNode::create(*Node)); + } + void matchDispatch(const ento::SymExpr *Node) { + matchWithFilter(DynTypedNode::create(*Node)); + } +#endif // CLANG_ENABLE_STATIC_ANALYZER void matchDispatch(const void *) { /* Do nothing. */ } /// @} @@ -1023,6 +1101,25 @@ CtorInit); } +#if CLANG_ENABLE_STATIC_ANALYZER +bool MatchASTVisitor::TraverseSVal(ento::SVal V) { + match(V); + return RecursiveASTVisitor::TraverseSVal(V); +} +bool MatchASTVisitor::TraverseMemRegion(const ento::MemRegion *MR) { + if (!MR) + return true; + match(*MR); + return RecursiveASTVisitor::TraverseMemRegion(MR); +} +bool MatchASTVisitor::TraverseSymExpr(const ento::SymExpr *SE) { + if (!SE) + return true; + match(*SE); + return RecursiveASTVisitor::TraverseSymExpr(SE); +} +#endif // CLANG_ENABLE_STATIC_ANALYZER + class MatchASTConsumer : public ASTConsumer { public: MatchASTConsumer(MatchFinder *Finder, @@ -1059,7 +1156,7 @@ void MatchFinder::addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action) { - Matchers.DeclOrStmt.emplace_back(NodeMatch, Action); + Matchers.BaseMatchers.emplace_back(NodeMatch, Action); Matchers.AllCallbacks.insert(Action); } @@ -1071,7 +1168,7 @@ void MatchFinder::addMatcher(const StatementMatcher &NodeMatch, MatchCallback *Action) { - Matchers.DeclOrStmt.emplace_back(NodeMatch, Action); + Matchers.BaseMatchers.emplace_back(NodeMatch, Action); Matchers.AllCallbacks.insert(Action); } @@ -1099,6 +1196,24 @@ Matchers.AllCallbacks.insert(Action); } +#if CLANG_ENABLE_STATIC_ANALYZER +void MatchFinder::addMatcher(const SValMatcher &NodeMatch, + MatchCallback *Action) { + Matchers.BaseMatchers.emplace_back(NodeMatch, Action); + Matchers.AllCallbacks.insert(Action); +} +void MatchFinder::addMatcher(const MemRegionMatcher &NodeMatch, + MatchCallback *Action) { + Matchers.BaseMatchers.emplace_back(NodeMatch, Action); + Matchers.AllCallbacks.insert(Action); +} +void MatchFinder::addMatcher(const SymExprMatcher &NodeMatch, + MatchCallback *Action) { + Matchers.BaseMatchers.emplace_back(NodeMatch, Action); + Matchers.AllCallbacks.insert(Action); +} +#endif // CLANG_ENABLE_STATIC_ANALYZER + bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, MatchCallback *Action) { if (NodeMatch.canConvertTo()) { @@ -1123,6 +1238,18 @@ addMatcher(NodeMatch.convertTo(), Action); return true; } +#if CLANG_ENABLE_STATIC_ANALYZER + else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } else if (NodeMatch.canConvertTo()) { + addMatcher(NodeMatch.convertTo(), Action); + return true; + } +#endif // CLANG_ENABLE_STATIC_ANALYZER return false; } Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -946,5 +946,82 @@ const internal::VariadicDynCastAllOfMatcher cxxDeductionGuideDecl; +#if CLANG_ENABLE_STATIC_ANALYZER + +const internal::VariadicAllOfMatcher sVal; +template +using SValMatch = const internal::VariadicDynCastAllOfMatcher; +SValMatch definedSVal; +SValMatch definedOrUnknownSVal; +SValMatch undefinedVal; +SValMatch unknownVal; +SValMatch locConcreteInt; +SValMatch locGotoLabel; +SValMatch locMemRegionVal; +SValMatch compoundVal; +SValMatch nonLocConcreteInt; +SValMatch lazyCompoundVal; +SValMatch locAsInteger; +SValMatch pointerToMember; +SValMatch symbolVal; + +const internal::VariadicAllOfMatcher memRegion; +template +using MemRegionMatch = + const internal::VariadicDynCastAllOfMatcher; +MemRegionMatch allocaRegion; +MemRegionMatch blockCodeRegion; +MemRegionMatch blockDataRegion; +MemRegionMatch codeTextRegion; +MemRegionMatch compoundLiteralRegion; +MemRegionMatch cxxBaseObjectRegion; +MemRegionMatch cxxDerivedObjectRegion; +MemRegionMatch cxxTempObjectRegion; +MemRegionMatch cxxThisRegion; +MemRegionMatch declRegion; +MemRegionMatch elementRegion; +MemRegionMatch fieldRegion; +MemRegionMatch functionCodeRegion; +MemRegionMatch objcIvarRegion; +MemRegionMatch objcStringRegion; +MemRegionMatch stringRegion; +MemRegionMatch subRegion; +MemRegionMatch symbolicRegion; +MemRegionMatch typedRegion; +MemRegionMatch typedValueRegion; +MemRegionMatch varRegion; + +MemRegionMatch codeSpaceRegion; +MemRegionMatch globalImmutableSpaceRegion; +MemRegionMatch globalInternalSpaceRegion; +MemRegionMatch globalSystemSpaceRegion; +MemRegionMatch globalsSpaceRegion; +MemRegionMatch heapSpaceRegion; +MemRegionMatch memSpaceRegion; +MemRegionMatch nonStaticGlobalSpaceRegion; +MemRegionMatch stackSpaceRegion; +MemRegionMatch stackArgumentsSpaceRegion; +MemRegionMatch stackLocalsSpaceRegion; +MemRegionMatch staticGlobalSpaceRegion; +MemRegionMatch unknownSpaceRegion; + +const internal::VariadicAllOfMatcher symExpr; +template +using SymExprMatch = + const internal::VariadicDynCastAllOfMatcher; +SymExprMatch binarySymExpr; +SymExprMatch intSymExpr; +SymExprMatch symIntExpr; +SymExprMatch symSymExpr; +SymExprMatch symbolCast; +SymExprMatch symbolData; +SymExprMatch symbolConjured; +SymExprMatch symbolDerived; +SymExprMatch symbolExtent; +SymExprMatch symbolMetadata; +SymExprMatch symbolRegionValue; + +#endif // CLANG_ENABLE_STATIC_ANALYZER + } // end namespace ast_matchers } // end namespace clang Index: clang/unittests/StaticAnalyzer/CMakeLists.txt =================================================================== --- clang/unittests/StaticAnalyzer/CMakeLists.txt +++ clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_unittest(StaticAnalysisTests AnalyzerOptionsTest.cpp CallDescriptionTest.cpp + MatcherTest.cpp StoreTest.cpp RegisterCustomCheckersTest.cpp SymbolReaperTest.cpp Index: clang/unittests/StaticAnalyzer/MatcherTest.h =================================================================== --- /dev/null +++ clang/unittests/StaticAnalyzer/MatcherTest.h @@ -0,0 +1,132 @@ +//===- unittests/StaticAnalyzer/MatcherTest.h - Matcher test helper -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Reusables.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { + +using namespace ast_matchers; + +template struct MatchContext { + MatchContext(const MatcherTy &Matcher, StringRef VarDeclName, + unsigned ExpectedMatchCount) + : Matcher(Matcher), VarDeclName(VarDeclName), + ExpectedMatchCount(ExpectedMatchCount), IsFound(false) {} + const MatcherTy &Matcher; + StringRef VarDeclName; + unsigned ExpectedMatchCount; + bool IsFound; +}; + +template class Consumer : public ExprEngineConsumer { +public: + Consumer(CompilerInstance &CI, MatchContext &MC) + : ExprEngineConsumer(CI), MC(MC) {} + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + const auto *FD = dyn_cast(DG.getSingleDecl()); + if (!FD || !FD->isDefined()) + return false; + + const StackFrameContext *SFC = + Eng.getAnalysisDeclContextManager().getStackFrame(FD); + Eng.ExecuteWorkList(SFC); + + ProgramStateRef State = + Eng.getCoreEngine().getWorkList()->dequeue().getNode()->getState(); + + const auto *VD = findDeclByName(FD, MC.VarDeclName); + SVal V = State->getSVal(State->getRegion(VD, SFC)); + + auto Match = match(MC.Matcher, V, Eng.getContext()); + + if (MC.ExpectedMatchCount > 0 && Match.size() == 0) { + llvm::errs() << "\nTried to match on: '" << V + << "'.\nNo match found.\n\n"; + return true; + } + + if (MC.ExpectedMatchCount == 0 && Match.size() > 0) { + llvm::errs() << "\nTried to match on: '" << V + << "'.\nExpected no matches, but '" << Match.size() + << "' match found.\n\n"; + return true; + } + + if (MC.ExpectedMatchCount > 0 && Match.size() != MC.ExpectedMatchCount) { + llvm::errs() << "\nTried to match on: '" << V << "'.\n"; + + if (MC.ExpectedMatchCount == 1) + llvm::errs() << "Multiple matches found (" << Match.size() << ").\n"; + else + llvm::errs() << "Expected '" << MC.ExpectedMatchCount + << "' matches, but '" << Match.size() << "' found.\n\n"; + return true; + } + + MC.IsFound = true; + return true; + } + +private: + MatchContext &MC; +}; + +template class Action : public ASTFrontendAction { +public: + Action(MatchContext &MC) : MC(MC) {} + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef) override { + return std::make_unique>(CI, MC); + } + +private: + MatchContext &MC; +}; + +template +testing::AssertionResult testMatch(StringRef Code, const T &Matcher, + StringRef VarDeclName, + unsigned ExpectedMatchCount) { + MatchContext MC(Matcher, VarDeclName, ExpectedMatchCount); + + if (!tooling::runToolOnCode(std::make_unique>(MC), Code)) + return testing::AssertionFailure() << "Unable to run.\n"; + + return MC.IsFound ? testing::AssertionSuccess() : testing::AssertionFailure(); +} + +template +testing::AssertionResult match(StringRef Code, StringRef VarDeclName, + const T &Matcher) { + return testMatch(Code, Matcher, VarDeclName, /*ExpectedMatchCount=*/1); +} + +template +testing::AssertionResult not_match(StringRef Code, StringRef VarDeclName, + const T &Matcher) { + return testMatch(Code, Matcher, VarDeclName, /*ExpectedMatchCount=*/0); +} + +template +testing::AssertionResult multi_match(StringRef Code, StringRef VarDeclName, + unsigned ExpectedMatchCount, + const T &Matcher) { + return testMatch(Code, Matcher, VarDeclName, ExpectedMatchCount); +} + +} // namespace ento +} // namespace clang Index: clang/unittests/StaticAnalyzer/MatcherTest.cpp =================================================================== --- /dev/null +++ clang/unittests/StaticAnalyzer/MatcherTest.cpp @@ -0,0 +1,119 @@ +//===- unittests/StaticAnalyzer/MatcherTest.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MatcherTest.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { + +using namespace ast_matchers; + +TEST(SValMatcher, RawSValMatch) { + EXPECT_TRUE(match("void f() { int x = 13; }", "x", sVal())); +} + +TEST(SValMatcher, SValHas) { + EXPECT_TRUE( + match("void f(int a) { int x = a + 13; }", "x", sVal(has(symIntExpr())))); + EXPECT_TRUE(match("void f(int x) {}", "x", sVal(has(symbolRegionValue())))); +} + +TEST(SValMatcher, SValDescendant) { + EXPECT_TRUE(match("void f() { int x = 13; }", "x", nonLocConcreteInt())); + EXPECT_TRUE(match("void f(int a) { int x = a + 13; }", "x", + sVal(hasDescendant(nonLocConcreteInt())))); + EXPECT_TRUE(not_match("void f() { int x = 13; }", "x", + sVal(hasDescendant(nonLocConcreteInt())))); +} + +TEST(SymExprMatcher, RawSymExprMatch) { + EXPECT_TRUE(match("void f(int a) { int x = a + 13; };", "x", + symbolVal(has(symExpr())))); + EXPECT_TRUE(not_match("void f(int a) { int x = a + 13; };", "x", symExpr())); +} + +TEST(SymExprMatcher, SymExprDescendant) { + EXPECT_TRUE(match("void f(int a) { int x = a + 13; }", "x", + symbolVal(has(symIntExpr(allOf( + hasDescendant(symbolRegionValue()), + hasDescendant(nonLocConcreteInt(equals(13))))))))); +} + +TEST(SymExprMatcher, IntSymExprNotExist) { + EXPECT_TRUE( + not_match("void f(int a) { int x = 13 + a; }", "x", intSymExpr())); +} + +TEST(SymExprMatcher, SymExprHasOperator) { + EXPECT_TRUE( + match("void f(int a) { int x = a + 13; }", "x", + symbolVal(has(symIntExpr(hasSymLHS(symbolRegionValue()), + hasSymRHS(nonLocConcreteInt(equals(13))), + hasOperatorName("+")))))); +} + +TEST(SymExprMatcher, SymExprHasAnyOperator) { + EXPECT_TRUE(not_match("void f(int a) { int x = a + 13; }", "x", + symIntExpr(hasAnyOperatorName("*", "/")))); +} + +TEST(MemRegionMatcher, RawMemRegionMatch) { + EXPECT_TRUE(match("void f(int a) { int x = a; }", "x", + symbolVal(has(symbolRegionValue(has(memRegion())))))); + EXPECT_TRUE(not_match("void f(int a) { int x = a; }", "x", memRegion())); + EXPECT_TRUE( + not_match("void f(int a) { int x = a; }", "x", symbolRegionValue())); + EXPECT_TRUE( + not_match("void f(int a) { int x = a; }", "x", locMemRegionVal())); +} + +TEST(MemRegionMatcher, MemorySpace) { + EXPECT_TRUE(match("void f(int a) { int x = a; }", "x", + symbolVal(has(symbolRegionValue(has(memRegion( + hasMemorySpace(stackArgumentsSpaceRegion())))))))); + EXPECT_TRUE(match( + "int main(int argc, char *argv[]) { char *x = argv[0]; }", "x", + locMemRegionVal(has(memRegion(hasMemorySpace(unknownSpaceRegion())))))); +} + +TEST(MemRegionMatcher, MemRegionHierarchy) { + // &SymRegion{reg_$2},0,char*}>} + // SymRegion(ElementRegion(SymRegion(VarRegion()))) + EXPECT_TRUE(match("int main(int argc, char *argv[]) { char *x = argv[0]; }", + "x", + locMemRegionVal(has(symbolicRegion(has(symbolRegionValue( + has(elementRegion(hasSymbolicBase(symbolicRegion( + has(symbolRegionValue(has(varRegion())))))))))))))); +} + +TEST(MemRegionMatcher, MemRegionTraversing) { + // &SymRegion{reg_$2},0,char*}>} + // SymRegion(ElementRegion(SymRegion(VarRegion()))) + EXPECT_TRUE( + multi_match("int main(int argc, char *argv[]) { char *x = argv[0]; }", + "x", 2, sVal(forEachDescendant(symbolicRegion().bind("r"))))); + EXPECT_TRUE( + multi_match("int main(int argc, char *argv[]) { char *x = argv[0]; }", + "x", 4, sVal(forEachDescendant(subRegion().bind("r"))))); +} + +TEST(MemRegionMatcher, DescendIntoASTMatcher) { + // &SymRegion{reg_$2},0,char*}>} + // SymRegion(ElementRegion(SymRegion(VarRegion()))) + EXPECT_TRUE( + match("int main(int argc, char *argv[]) { char *x = argv[0]; }", "x", + sVal(hasDescendant(elementRegion(hasSymbolicBase( + hasDescendant(varRegion(hasDecl(hasName("argv")))))))))); +} + +} // namespace ento +} // namespace clang