263 lines · cpp
1//===- unittest/Tooling/LookupTest.cpp ------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/Refactoring/Lookup.h"10#include "TestVisitor.h"11#include "clang/AST/TypeLoc.h"12#include "clang/Basic/SourceLocation.h"13using namespace clang;14 15namespace {16struct GetDeclsVisitor : TestVisitor {17 std::function<void(CallExpr *)> OnCall;18 std::function<void(RecordTypeLoc)> OnRecordTypeLoc;19 std::function<void(UsingTypeLoc)> OnUsingTypeLoc;20 SmallVector<Decl *, 4> DeclStack;21 22 bool VisitCallExpr(CallExpr *Expr) override {23 if (OnCall)24 OnCall(Expr);25 return true;26 }27 28 bool VisitRecordTypeLoc(RecordTypeLoc Loc) override {29 if (OnRecordTypeLoc)30 OnRecordTypeLoc(Loc);31 return true;32 }33 34 bool VisitUsingTypeLoc(UsingTypeLoc Loc) override {35 if (OnUsingTypeLoc)36 OnUsingTypeLoc(Loc);37 return true;38 }39 40 bool TraverseDecl(Decl *D) override {41 DeclStack.push_back(D);42 bool Ret = TestVisitor::TraverseDecl(D);43 DeclStack.pop_back();44 return Ret;45 }46};47 48TEST(LookupTest, replaceNestedFunctionName) {49 GetDeclsVisitor Visitor;50 51 auto replaceCallExpr = [&](const CallExpr *Expr,52 StringRef ReplacementString) {53 const auto *Callee = cast<DeclRefExpr>(Expr->getCallee()->IgnoreImplicit());54 const ValueDecl *FD = Callee->getDecl();55 return tooling::replaceNestedName(56 Callee->getQualifier(), Callee->getLocation(),57 Visitor.DeclStack.back()->getDeclContext(), FD, ReplacementString);58 };59 60 Visitor.OnCall = [&](CallExpr *Expr) {61 EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));62 };63 Visitor.runOver("namespace a { void foo(); }\n"64 "namespace a { void f() { foo(); } }\n");65 66 Visitor.OnCall = [&](CallExpr *Expr) {67 EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));68 };69 Visitor.runOver("namespace a { void foo(); }\n"70 "namespace a { void f() { foo(); } }\n");71 72 Visitor.OnCall = [&](CallExpr *Expr) {73 EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));74 };75 Visitor.runOver("namespace a { void foo(); }\n"76 "namespace b { void f() { a::foo(); } }\n");77 78 Visitor.OnCall = [&](CallExpr *Expr) {79 EXPECT_EQ("::a::bar", replaceCallExpr(Expr, "::a::bar"));80 };81 Visitor.runOver("namespace a { void foo(); }\n"82 "namespace b { namespace a { void foo(); }\n"83 "void f() { a::foo(); } }\n");84 85 Visitor.OnCall = [&](CallExpr *Expr) {86 EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));87 };88 Visitor.runOver("namespace a { namespace b { void foo(); }\n"89 "void f() { b::foo(); } }\n");90 91 Visitor.OnCall = [&](CallExpr *Expr) {92 EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));93 };94 Visitor.runOver("namespace a { namespace b { void foo(); }\n"95 "void f() { b::foo(); } }\n");96 97 Visitor.OnCall = [&](CallExpr *Expr) {98 EXPECT_EQ("bar", replaceCallExpr(Expr, "::bar"));99 };100 Visitor.runOver("void foo(); void f() { foo(); }\n");101 102 Visitor.OnCall = [&](CallExpr *Expr) {103 EXPECT_EQ("::bar", replaceCallExpr(Expr, "::bar"));104 };105 Visitor.runOver("void foo(); void f() { ::foo(); }\n");106 107 Visitor.OnCall = [&](CallExpr *Expr) {108 EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));109 };110 Visitor.runOver("namespace a { void foo(); }\nvoid f() { a::foo(); }\n");111 112 Visitor.OnCall = [&](CallExpr *Expr) {113 EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));114 };115 Visitor.runOver("namespace a { int foo(); }\nauto f = a::foo();\n");116 117 Visitor.OnCall = [&](CallExpr *Expr) {118 EXPECT_EQ("bar", replaceCallExpr(Expr, "::a::bar"));119 };120 Visitor.runOver(121 "namespace a { int foo(); }\nusing a::foo;\nauto f = foo();\n");122 123 Visitor.OnCall = [&](CallExpr *Expr) {124 EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar"));125 };126 Visitor.runOver("namespace a { namespace b { void foo(); } }\n"127 "namespace a { namespace b { namespace {"128 "void f() { foo(); }"129 "} } }\n");130 131 Visitor.OnCall = [&](CallExpr *Expr) {132 EXPECT_EQ("x::bar", replaceCallExpr(Expr, "::a::x::bar"));133 };134 Visitor.runOver("namespace a { namespace b { void foo(); } }\n"135 "namespace a { namespace b { namespace c {"136 "void f() { foo(); }"137 "} } }\n");138 139 // If the shortest name is ambiguous, we need to add more qualifiers.140 Visitor.OnCall = [&](CallExpr *Expr) {141 EXPECT_EQ("a::y::bar", replaceCallExpr(Expr, "::a::y::bar"));142 };143 Visitor.runOver(R"(144 namespace a {145 namespace b {146 namespace x { void foo() {} }147 namespace y { void foo() {} }148 }149 }150 151 namespace a {152 namespace b {153 void f() { x::foo(); }154 }155 })");156 157 Visitor.OnCall = [&](CallExpr *Expr) {158 // y::bar would be ambiguous due to "a::b::y".159 EXPECT_EQ("::y::bar", replaceCallExpr(Expr, "::y::bar"));160 };161 Visitor.runOver(R"(162 namespace a {163 namespace b {164 void foo() {}165 namespace y { }166 }167 }168 169 namespace a {170 namespace b {171 void f() { foo(); }172 }173 })");174 175 Visitor.OnCall = [&](CallExpr *Expr) {176 EXPECT_EQ("y::bar", replaceCallExpr(Expr, "::y::bar"));177 };178 Visitor.runOver(R"(179 namespace a {180 namespace b {181 namespace x { void foo() {} }182 namespace y { void foo() {} }183 }184 }185 186 void f() { a::b::x::foo(); }187 )");188}189 190TEST(LookupTest, replaceNestedClassName) {191 GetDeclsVisitor Visitor;192 193 auto replaceTypeLoc = [&](const NamedDecl *ND, SourceLocation Loc,194 StringRef ReplacementString) {195 return tooling::replaceNestedName(196 /*Use=*/std::nullopt, Loc, Visitor.DeclStack.back()->getDeclContext(),197 ND, ReplacementString);198 };199 200 Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {201 // Filter Types by name since there are other `RecordTypeLoc` in the test202 // file.203 if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo") {204 EXPECT_EQ("x::Bar", replaceTypeLoc(Type.getDecl(), Type.getBeginLoc(),205 "::a::x::Bar"));206 }207 };208 Visitor.runOver("namespace a { namespace b {\n"209 "class Foo;\n"210 "namespace c { Foo f();; }\n"211 "} }\n");212 213 Visitor.OnUsingTypeLoc = [&](UsingTypeLoc Type) {214 // Filter Types by name since there are other `RecordTypeLoc` in the test215 // file.216 // `a::b::Foo` in using shadow decl is not `TypeLoc`.217 auto *TD = Type.getDecl()->getTargetDecl();218 if (TD->getQualifiedNameAsString() == "a::b::Foo") {219 EXPECT_EQ("Bar", replaceTypeLoc(TD, Type.getBeginLoc(), "::a::x::Bar"));220 }221 };222 Visitor.runOver("namespace a { namespace b { class Foo {}; } }\n"223 "namespace c { using a::b::Foo; Foo f();; }\n");224 225 // Rename TypeLoc `x::y::Old` to new name `x::Foo` at [0] and check that the226 // type is replaced with "Foo" instead of "x::Foo". Although there is a symbol227 // `x::y::Foo` in c.cc [1], it should not make "Foo" at [0] ambiguous because228 // it's not visible at [0].229 Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {230 if (Type.getDecl()->getQualifiedNameAsString() == "x::y::Old") {231 EXPECT_EQ("Foo",232 replaceTypeLoc(Type.getDecl(), Type.getBeginLoc(), "::x::Foo"));233 }234 };235 Visitor.runOver(R"(236 // a.h237 namespace x {238 namespace y {239 class Old {};240 class Other {};241 }242 }243 244 // b.h245 namespace x {246 namespace y {247 // This is to be renamed to x::Foo248 // The expected replacement is "Foo".249 Old f; // [0].250 }251 }252 253 // c.cc254 namespace x {255 namespace y {256 using Foo = ::x::y::Other; // [1]257 }258 }259 )");260}261 262} // end anonymous namespace263