Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 1 | //===--- VariantValue.h - Polymorphic value type -*- C++ -*-===/ |
| 2 | // The LLVM Compiler Infrastructure |
| 3 | // |
| 4 | // This file is distributed under the University of Illinois Open Source |
| 5 | // License. See LICENSE.TXT for details. |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// |
| 9 | /// \file |
| 10 | /// \brief Polymorphic value type. |
| 11 | /// |
| 12 | /// Supports all the types required for dynamic Matcher construction. |
| 13 | /// Used by the registry to construct matchers in a generic way. |
| 14 | /// |
| 15 | //===----------------------------------------------------------------------===// |
| 16 | |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 17 | #ifndef LLVM_CLANG_ASTMATCHERS_DYNAMIC_VARIANTVALUE_H |
| 18 | #define LLVM_CLANG_ASTMATCHERS_DYNAMIC_VARIANTVALUE_H |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 19 | |
| 20 | #include "clang/ASTMatchers/ASTMatchers.h" |
| 21 | #include "clang/ASTMatchers/ASTMatchersInternal.h" |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 22 | #include "llvm/ADT/IntrusiveRefCntPtr.h" |
Samuel Benzaquen | b7488d7 | 2013-10-29 14:37:15 +0000 | [diff] [blame] | 23 | #include "llvm/ADT/Optional.h" |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 24 | #include "llvm/ADT/Twine.h" |
Stephen Hines | 651f13c | 2014-04-23 16:59:28 -0700 | [diff] [blame] | 25 | #include <memory> |
| 26 | #include <vector> |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 27 | |
| 28 | namespace clang { |
| 29 | namespace ast_matchers { |
| 30 | namespace dynamic { |
| 31 | |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 32 | /// \brief Kind identifier. |
| 33 | /// |
| 34 | /// It supports all types that VariantValue can contain. |
| 35 | class ArgKind { |
| 36 | public: |
| 37 | enum Kind { |
| 38 | AK_Matcher, |
| 39 | AK_Unsigned, |
| 40 | AK_String |
| 41 | }; |
| 42 | /// \brief Constructor for non-matcher types. |
| 43 | ArgKind(Kind K) : K(K) { assert(K != AK_Matcher); } |
| 44 | |
| 45 | /// \brief Constructor for matcher types. |
| 46 | ArgKind(ast_type_traits::ASTNodeKind MatcherKind) |
| 47 | : K(AK_Matcher), MatcherKind(MatcherKind) {} |
| 48 | |
| 49 | Kind getArgKind() const { return K; } |
| 50 | ast_type_traits::ASTNodeKind getMatcherKind() const { |
| 51 | assert(K == AK_Matcher); |
| 52 | return MatcherKind; |
| 53 | } |
| 54 | |
| 55 | /// \brief Determines if this type can be converted to \p To. |
| 56 | /// |
| 57 | /// \param To the requested destination type. |
| 58 | /// |
| 59 | /// \param Specificity value corresponding to the "specificity" of the |
| 60 | /// convertion. |
| 61 | bool isConvertibleTo(ArgKind To, unsigned *Specificity) const; |
| 62 | |
| 63 | bool operator<(const ArgKind &Other) const { |
| 64 | if (K == AK_Matcher && Other.K == AK_Matcher) |
| 65 | return MatcherKind < Other.MatcherKind; |
| 66 | return K < Other.K; |
| 67 | } |
| 68 | |
| 69 | /// \brief String representation of the type. |
| 70 | std::string asString() const; |
| 71 | |
| 72 | private: |
| 73 | Kind K; |
| 74 | ast_type_traits::ASTNodeKind MatcherKind; |
| 75 | }; |
| 76 | |
Reid Kleckner | 6e01994 | 2013-05-14 20:30:49 +0000 | [diff] [blame] | 77 | using ast_matchers::internal::DynTypedMatcher; |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 78 | |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 79 | /// \brief A variant matcher object. |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 80 | /// |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 81 | /// The purpose of this object is to abstract simple and polymorphic matchers |
| 82 | /// into a single object type. |
| 83 | /// Polymorphic matchers might be implemented as a list of all the possible |
| 84 | /// overloads of the matcher. \c VariantMatcher knows how to select the |
| 85 | /// appropriate overload when needed. |
| 86 | /// To get a real matcher object out of a \c VariantMatcher you can do: |
| 87 | /// - getSingleMatcher() which returns a matcher, only if it is not ambiguous |
| 88 | /// to decide which matcher to return. Eg. it contains only a single |
| 89 | /// matcher, or a polymorphic one with only one overload. |
| 90 | /// - hasTypedMatcher<T>()/getTypedMatcher<T>(): These calls will determine if |
| 91 | /// the underlying matcher(s) can unambiguously return a Matcher<T>. |
| 92 | class VariantMatcher { |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 93 | /// \brief Methods that depend on T from hasTypedMatcher/getTypedMatcher. |
| 94 | class MatcherOps { |
| 95 | public: |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 96 | MatcherOps(ast_type_traits::ASTNodeKind NodeKind) : NodeKind(NodeKind) {} |
| 97 | |
| 98 | bool canConstructFrom(const DynTypedMatcher &Matcher, |
| 99 | bool &IsExactMatch) const; |
| 100 | |
| 101 | /// \brief Convert \p Matcher the destination type and return it as a new |
| 102 | /// DynTypedMatcher. |
| 103 | virtual DynTypedMatcher |
| 104 | convertMatcher(const DynTypedMatcher &Matcher) const = 0; |
| 105 | |
| 106 | /// \brief Constructs a variadic typed matcher from \p InnerMatchers. |
| 107 | /// Will try to convert each inner matcher to the destination type and |
| 108 | /// return llvm::None if it fails to do so. |
| 109 | llvm::Optional<DynTypedMatcher> |
| 110 | constructVariadicOperator(DynTypedMatcher::VariadicOperator Op, |
| 111 | ArrayRef<VariantMatcher> InnerMatchers) const; |
| 112 | |
| 113 | protected: |
| 114 | ~MatcherOps() {} |
| 115 | |
| 116 | private: |
| 117 | ast_type_traits::ASTNodeKind NodeKind; |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 118 | }; |
| 119 | |
| 120 | /// \brief Payload interface to be specialized by each matcher type. |
| 121 | /// |
| 122 | /// It follows a similar interface as VariantMatcher itself. |
| 123 | class Payload : public RefCountedBaseVPTR { |
| 124 | public: |
| 125 | virtual ~Payload(); |
Samuel Benzaquen | b7488d7 | 2013-10-29 14:37:15 +0000 | [diff] [blame] | 126 | virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const = 0; |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 127 | virtual std::string getTypeAsString() const = 0; |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 128 | virtual llvm::Optional<DynTypedMatcher> |
| 129 | getTypedMatcher(const MatcherOps &Ops) const = 0; |
| 130 | virtual bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind, |
| 131 | unsigned *Specificity) const = 0; |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 132 | }; |
| 133 | |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 134 | public: |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 135 | /// \brief A null matcher. |
| 136 | VariantMatcher(); |
| 137 | |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 138 | /// \brief Clones the provided matcher. |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 139 | static VariantMatcher SingleMatcher(const DynTypedMatcher &Matcher); |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 140 | |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 141 | /// \brief Clones the provided matchers. |
| 142 | /// |
| 143 | /// They should be the result of a polymorphic matcher. |
Stephen Hines | 6bcf27b | 2014-05-29 04:14:42 -0700 | [diff] [blame] | 144 | static VariantMatcher |
| 145 | PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers); |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 146 | |
Samuel Benzaquen | a735090 | 2013-08-28 18:42:04 +0000 | [diff] [blame] | 147 | /// \brief Creates a 'variadic' operator matcher. |
| 148 | /// |
| 149 | /// It will bind to the appropriate type on getTypedMatcher<T>(). |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 150 | static VariantMatcher |
| 151 | VariadicOperatorMatcher(DynTypedMatcher::VariadicOperator Op, |
| 152 | std::vector<VariantMatcher> Args); |
Samuel Benzaquen | a735090 | 2013-08-28 18:42:04 +0000 | [diff] [blame] | 153 | |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 154 | /// \brief Makes the matcher the "null" matcher. |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 155 | void reset(); |
| 156 | |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 157 | /// \brief Whether the matcher is null. |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 158 | bool isNull() const { return !Value; } |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 159 | |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 160 | /// \brief Return a single matcher, if there is no ambiguity. |
| 161 | /// |
Samuel Benzaquen | b7488d7 | 2013-10-29 14:37:15 +0000 | [diff] [blame] | 162 | /// \returns the matcher, if there is only one matcher. An empty Optional, if |
| 163 | /// the underlying matcher is a polymorphic matcher with more than one |
| 164 | /// representation. |
| 165 | llvm::Optional<DynTypedMatcher> getSingleMatcher() const; |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 166 | |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 167 | /// \brief Determines if the contained matcher can be converted to |
| 168 | /// \c Matcher<T>. |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 169 | /// |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 170 | /// For the Single case, it returns true if it can be converted to |
| 171 | /// \c Matcher<T>. |
| 172 | /// For the Polymorphic case, it returns true if one, and only one, of the |
| 173 | /// overloads can be converted to \c Matcher<T>. If there are more than one |
| 174 | /// that can, the result would be ambiguous and false is returned. |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 175 | template <class T> |
| 176 | bool hasTypedMatcher() const { |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 177 | if (!Value) return false; |
| 178 | return Value->getTypedMatcher(TypedMatcherOps<T>()).hasValue(); |
| 179 | } |
| 180 | |
| 181 | /// \brief Determines if the contained matcher can be converted to \p Kind. |
| 182 | /// |
| 183 | /// \param Kind the requested destination type. |
| 184 | /// |
| 185 | /// \param Specificity value corresponding to the "specificity" of the |
| 186 | /// convertion. |
| 187 | bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind, |
| 188 | unsigned *Specificity) const { |
| 189 | if (Value) |
| 190 | return Value->isConvertibleTo(Kind, Specificity); |
| 191 | return false; |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 192 | } |
| 193 | |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 194 | /// \brief Return this matcher as a \c Matcher<T>. |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 195 | /// |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 196 | /// Handles the different types (Single, Polymorphic) accordingly. |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 197 | /// Asserts that \c hasTypedMatcher<T>() is true. |
| 198 | template <class T> |
| 199 | ast_matchers::internal::Matcher<T> getTypedMatcher() const { |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 200 | assert(hasTypedMatcher<T>() && "hasTypedMatcher<T>() == false"); |
| 201 | return Value->getTypedMatcher(TypedMatcherOps<T>()) |
| 202 | ->template convertTo<T>(); |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | /// \brief String representation of the type of the value. |
| 206 | /// |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 207 | /// If the underlying matcher is a polymorphic one, the string will show all |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 208 | /// the types. |
| 209 | std::string getTypeAsString() const; |
| 210 | |
| 211 | private: |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 212 | explicit VariantMatcher(Payload *Value) : Value(Value) {} |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 213 | |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 214 | template <typename T> struct TypedMatcherOps; |
| 215 | |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 216 | class SinglePayload; |
| 217 | class PolymorphicPayload; |
Samuel Benzaquen | a735090 | 2013-08-28 18:42:04 +0000 | [diff] [blame] | 218 | class VariadicOpPayload; |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 219 | |
Samuel Benzaquen | 544ae5b | 2013-08-22 16:38:33 +0000 | [diff] [blame] | 220 | IntrusiveRefCntPtr<const Payload> Value; |
Samuel Benzaquen | ef7eb02 | 2013-06-21 15:51:31 +0000 | [diff] [blame] | 221 | }; |
| 222 | |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 223 | template <typename T> |
| 224 | struct VariantMatcher::TypedMatcherOps final : VariantMatcher::MatcherOps { |
| 225 | TypedMatcherOps() |
| 226 | : MatcherOps(ast_type_traits::ASTNodeKind::getFromNodeKind<T>()) {} |
| 227 | typedef ast_matchers::internal::Matcher<T> MatcherT; |
| 228 | |
| 229 | DynTypedMatcher |
| 230 | convertMatcher(const DynTypedMatcher &Matcher) const override { |
| 231 | return DynTypedMatcher(Matcher.convertTo<T>()); |
| 232 | } |
| 233 | }; |
| 234 | |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 235 | /// \brief Variant value class. |
| 236 | /// |
| 237 | /// Basically, a tagged union with value type semantics. |
| 238 | /// It is used by the registry as the return value and argument type for the |
| 239 | /// matcher factory methods. |
| 240 | /// It can be constructed from any of the supported types. It supports |
| 241 | /// copy/assignment. |
| 242 | /// |
| 243 | /// Supported types: |
Samuel Benzaquen | 7a337af | 2013-06-04 15:46:22 +0000 | [diff] [blame] | 244 | /// - \c unsigned |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 245 | /// - \c std::string |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 246 | /// - \c VariantMatcher (\c DynTypedMatcher / \c Matcher<T>) |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 247 | class VariantValue { |
| 248 | public: |
| 249 | VariantValue() : Type(VT_Nothing) {} |
| 250 | |
| 251 | VariantValue(const VariantValue &Other); |
| 252 | ~VariantValue(); |
| 253 | VariantValue &operator=(const VariantValue &Other); |
| 254 | |
| 255 | /// \brief Specific constructors for each supported type. |
Samuel Benzaquen | 7a337af | 2013-06-04 15:46:22 +0000 | [diff] [blame] | 256 | VariantValue(unsigned Unsigned); |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 257 | VariantValue(const std::string &String); |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 258 | VariantValue(const VariantMatcher &Matchers); |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 259 | |
Stephen Hines | 6bcf27b | 2014-05-29 04:14:42 -0700 | [diff] [blame] | 260 | /// \brief Returns true iff this is not an empty value. |
Stephen Hines | 0e2c34f | 2015-03-23 12:09:02 -0700 | [diff] [blame] | 261 | explicit operator bool() const { return hasValue(); } |
Stephen Hines | 6bcf27b | 2014-05-29 04:14:42 -0700 | [diff] [blame] | 262 | bool hasValue() const { return Type != VT_Nothing; } |
| 263 | |
Samuel Benzaquen | 7a337af | 2013-06-04 15:46:22 +0000 | [diff] [blame] | 264 | /// \brief Unsigned value functions. |
| 265 | bool isUnsigned() const; |
| 266 | unsigned getUnsigned() const; |
| 267 | void setUnsigned(unsigned Unsigned); |
| 268 | |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 269 | /// \brief String value functions. |
| 270 | bool isString() const; |
| 271 | const std::string &getString() const; |
| 272 | void setString(const std::string &String); |
| 273 | |
| 274 | /// \brief Matcher value functions. |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 275 | bool isMatcher() const; |
| 276 | const VariantMatcher &getMatcher() const; |
| 277 | void setMatcher(const VariantMatcher &Matcher); |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 278 | |
Stephen Hines | 176edba | 2014-12-01 14:53:08 -0800 | [diff] [blame] | 279 | /// \brief Determines if the contained value can be converted to \p Kind. |
| 280 | /// |
| 281 | /// \param Kind the requested destination type. |
| 282 | /// |
| 283 | /// \param Specificity value corresponding to the "specificity" of the |
| 284 | /// convertion. |
| 285 | bool isConvertibleTo(ArgKind Kind, unsigned* Specificity) const; |
| 286 | |
| 287 | /// \brief Determines if the contained value can be converted to any kind |
| 288 | /// in \p Kinds. |
| 289 | /// |
| 290 | /// \param Kinds the requested destination types. |
| 291 | /// |
| 292 | /// \param Specificity value corresponding to the "specificity" of the |
| 293 | /// convertion. It is the maximum specificity of all the possible |
| 294 | /// conversions. |
| 295 | bool isConvertibleTo(ArrayRef<ArgKind> Kinds, unsigned *Specificity) const; |
| 296 | |
Samuel Benzaquen | 76c2f92 | 2013-06-20 14:28:32 +0000 | [diff] [blame] | 297 | /// \brief String representation of the type of the value. |
| 298 | std::string getTypeAsString() const; |
| 299 | |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 300 | private: |
| 301 | void reset(); |
| 302 | |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 303 | /// \brief All supported value types. |
| 304 | enum ValueType { |
| 305 | VT_Nothing, |
Samuel Benzaquen | 7a337af | 2013-06-04 15:46:22 +0000 | [diff] [blame] | 306 | VT_Unsigned, |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 307 | VT_String, |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 308 | VT_Matcher |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 309 | }; |
| 310 | |
| 311 | /// \brief All supported value types. |
| 312 | union AllValues { |
Samuel Benzaquen | 7a337af | 2013-06-04 15:46:22 +0000 | [diff] [blame] | 313 | unsigned Unsigned; |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 314 | std::string *String; |
Samuel Benzaquen | 9d02807 | 2013-08-13 14:54:51 +0000 | [diff] [blame] | 315 | VariantMatcher *Matcher; |
Manuel Klimek | f7f295f | 2013-05-14 09:13:00 +0000 | [diff] [blame] | 316 | }; |
| 317 | |
| 318 | ValueType Type; |
| 319 | AllValues Value; |
| 320 | }; |
| 321 | |
| 322 | } // end namespace dynamic |
| 323 | } // end namespace ast_matchers |
| 324 | } // end namespace clang |
| 325 | |
| 326 | #endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H |