diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h @@ -83,6 +83,33 @@ /// It's false, if and only if we expect a single identifier, such as /// `getenv`. It's true for `std::swap`, or `my::detail::container::data`. bool hasQualifiedNameParts() const { return QualifiedName.size() > 1; } + + /// @name Matching CallDescriptions against a CallEvent + /// @{ + + /// Returns true if the CallEvent is a call to a function that matches + /// the CallDescription. + /// + /// \note This function is not intended to be used to match Obj-C method + /// calls. + bool matches(const CallEvent &Call) const; + + /// Returns true whether the CallEvent matches on any of the CallDescriptions + /// supplied. + /// + /// \note This function is not intended to be used to match Obj-C method + /// calls. + friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1) { + return CD1.matches(Call); + } + + /// \copydoc clang::ento::matchesAny(const CallEvent &, const CallDescription &) + template + friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1, + const Ts &...CDs) { + return CD1.matches(Call) || matchesAny(Call, CDs...); + } + /// @} }; /// An immutable map from CallDescriptions to arbitrary data. Provides a unified @@ -113,7 +140,7 @@ // Slow path: linear lookup. // TODO: Implement some sort of fast path. for (const std::pair &I : LinearMap) - if (Call.isCalled(I.first)) + if (I.first.matches(Call)) return &I.second; return nullptr; diff --git a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp --- a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" @@ -47,6 +48,88 @@ Optional RequiredParams /*= None*/) : CallDescription(0, QualifiedName, RequiredArgs, RequiredParams) {} +bool ento::CallDescription::matches(const CallEvent &Call) const { + // FIXME: Add ObjC Message support. + if (Call.getKind() == CE_ObjCMessage) + return false; + + const auto *FD = dyn_cast_or_null(Call.getDecl()); + if (!FD) + return false; + + if (Flags & CDF_MaybeBuiltin) { + return CheckerContext::isCLibraryFunction(FD, getFunctionName()) && + (!RequiredArgs || RequiredArgs <= Call.getNumArgs()) && + (!RequiredParams || RequiredParams <= Call.parameters().size()); + } + + if (!II.hasValue()) { + II = &Call.getState()->getStateManager().getContext().Idents.get( + getFunctionName()); + } + + const auto MatchNameOnly = [](const CallDescription &CD, + const NamedDecl *ND) -> bool { + DeclarationName Name = ND->getDeclName(); + if (const auto *II = Name.getAsIdentifierInfo()) + return II == CD.II.getValue(); // Fast case. + + // Fallback to the slow stringification and comparison for: + // C++ overloaded operators, constructors, destructors, etc. + // FIXME This comparison is way SLOWER than comparing pointers. + // At some point in the future, we should compare FunctionDecl pointers. + return Name.getAsString() == CD.getFunctionName(); + }; + + const auto ExactMatchArgAndParamCounts = + [](const CallEvent &Call, const CallDescription &CD) -> bool { + const bool ArgsMatch = + !CD.RequiredArgs || CD.RequiredArgs == Call.getNumArgs(); + const bool ParamsMatch = + !CD.RequiredParams || CD.RequiredParams == Call.parameters().size(); + return ArgsMatch && ParamsMatch; + }; + + const auto MatchQualifiedNameParts = [](const CallDescription &CD, + const Decl *D) -> bool { + const auto FindNextNamespaceOrRecord = + [](const DeclContext *Ctx) -> const DeclContext * { + while (Ctx && !isa(Ctx)) + Ctx = Ctx->getParent(); + return Ctx; + }; + + auto QualifierPartsIt = CD.begin_qualified_name_parts(); + const auto QualifierPartsEndIt = CD.end_qualified_name_parts(); + + // Match namespace and record names. Skip unrelated names if they don't + // match. + const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext()); + for (; Ctx && QualifierPartsIt != QualifierPartsEndIt; + Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) { + // If not matched just continue and try matching for the next one. + if (cast(Ctx)->getName() != *QualifierPartsIt) + continue; + ++QualifierPartsIt; + } + + // We matched if we consumed all expected qualifier segments. + return QualifierPartsIt == QualifierPartsEndIt; + }; + + // Let's start matching... + if (!ExactMatchArgAndParamCounts(Call, *this)) + return false; + + if (!MatchNameOnly(*this, FD)) + return false; + + if (!hasQualifiedNameParts()) + return true; + + return MatchQualifiedNameParts(*this, FD); +} + ento::CallDescriptionSet::CallDescriptionSet( std::initializer_list &&List) { Impl.LinearMap.reserve(List.size()); diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -304,85 +304,7 @@ } bool CallEvent::isCalled(const CallDescription &CD) const { - // FIXME: Add ObjC Message support. - if (getKind() == CE_ObjCMessage) - return false; - - const auto *FD = dyn_cast_or_null(getDecl()); - if (!FD) - return false; - - if (CD.Flags & CDF_MaybeBuiltin) { - return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && - (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) && - (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); - } - - if (!CD.II.hasValue()) { - CD.II = &getState()->getStateManager().getContext().Idents.get( - CD.getFunctionName()); - } - - const auto MatchNameOnly = [](const CallDescription &CD, - const NamedDecl *ND) -> bool { - DeclarationName Name = ND->getDeclName(); - if (const auto *II = Name.getAsIdentifierInfo()) - return II == CD.II.getValue(); // Fast case. - - // Fallback to the slow stringification and comparison for: - // C++ overloaded operators, constructors, destructors, etc. - // FIXME This comparison is way SLOWER than comparing pointers. - // At some point in the future, we should compare FunctionDecl pointers. - return Name.getAsString() == CD.getFunctionName(); - }; - - const auto ExactMatchArgAndParamCounts = - [](const CallEvent &Call, const CallDescription &CD) -> bool { - const bool ArgsMatch = - !CD.RequiredArgs || CD.RequiredArgs == Call.getNumArgs(); - const bool ParamsMatch = - !CD.RequiredParams || CD.RequiredParams == Call.parameters().size(); - return ArgsMatch && ParamsMatch; - }; - - const auto MatchQualifiedNameParts = [](const CallDescription &CD, - const Decl *D) -> bool { - const auto FindNextNamespaceOrRecord = - [](const DeclContext *Ctx) -> const DeclContext * { - while (Ctx && !isa(Ctx)) - Ctx = Ctx->getParent(); - return Ctx; - }; - - auto QualifierPartsIt = CD.begin_qualified_name_parts(); - const auto QualifierPartsEndIt = CD.end_qualified_name_parts(); - - // Match namespace and record names. Skip unrelated names if they don't - // match. - const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext()); - for (; Ctx && QualifierPartsIt != QualifierPartsEndIt; - Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) { - // If not matched just continue and try matching for the next one. - if (cast(Ctx)->getName() != *QualifierPartsIt) - continue; - ++QualifierPartsIt; - } - - // We matched if we consumed all expected qualifier segments. - return QualifierPartsIt == QualifierPartsEndIt; - }; - - // Let's start matching... - if (!ExactMatchArgAndParamCounts(*this, CD)) - return false; - - if (!MatchNameOnly(CD, FD)) - return false; - - if (!CD.hasQualifiedNameParts()) - return true; - - return MatchQualifiedNameParts(CD, FD); + return CD.matches(*this); } SVal CallEvent::getArgSVal(unsigned Index) const {