| //===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/QualTypeNames.h" |
| #include "TestVisitor.h" |
| using namespace clang; |
| |
| namespace { |
| struct TypeNameVisitor : TestVisitor<TypeNameVisitor> { |
| llvm::StringMap<std::string> ExpectedQualTypeNames; |
| bool WithGlobalNsPrefix = false; |
| |
| // ValueDecls are the least-derived decl with both a qualtype and a |
| // name. |
| bool TraverseDecl(Decl *D) { |
| return true; // Always continue |
| } |
| |
| bool VisitValueDecl(const ValueDecl *VD) { |
| std::string ExpectedName = |
| ExpectedQualTypeNames.lookup(VD->getNameAsString()); |
| if (ExpectedName != "") { |
| PrintingPolicy Policy(Context->getPrintingPolicy()); |
| Policy.SuppressScope = false; |
| Policy.AnonymousTagLocations = true; |
| Policy.PolishForDeclaration = true; |
| Policy.SuppressUnwrittenScope = true; |
| std::string ActualName = TypeName::getFullyQualifiedName( |
| VD->getType(), *Context, Policy, WithGlobalNsPrefix); |
| if (ExpectedName != ActualName) { |
| // A custom message makes it much easier to see what declaration |
| // failed compared to EXPECT_EQ. |
| EXPECT_TRUE(false) << "Typename::getFullyQualifiedName failed for " |
| << VD->getQualifiedNameAsString() << std::endl |
| << " Actual: " << ActualName << std::endl |
| << " Exepcted: " << ExpectedName; |
| } |
| } |
| return true; |
| } |
| }; |
| |
| // named namespaces inside anonymous namespaces |
| |
| TEST(QualTypeNameTest, getFullyQualifiedName) { |
| TypeNameVisitor Visitor; |
| // Simple case to test the test framework itself. |
| Visitor.ExpectedQualTypeNames["CheckInt"] = "int"; |
| |
| // Keeping the names of the variables whose types we check unique |
| // within the entire test--regardless of their own scope--makes it |
| // easier to diagnose test failures. |
| |
| // Simple namespace qualifier |
| Visitor.ExpectedQualTypeNames["CheckA"] = "A::B::Class0"; |
| // Lookup up the enclosing scopes, then down another one. (These |
| // appear as elaborated type in the AST. In that case--even if |
| // policy.SuppressScope = 0--qual_type.getAsString(policy) only |
| // gives the name as it appears in the source, not the full name. |
| Visitor.ExpectedQualTypeNames["CheckB"] = "A::B::C::Class1"; |
| // Template parameter expansion. |
| Visitor.ExpectedQualTypeNames["CheckC"] = |
| "A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>"; |
| // Recursive template parameter expansion. |
| Visitor.ExpectedQualTypeNames["CheckD"] = |
| "A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, " |
| "A::B::Template0<int, long> >"; |
| // Variadic Template expansion. |
| Visitor.ExpectedQualTypeNames["CheckE"] = |
| "A::Variadic<int, A::B::Template0<int, char>, " |
| "A::B::Template1<int, long>, A::B::C::MyInt>"; |
| // Using declarations should be fully expanded. |
| Visitor.ExpectedQualTypeNames["CheckF"] = "A::B::Class0"; |
| // Elements found within "using namespace foo;" should be fully |
| // expanded. |
| Visitor.ExpectedQualTypeNames["CheckG"] = "A::B::C::MyInt"; |
| // Type inside function |
| Visitor.ExpectedQualTypeNames["CheckH"] = "struct X"; |
| // Anonymous Namespaces |
| Visitor.ExpectedQualTypeNames["CheckI"] = "aClass"; |
| // Keyword inclusion with namespaces |
| Visitor.ExpectedQualTypeNames["CheckJ"] = "struct A::aStruct"; |
| // Anonymous Namespaces nested in named namespaces and vice-versa. |
| Visitor.ExpectedQualTypeNames["CheckK"] = "D::aStruct"; |
| // Namespace alias |
| Visitor.ExpectedQualTypeNames["CheckL"] = "A::B::C::MyInt"; |
| Visitor.ExpectedQualTypeNames["non_dependent_type_var"] = |
| "Foo<X>::non_dependent_type"; |
| Visitor.ExpectedQualTypeNames["AnEnumVar"] = "EnumScopeClass::AnEnum"; |
| Visitor.ExpectedQualTypeNames["AliasTypeVal"] = "A::B::C::InnerAlias<int>"; |
| Visitor.ExpectedQualTypeNames["CheckM"] = "const A::B::Class0 *"; |
| Visitor.ExpectedQualTypeNames["CheckN"] = "const X *"; |
| Visitor.runOver( |
| "int CheckInt;\n" |
| "template <typename T>\n" |
| "class OuterTemplateClass { };\n" |
| "namespace A {\n" |
| " namespace B {\n" |
| " class Class0 { };\n" |
| " namespace C {\n" |
| " typedef int MyInt;" |
| " template <typename T>\n" |
| " using InnerAlias = OuterTemplateClass<T>;\n" |
| " InnerAlias<int> AliasTypeVal;\n" |
| " }\n" |
| " template<class X, class Y> class Template0;" |
| " template<class X, class Y> class Template1;" |
| " typedef B::Class0 AnotherClass;\n" |
| " void Function1(Template0<C::MyInt,\n" |
| " AnotherClass> CheckC);\n" |
| " void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n" |
| " Template0<int, long> > CheckD);\n" |
| " void Function3(const B::Class0* CheckM);\n" |
| " }\n" |
| "template<typename... Values> class Variadic {};\n" |
| "Variadic<int, B::Template0<int, char>, " |
| " B::Template1<int, long>, " |
| " B::C::MyInt > CheckE;\n" |
| " namespace BC = B::C;\n" |
| " BC::MyInt CheckL;\n" |
| "}\n" |
| "using A::B::Class0;\n" |
| "void Function(Class0 CheckF);\n" |
| "using namespace A::B::C;\n" |
| "void Function(MyInt CheckG);\n" |
| "void f() {\n" |
| " struct X {} CheckH;\n" |
| "}\n" |
| "struct X;\n" |
| "void f(const ::X* CheckN) {}\n" |
| "namespace {\n" |
| " class aClass {};\n" |
| " aClass CheckI;\n" |
| "}\n" |
| "namespace A {\n" |
| " struct aStruct {} CheckJ;\n" |
| "}\n" |
| "namespace {\n" |
| " namespace D {\n" |
| " namespace {\n" |
| " class aStruct {};\n" |
| " aStruct CheckK;\n" |
| " }\n" |
| " }\n" |
| "}\n" |
| "template<class T> struct Foo {\n" |
| " typedef typename T::A dependent_type;\n" |
| " typedef int non_dependent_type;\n" |
| " dependent_type dependent_type_var;\n" |
| " non_dependent_type non_dependent_type_var;\n" |
| "};\n" |
| "struct X { typedef int A; };" |
| "Foo<X> var;" |
| "void F() {\n" |
| " var.dependent_type_var = 0;\n" |
| "var.non_dependent_type_var = 0;\n" |
| "}\n" |
| "class EnumScopeClass {\n" |
| "public:\n" |
| " enum AnEnum { ZERO, ONE };\n" |
| "};\n" |
| "EnumScopeClass::AnEnum AnEnumVar;\n", |
| TypeNameVisitor::Lang_CXX11 |
| ); |
| |
| TypeNameVisitor Complex; |
| Complex.ExpectedQualTypeNames["CheckTX"] = "B::TX"; |
| Complex.runOver( |
| "namespace A {" |
| " struct X {};" |
| "}" |
| "using A::X;" |
| "namespace fake_std {" |
| " template<class... Types > class tuple {};" |
| "}" |
| "namespace B {" |
| " using fake_std::tuple;" |
| " typedef tuple<X> TX;" |
| " TX CheckTX;" |
| " struct A { typedef int X; };" |
| "}"); |
| |
| TypeNameVisitor GlobalNsPrefix; |
| GlobalNsPrefix.WithGlobalNsPrefix = true; |
| GlobalNsPrefix.ExpectedQualTypeNames["IntVal"] = "int"; |
| GlobalNsPrefix.ExpectedQualTypeNames["BoolVal"] = "bool"; |
| GlobalNsPrefix.ExpectedQualTypeNames["XVal"] = "::A::B::X"; |
| GlobalNsPrefix.ExpectedQualTypeNames["IntAliasVal"] = "::A::B::Alias<int>"; |
| GlobalNsPrefix.ExpectedQualTypeNames["ZVal"] = "::A::B::Y::Z"; |
| GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z"; |
| GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct"; |
| GlobalNsPrefix.ExpectedQualTypeNames["YZMPtr"] = "::A::B::X ::A::B::Y::Z::*"; |
| GlobalNsPrefix.runOver( |
| "namespace A {\n" |
| " namespace B {\n" |
| " int IntVal;\n" |
| " bool BoolVal;\n" |
| " struct X {};\n" |
| " X XVal;\n" |
| " template <typename T> class CCC { };\n" |
| " template <typename T>\n" |
| " using Alias = CCC<T>;\n" |
| " Alias<int> IntAliasVal;\n" |
| " struct Y { struct Z { X YZIPtr; }; };\n" |
| " Y::Z ZVal;\n" |
| " X Y::Z::*YZMPtr;\n" |
| " }\n" |
| "}\n" |
| "struct Z {};\n" |
| "Z GlobalZVal;\n" |
| "namespace {\n" |
| " namespace D {\n" |
| " namespace {\n" |
| " class aStruct {};\n" |
| " aStruct CheckK;\n" |
| " }\n" |
| " }\n" |
| "}\n" |
| ); |
| |
| TypeNameVisitor InlineNamespace; |
| InlineNamespace.ExpectedQualTypeNames["c"] = "B::C"; |
| InlineNamespace.runOver("inline namespace A {\n" |
| " namespace B {\n" |
| " class C {};\n" |
| " }\n" |
| "}\n" |
| "using namespace A::B;\n" |
| "C c;\n", |
| TypeNameVisitor::Lang_CXX11); |
| |
| TypeNameVisitor AnonStrucs; |
| AnonStrucs.ExpectedQualTypeNames["a"] = "short"; |
| AnonStrucs.ExpectedQualTypeNames["un_in_st_1"] = |
| "union (anonymous struct at input.cc:1:1)::(anonymous union at " |
| "input.cc:2:27)"; |
| AnonStrucs.ExpectedQualTypeNames["b"] = "short"; |
| AnonStrucs.ExpectedQualTypeNames["un_in_st_2"] = |
| "union (anonymous struct at input.cc:1:1)::(anonymous union at " |
| "input.cc:5:27)"; |
| AnonStrucs.ExpectedQualTypeNames["anon_st"] = |
| "struct (anonymous struct at input.cc:1:1)"; |
| AnonStrucs.runOver(R"(struct { |
| union { |
| short a; |
| } un_in_st_1; |
| union { |
| short b; |
| } un_in_st_2; |
| } anon_st;)"); |
| } |
| |
| } // end anonymous namespace |