Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:37:07

0001 //===- CallDescription.h - function/method call matching       --*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 /// \file This file defines a generic mechanism for matching for function and
0010 /// method calls of C, C++, and Objective-C languages. Instances of these
0011 /// classes are frequently used together with the CallEvent classes.
0012 //
0013 //===----------------------------------------------------------------------===//
0014 
0015 #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLDESCRIPTION_H
0016 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLDESCRIPTION_H
0017 
0018 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
0019 #include "llvm/ADT/ArrayRef.h"
0020 #include "llvm/Support/Compiler.h"
0021 #include <optional>
0022 #include <vector>
0023 
0024 namespace clang {
0025 class IdentifierInfo;
0026 } // namespace clang
0027 
0028 namespace clang {
0029 namespace ento {
0030 /// A `CallDescription` is a pattern that can be used to _match_ calls
0031 /// based on the qualified name and the argument/parameter counts.
0032 class CallDescription {
0033 public:
0034   enum class Mode {
0035     /// Match calls to functions from the C standard library. This also
0036     /// recognizes builtin variants whose name is derived by adding
0037     /// "__builtin", "__inline" or similar prefixes or suffixes; but only
0038     /// matches functions than are externally visible and are declared either
0039     /// directly within a TU or in the namespace 'std'.
0040     /// For the exact heuristics, see CheckerContext::isCLibraryFunction().
0041     CLibrary,
0042 
0043     /// An extended version of the `CLibrary` mode that also matches the
0044     /// hardened variants like __FOO_chk() and __builtin__FOO_chk() that take
0045     /// additional arguments compared to the "regular" function FOO().
0046     /// This is not the default behavior of `CLibrary` because in this case the
0047     /// checker code must be prepared to handle the different parametrization.
0048     /// For the exact heuristics, see CheckerContext::isHardenedVariantOf().
0049     CLibraryMaybeHardened,
0050 
0051     /// Matches "simple" functions that are not methods. (Static methods are
0052     /// methods.)
0053     SimpleFunc,
0054 
0055     /// Matches a C++ method (may be static, may be virtual, may be an
0056     /// overloaded operator, a constructor or a destructor).
0057     CXXMethod,
0058 
0059     /// Match any CallEvent that is not an ObjCMethodCall. This should not be
0060     /// used when the checker looks for a concrete function (and knows whether
0061     /// it is a method); but GenericTaintChecker uses this mode to match
0062     /// functions whose name was configured by the user.
0063     Unspecified,
0064 
0065     /// FIXME: Add support for ObjCMethodCall events (I'm not adding it because
0066     /// I'm not familiar with Objective-C). Note that currently an early return
0067     /// in `bool matches(const CallEvent &Call) const;` discards all
0068     /// Objective-C method calls.
0069   };
0070 
0071 private:
0072   friend class CallEvent;
0073   using MaybeCount = std::optional<unsigned>;
0074 
0075   mutable std::optional<const IdentifierInfo *> II;
0076   // The list of the qualified names used to identify the specified CallEvent,
0077   // e.g. "{a, b}" represent the qualified names, like "a::b".
0078   std::vector<std::string> QualifiedName;
0079   MaybeCount RequiredArgs;
0080   MaybeCount RequiredParams;
0081   Mode MatchAs;
0082 
0083 public:
0084   /// Constructs a CallDescription object.
0085   ///
0086   /// @param MatchAs Specifies the kind of the call that should be matched.
0087   ///
0088   /// @param QualifiedName The list of the name qualifiers of the function that
0089   /// will be matched. The user is allowed to skip any of the qualifiers.
0090   /// For example, {"std", "basic_string", "c_str"} would match both
0091   /// std::basic_string<...>::c_str() and std::__1::basic_string<...>::c_str().
0092   ///
0093   /// @param RequiredArgs The expected number of arguments that are passed to
0094   /// the function. Omit this parameter (or pass std::nullopt) to match every
0095   /// occurrence without checking the argument count in the call.
0096   ///
0097   /// @param RequiredParams The expected number of parameters in the function
0098   /// definition that is called. Omit this parameter to match every occurrence
0099   /// without checking the parameter count in the definition.
0100   CallDescription(Mode MatchAs, ArrayRef<StringRef> QualifiedName,
0101                   MaybeCount RequiredArgs = std::nullopt,
0102                   MaybeCount RequiredParams = std::nullopt);
0103 
0104   /// Get the name of the function that this object matches.
0105   StringRef getFunctionName() const { return QualifiedName.back(); }
0106 
0107   /// Get the qualified name parts in reversed order.
0108   /// E.g. { "std", "vector", "data" } -> "vector", "std"
0109   auto begin_qualified_name_parts() const {
0110     return std::next(QualifiedName.rbegin());
0111   }
0112   auto end_qualified_name_parts() const { return QualifiedName.rend(); }
0113 
0114   /// It's false, if and only if we expect a single identifier, such as
0115   /// `getenv`. It's true for `std::swap`, or `my::detail::container::data`.
0116   bool hasQualifiedNameParts() const { return QualifiedName.size() > 1; }
0117 
0118   /// @name Matching CallDescriptions against a CallEvent
0119   /// @{
0120 
0121   /// Returns true if the CallEvent is a call to a function that matches
0122   /// the CallDescription.
0123   ///
0124   /// \note This function is not intended to be used to match Obj-C method
0125   /// calls.
0126   bool matches(const CallEvent &Call) const;
0127 
0128   /// Returns true whether the CallEvent matches on any of the CallDescriptions
0129   /// supplied.
0130   ///
0131   /// \note This function is not intended to be used to match Obj-C method
0132   /// calls.
0133   friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1) {
0134     return CD1.matches(Call);
0135   }
0136 
0137   /// \copydoc clang::ento::CallDescription::matchesAny(const CallEvent &, const CallDescription &)
0138   template <typename... Ts>
0139   friend bool matchesAny(const CallEvent &Call, const CallDescription &CD1,
0140                          const Ts &...CDs) {
0141     return CD1.matches(Call) || matchesAny(Call, CDs...);
0142   }
0143   /// @}
0144 
0145   /// @name Matching CallDescriptions against a CallExpr
0146   /// @{
0147 
0148   /// Returns true if the CallExpr is a call to a function that matches the
0149   /// CallDescription.
0150   ///
0151   /// When available, always prefer matching with a CallEvent! This function
0152   /// exists only when that is not available, for example, when _only_
0153   /// syntactic check is done on a piece of code.
0154   ///
0155   /// Also, StdLibraryFunctionsChecker::Signature is likely a better candicade
0156   /// for syntactic only matching if you are writing a new checker. This is
0157   /// handy if a CallDescriptionMap is already there.
0158   ///
0159   /// The function is imprecise because CallEvent may know path sensitive
0160   /// information, such as the precise argument count (see comments for
0161   /// CallEvent::getNumArgs), the called function if it was called through a
0162   /// function pointer, and other information not available syntactically.
0163   bool matchesAsWritten(const CallExpr &CE) const;
0164 
0165   /// Returns true whether the CallExpr matches on any of the CallDescriptions
0166   /// supplied.
0167   ///
0168   /// \note This function is not intended to be used to match Obj-C method
0169   /// calls.
0170   friend bool matchesAnyAsWritten(const CallExpr &CE,
0171                                   const CallDescription &CD1) {
0172     return CD1.matchesAsWritten(CE);
0173   }
0174 
0175   /// \copydoc clang::ento::CallDescription::matchesAnyAsWritten(const CallExpr &, const CallDescription &)
0176   template <typename... Ts>
0177   friend bool matchesAnyAsWritten(const CallExpr &CE,
0178                                   const CallDescription &CD1,
0179                                   const Ts &...CDs) {
0180     return CD1.matchesAsWritten(CE) || matchesAnyAsWritten(CE, CDs...);
0181   }
0182   /// @}
0183 
0184 private:
0185   bool matchesImpl(const FunctionDecl *Callee, size_t ArgCount,
0186                    size_t ParamCount) const;
0187 
0188   bool matchNameOnly(const NamedDecl *ND) const;
0189   bool matchQualifiedNameParts(const Decl *D) const;
0190 };
0191 
0192 /// An immutable map from CallDescriptions to arbitrary data. Provides a unified
0193 /// way for checkers to react on function calls.
0194 template <typename T> class CallDescriptionMap {
0195   friend class CallDescriptionSet;
0196 
0197   // Some call descriptions aren't easily hashable (eg., the ones with qualified
0198   // names in which some sections are omitted), so let's put them
0199   // in a simple vector and use linear lookup.
0200   // TODO: Implement an actual map for fast lookup for "hashable" call
0201   // descriptions (eg., the ones for C functions that just match the name).
0202   std::vector<std::pair<CallDescription, T>> LinearMap;
0203 
0204 public:
0205   CallDescriptionMap(
0206       std::initializer_list<std::pair<CallDescription, T>> &&List)
0207       : LinearMap(List) {}
0208 
0209   template <typename InputIt>
0210   CallDescriptionMap(InputIt First, InputIt Last) : LinearMap(First, Last) {}
0211 
0212   ~CallDescriptionMap() = default;
0213 
0214   // These maps are usually stored once per checker, so let's make sure
0215   // we don't do redundant copies.
0216   CallDescriptionMap(const CallDescriptionMap &) = delete;
0217   CallDescriptionMap &operator=(const CallDescription &) = delete;
0218 
0219   CallDescriptionMap(CallDescriptionMap &&) = default;
0220   CallDescriptionMap &operator=(CallDescriptionMap &&) = default;
0221 
0222   [[nodiscard]] const T *lookup(const CallEvent &Call) const {
0223     // Slow path: linear lookup.
0224     // TODO: Implement some sort of fast path.
0225     for (const std::pair<CallDescription, T> &I : LinearMap)
0226       if (I.first.matches(Call))
0227         return &I.second;
0228 
0229     return nullptr;
0230   }
0231 
0232   /// When available, always prefer lookup with a CallEvent! This function
0233   /// exists only when that is not available, for example, when _only_
0234   /// syntactic check is done on a piece of code.
0235   ///
0236   /// Also, StdLibraryFunctionsChecker::Signature is likely a better candicade
0237   /// for syntactic only matching if you are writing a new checker. This is
0238   /// handy if a CallDescriptionMap is already there.
0239   ///
0240   /// The function is imprecise because CallEvent may know path sensitive
0241   /// information, such as the precise argument count (see comments for
0242   /// CallEvent::getNumArgs), the called function if it was called through a
0243   /// function pointer, and other information not available syntactically.
0244   [[nodiscard]] const T *lookupAsWritten(const CallExpr &Call) const {
0245     // Slow path: linear lookup.
0246     // TODO: Implement some sort of fast path.
0247     for (const std::pair<CallDescription, T> &I : LinearMap)
0248       if (I.first.matchesAsWritten(Call))
0249         return &I.second;
0250 
0251     return nullptr;
0252   }
0253 };
0254 
0255 /// Enumerators of this enum class are used to construct CallDescription
0256 /// objects; in that context the fully qualified name is needlessly verbose.
0257 using CDM = CallDescription::Mode;
0258 
0259 /// An immutable set of CallDescriptions.
0260 /// Checkers can efficiently decide if a given CallEvent matches any
0261 /// CallDescription in the set.
0262 class CallDescriptionSet {
0263   CallDescriptionMap<bool /*unused*/> Impl = {};
0264 
0265 public:
0266   CallDescriptionSet(std::initializer_list<CallDescription> &&List);
0267 
0268   CallDescriptionSet(const CallDescriptionSet &) = delete;
0269   CallDescriptionSet &operator=(const CallDescription &) = delete;
0270 
0271   [[nodiscard]] bool contains(const CallEvent &Call) const;
0272 
0273   /// When available, always prefer lookup with a CallEvent! This function
0274   /// exists only when that is not available, for example, when _only_
0275   /// syntactic check is done on a piece of code.
0276   ///
0277   /// Also, StdLibraryFunctionsChecker::Signature is likely a better candicade
0278   /// for syntactic only matching if you are writing a new checker. This is
0279   /// handy if a CallDescriptionMap is already there.
0280   ///
0281   /// The function is imprecise because CallEvent may know path sensitive
0282   /// information, such as the precise argument count (see comments for
0283   /// CallEvent::getNumArgs), the called function if it was called through a
0284   /// function pointer, and other information not available syntactically.
0285   [[nodiscard]] bool containsAsWritten(const CallExpr &CE) const;
0286 };
0287 
0288 } // namespace ento
0289 } // namespace clang
0290 
0291 #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CALLDESCRIPTION_H