1 //===- unittest/Introspection/IntrospectionTest.cpp ----------*- C++ -*---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Tests for AST location API introspection.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "clang/AST/ASTContext.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "clang/Tooling/NodeIntrospection.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "gmock/gmock-matchers.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 
22 using namespace clang;
23 using namespace clang::ast_matchers;
24 using namespace clang::tooling;
25 
26 using ::testing::Pair;
27 using ::testing::UnorderedElementsAre;
28 
29 template<typename T, typename MapType>
30 std::map<std::string, T>
31 FormatExpected(const MapType &Accessors) {
32   std::map<std::string, T> Result;
33   llvm::transform(Accessors,
34                   std::inserter(Result, Result.end()),
35                   [](const auto &Accessor) {
36                     return std::make_pair(
37                         LocationCallFormatterCpp::format(Accessor.second.get()),
38                         Accessor.first);
39                   });
40   return Result;
41 }
42 
43 #define STRING_LOCATION_PAIR(INSTANCE, LOC) Pair(#LOC, INSTANCE->LOC)
44 
45 TEST(Introspection, SourceLocations_Stmt) {
46   auto AST = buildASTFromCode("void foo() {} void bar() { foo(); }", "foo.cpp",
47                               std::make_shared<PCHContainerOperations>());
48   auto &Ctx = AST->getASTContext();
49   auto &TU = *Ctx.getTranslationUnitDecl();
50 
51   auto BoundNodes = ast_matchers::match(
52       decl(hasDescendant(
53           callExpr(callee(functionDecl(hasName("foo")))).bind("fooCall"))),
54       TU, Ctx);
55 
56   EXPECT_EQ(BoundNodes.size(), 1u);
57 
58   auto *FooCall = BoundNodes[0].getNodeAs<CallExpr>("fooCall");
59 
60   auto Result = NodeIntrospection::GetLocations(FooCall);
61 
62   if (Result.LocationAccessors.empty() && Result.RangeAccessors.empty())
63   {
64     return;
65   }
66 
67   auto ExpectedLocations =
68     FormatExpected<SourceLocation>(Result.LocationAccessors);
69 
70   EXPECT_THAT(
71       ExpectedLocations,
72       UnorderedElementsAre(STRING_LOCATION_PAIR(FooCall, getBeginLoc()),
73                            STRING_LOCATION_PAIR(FooCall, getEndLoc()),
74                            STRING_LOCATION_PAIR(FooCall, getExprLoc()),
75                            STRING_LOCATION_PAIR(FooCall, getRParenLoc())));
76 
77   auto ExpectedRanges = FormatExpected<SourceRange>(Result.RangeAccessors);
78 
79   EXPECT_THAT(ExpectedRanges, UnorderedElementsAre(STRING_LOCATION_PAIR(
80                                   FooCall, getSourceRange())));
81 }
82 
83 TEST(Introspection, SourceLocations_Decl) {
84   auto AST =
85       buildASTFromCode(R"cpp(
86 namespace ns1 {
87 namespace ns2 {
88 template <typename T, typename U> struct Foo {};
89 template <typename T, typename U> struct Bar {
90   struct Nested {
91     template <typename A, typename B>
92     Foo<A, B> method(int i, bool b) const noexcept(true);
93   };
94 };
95 } // namespace ns2
96 } // namespace ns1
97 
98 template <typename T, typename U>
99 template <typename A, typename B>
100 ns1::ns2::Foo<A, B> ns1::ns2::Bar<T, U>::Nested::method(int i, bool b) const
101     noexcept(true) {}
102 )cpp",
103                        "foo.cpp", std::make_shared<PCHContainerOperations>());
104   auto &Ctx = AST->getASTContext();
105   auto &TU = *Ctx.getTranslationUnitDecl();
106 
107   auto BoundNodes = ast_matchers::match(
108       decl(hasDescendant(
109           cxxMethodDecl(hasName("method")).bind("method"))),
110       TU, Ctx);
111 
112   EXPECT_EQ(BoundNodes.size(), 1u);
113 
114   const auto *MethodDecl = BoundNodes[0].getNodeAs<CXXMethodDecl>("method");
115 
116   auto Result = NodeIntrospection::GetLocations(MethodDecl);
117 
118   if (Result.LocationAccessors.empty() && Result.RangeAccessors.empty()) {
119     return;
120   }
121 
122   auto ExpectedLocations =
123       FormatExpected<SourceLocation>(Result.LocationAccessors);
124 
125   EXPECT_THAT(ExpectedLocations,
126               UnorderedElementsAre(
127                   STRING_LOCATION_PAIR(MethodDecl, getBeginLoc()),
128                   STRING_LOCATION_PAIR(MethodDecl, getBodyRBrace()),
129                   STRING_LOCATION_PAIR(MethodDecl, getEllipsisLoc()),
130                   STRING_LOCATION_PAIR(MethodDecl, getInnerLocStart()),
131                   STRING_LOCATION_PAIR(MethodDecl, getLocation()),
132                   STRING_LOCATION_PAIR(MethodDecl, getOuterLocStart()),
133                   STRING_LOCATION_PAIR(MethodDecl, getPointOfInstantiation()),
134                   STRING_LOCATION_PAIR(MethodDecl, getTypeSpecEndLoc()),
135                   STRING_LOCATION_PAIR(MethodDecl, getTypeSpecStartLoc()),
136                   STRING_LOCATION_PAIR(MethodDecl, getEndLoc())));
137 
138   auto ExpectedRanges = FormatExpected<SourceRange>(Result.RangeAccessors);
139 
140   EXPECT_THAT(
141       ExpectedRanges,
142       UnorderedElementsAre(
143           STRING_LOCATION_PAIR(MethodDecl, getExceptionSpecSourceRange()),
144           STRING_LOCATION_PAIR(MethodDecl, getParametersSourceRange()),
145           STRING_LOCATION_PAIR(MethodDecl, getReturnTypeSourceRange()),
146           STRING_LOCATION_PAIR(MethodDecl, getSourceRange())));
147 }
148