diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -1237,9 +1237,7 @@ /// arguments and the name of the function. class CallDescription { friend CallEvent; - - mutable IdentifierInfo *II = nullptr; - mutable bool IsLookupDone = false; + mutable Optional II; // The list of the qualified names used to identify the specified CallEvent, // e.g. "{a, b}" represent the qualified names, like "a::b". std::vector QualifiedName; @@ -1273,7 +1271,9 @@ Optional RequiredParams = None) : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs), RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), - Flags(Flags) {} + Flags(Flags) { + assert(!QualifiedName.empty()); + } /// Construct a CallDescription with default flags. CallDescription(ArrayRef QualifiedName, @@ -1283,6 +1283,17 @@ /// Get the name of the function that this object matches. StringRef getFunctionName() const { return QualifiedName.back(); } + + /// Get the qualified name parts in reversed order. + /// E.g. { "std", "vector", "data" } -> "vector", "std" + auto begin_qualified_name_parts() const { + return std::next(QualifiedName.rbegin()); + } + auto end_qualified_name_parts() const { return QualifiedName.rend(); } + + /// 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; } }; /// An immutable map from CallDescriptions to arbitrary data. Provides a unified 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 @@ -307,10 +307,7 @@ if (getKind() == CE_ObjCMessage) return false; - const IdentifierInfo *II = getCalleeIdentifier(); - if (!II) - return false; - const FunctionDecl *FD = dyn_cast_or_null(getDecl()); + const auto *FD = dyn_cast_or_null(getDecl()); if (!FD) return false; @@ -320,44 +317,69 @@ (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); } - if (!CD.IsLookupDone) { - CD.IsLookupDone = true; + if (!CD.II.hasValue()) { CD.II = &getState()->getStateManager().getContext().Idents.get( CD.getFunctionName()); } - if (II != CD.II) - return false; + 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. - // If CallDescription provides prefix names, use them to improve matching - // accuracy. - if (CD.QualifiedName.size() > 1 && FD) { - const DeclContext *Ctx = FD->getDeclContext(); - // See if we'll be able to match them all. - size_t NumUnmatched = CD.QualifiedName.size() - 1; - for (; Ctx && isa(Ctx); Ctx = Ctx->getParent()) { - if (NumUnmatched == 0) - break; + // Simply report mismatch for: + // C++ overloaded operators, constructors, destructors, etc. + return false; + }; - if (const auto *ND = dyn_cast(Ctx)) { - if (ND->getName() == CD.QualifiedName[NumUnmatched - 1]) - --NumUnmatched; - continue; - } + 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; + }; - if (const auto *RD = dyn_cast(Ctx)) { - if (RD->getName() == CD.QualifiedName[NumUnmatched - 1]) - --NumUnmatched; + 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; } - if (NumUnmatched > 0) - return false; - } + // 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 (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) && - (!CD.RequiredParams || CD.RequiredParams == parameters().size()); + return MatchQualifiedNameParts(CD, FD); } SVal CallEvent::getArgSVal(unsigned Index) const {