//===--- VariantValue.cpp - Polymorphic value type -*- 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 // //===----------------------------------------------------------------------===// /// /// \file /// Polymorphic value type. /// //===----------------------------------------------------------------------===// #include "clang/ASTMatchers/Dynamic/VariantValue.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/STLExtras.h" namespace clang { namespace ast_matchers { namespace dynamic { std::string ArgKind::asString() const { switch (getArgKind()) { case AK_Matcher: return (Twine("Matcher<") + MatcherKind.asStringRef() + ">").str(); case AK_Boolean: return "boolean"; case AK_Double: return "double"; case AK_Unsigned: return "unsigned"; case AK_String: return "string"; } llvm_unreachable("unhandled ArgKind"); } bool ArgKind::isConvertibleTo(ArgKind To, unsigned *Specificity) const { if (K != To.K) return false; if (K != AK_Matcher) { if (Specificity) *Specificity = 1; return true; } unsigned Distance; if (!MatcherKind.isBaseOf(To.MatcherKind, &Distance)) return false; if (Specificity) *Specificity = 100 - Distance; return true; } bool VariantMatcher::MatcherOps::canConstructFrom(const DynTypedMatcher &Matcher, bool &IsExactMatch) const { IsExactMatch = Matcher.getSupportedKind().isSame(NodeKind); return Matcher.canConvertTo(NodeKind); } llvm::Optional VariantMatcher::MatcherOps::constructVariadicOperator( DynTypedMatcher::VariadicOperator Op, ArrayRef InnerMatchers) const { std::vector DynMatchers; for (const auto &InnerMatcher : InnerMatchers) { // Abort if any of the inner matchers can't be converted to // Matcher. if (!InnerMatcher.Value) return llvm::None; llvm::Optional Inner = InnerMatcher.Value->getTypedMatcher(*this); if (!Inner) return llvm::None; DynMatchers.push_back(*Inner); } return DynTypedMatcher::constructVariadic(Op, NodeKind, DynMatchers); } VariantMatcher::Payload::~Payload() {} class VariantMatcher::SinglePayload : public VariantMatcher::Payload { public: SinglePayload(const DynTypedMatcher &Matcher) : Matcher(Matcher) {} llvm::Optional getSingleMatcher() const override { return Matcher; } std::string getTypeAsString() const override { return (Twine("Matcher<") + Matcher.getSupportedKind().asStringRef() + ">") .str(); } llvm::Optional getTypedMatcher(const MatcherOps &Ops) const override { bool Ignore; if (Ops.canConstructFrom(Matcher, Ignore)) return Matcher; return llvm::None; } bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind, unsigned *Specificity) const override { return ArgKind(Matcher.getSupportedKind()) .isConvertibleTo(Kind, Specificity); } private: const DynTypedMatcher Matcher; }; class VariantMatcher::PolymorphicPayload : public VariantMatcher::Payload { public: PolymorphicPayload(std::vector MatchersIn) : Matchers(std::move(MatchersIn)) {} ~PolymorphicPayload() override {} llvm::Optional getSingleMatcher() const override { if (Matchers.size() != 1) return llvm::Optional(); return Matchers[0]; } std::string getTypeAsString() const override { std::string Inner; for (size_t i = 0, e = Matchers.size(); i != e; ++i) { if (i != 0) Inner += "|"; Inner += Matchers[i].getSupportedKind().asStringRef(); } return (Twine("Matcher<") + Inner + ">").str(); } llvm::Optional getTypedMatcher(const MatcherOps &Ops) const override { bool FoundIsExact = false; const DynTypedMatcher *Found = nullptr; int NumFound = 0; for (size_t i = 0, e = Matchers.size(); i != e; ++i) { bool IsExactMatch; if (Ops.canConstructFrom(Matchers[i], IsExactMatch)) { if (Found) { if (FoundIsExact) { assert(!IsExactMatch && "We should not have two exact matches."); continue; } } Found = &Matchers[i]; FoundIsExact = IsExactMatch; ++NumFound; } } // We only succeed if we found exactly one, or if we found an exact match. if (Found && (FoundIsExact || NumFound == 1)) return *Found; return llvm::None; } bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind, unsigned *Specificity) const override { unsigned MaxSpecificity = 0; for (const DynTypedMatcher &Matcher : Matchers) { unsigned ThisSpecificity; if (ArgKind(Matcher.getSupportedKind()) .isConvertibleTo(Kind, &ThisSpecificity)) { MaxSpecificity = std::max(MaxSpecificity, ThisSpecificity); } } if (Specificity) *Specificity = MaxSpecificity; return MaxSpecificity > 0; } const std::vector Matchers; }; class VariantMatcher::VariadicOpPayload : public VariantMatcher::Payload { public: VariadicOpPayload(DynTypedMatcher::VariadicOperator Op, std::vector Args) : Op(Op), Args(std::move(Args)) {} llvm::Optional getSingleMatcher() const override { return llvm::Optional(); } std::string getTypeAsString() const override { std::string Inner; for (size_t i = 0, e = Args.size(); i != e; ++i) { if (i != 0) Inner += "&"; Inner += Args[i].getTypeAsString(); } return Inner; } llvm::Optional getTypedMatcher(const MatcherOps &Ops) const override { return Ops.constructVariadicOperator(Op, Args); } bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind, unsigned *Specificity) const override { for (const VariantMatcher &Matcher : Args) { if (!Matcher.isConvertibleTo(Kind, Specificity)) return false; } return true; } private: const DynTypedMatcher::VariadicOperator Op; const std::vector Args; }; VariantMatcher::VariantMatcher() {} VariantMatcher VariantMatcher::SingleMatcher(const DynTypedMatcher &Matcher) { return VariantMatcher(std::make_shared(Matcher)); } VariantMatcher VariantMatcher::PolymorphicMatcher(std::vector Matchers) { return VariantMatcher( std::make_shared(std::move(Matchers))); } VariantMatcher VariantMatcher::VariadicOperatorMatcher( DynTypedMatcher::VariadicOperator Op, std::vector Args) { return VariantMatcher( std::make_shared(Op, std::move(Args))); } llvm::Optional VariantMatcher::getSingleMatcher() const { return Value ? Value->getSingleMatcher() : llvm::Optional(); } void VariantMatcher::reset() { Value.reset(); } std::string VariantMatcher::getTypeAsString() const { if (Value) return Value->getTypeAsString(); return ""; } VariantValue::VariantValue(const VariantValue &Other) : Type(VT_Nothing) { *this = Other; } VariantValue::VariantValue(bool Boolean) : Type(VT_Nothing) { setBoolean(Boolean); } VariantValue::VariantValue(double Double) : Type(VT_Nothing) { setDouble(Double); } VariantValue::VariantValue(unsigned Unsigned) : Type(VT_Nothing) { setUnsigned(Unsigned); } VariantValue::VariantValue(StringRef String) : Type(VT_Nothing) { setString(String); } VariantValue::VariantValue(const VariantMatcher &Matcher) : Type(VT_Nothing) { setMatcher(Matcher); } VariantValue::~VariantValue() { reset(); } VariantValue &VariantValue::operator=(const VariantValue &Other) { if (this == &Other) return *this; reset(); switch (Other.Type) { case VT_Boolean: setBoolean(Other.getBoolean()); break; case VT_Double: setDouble(Other.getDouble()); break; case VT_Unsigned: setUnsigned(Other.getUnsigned()); break; case VT_String: setString(Other.getString()); break; case VT_Matcher: setMatcher(Other.getMatcher()); break; case VT_Nothing: Type = VT_Nothing; break; } return *this; } void VariantValue::reset() { switch (Type) { case VT_String: delete Value.String; break; case VT_Matcher: delete Value.Matcher; break; // Cases that do nothing. case VT_Boolean: case VT_Double: case VT_Unsigned: case VT_Nothing: break; } Type = VT_Nothing; } bool VariantValue::isBoolean() const { return Type == VT_Boolean; } bool VariantValue::getBoolean() const { assert(isBoolean()); return Value.Boolean; } void VariantValue::setBoolean(bool NewValue) { reset(); Type = VT_Boolean; Value.Boolean = NewValue; } bool VariantValue::isDouble() const { return Type == VT_Double; } double VariantValue::getDouble() const { assert(isDouble()); return Value.Double; } void VariantValue::setDouble(double NewValue) { reset(); Type = VT_Double; Value.Double = NewValue; } bool VariantValue::isUnsigned() const { return Type == VT_Unsigned; } unsigned VariantValue::getUnsigned() const { assert(isUnsigned()); return Value.Unsigned; } void VariantValue::setUnsigned(unsigned NewValue) { reset(); Type = VT_Unsigned; Value.Unsigned = NewValue; } bool VariantValue::isString() const { return Type == VT_String; } const std::string &VariantValue::getString() const { assert(isString()); return *Value.String; } void VariantValue::setString(StringRef NewValue) { reset(); Type = VT_String; Value.String = new std::string(NewValue); } bool VariantValue::isMatcher() const { return Type == VT_Matcher; } const VariantMatcher &VariantValue::getMatcher() const { assert(isMatcher()); return *Value.Matcher; } void VariantValue::setMatcher(const VariantMatcher &NewValue) { reset(); Type = VT_Matcher; Value.Matcher = new VariantMatcher(NewValue); } bool VariantValue::isConvertibleTo(ArgKind Kind, unsigned *Specificity) const { switch (Kind.getArgKind()) { case ArgKind::AK_Boolean: if (!isBoolean()) return false; *Specificity = 1; return true; case ArgKind::AK_Double: if (!isDouble()) return false; *Specificity = 1; return true; case ArgKind::AK_Unsigned: if (!isUnsigned()) return false; *Specificity = 1; return true; case ArgKind::AK_String: if (!isString()) return false; *Specificity = 1; return true; case ArgKind::AK_Matcher: if (!isMatcher()) return false; return getMatcher().isConvertibleTo(Kind.getMatcherKind(), Specificity); } llvm_unreachable("Invalid Type"); } bool VariantValue::isConvertibleTo(ArrayRef Kinds, unsigned *Specificity) const { unsigned MaxSpecificity = 0; for (const ArgKind& Kind : Kinds) { unsigned ThisSpecificity; if (!isConvertibleTo(Kind, &ThisSpecificity)) continue; MaxSpecificity = std::max(MaxSpecificity, ThisSpecificity); } if (Specificity && MaxSpecificity > 0) { *Specificity = MaxSpecificity; } return MaxSpecificity > 0; } std::string VariantValue::getTypeAsString() const { switch (Type) { case VT_String: return "String"; case VT_Matcher: return getMatcher().getTypeAsString(); case VT_Boolean: return "Boolean"; case VT_Double: return "Double"; case VT_Unsigned: return "Unsigned"; case VT_Nothing: return "Nothing"; } llvm_unreachable("Invalid Type"); } } // end namespace dynamic } // end namespace ast_matchers } // end namespace clang