2019-05-07 07:11:56 +00:00
|
|
|
//===-- RenameTests.cpp -----------------------------------------*- 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Annotations.h"
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "ClangdServer.h"
|
|
|
|
#include "SyncAPI.h"
|
2019-05-07 07:11:56 +00:00
|
|
|
#include "TestFS.h"
|
|
|
|
#include "TestTU.h"
|
2019-10-23 14:40:20 +02:00
|
|
|
#include "index/Ref.h"
|
2019-05-07 07:11:56 +00:00
|
|
|
#include "refactor/Rename.h"
|
2020-04-16 23:12:09 +02:00
|
|
|
#include "support/TestTracer.h"
|
2019-05-07 07:11:56 +00:00
|
|
|
#include "clang/Tooling/Core/Replacement.h"
|
2019-12-09 17:00:51 +01:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2019-10-23 14:40:20 +02:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
2020-11-20 10:38:41 +01:00
|
|
|
#include <algorithm>
|
2019-05-07 07:11:56 +00:00
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace {
|
|
|
|
|
2020-04-16 23:12:09 +02:00
|
|
|
using testing::ElementsAre;
|
2019-10-23 14:40:20 +02:00
|
|
|
using testing::Eq;
|
2019-12-09 17:00:51 +01:00
|
|
|
using testing::IsEmpty;
|
2020-04-16 23:12:09 +02:00
|
|
|
using testing::Pair;
|
|
|
|
using testing::SizeIs;
|
2019-10-23 14:40:20 +02:00
|
|
|
using testing::UnorderedElementsAre;
|
2019-12-09 17:00:51 +01:00
|
|
|
using testing::UnorderedElementsAreArray;
|
2019-10-23 14:40:20 +02:00
|
|
|
|
2021-03-10 13:41:27 +00:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
|
|
|
|
createOverlay(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Base,
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Overlay) {
|
|
|
|
auto OFS =
|
|
|
|
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(std::move(Base));
|
|
|
|
OFS->pushOverlay(std::move(Overlay));
|
|
|
|
return OFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getVFSFromAST(ParsedAST &AST) {
|
|
|
|
return &AST.getSourceManager().getFileManager().getVirtualFileSystem();
|
|
|
|
}
|
|
|
|
|
2020-01-04 10:28:41 -05:00
|
|
|
// Convert a Range to a Ref.
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
Ref refWithRange(const clangd::Range &Range, const std::string &URI) {
|
|
|
|
Ref Result;
|
2020-02-06 10:28:47 +01:00
|
|
|
Result.Kind = RefKind::Reference | RefKind::Spelled;
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
Result.Location.Start.setLine(Range.start.line);
|
|
|
|
Result.Location.Start.setColumn(Range.start.character);
|
|
|
|
Result.Location.End.setLine(Range.end.line);
|
|
|
|
Result.Location.End.setColumn(Range.end.character);
|
|
|
|
Result.Location.FileURI = URI.c_str();
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2019-10-23 14:40:20 +02:00
|
|
|
// Build a RefSlab from all marked ranges in the annotation. The ranges are
|
|
|
|
// assumed to associate with the given SymbolName.
|
|
|
|
std::unique_ptr<RefSlab> buildRefSlab(const Annotations &Code,
|
|
|
|
llvm::StringRef SymbolName,
|
|
|
|
llvm::StringRef Path) {
|
|
|
|
RefSlab::Builder Builder;
|
|
|
|
TestTU TU;
|
2020-01-28 20:23:46 +01:00
|
|
|
TU.HeaderCode = std::string(Code.code());
|
2019-10-23 14:40:20 +02:00
|
|
|
auto Symbols = TU.headerSymbols();
|
|
|
|
const auto &SymbolID = findSymbol(Symbols, SymbolName).ID;
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
std::string PathURI = URI::create(Path).toString();
|
|
|
|
for (const auto &Range : Code.ranges())
|
|
|
|
Builder.insert(SymbolID, refWithRange(Range, PathURI));
|
2019-10-23 14:40:20 +02:00
|
|
|
|
|
|
|
return std::make_unique<RefSlab>(std::move(Builder).build());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<
|
2019-12-05 10:43:29 +01:00
|
|
|
std::pair</*FilePath*/ std::string, /*CodeAfterRename*/ std::string>>
|
2019-10-23 14:40:20 +02:00
|
|
|
applyEdits(FileEdits FE) {
|
|
|
|
std::vector<std::pair<std::string, std::string>> Results;
|
|
|
|
for (auto &It : FE)
|
|
|
|
Results.emplace_back(
|
|
|
|
It.first().str(),
|
|
|
|
llvm::cantFail(tooling::applyAllReplacements(
|
|
|
|
It.getValue().InitialCode, It.getValue().Replacements)));
|
|
|
|
return Results;
|
2019-06-25 08:43:17 +00:00
|
|
|
}
|
|
|
|
|
2019-11-06 15:08:59 +01:00
|
|
|
// Generates an expected rename result by replacing all ranges in the given
|
|
|
|
// annotation with the NewName.
|
|
|
|
std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
|
|
|
|
std::string Result;
|
|
|
|
unsigned NextChar = 0;
|
|
|
|
llvm::StringRef Code = Test.code();
|
|
|
|
for (const auto &R : Test.llvm::Annotations::ranges()) {
|
|
|
|
assert(R.Begin <= R.End && NextChar <= R.Begin);
|
|
|
|
Result += Code.substr(NextChar, R.Begin - NextChar);
|
|
|
|
Result += NewName;
|
|
|
|
NextChar = R.End;
|
|
|
|
}
|
|
|
|
Result += Code.substr(NextChar);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
TEST(RenameTest, WithinFileRename) {
|
2020-11-10 10:08:42 +01:00
|
|
|
// For each "^" this test moves cursor to its location and applies renaming
|
|
|
|
// while checking that all identifiers in [[]] ranges are also renamed.
|
2019-11-06 15:08:59 +01:00
|
|
|
llvm::StringRef Tests[] = {
|
2019-11-19 10:10:43 +01:00
|
|
|
// Function.
|
2019-11-06 15:08:59 +01:00
|
|
|
R"cpp(
|
2019-11-19 10:10:43 +01:00
|
|
|
void [[foo^]]() {
|
2019-11-06 15:08:59 +01:00
|
|
|
[[fo^o]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Type.
|
2019-11-06 15:08:59 +01:00
|
|
|
R"cpp(
|
2019-11-19 10:10:43 +01:00
|
|
|
struct [[foo^]] {};
|
2019-11-06 15:08:59 +01:00
|
|
|
[[foo]] test() {
|
|
|
|
[[f^oo]] x;
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Local variable.
|
2019-11-06 15:08:59 +01:00
|
|
|
R"cpp(
|
|
|
|
void bar() {
|
|
|
|
if (auto [[^foo]] = 5) {
|
|
|
|
[[foo]] = 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-11-19 10:10:43 +01:00
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
// Class, its constructor and destructor.
|
2019-11-19 10:10:43 +01:00
|
|
|
R"cpp(
|
|
|
|
class [[F^oo]] {
|
|
|
|
[[F^oo]]();
|
2020-11-10 10:08:42 +01:00
|
|
|
~[[F^oo]]();
|
|
|
|
[[F^oo]] *foo(int x);
|
|
|
|
|
|
|
|
[[F^oo]] *Ptr;
|
2019-11-19 10:10:43 +01:00
|
|
|
};
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]]::[[Fo^o]]() {}
|
|
|
|
[[F^oo]]::~[[Fo^o]]() {}
|
|
|
|
[[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
|
2019-11-19 10:10:43 +01:00
|
|
|
)cpp",
|
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
// Template class, its constructor and destructor.
|
2020-01-21 11:50:57 +01:00
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class [[F^oo]] {
|
|
|
|
[[F^oo]]();
|
|
|
|
~[[F^oo]]();
|
2020-11-10 10:08:42 +01:00
|
|
|
void f([[F^oo]] x);
|
2020-01-21 11:50:57 +01:00
|
|
|
};
|
2020-11-13 12:27:36 +01:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
[[F^oo]]<T>::[[Fo^o]]() {}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
[[F^oo]]<T>::~[[Fo^o]]() {}
|
2020-01-21 11:50:57 +01:00
|
|
|
)cpp",
|
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
// Template class constructor.
|
2020-02-07 14:27:54 +01:00
|
|
|
R"cpp(
|
|
|
|
class [[F^oo]] {
|
|
|
|
template<typename T>
|
|
|
|
[[Fo^o]]();
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
[[F^oo]](T t);
|
|
|
|
};
|
2020-11-13 12:27:36 +01:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
[[F^oo]]::[[Fo^o]]() {}
|
2020-02-07 14:27:54 +01:00
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Class in template argument.
|
|
|
|
R"cpp(
|
|
|
|
class [[F^oo]] {};
|
|
|
|
template <typename T> void func();
|
|
|
|
template <typename T> class Baz {};
|
|
|
|
int main() {
|
|
|
|
func<[[F^oo]]>();
|
|
|
|
Baz<[[F^oo]]> obj;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Forward class declaration without definition.
|
|
|
|
R"cpp(
|
|
|
|
class [[F^oo]];
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]] *f();
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Member function.
|
|
|
|
R"cpp(
|
|
|
|
struct X {
|
|
|
|
void [[F^oo]]() {}
|
|
|
|
void Baz() { [[F^oo]](); }
|
|
|
|
};
|
2019-11-19 10:10:43 +01:00
|
|
|
)cpp",
|
|
|
|
|
2020-11-13 12:27:36 +01:00
|
|
|
// Templated method instantiation.
|
|
|
|
R"cpp(
|
|
|
|
template<typename T>
|
|
|
|
class Foo {
|
|
|
|
public:
|
|
|
|
static T [[f^oo]]() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
void bar() {
|
|
|
|
Foo<int>::[[f^oo]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T>
|
|
|
|
class Foo {
|
|
|
|
public:
|
|
|
|
T [[f^oo]]() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
void bar() {
|
|
|
|
Foo<int>().[[f^oo]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Template class (partial) specializations.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class [[F^oo]] {};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
class [[F^oo]]<bool> {};
|
|
|
|
template <typename T>
|
|
|
|
class [[F^oo]]<T*> {};
|
|
|
|
|
|
|
|
void test() {
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]]<int> x;
|
|
|
|
[[F^oo]]<bool> y;
|
|
|
|
[[F^oo]]<int*> z;
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
[clang-rename] Add the USR of incomplete decl to the USRSet.
Summary:
This fixes a clangd rename issue, which is missing the reference of
an incomplete specialization.
Unfortunately, I didn't reproduce this issue in clang-rename, I guess
the input `FoundDecl` of AdditionalUSRFinder is different in clangd vs
clang-rename, clang-rename uses the underlying CXXRecordDecl of the
ClassTemplateDecl, which is fixed in https://github.com/llvm/llvm-project/commit/5d862c042b52ae2aad37471d0b83b6c678a520e3;
while clangd-rename uses the ClassTemplateDecl.
Reviewers: kbobyrev
Reviewed By: kbobyrev
Subscribers: ilya-biryukov, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D74829
2020-02-25 16:32:22 +01:00
|
|
|
// Incomplete class specializations
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class [[Fo^o]] {};
|
2020-11-10 10:08:42 +01:00
|
|
|
void func([[F^oo]]<int>);
|
[clang-rename] Add the USR of incomplete decl to the USRSet.
Summary:
This fixes a clangd rename issue, which is missing the reference of
an incomplete specialization.
Unfortunately, I didn't reproduce this issue in clang-rename, I guess
the input `FoundDecl` of AdditionalUSRFinder is different in clangd vs
clang-rename, clang-rename uses the underlying CXXRecordDecl of the
ClassTemplateDecl, which is fixed in https://github.com/llvm/llvm-project/commit/5d862c042b52ae2aad37471d0b83b6c678a520e3;
while clangd-rename uses the ClassTemplateDecl.
Reviewers: kbobyrev
Reviewed By: kbobyrev
Subscribers: ilya-biryukov, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D74829
2020-02-25 16:32:22 +01:00
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Template class instantiations.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class [[F^oo]] {
|
|
|
|
public:
|
|
|
|
T foo(T arg, T& ref, T* ptr) {
|
|
|
|
T value;
|
|
|
|
int number = 42;
|
|
|
|
value = (T)number;
|
|
|
|
value = static_cast<T>(number);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
static void foo(T value) {}
|
|
|
|
T member;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void func() {
|
|
|
|
[[F^oo]]<T> obj;
|
|
|
|
obj.member = T();
|
|
|
|
[[Foo]]<T>::foo();
|
|
|
|
}
|
|
|
|
|
|
|
|
void test() {
|
|
|
|
[[F^oo]]<int> i;
|
|
|
|
i.member = 0;
|
|
|
|
[[F^oo]]<int>::foo(0);
|
|
|
|
|
|
|
|
[[F^oo]]<bool> b;
|
|
|
|
b.member = false;
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]]<bool>::foo(false);
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Template class methods.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class A {
|
|
|
|
public:
|
|
|
|
void [[f^oo]]() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
void func() {
|
|
|
|
A<int>().[[f^oo]]();
|
|
|
|
A<double>().[[f^oo]]();
|
|
|
|
A<float>().[[f^oo]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2020-11-13 12:27:36 +01:00
|
|
|
// Templated class specialization.
|
|
|
|
R"cpp(
|
|
|
|
template<typename T, typename U=bool>
|
|
|
|
class [[Foo^]];
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
class [[Foo^]] {};
|
|
|
|
|
|
|
|
template<typename T=int, typename U>
|
|
|
|
class [[Foo^]];
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T=float, typename U=int>
|
|
|
|
class [[Foo^]];
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
class [[Foo^]] {};
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Function template specialization.
|
|
|
|
R"cpp(
|
|
|
|
template<typename T=int, typename U=bool>
|
|
|
|
U [[foo^]]();
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
U [[foo^]]() {};
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T, typename U>
|
|
|
|
U [[foo^]]() {};
|
|
|
|
|
|
|
|
template<typename T=int, typename U=bool>
|
|
|
|
U [[foo^]]();
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T=int, typename U=bool>
|
|
|
|
U [[foo^]]();
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
U [[foo^]]();
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
void [[f^oo]](T t);
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void [[f^oo]](int a);
|
|
|
|
|
|
|
|
void test() {
|
|
|
|
[[f^oo]]<double>(1);
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Variable template.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T, int U>
|
|
|
|
bool [[F^oo]] = true;
|
|
|
|
|
|
|
|
// Explicit template specialization
|
|
|
|
template <>
|
|
|
|
bool [[F^oo]]<int, 0> = false;
|
|
|
|
|
|
|
|
// Partial template specialization
|
|
|
|
template <typename T>
|
|
|
|
bool [[F^oo]]<T, 1> = false;
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
// Ref to the explicit template specialization
|
|
|
|
[[F^oo]]<int, 0>;
|
|
|
|
// Ref to the primary template.
|
|
|
|
[[F^oo]]<double, 2>;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Complicated class type.
|
|
|
|
R"cpp(
|
|
|
|
// Forward declaration.
|
|
|
|
class [[Fo^o]];
|
|
|
|
class Baz {
|
|
|
|
virtual int getValue() const = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
class [[F^oo]] : public Baz {
|
|
|
|
public:
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]](int value = 0) : x(value) {}
|
2019-11-19 10:10:43 +01:00
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]] &operator++(int);
|
2019-11-19 10:10:43 +01:00
|
|
|
|
|
|
|
bool operator<([[Foo]] const &rhs);
|
|
|
|
int getValue() const;
|
|
|
|
private:
|
|
|
|
int x;
|
|
|
|
};
|
|
|
|
|
|
|
|
void func() {
|
2020-11-10 10:08:42 +01:00
|
|
|
[[F^oo]] *Pointer = 0;
|
|
|
|
[[F^oo]] Variable = [[Foo]](10);
|
|
|
|
for ([[F^oo]] it; it < Variable; it++);
|
|
|
|
const [[F^oo]] *C = new [[Foo]]();
|
|
|
|
const_cast<[[F^oo]] *>(C)->getValue();
|
|
|
|
[[F^oo]] foo;
|
2019-11-19 10:10:43 +01:00
|
|
|
const Baz &BazReference = foo;
|
|
|
|
const Baz *BazPointer = &foo;
|
|
|
|
reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
|
|
|
|
static_cast<const [[^Foo]] &>(BazReference).getValue();
|
|
|
|
static_cast<const [[^Foo]] *>(BazPointer)->getValue();
|
|
|
|
}
|
2020-01-21 05:33:39 +01:00
|
|
|
)cpp",
|
|
|
|
|
2020-11-13 12:27:36 +01:00
|
|
|
// Static class member.
|
|
|
|
R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
static Foo *[[Static^Member]];
|
|
|
|
};
|
|
|
|
|
|
|
|
Foo* Foo::[[Static^Member]] = nullptr;
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
Foo* Pointer = Foo::[[Static^Member]];
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
// Reference in lambda parameters.
|
|
|
|
R"cpp(
|
|
|
|
template <class T>
|
|
|
|
class function;
|
|
|
|
template <class R, class... ArgTypes>
|
|
|
|
class function<R(ArgTypes...)> {
|
|
|
|
public:
|
|
|
|
template <typename Functor>
|
|
|
|
function(Functor f) {}
|
|
|
|
|
|
|
|
function() {}
|
|
|
|
|
|
|
|
R operator()(ArgTypes...) const {}
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace ns {
|
|
|
|
class [[Old]] {};
|
|
|
|
void f() {
|
|
|
|
function<void([[Old]])> func;
|
|
|
|
}
|
|
|
|
} // namespace ns
|
|
|
|
)cpp",
|
|
|
|
|
2020-01-21 05:33:39 +01:00
|
|
|
// Destructor explicit call.
|
|
|
|
R"cpp(
|
|
|
|
class [[F^oo]] {
|
|
|
|
public:
|
|
|
|
~[[^Foo]]();
|
|
|
|
};
|
|
|
|
|
|
|
|
[[Foo^]]::~[[^Foo]]() {}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
[[Fo^o]] f;
|
|
|
|
f.~/*something*/[[^Foo]]();
|
|
|
|
f.~[[^Foo]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Derived destructor explicit call.
|
|
|
|
R"cpp(
|
|
|
|
class [[Bas^e]] {};
|
[clangd] Errors in TestTU cause test failures unless suppressed with error-ok.
Summary:
The historic behavior of TestTU is to gather diagnostics and otherwise ignore
them. So if a test has a syntax error, and doesn't assert diagnostics, it
silently misbehaves.
This can be annoying when developing tests, as evidenced by various tests
gaining "assert no diagnostics" where that's not really the point of the test.
This patch aims to make that default behavior. For the first error
(not warning), TestTU will call ADD_FAILURE().
This can be suppressed with a comment containing "error-ok". For now that will
suppress any errors in the TU. We can make this stricter later -verify style.
(-verify itself is hard to reuse because of DiagnosticConsumer interfaces...)
A magic-comment was chosen over a TestTU option because of table-driven tests.
In addition to the behavior change, this patch:
- adds //error-ok where we're knowingly testing invalid code
(e.g. for diagnostics, crash-resilience, or token-level tests)
- fixes a bunch of errors in the checked-in tests, mostly trivial (missing ;)
- removes a bunch of now-redundant instances of "assert no diagnostics"
Reviewers: kadircet
Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D73199
2020-01-22 16:38:41 +01:00
|
|
|
class Derived : public [[Bas^e]] {};
|
2020-01-21 05:33:39 +01:00
|
|
|
|
|
|
|
int main() {
|
|
|
|
[[Bas^e]] *foo = new Derived();
|
|
|
|
foo->[[^Base]]::~[[^Base]]();
|
|
|
|
}
|
2019-11-19 10:10:43 +01:00
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// CXXConstructor initializer list.
|
|
|
|
R"cpp(
|
|
|
|
class Baz {};
|
|
|
|
class Qux {
|
|
|
|
Baz [[F^oo]];
|
|
|
|
public:
|
|
|
|
Qux();
|
|
|
|
};
|
|
|
|
Qux::Qux() : [[F^oo]]() {}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// DeclRefExpr.
|
|
|
|
R"cpp(
|
|
|
|
class C {
|
|
|
|
public:
|
|
|
|
static int [[F^oo]];
|
|
|
|
};
|
|
|
|
|
|
|
|
int foo(int x);
|
|
|
|
#define MACRO(a) foo(a)
|
|
|
|
|
|
|
|
void func() {
|
|
|
|
C::[[F^oo]] = 1;
|
|
|
|
MACRO(C::[[Foo]]);
|
|
|
|
int y = C::[[F^oo]];
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Macros.
|
|
|
|
R"cpp(
|
|
|
|
// no rename inside macro body.
|
|
|
|
#define M1 foo
|
|
|
|
#define M2(x) x
|
|
|
|
int [[fo^o]]();
|
|
|
|
void boo(int);
|
|
|
|
|
|
|
|
void qoo() {
|
2020-11-10 10:08:42 +01:00
|
|
|
[[f^oo]]();
|
|
|
|
boo([[f^oo]]());
|
2019-11-19 10:10:43 +01:00
|
|
|
M1();
|
|
|
|
boo(M1());
|
2020-11-10 10:08:42 +01:00
|
|
|
M2([[f^oo]]());
|
2019-11-19 10:10:43 +01:00
|
|
|
M2(M1()); // foo is inside the nested macro body.
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// MemberExpr in macros
|
|
|
|
R"cpp(
|
|
|
|
class Baz {
|
|
|
|
public:
|
|
|
|
int [[F^oo]];
|
|
|
|
};
|
|
|
|
int qux(int x);
|
|
|
|
#define MACRO(a) qux(a)
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
Baz baz;
|
2020-11-10 10:08:42 +01:00
|
|
|
baz.[[F^oo]] = 1;
|
|
|
|
MACRO(baz.[[F^oo]]);
|
|
|
|
int y = baz.[[F^oo]];
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2020-11-27 03:59:24 +01:00
|
|
|
// Fields in classes & partial and full specialiations.
|
|
|
|
R"cpp(
|
|
|
|
template<typename T>
|
|
|
|
struct Foo {
|
|
|
|
T [[Vari^able]] = 42;
|
|
|
|
};
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
Foo<int> f;
|
|
|
|
f.[[Varia^ble]] = 9000;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T, typename U>
|
|
|
|
struct Foo {
|
|
|
|
T Variable[42];
|
|
|
|
U Another;
|
|
|
|
|
|
|
|
void bar() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct Foo<T, bool> {
|
|
|
|
T [[Var^iable]];
|
|
|
|
void bar() { ++[[Var^iable]]; }
|
|
|
|
};
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
Foo<unsigned, bool> f;
|
|
|
|
f.[[Var^iable]] = 9000;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T, typename U>
|
|
|
|
struct Foo {
|
|
|
|
T Variable[42];
|
|
|
|
U Another;
|
|
|
|
|
|
|
|
void bar() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct Foo<T, bool> {
|
|
|
|
T Variable;
|
|
|
|
void bar() { ++Variable; }
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct Foo<unsigned, bool> {
|
|
|
|
unsigned [[Var^iable]];
|
|
|
|
void bar() { ++[[Var^iable]]; }
|
|
|
|
};
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
Foo<unsigned, bool> f;
|
|
|
|
f.[[Var^iable]] = 9000;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
// Static fields.
|
|
|
|
R"cpp(
|
|
|
|
struct Foo {
|
|
|
|
static int [[Var^iable]];
|
|
|
|
};
|
|
|
|
|
|
|
|
int Foo::[[Var^iable]] = 42;
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
int LocalInt = Foo::[[Var^iable]];
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
template<typename T>
|
|
|
|
struct Foo {
|
|
|
|
static T [[Var^iable]];
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
|
|
|
int Foo<int>::[[Var^iable]] = 42;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
bool Foo<bool>::[[Var^iable]] = true;
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
int LocalInt = Foo<int>::[[Var^iable]];
|
|
|
|
bool LocalBool = Foo<bool>::[[Var^iable]];
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
// Template parameters.
|
|
|
|
R"cpp(
|
|
|
|
template <typename [[^T]]>
|
|
|
|
class Foo {
|
2020-11-10 10:08:42 +01:00
|
|
|
[[T^]] foo([[T^]] arg, [[T^]]& ref, [[^T]]* ptr) {
|
2019-11-19 10:10:43 +01:00
|
|
|
[[T]] value;
|
|
|
|
int number = 42;
|
2020-11-10 10:08:42 +01:00
|
|
|
value = ([[T^]])number;
|
2019-11-19 10:10:43 +01:00
|
|
|
value = static_cast<[[^T]]>(number);
|
|
|
|
return value;
|
|
|
|
}
|
2020-11-10 10:08:42 +01:00
|
|
|
static void foo([[T^]] value) {}
|
|
|
|
[[T^]] member;
|
2019-11-19 10:10:43 +01:00
|
|
|
};
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Typedef.
|
|
|
|
R"cpp(
|
2020-01-27 10:39:55 +01:00
|
|
|
namespace ns {
|
2019-11-19 10:10:43 +01:00
|
|
|
class basic_string {};
|
|
|
|
typedef basic_string [[s^tring]];
|
2020-01-27 10:39:55 +01:00
|
|
|
} // namespace ns
|
2019-11-19 10:10:43 +01:00
|
|
|
|
2020-01-27 10:39:55 +01:00
|
|
|
ns::[[s^tring]] foo();
|
2019-11-19 10:10:43 +01:00
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Variable.
|
|
|
|
R"cpp(
|
|
|
|
namespace A {
|
|
|
|
int [[F^oo]];
|
|
|
|
}
|
|
|
|
int Foo;
|
|
|
|
int Qux = Foo;
|
|
|
|
int Baz = A::[[^Foo]];
|
|
|
|
void fun() {
|
|
|
|
struct {
|
|
|
|
int Foo;
|
|
|
|
} b = {100};
|
|
|
|
int Foo = 100;
|
|
|
|
Baz = Foo;
|
|
|
|
{
|
|
|
|
extern int Foo;
|
|
|
|
Baz = Foo;
|
|
|
|
Foo = A::[[F^oo]] + Baz;
|
|
|
|
A::[[Fo^o]] = b.Foo;
|
|
|
|
}
|
|
|
|
Foo = b.Foo;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Namespace alias.
|
|
|
|
R"cpp(
|
|
|
|
namespace a { namespace b { void foo(); } }
|
|
|
|
namespace [[^x]] = a::b;
|
|
|
|
void bar() {
|
2020-11-10 10:08:42 +01:00
|
|
|
[[x^]]::foo();
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
// Enum.
|
|
|
|
R"cpp(
|
|
|
|
enum [[C^olor]] { Red, Green, Blue };
|
|
|
|
void foo() {
|
|
|
|
[[C^olor]] c;
|
|
|
|
c = [[C^olor]]::Blue;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Scoped enum.
|
2019-11-19 10:10:43 +01:00
|
|
|
R"cpp(
|
|
|
|
enum class [[K^ind]] { ABC };
|
|
|
|
void ff() {
|
|
|
|
[[K^ind]] s;
|
2020-11-10 10:08:42 +01:00
|
|
|
s = [[K^ind]]::ABC;
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
2020-11-10 10:08:42 +01:00
|
|
|
// Template class in template argument list.
|
2019-11-19 10:10:43 +01:00
|
|
|
R"cpp(
|
|
|
|
template<typename T>
|
|
|
|
class [[Fo^o]] {};
|
|
|
|
template <template<typename> class Z> struct Bar { };
|
2020-11-10 10:08:42 +01:00
|
|
|
template <> struct Bar<[[F^oo]]> {};
|
2019-11-19 10:10:43 +01:00
|
|
|
)cpp",
|
2020-02-10 11:53:17 +01:00
|
|
|
|
|
|
|
// Designated initializer.
|
|
|
|
R"cpp(
|
|
|
|
struct Bar {
|
|
|
|
int [[Fo^o]];
|
|
|
|
};
|
|
|
|
Bar bar { .[[^Foo]] = 42 };
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Nested designated initializer.
|
|
|
|
R"cpp(
|
|
|
|
struct Baz {
|
|
|
|
int Field;
|
|
|
|
};
|
|
|
|
struct Bar {
|
|
|
|
Baz [[Fo^o]];
|
|
|
|
};
|
|
|
|
// FIXME: v selecting here results in renaming Field.
|
|
|
|
Bar bar { .[[Foo]].Field = 42 };
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
struct Baz {
|
|
|
|
int [[Fiel^d]];
|
|
|
|
};
|
|
|
|
struct Bar {
|
|
|
|
Baz Foo;
|
|
|
|
};
|
|
|
|
Bar bar { .Foo.[[^Field]] = 42 };
|
|
|
|
)cpp",
|
2020-11-10 10:08:42 +01:00
|
|
|
|
|
|
|
// Templated alias.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class X { T t; };
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
using [[Fo^o]] = X<T>;
|
|
|
|
|
|
|
|
void bar() {
|
|
|
|
[[Fo^o]]<int> Bar;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Alias.
|
|
|
|
R"cpp(
|
|
|
|
class X {};
|
|
|
|
using [[F^oo]] = X;
|
|
|
|
|
|
|
|
void bar() {
|
|
|
|
[[Fo^o]] Bar;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Alias within a namespace.
|
|
|
|
R"cpp(
|
|
|
|
namespace x { class X {}; }
|
|
|
|
namespace ns {
|
|
|
|
using [[Fo^o]] = x::X;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bar() {
|
|
|
|
ns::[[Fo^o]] Bar;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Alias within macros.
|
|
|
|
R"cpp(
|
|
|
|
namespace x { class Old {}; }
|
|
|
|
namespace ns {
|
|
|
|
#define REF(alias) alias alias_var;
|
|
|
|
|
|
|
|
#define ALIAS(old) \
|
|
|
|
using old##Alias = x::old; \
|
|
|
|
REF(old##Alias);
|
|
|
|
|
|
|
|
ALIAS(Old);
|
|
|
|
|
|
|
|
[[Old^Alias]] old_alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bar() {
|
|
|
|
ns::[[Old^Alias]] Bar;
|
|
|
|
}
|
|
|
|
)cpp",
|
2020-11-13 12:27:36 +01:00
|
|
|
|
|
|
|
// User defined conversion.
|
|
|
|
R"cpp(
|
|
|
|
class [[F^oo]] {
|
|
|
|
public:
|
|
|
|
[[F^oo]]() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Baz {
|
|
|
|
public:
|
|
|
|
operator [[F^oo]]() {
|
|
|
|
return [[F^oo]]();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
Baz boo;
|
|
|
|
[[F^oo]] foo = static_cast<[[F^oo]]>(boo);
|
|
|
|
}
|
|
|
|
)cpp",
|
2020-12-03 12:57:41 +01:00
|
|
|
|
|
|
|
// ObjC, should not crash.
|
|
|
|
R"cpp(
|
|
|
|
@interface ObjC {
|
|
|
|
char [[da^ta]];
|
|
|
|
} @end
|
|
|
|
)cpp",
|
2022-10-08 01:24:13 +02:00
|
|
|
|
|
|
|
// Issue 170: Rename symbol introduced by UsingDecl
|
|
|
|
R"cpp(
|
|
|
|
namespace ns { void [[f^oo]](); }
|
|
|
|
|
|
|
|
using ns::[[f^oo]];
|
|
|
|
|
|
|
|
void f() {
|
|
|
|
[[f^oo]]();
|
|
|
|
auto p = &[[f^oo]];
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
|
|
|
|
// Issue 170: using decl that imports multiple overloads
|
|
|
|
// -> Only the overload under the cursor is renamed
|
|
|
|
R"cpp(
|
|
|
|
namespace ns { int [[^foo]](int); char foo(char); }
|
|
|
|
using ns::[[foo]];
|
|
|
|
void f() {
|
|
|
|
[[^foo]](42);
|
|
|
|
foo('x');
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-05-07 07:45:41 +00:00
|
|
|
};
|
2020-11-10 10:08:42 +01:00
|
|
|
llvm::StringRef NewName = "NewName";
|
2020-01-07 13:58:17 -05:00
|
|
|
for (llvm::StringRef T : Tests) {
|
2020-01-27 14:09:27 +01:00
|
|
|
SCOPED_TRACE(T);
|
2019-11-06 15:08:59 +01:00
|
|
|
Annotations Code(T);
|
2019-05-07 07:45:41 +00:00
|
|
|
auto TU = TestTU::withCode(Code.code());
|
2020-12-03 12:57:41 +01:00
|
|
|
TU.ExtraArgs.push_back("-xobjective-c++");
|
2019-05-07 07:45:41 +00:00
|
|
|
auto AST = TU.build();
|
2021-02-11 15:57:54 +01:00
|
|
|
auto Index = TU.index();
|
2019-11-19 10:10:43 +01:00
|
|
|
for (const auto &RenamePos : Code.points()) {
|
2021-02-11 15:57:54 +01:00
|
|
|
auto RenameResult =
|
2021-03-10 13:41:27 +00:00
|
|
|
rename({RenamePos, NewName, AST, testPath(TU.Filename),
|
|
|
|
getVFSFromAST(AST), Index.get()});
|
2019-10-23 14:40:20 +02:00
|
|
|
ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
|
2020-10-02 16:01:25 +02:00
|
|
|
ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
|
|
|
|
EXPECT_EQ(
|
|
|
|
applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
|
|
|
|
expectedResult(Code, NewName));
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
2019-05-07 07:45:41 +00:00
|
|
|
}
|
2019-05-07 07:11:56 +00:00
|
|
|
}
|
|
|
|
|
2019-06-25 08:43:17 +00:00
|
|
|
TEST(RenameTest, Renameable) {
|
2019-06-26 08:10:26 +00:00
|
|
|
struct Case {
|
2019-06-27 13:24:10 +00:00
|
|
|
const char *Code;
|
2019-06-26 08:10:26 +00:00
|
|
|
const char* ErrorMessage; // null if no error
|
2019-06-27 13:24:10 +00:00
|
|
|
bool IsHeaderFile;
|
2021-03-22 11:18:18 +01:00
|
|
|
llvm::StringRef NewName = "MockName";
|
2019-06-26 08:10:26 +00:00
|
|
|
};
|
2019-06-27 13:24:10 +00:00
|
|
|
const bool HeaderFile = true;
|
2019-06-26 08:10:26 +00:00
|
|
|
Case Cases[] = {
|
|
|
|
{R"cpp(// allow -- function-local
|
2019-06-25 08:43:17 +00:00
|
|
|
void f(int [[Lo^cal]]) {
|
|
|
|
[[Local]] = 2;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
nullptr, HeaderFile},
|
2019-06-25 08:43:17 +00:00
|
|
|
|
2020-01-04 10:28:41 -05:00
|
|
|
{R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
|
2019-06-25 08:43:17 +00:00
|
|
|
namespace {
|
|
|
|
class Unin^dexable {};
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"not eligible for indexing", HeaderFile},
|
2019-11-29 14:58:44 +01:00
|
|
|
|
2019-06-26 08:10:26 +00:00
|
|
|
{R"cpp(// disallow -- namespace symbol isn't supported
|
2019-11-18 14:56:59 +01:00
|
|
|
namespace n^s {}
|
2019-06-26 08:10:26 +00:00
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"not a supported kind", HeaderFile},
|
2019-06-27 13:24:10 +00:00
|
|
|
|
2019-07-01 09:26:48 +00:00
|
|
|
{
|
|
|
|
R"cpp(
|
|
|
|
#define MACRO 1
|
|
|
|
int s = MAC^RO;
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"not a supported kind", HeaderFile},
|
2019-07-01 09:26:48 +00:00
|
|
|
|
2019-09-16 10:16:56 +00:00
|
|
|
{
|
|
|
|
R"cpp(
|
2019-11-18 14:56:59 +01:00
|
|
|
struct X { X operator++(int); };
|
2019-09-16 10:16:56 +00:00
|
|
|
void f(X x) {x+^+;})cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"no symbol", HeaderFile},
|
2019-11-19 10:10:43 +01:00
|
|
|
|
2020-12-04 12:23:26 +01:00
|
|
|
{R"cpp(// disallow rename on non-normal identifiers.
|
|
|
|
@interface Foo {}
|
|
|
|
-(int) fo^o:(int)x; // Token is an identifier, but declaration name isn't a simple identifier.
|
|
|
|
@end
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"not a supported kind", HeaderFile},
|
2019-11-19 10:10:43 +01:00
|
|
|
{R"cpp(
|
|
|
|
void foo(int);
|
|
|
|
void foo(char);
|
|
|
|
template <typename T> void f(T t) {
|
|
|
|
fo^o(t);
|
|
|
|
})cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"multiple symbols", !HeaderFile},
|
2019-11-19 10:10:43 +01:00
|
|
|
|
|
|
|
{R"cpp(// disallow rename on unrelated token.
|
|
|
|
cl^ass Foo {};
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"no symbol", !HeaderFile},
|
2019-11-19 10:10:43 +01:00
|
|
|
|
|
|
|
{R"cpp(// disallow rename on unrelated token.
|
|
|
|
temp^late<typename T>
|
|
|
|
class Foo {};
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"no symbol", !HeaderFile},
|
2020-11-10 08:49:57 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
namespace {
|
|
|
|
int Conflict;
|
|
|
|
int Va^r;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2020-11-10 08:49:57 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
int Conflict;
|
|
|
|
int Va^r;
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2020-11-10 08:49:57 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
class Foo {
|
|
|
|
int Conflict;
|
|
|
|
int Va^r;
|
|
|
|
};
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2020-11-10 08:49:57 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
enum E {
|
|
|
|
Conflict,
|
|
|
|
Fo^o,
|
|
|
|
};
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2020-11-10 08:49:57 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
int Conflict;
|
|
|
|
enum E { // transparent context.
|
|
|
|
F^oo,
|
|
|
|
};
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2020-11-10 08:49:57 +01:00
|
|
|
|
2021-02-04 09:45:36 +01:00
|
|
|
{R"cpp(
|
2020-11-10 08:49:57 +01:00
|
|
|
void func() {
|
2021-02-04 09:45:36 +01:00
|
|
|
bool Whatever;
|
|
|
|
int V^ar;
|
|
|
|
char Conflict;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 09:45:36 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
void func() {
|
|
|
|
if (int Conflict = 42) {
|
|
|
|
int V^ar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 09:45:36 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
void func() {
|
|
|
|
if (int Conflict = 42) {
|
|
|
|
} else {
|
|
|
|
bool V^ar;
|
|
|
|
}
|
2020-11-10 08:49:57 +01:00
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 09:45:36 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
void func() {
|
|
|
|
if (int V^ar = 42) {
|
|
|
|
} else {
|
|
|
|
bool Conflict;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 09:45:36 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
void func() {
|
|
|
|
while (int V^ar = 10) {
|
|
|
|
bool Conflict = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 09:45:36 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
|
|
|
void func() {
|
|
|
|
for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
|
|
|
|
++Something) {
|
|
|
|
int V^ar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 09:45:36 +01:00
|
|
|
|
2021-02-04 21:36:22 +01:00
|
|
|
{R"cpp(
|
|
|
|
void func() {
|
|
|
|
for (int V^ar = 14, Conflict = 42;;) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 21:36:22 +01:00
|
|
|
|
2021-02-04 09:45:36 +01:00
|
|
|
{R"cpp(
|
|
|
|
void func(int Conflict) {
|
|
|
|
bool V^ar;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 21:36:22 +01:00
|
|
|
|
|
|
|
{R"cpp(
|
2021-02-09 11:46:22 +01:00
|
|
|
void func(int Var);
|
|
|
|
|
2021-02-04 21:36:22 +01:00
|
|
|
void func(int V^ar) {
|
|
|
|
bool Conflict;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2021-02-04 21:36:22 +01:00
|
|
|
|
2021-02-09 11:46:22 +01:00
|
|
|
{R"cpp(// No conflict: only forward declaration's argument is renamed.
|
|
|
|
void func(int [[V^ar]]);
|
|
|
|
|
|
|
|
void func(int Var) {
|
|
|
|
bool Conflict;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
nullptr, !HeaderFile, "Conflict"},
|
2021-02-09 11:46:22 +01:00
|
|
|
|
2021-02-04 21:36:22 +01:00
|
|
|
{R"cpp(
|
|
|
|
void func(int V^ar, int Conflict) {
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"conflict", !HeaderFile, "Conflict"},
|
2020-11-11 11:13:43 +01:00
|
|
|
|
2023-05-16 17:41:30 +02:00
|
|
|
{R"cpp(
|
|
|
|
void func(int);
|
|
|
|
void [[o^therFunc]](double);
|
|
|
|
)cpp",
|
|
|
|
nullptr, !HeaderFile, "func"},
|
|
|
|
{R"cpp(
|
|
|
|
struct S {
|
|
|
|
void func(int);
|
|
|
|
void [[o^therFunc]](double);
|
|
|
|
};
|
|
|
|
)cpp",
|
|
|
|
nullptr, !HeaderFile, "func"},
|
|
|
|
|
2021-12-19 22:23:45 +03:00
|
|
|
{R"cpp(
|
|
|
|
int V^ar;
|
|
|
|
)cpp",
|
|
|
|
"\"const\" is a keyword", !HeaderFile, "const"},
|
|
|
|
|
2020-11-11 11:13:43 +01:00
|
|
|
{R"cpp(// Trying to rename into the same name, SameName == SameName.
|
|
|
|
void func() {
|
|
|
|
int S^ameName;
|
|
|
|
}
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"new name is the same", !HeaderFile, "SameName"},
|
2021-01-26 18:58:53 +00:00
|
|
|
{R"cpp(// Ensure it doesn't associate base specifier with base name.
|
|
|
|
struct A {};
|
|
|
|
struct B : priv^ate A {};
|
|
|
|
)cpp",
|
2021-02-11 15:57:54 +01:00
|
|
|
"Cannot rename symbol: there is no symbol at the given location", false},
|
2021-06-11 13:07:14 +02:00
|
|
|
{R"cpp(// Ensure it doesn't associate base specifier with base name.
|
|
|
|
/*error-ok*/
|
|
|
|
struct A {
|
|
|
|
A() : inva^lid(0) {}
|
|
|
|
};
|
|
|
|
)cpp",
|
|
|
|
"no symbol", false},
|
2022-10-08 01:24:13 +02:00
|
|
|
|
|
|
|
{R"cpp(// FIXME we probably want to rename both overloads here,
|
|
|
|
// but renaming currently assumes there's only a
|
|
|
|
// single canonical declaration.
|
|
|
|
namespace ns { int foo(int); char foo(char); }
|
|
|
|
using ns::^foo;
|
|
|
|
)cpp",
|
|
|
|
"there are multiple symbols at the given location", !HeaderFile},
|
2019-06-25 08:43:17 +00:00
|
|
|
};
|
|
|
|
|
2019-06-26 08:10:26 +00:00
|
|
|
for (const auto& Case : Cases) {
|
2020-01-27 14:09:27 +01:00
|
|
|
SCOPED_TRACE(Case.Code);
|
2019-06-27 13:24:10 +00:00
|
|
|
Annotations T(Case.Code);
|
2019-06-25 08:43:17 +00:00
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
2019-11-19 10:10:43 +01:00
|
|
|
TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
|
2019-06-27 13:24:10 +00:00
|
|
|
if (Case.IsHeaderFile) {
|
|
|
|
// We open the .h file as the main file.
|
|
|
|
TU.Filename = "test.h";
|
|
|
|
// Parsing the .h file as C++ include.
|
|
|
|
TU.ExtraArgs.push_back("-xobjective-c++-header");
|
|
|
|
}
|
2019-06-25 08:43:17 +00:00
|
|
|
auto AST = TU.build();
|
2020-10-06 15:46:40 +02:00
|
|
|
llvm::StringRef NewName = Case.NewName;
|
2021-02-11 15:57:54 +01:00
|
|
|
auto Results = rename({T.point(), NewName, AST, testPath(TU.Filename)});
|
2019-06-25 08:43:17 +00:00
|
|
|
bool WantRename = true;
|
|
|
|
if (T.ranges().empty())
|
|
|
|
WantRename = false;
|
|
|
|
if (!WantRename) {
|
2019-06-26 08:10:26 +00:00
|
|
|
assert(Case.ErrorMessage && "Error message must be set!");
|
2019-11-06 15:08:59 +01:00
|
|
|
EXPECT_FALSE(Results)
|
2019-10-23 14:40:20 +02:00
|
|
|
<< "expected rename returned an error: " << T.code();
|
2019-06-26 08:10:26 +00:00
|
|
|
auto ActualMessage = llvm::toString(Results.takeError());
|
|
|
|
EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
|
2019-06-25 08:43:17 +00:00
|
|
|
} else {
|
2019-10-23 14:40:20 +02:00
|
|
|
EXPECT_TRUE(bool(Results)) << "rename returned an error: "
|
2019-06-25 08:43:17 +00:00
|
|
|
<< llvm::toString(Results.takeError());
|
2023-05-16 17:41:30 +02:00
|
|
|
EXPECT_EQ(Results->LocalChanges, T.ranges());
|
2019-06-25 08:43:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-31 13:53:22 +01:00
|
|
|
MATCHER_P(newText, T, "") { return arg.newText == T; }
|
|
|
|
|
|
|
|
TEST(RenameTest, IndexMergeMainFile) {
|
|
|
|
Annotations Code("int ^x();");
|
|
|
|
TestTU TU = TestTU::withCode(Code.code());
|
|
|
|
TU.Filename = "main.cc";
|
|
|
|
auto AST = TU.build();
|
|
|
|
|
|
|
|
auto Main = testPath("main.cc");
|
2021-03-10 13:41:27 +00:00
|
|
|
auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
|
|
|
|
InMemFS->addFile(testPath("main.cc"), 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(Code.code()));
|
|
|
|
InMemFS->addFile(testPath("other.cc"), 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(Code.code()));
|
2021-01-31 13:53:22 +01:00
|
|
|
|
|
|
|
auto Rename = [&](const SymbolIndex *Idx) {
|
2021-03-10 13:41:27 +00:00
|
|
|
RenameInputs Inputs{Code.point(),
|
|
|
|
"xPrime",
|
|
|
|
AST,
|
|
|
|
Main,
|
|
|
|
Idx ? createOverlay(getVFSFromAST(AST), InMemFS)
|
|
|
|
: nullptr,
|
|
|
|
Idx,
|
|
|
|
RenameOptions()};
|
2021-01-31 13:53:22 +01:00
|
|
|
auto Results = rename(Inputs);
|
|
|
|
EXPECT_TRUE(bool(Results)) << llvm::toString(Results.takeError());
|
|
|
|
return std::move(*Results);
|
|
|
|
};
|
|
|
|
|
|
|
|
// We do not expect to see duplicated edits from AST vs index.
|
|
|
|
auto Results = Rename(TU.index().get());
|
|
|
|
EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
|
|
|
|
EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
|
|
|
|
ElementsAre(newText("xPrime")));
|
|
|
|
|
|
|
|
// Sanity check: we do expect to see index results!
|
|
|
|
TU.Filename = "other.cc";
|
|
|
|
Results = Rename(TU.index().get());
|
|
|
|
EXPECT_THAT(Results.GlobalChanges.keys(),
|
|
|
|
UnorderedElementsAre(Main, testPath("other.cc")));
|
|
|
|
|
2021-02-15 09:00:49 +01:00
|
|
|
#ifdef CLANGD_PATH_CASE_INSENSITIVE
|
2021-01-31 13:53:22 +01:00
|
|
|
// On case-insensitive systems, no duplicates if AST vs index case differs.
|
|
|
|
// https://github.com/clangd/clangd/issues/665
|
|
|
|
TU.Filename = "MAIN.CC";
|
|
|
|
Results = Rename(TU.index().get());
|
|
|
|
EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
|
|
|
|
EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
|
|
|
|
ElementsAre(newText("xPrime")));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:10:43 +01:00
|
|
|
TEST(RenameTest, MainFileReferencesOnly) {
|
|
|
|
// filter out references not from main file.
|
|
|
|
llvm::StringRef Test =
|
|
|
|
R"cpp(
|
|
|
|
void test() {
|
|
|
|
int [[fo^o]] = 1;
|
|
|
|
// rename references not from main file are not included.
|
|
|
|
#include "foo.inc"
|
|
|
|
})cpp";
|
|
|
|
|
|
|
|
Annotations Code(Test);
|
|
|
|
auto TU = TestTU::withCode(Code.code());
|
|
|
|
TU.AdditionalFiles["foo.inc"] = R"cpp(
|
|
|
|
#define Macro(X) X
|
|
|
|
&Macro(foo);
|
|
|
|
&foo;
|
|
|
|
)cpp";
|
|
|
|
auto AST = TU.build();
|
|
|
|
llvm::StringRef NewName = "abcde";
|
|
|
|
|
|
|
|
auto RenameResult =
|
2019-10-23 14:40:20 +02:00
|
|
|
rename({Code.point(), NewName, AST, testPath(TU.Filename)});
|
2019-11-19 10:10:43 +01:00
|
|
|
ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << Code.point();
|
2020-10-02 16:01:25 +02:00
|
|
|
ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
|
|
|
|
EXPECT_EQ(applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
|
2019-10-23 14:40:20 +02:00
|
|
|
expectedResult(Code, NewName));
|
|
|
|
}
|
|
|
|
|
2022-01-05 10:42:45 +01:00
|
|
|
TEST(RenameTest, NoRenameOnSymbolsFromSystemHeaders) {
|
|
|
|
llvm::StringRef Test =
|
|
|
|
R"cpp(
|
2022-01-18 09:43:40 +01:00
|
|
|
#include <cstdlib>
|
2022-01-05 10:42:45 +01:00
|
|
|
#include <system>
|
2022-01-18 09:43:40 +01:00
|
|
|
|
2022-01-05 10:42:45 +01:00
|
|
|
SystemSym^bol abc;
|
2022-01-18 09:43:40 +01:00
|
|
|
|
|
|
|
void foo() { at^oi("9000"); }
|
2022-01-05 10:42:45 +01:00
|
|
|
)cpp";
|
|
|
|
|
|
|
|
Annotations Code(Test);
|
|
|
|
auto TU = TestTU::withCode(Code.code());
|
|
|
|
TU.AdditionalFiles["system"] = R"cpp(
|
|
|
|
class SystemSymbol {};
|
|
|
|
)cpp";
|
2022-01-18 09:43:40 +01:00
|
|
|
TU.AdditionalFiles["cstdlib"] = R"cpp(
|
|
|
|
int atoi(const char *str);
|
|
|
|
)cpp";
|
2022-01-05 10:42:45 +01:00
|
|
|
TU.ExtraArgs = {"-isystem", testRoot()};
|
|
|
|
auto AST = TU.build();
|
|
|
|
llvm::StringRef NewName = "abcde";
|
|
|
|
|
2022-01-18 09:43:40 +01:00
|
|
|
// Clangd will not allow renaming symbols from the system headers for
|
|
|
|
// correctness.
|
|
|
|
for (auto &Point : Code.points()) {
|
|
|
|
auto Results = rename({Point, NewName, AST, testPath(TU.Filename)});
|
|
|
|
EXPECT_FALSE(Results) << "expected rename returned an error: "
|
|
|
|
<< Code.code();
|
|
|
|
auto ActualMessage = llvm::toString(Results.takeError());
|
|
|
|
EXPECT_THAT(ActualMessage, testing::HasSubstr("not a supported kind"));
|
|
|
|
}
|
2022-01-05 10:42:45 +01:00
|
|
|
}
|
|
|
|
|
2020-06-19 15:05:26 -07:00
|
|
|
TEST(RenameTest, ProtobufSymbolIsExcluded) {
|
2020-02-05 12:31:11 +01:00
|
|
|
Annotations Code("Prot^obuf buf;");
|
|
|
|
auto TU = TestTU::withCode(Code.code());
|
|
|
|
TU.HeaderCode =
|
|
|
|
R"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
|
|
class Protobuf {};
|
|
|
|
)cpp";
|
|
|
|
TU.HeaderFilename = "protobuf.pb.h";
|
|
|
|
auto AST = TU.build();
|
|
|
|
auto Results = rename({Code.point(), "newName", AST, testPath(TU.Filename)});
|
|
|
|
EXPECT_FALSE(Results);
|
|
|
|
EXPECT_THAT(llvm::toString(Results.takeError()),
|
|
|
|
testing::HasSubstr("not a supported kind"));
|
|
|
|
}
|
|
|
|
|
2020-10-02 16:01:25 +02:00
|
|
|
TEST(RenameTest, PrepareRename) {
|
|
|
|
Annotations FooH("void func();");
|
|
|
|
Annotations FooCC(R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
void [[fu^nc]]() {}
|
|
|
|
)cpp");
|
|
|
|
std::string FooHPath = testPath("foo.h");
|
|
|
|
std::string FooCCPath = testPath("foo.cc");
|
|
|
|
MockFS FS;
|
|
|
|
FS.Files[FooHPath] = std::string(FooH.code());
|
|
|
|
FS.Files[FooCCPath] = std::string(FooCC.code());
|
|
|
|
|
|
|
|
auto ServerOpts = ClangdServer::optsForTest();
|
|
|
|
ServerOpts.BuildDynamicSymbolIndex = true;
|
|
|
|
|
2020-11-25 11:46:41 +01:00
|
|
|
trace::TestTracer Tracer;
|
2020-10-02 16:01:25 +02:00
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
ClangdServer Server(CDB, FS, ServerOpts);
|
|
|
|
runAddDocument(Server, FooHPath, FooH.code());
|
|
|
|
runAddDocument(Server, FooCCPath, FooCC.code());
|
|
|
|
|
2020-10-07 21:16:45 +02:00
|
|
|
auto Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
|
2022-12-05 21:49:31 -08:00
|
|
|
/*NewName=*/std::nullopt, {});
|
2020-11-11 11:13:43 +01:00
|
|
|
// Verify that for multi-file rename, we only return main-file occurrences.
|
2020-10-02 16:01:25 +02:00
|
|
|
ASSERT_TRUE(bool(Results)) << Results.takeError();
|
|
|
|
// We don't know the result is complete in prepareRename (passing a nullptr
|
|
|
|
// index internally), so GlobalChanges should be empty.
|
|
|
|
EXPECT_TRUE(Results->GlobalChanges.empty());
|
|
|
|
EXPECT_THAT(FooCC.ranges(),
|
|
|
|
testing::UnorderedElementsAreArray(Results->LocalChanges));
|
|
|
|
|
2020-11-11 11:13:43 +01:00
|
|
|
// Name validation.
|
2021-02-11 15:57:54 +01:00
|
|
|
Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
|
|
|
|
/*NewName=*/std::string("int"), {});
|
2020-10-07 21:16:45 +02:00
|
|
|
EXPECT_FALSE(Results);
|
|
|
|
EXPECT_THAT(llvm::toString(Results.takeError()),
|
|
|
|
testing::HasSubstr("keyword"));
|
2020-11-25 10:49:54 +01:00
|
|
|
EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "Keywords"),
|
|
|
|
ElementsAre(1));
|
2021-03-11 13:56:24 +01:00
|
|
|
|
|
|
|
for (std::string BadIdent : {"foo!bar", "123foo", "😀@"}) {
|
|
|
|
Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
|
|
|
|
/*NewName=*/BadIdent, {});
|
|
|
|
EXPECT_FALSE(Results);
|
|
|
|
EXPECT_THAT(llvm::toString(Results.takeError()),
|
|
|
|
testing::HasSubstr("identifier"));
|
|
|
|
EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "BadIdentifier"),
|
|
|
|
ElementsAre(1));
|
|
|
|
}
|
|
|
|
for (std::string GoodIdent : {"fooBar", "__foo$", "😀"}) {
|
|
|
|
Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
|
|
|
|
/*NewName=*/GoodIdent, {});
|
|
|
|
EXPECT_TRUE(bool(Results));
|
|
|
|
}
|
2020-10-02 16:01:25 +02:00
|
|
|
}
|
|
|
|
|
2019-12-05 12:08:31 +01:00
|
|
|
TEST(CrossFileRenameTests, DirtyBuffer) {
|
2019-10-23 14:40:20 +02:00
|
|
|
Annotations FooCode("class [[Foo]] {};");
|
|
|
|
std::string FooPath = testPath("foo.cc");
|
|
|
|
Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
|
|
|
|
Annotations BarCode("void [[Bar]]() {}");
|
|
|
|
std::string BarPath = testPath("bar.cc");
|
|
|
|
// Build the index, the index has "Foo" references from foo.cc and "Bar"
|
|
|
|
// references from bar.cc.
|
2021-02-05 12:32:47 +03:00
|
|
|
FileSymbols FSymbols(IndexContents::All);
|
2019-10-23 14:40:20 +02:00
|
|
|
FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
|
|
|
|
nullptr, false);
|
|
|
|
FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
|
|
|
|
nullptr, false);
|
|
|
|
auto Index = FSymbols.buildIndex(IndexType::Light);
|
|
|
|
|
|
|
|
Annotations MainCode("class [[Fo^o]] {};");
|
|
|
|
auto MainFilePath = testPath("main.cc");
|
2021-03-10 13:41:27 +00:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
|
|
|
|
new llvm::vfs::InMemoryFileSystem;
|
|
|
|
InMemFS->addFile(FooPath, 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer.code()));
|
2019-10-23 14:40:20 +02:00
|
|
|
|
|
|
|
// Run rename on Foo, there is a dirty buffer for foo.cc, rename should
|
|
|
|
// respect the dirty buffer.
|
|
|
|
TestTU TU = TestTU::withCode(MainCode.code());
|
|
|
|
auto AST = TU.build();
|
|
|
|
llvm::StringRef NewName = "newName";
|
2021-03-10 13:41:27 +00:00
|
|
|
auto Results =
|
|
|
|
rename({MainCode.point(), NewName, AST, MainFilePath,
|
|
|
|
createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
|
2019-10-23 14:40:20 +02:00
|
|
|
ASSERT_TRUE(bool(Results)) << Results.takeError();
|
|
|
|
EXPECT_THAT(
|
2020-10-02 16:01:25 +02:00
|
|
|
applyEdits(std::move(Results->GlobalChanges)),
|
2019-10-23 14:40:20 +02:00
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
|
|
|
|
Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
|
|
|
|
|
|
|
|
// Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
|
|
|
|
// so we should read file content from VFS.
|
|
|
|
MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
|
|
|
|
TU = TestTU::withCode(MainCode.code());
|
|
|
|
// Set a file "bar.cc" on disk.
|
2020-01-28 20:23:46 +01:00
|
|
|
TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
|
2019-10-23 14:40:20 +02:00
|
|
|
AST = TU.build();
|
2021-03-10 13:41:27 +00:00
|
|
|
Results = rename({MainCode.point(), NewName, AST, MainFilePath,
|
|
|
|
createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
|
2019-10-23 14:40:20 +02:00
|
|
|
ASSERT_TRUE(bool(Results)) << Results.takeError();
|
|
|
|
EXPECT_THAT(
|
2020-10-02 16:01:25 +02:00
|
|
|
applyEdits(std::move(Results->GlobalChanges)),
|
2019-10-23 14:40:20 +02:00
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
|
|
|
|
Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
|
2019-11-28 12:47:32 +01:00
|
|
|
|
|
|
|
// Run rename on a pagination index which couldn't return all refs in one
|
|
|
|
// request, we reject rename on this case.
|
|
|
|
class PaginationIndex : public SymbolIndex {
|
|
|
|
bool refs(const RefsRequest &Req,
|
|
|
|
llvm::function_ref<void(const Ref &)> Callback) const override {
|
|
|
|
return true; // has more references
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fuzzyFind(
|
|
|
|
const FuzzyFindRequest &Req,
|
|
|
|
llvm::function_ref<void(const Symbol &)> Callback) const override {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void
|
|
|
|
lookup(const LookupRequest &Req,
|
|
|
|
llvm::function_ref<void(const Symbol &)> Callback) const override {}
|
|
|
|
|
|
|
|
void relations(const RelationsRequest &Req,
|
|
|
|
llvm::function_ref<void(const SymbolID &, const Symbol &)>
|
|
|
|
Callback) const override {}
|
2020-12-18 15:14:15 +03:00
|
|
|
|
2021-02-05 12:32:47 +03:00
|
|
|
llvm::unique_function<IndexContents(llvm::StringRef) const>
|
2020-12-18 15:14:15 +03:00
|
|
|
indexedFiles() const override {
|
2021-02-05 12:32:47 +03:00
|
|
|
return [](llvm::StringRef) { return IndexContents::None; };
|
2020-12-18 15:14:15 +03:00
|
|
|
}
|
|
|
|
|
2019-11-28 12:47:32 +01:00
|
|
|
size_t estimateMemoryUsage() const override { return 0; }
|
|
|
|
} PIndex;
|
2021-03-10 13:41:27 +00:00
|
|
|
Results = rename({MainCode.point(), NewName, AST, MainFilePath,
|
|
|
|
createOverlay(getVFSFromAST(AST), InMemFS), &PIndex});
|
2019-11-28 12:47:32 +01:00
|
|
|
EXPECT_FALSE(Results);
|
|
|
|
EXPECT_THAT(llvm::toString(Results.takeError()),
|
|
|
|
testing::HasSubstr("too many occurrences"));
|
2019-10-23 14:40:20 +02:00
|
|
|
}
|
|
|
|
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
|
|
|
|
auto MainCode = Annotations("int [[^x]] = 2;");
|
|
|
|
auto MainFilePath = testPath("main.cc");
|
|
|
|
auto BarCode = Annotations("int [[x]];");
|
|
|
|
auto BarPath = testPath("bar.cc");
|
|
|
|
auto TU = TestTU::withCode(MainCode.code());
|
|
|
|
// Set a file "bar.cc" on disk.
|
2020-01-28 20:23:46 +01:00
|
|
|
TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
auto AST = TU.build();
|
|
|
|
std::string BarPathURI = URI::create(BarPath).toString();
|
|
|
|
Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
|
|
|
|
// The index will return duplicated refs, our code should be robost to handle
|
|
|
|
// it.
|
|
|
|
class DuplicatedXRefIndex : public SymbolIndex {
|
|
|
|
public:
|
|
|
|
DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
|
|
|
|
bool refs(const RefsRequest &Req,
|
|
|
|
llvm::function_ref<void(const Ref &)> Callback) const override {
|
|
|
|
// Return two duplicated refs.
|
|
|
|
Callback(ReturnedRef);
|
|
|
|
Callback(ReturnedRef);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fuzzyFind(const FuzzyFindRequest &,
|
|
|
|
llvm::function_ref<void(const Symbol &)>) const override {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void lookup(const LookupRequest &,
|
|
|
|
llvm::function_ref<void(const Symbol &)>) const override {}
|
|
|
|
|
|
|
|
void relations(const RelationsRequest &,
|
|
|
|
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
|
|
|
|
const override {}
|
2020-12-18 15:14:15 +03:00
|
|
|
|
2021-02-05 12:32:47 +03:00
|
|
|
llvm::unique_function<IndexContents(llvm::StringRef) const>
|
2020-12-18 15:14:15 +03:00
|
|
|
indexedFiles() const override {
|
2021-02-05 12:32:47 +03:00
|
|
|
return [](llvm::StringRef) { return IndexContents::None; };
|
2020-12-18 15:14:15 +03:00
|
|
|
}
|
|
|
|
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
size_t estimateMemoryUsage() const override { return 0; }
|
|
|
|
Ref ReturnedRef;
|
|
|
|
} DIndex(XRefInBarCC);
|
|
|
|
llvm::StringRef NewName = "newName";
|
2021-03-10 13:41:27 +00:00
|
|
|
auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
|
|
|
|
getVFSFromAST(AST), &DIndex});
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
ASSERT_TRUE(bool(Results)) << Results.takeError();
|
|
|
|
EXPECT_THAT(
|
2020-10-02 16:01:25 +02:00
|
|
|
applyEdits(std::move(Results->GlobalChanges)),
|
[clangd] Deduplicate refs from index for cross-file rename.
Summary:
If the index returns duplicated refs, it will trigger the assertion in
BuildRenameEdit (we expect the processing position is always larger the
the previous one, but it is not true if we have duplication), and also
breaks our heuristics.
This patch make the code robost enough to handle duplications, also
save some cost of redundnat llvm::sort.
Though clangd's index doesn't return duplications, our internal index
kythe will.
Reviewers: ilya-biryukov
Subscribers: MaskRay, jkorous, mgrang, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D71300
2019-12-10 22:15:29 +01:00
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
|
|
|
|
Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
|
|
|
|
}
|
|
|
|
|
2019-12-05 12:08:31 +01:00
|
|
|
TEST(CrossFileRenameTests, WithUpToDateIndex) {
|
|
|
|
MockCompilationDatabase CDB;
|
|
|
|
CDB.ExtraClangFlags = {"-xc++"};
|
2020-01-20 14:14:52 +01:00
|
|
|
// rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
|
2019-12-12 13:10:59 +01:00
|
|
|
// expected rename occurrences.
|
2019-12-05 12:08:31 +01:00
|
|
|
struct Case {
|
|
|
|
llvm::StringRef FooH;
|
|
|
|
llvm::StringRef FooCC;
|
2019-12-12 13:10:59 +01:00
|
|
|
} Cases[] = {
|
|
|
|
{
|
|
|
|
// classes.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
class [[Fo^o]] {
|
|
|
|
[[Foo]]();
|
|
|
|
~[[Foo]]();
|
|
|
|
};
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
[[Foo]]::[[Foo]]() {}
|
|
|
|
[[Foo]]::~[[Foo]]() {}
|
|
|
|
|
|
|
|
void func() {
|
|
|
|
[[Foo]] foo;
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
2020-02-21 09:57:05 +01:00
|
|
|
{
|
|
|
|
// class templates.
|
|
|
|
R"cpp(
|
|
|
|
template <typename T>
|
|
|
|
class [[Foo]] {};
|
2020-04-05 15:28:11 +09:00
|
|
|
// FIXME: explicit template specializations are not supported due the
|
2020-02-21 09:57:05 +01:00
|
|
|
// clangd index limitations.
|
|
|
|
template <>
|
|
|
|
class Foo<double> {};
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
void func() {
|
|
|
|
[[F^oo]]<int> foo;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
},
|
2019-12-12 13:10:59 +01:00
|
|
|
{
|
|
|
|
// class methods.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
class Foo {
|
|
|
|
void [[f^oo]]();
|
|
|
|
};
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
void Foo::[[foo]]() {}
|
|
|
|
|
|
|
|
void func(Foo* p) {
|
|
|
|
p->[[foo]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
2022-09-07 12:03:55 +02:00
|
|
|
{
|
|
|
|
// virtual methods.
|
|
|
|
R"cpp(
|
|
|
|
class Base {
|
|
|
|
virtual void [[foo]]();
|
|
|
|
};
|
|
|
|
class Derived1 : public Base {
|
|
|
|
void [[f^oo]]() override;
|
|
|
|
};
|
|
|
|
class NotDerived {
|
|
|
|
void foo() {};
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
void Base::[[foo]]() {}
|
|
|
|
void Derived1::[[foo]]() {}
|
|
|
|
|
|
|
|
class Derived2 : public Derived1 {
|
|
|
|
void [[foo]]() override {};
|
|
|
|
};
|
|
|
|
|
|
|
|
void func(Base* b, Derived1* d1,
|
|
|
|
Derived2* d2, NotDerived* nd) {
|
|
|
|
b->[[foo]]();
|
|
|
|
d1->[[foo]]();
|
|
|
|
d2->[[foo]]();
|
|
|
|
nd->foo();
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// virtual templated method
|
|
|
|
R"cpp(
|
|
|
|
template <typename> class Foo { virtual void [[m]](); };
|
|
|
|
class Bar : Foo<int> { void [[^m]]() override; };
|
2022-09-07 13:30:08 +02:00
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
|
|
|
|
template<typename T> void Foo<T>::[[m]]() {}
|
|
|
|
// FIXME: not renamed as the index doesn't see this as an override of
|
|
|
|
// the canonical Foo<T>::m().
|
|
|
|
// https://github.com/clangd/clangd/issues/1325
|
|
|
|
class Baz : Foo<float> { void m() override; };
|
|
|
|
)cpp"
|
2022-09-07 12:03:55 +02:00
|
|
|
},
|
2019-12-12 13:10:59 +01:00
|
|
|
{
|
2020-01-20 14:14:52 +01:00
|
|
|
// rename on constructor and destructor.
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
|
|
|
class [[Foo]] {
|
|
|
|
[[^Foo]]();
|
|
|
|
~[[Foo^]]();
|
|
|
|
};
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
[[Foo]]::[[Foo]]() {}
|
|
|
|
[[Foo]]::~[[Foo]]() {}
|
|
|
|
|
|
|
|
void func() {
|
|
|
|
[[Foo]] foo;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// functions.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
void [[f^oo]]();
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
void [[foo]]() {}
|
|
|
|
|
|
|
|
void func() {
|
|
|
|
[[foo]]();
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// typedefs.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
typedef int [[IN^T]];
|
|
|
|
[[INT]] foo();
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
[[INT]] foo() {}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// usings.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
using [[I^NT]] = int;
|
|
|
|
[[INT]] foo();
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
[[INT]] foo() {}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// variables.
|
|
|
|
R"cpp(
|
2020-02-06 08:18:14 +01:00
|
|
|
static const int [[VA^R]] = 123;
|
2019-12-05 12:08:31 +01:00
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
int s = [[VAR]];
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// scope enums.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
enum class [[K^ind]] { ABC };
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
[[Kind]] ff() {
|
|
|
|
return [[Kind]]::ABC;
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// enum constants.
|
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
enum class Kind { [[A^BC]] };
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
R"cpp(
|
2019-12-05 12:08:31 +01:00
|
|
|
#include "foo.h"
|
|
|
|
Kind ff() {
|
|
|
|
return Kind::[[ABC]];
|
|
|
|
}
|
|
|
|
)cpp",
|
2019-12-12 13:10:59 +01:00
|
|
|
},
|
2020-02-06 11:41:17 +01:00
|
|
|
{
|
|
|
|
// Implicit references in macro expansions.
|
|
|
|
R"cpp(
|
|
|
|
class [[Fo^o]] {};
|
|
|
|
#define FooFoo Foo
|
|
|
|
#define FOO Foo
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
#include "foo.h"
|
|
|
|
void bar() {
|
|
|
|
[[Foo]] x;
|
|
|
|
FOO y;
|
|
|
|
FooFoo z;
|
|
|
|
}
|
|
|
|
)cpp",
|
|
|
|
},
|
2019-12-05 12:08:31 +01:00
|
|
|
};
|
|
|
|
|
2020-04-16 23:12:09 +02:00
|
|
|
trace::TestTracer Tracer;
|
|
|
|
for (const auto &T : Cases) {
|
2020-01-27 14:09:27 +01:00
|
|
|
SCOPED_TRACE(T.FooH);
|
2019-12-05 12:08:31 +01:00
|
|
|
Annotations FooH(T.FooH);
|
|
|
|
Annotations FooCC(T.FooCC);
|
|
|
|
std::string FooHPath = testPath("foo.h");
|
|
|
|
std::string FooCCPath = testPath("foo.cc");
|
|
|
|
|
2020-06-17 11:53:32 +02:00
|
|
|
MockFS FS;
|
2020-01-28 20:23:46 +01:00
|
|
|
FS.Files[FooHPath] = std::string(FooH.code());
|
|
|
|
FS.Files[FooCCPath] = std::string(FooCC.code());
|
2019-12-05 12:08:31 +01:00
|
|
|
|
|
|
|
auto ServerOpts = ClangdServer::optsForTest();
|
|
|
|
ServerOpts.BuildDynamicSymbolIndex = true;
|
2020-01-24 14:08:56 +01:00
|
|
|
ClangdServer Server(CDB, FS, ServerOpts);
|
2019-12-05 12:08:31 +01:00
|
|
|
|
|
|
|
// Add all files to clangd server to make sure the dynamic index has been
|
|
|
|
// built.
|
|
|
|
runAddDocument(Server, FooHPath, FooH.code());
|
|
|
|
runAddDocument(Server, FooCCPath, FooCC.code());
|
|
|
|
|
|
|
|
llvm::StringRef NewName = "NewName";
|
2020-01-20 14:14:52 +01:00
|
|
|
for (const auto &RenamePos : FooH.points()) {
|
2020-04-16 23:12:09 +02:00
|
|
|
EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
|
2021-02-11 15:57:54 +01:00
|
|
|
auto FileEditsList =
|
|
|
|
llvm::cantFail(runRename(Server, FooHPath, RenamePos, NewName, {}));
|
2020-04-16 23:12:09 +02:00
|
|
|
EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
|
2020-01-20 14:14:52 +01:00
|
|
|
EXPECT_THAT(
|
2020-10-02 16:01:25 +02:00
|
|
|
applyEdits(std::move(FileEditsList.GlobalChanges)),
|
2020-01-20 14:14:52 +01:00
|
|
|
UnorderedElementsAre(
|
|
|
|
Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
|
|
|
|
Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
|
|
|
|
}
|
2019-12-05 12:08:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 14:40:20 +02:00
|
|
|
TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
|
|
|
|
// cross-file rename should work for function-local symbols, even there is no
|
|
|
|
// index provided.
|
|
|
|
Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
|
|
|
|
auto TU = TestTU::withCode(Code.code());
|
|
|
|
auto Path = testPath(TU.Filename);
|
|
|
|
auto AST = TU.build();
|
|
|
|
llvm::StringRef NewName = "newName";
|
|
|
|
auto Results = rename({Code.point(), NewName, AST, Path});
|
|
|
|
ASSERT_TRUE(bool(Results)) << Results.takeError();
|
|
|
|
EXPECT_THAT(
|
2020-10-02 16:01:25 +02:00
|
|
|
applyEdits(std::move(Results->GlobalChanges)),
|
2019-10-23 14:40:20 +02:00
|
|
|
UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
|
2019-11-19 10:10:43 +01:00
|
|
|
}
|
|
|
|
|
2019-11-19 15:23:36 +01:00
|
|
|
TEST(CrossFileRenameTests, BuildRenameEdits) {
|
|
|
|
Annotations Code("[[😂]]");
|
|
|
|
auto LSPRange = Code.range();
|
2019-11-28 16:48:49 +01:00
|
|
|
llvm::StringRef FilePath = "/test/TestTU.cpp";
|
2019-12-10 21:13:36 +01:00
|
|
|
llvm::StringRef NewName = "abc";
|
|
|
|
auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
|
2019-11-19 15:23:36 +01:00
|
|
|
ASSERT_TRUE(bool(Edit)) << Edit.takeError();
|
|
|
|
ASSERT_EQ(1UL, Edit->Replacements.size());
|
2019-11-28 16:48:49 +01:00
|
|
|
EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
|
2019-11-19 15:23:36 +01:00
|
|
|
EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
|
|
|
|
|
|
|
|
// Test invalid range.
|
|
|
|
LSPRange.end = {10, 0}; // out of range
|
2019-12-10 21:13:36 +01:00
|
|
|
Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
|
2019-11-19 15:23:36 +01:00
|
|
|
EXPECT_FALSE(Edit);
|
|
|
|
EXPECT_THAT(llvm::toString(Edit.takeError()),
|
|
|
|
testing::HasSubstr("fail to convert"));
|
|
|
|
|
|
|
|
// Normal ascii characters.
|
|
|
|
Annotations T(R"cpp(
|
|
|
|
[[range]]
|
|
|
|
[[range]]
|
|
|
|
[[range]]
|
|
|
|
)cpp");
|
2019-12-10 21:13:36 +01:00
|
|
|
Edit = buildRenameEdit(FilePath, T.code(), T.ranges(), NewName);
|
2019-11-19 15:23:36 +01:00
|
|
|
ASSERT_TRUE(bool(Edit)) << Edit.takeError();
|
|
|
|
EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
|
2019-12-10 21:13:36 +01:00
|
|
|
expectedResult(T, NewName));
|
2019-11-19 15:23:36 +01:00
|
|
|
}
|
|
|
|
|
2019-12-09 17:00:51 +01:00
|
|
|
TEST(CrossFileRenameTests, adjustRenameRanges) {
|
|
|
|
// Ranges in IndexedCode indicate the indexed occurrences;
|
|
|
|
// ranges in DraftCode indicate the expected mapped result, empty indicates
|
|
|
|
// we expect no matched result found.
|
|
|
|
struct {
|
|
|
|
llvm::StringRef IndexedCode;
|
|
|
|
llvm::StringRef DraftCode;
|
|
|
|
} Tests[] = {
|
|
|
|
{
|
|
|
|
// both line and column are changed, not a near miss.
|
|
|
|
R"cpp(
|
|
|
|
int [[x]] = 0;
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
// insert a line.
|
|
|
|
double x = 0;
|
|
|
|
)cpp",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// subset.
|
|
|
|
R"cpp(
|
|
|
|
int [[x]] = 0;
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
int [[x]] = 0;
|
|
|
|
{int x = 0; }
|
|
|
|
)cpp",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// shift columns.
|
|
|
|
R"cpp(int [[x]] = 0; void foo(int x);)cpp",
|
|
|
|
R"cpp(double [[x]] = 0; void foo(double x);)cpp",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// shift lines.
|
|
|
|
R"cpp(
|
|
|
|
int [[x]] = 0;
|
|
|
|
void foo(int x);
|
|
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
|
|
// insert a line.
|
|
|
|
int [[x]] = 0;
|
|
|
|
void foo(int x);
|
|
|
|
)cpp",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
LangOptions LangOpts;
|
|
|
|
LangOpts.CPlusPlus = true;
|
|
|
|
for (const auto &T : Tests) {
|
2020-01-27 14:09:27 +01:00
|
|
|
SCOPED_TRACE(T.DraftCode);
|
2019-12-09 17:00:51 +01:00
|
|
|
Annotations Draft(T.DraftCode);
|
|
|
|
auto ActualRanges = adjustRenameRanges(
|
|
|
|
Draft.code(), "x", Annotations(T.IndexedCode).ranges(), LangOpts);
|
|
|
|
if (!ActualRanges)
|
|
|
|
EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
|
|
|
|
else
|
|
|
|
EXPECT_THAT(Draft.ranges(),
|
2020-01-27 14:09:27 +01:00
|
|
|
testing::UnorderedElementsAreArray(*ActualRanges));
|
2019-12-09 17:00:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(RangePatchingHeuristic, GetMappedRanges) {
|
|
|
|
// ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
|
|
|
|
// there are no mapped ranges.
|
|
|
|
struct {
|
|
|
|
llvm::StringRef IndexedCode;
|
|
|
|
llvm::StringRef LexedCode;
|
|
|
|
} Tests[] = {
|
|
|
|
{
|
|
|
|
// no lexed ranges.
|
|
|
|
"[[]]",
|
|
|
|
"",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// both line and column are changed, not a near miss.
|
|
|
|
R"([[]])",
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
)",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// subset.
|
|
|
|
"[[]]",
|
|
|
|
"^[[]] [[]]"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// shift columns.
|
|
|
|
"[[]] [[]]",
|
|
|
|
" ^[[]] ^[[]] [[]]"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
|
|
|
|
[[]] [[]]
|
|
|
|
)",
|
|
|
|
R"(
|
|
|
|
// insert a line
|
|
|
|
^[[]]
|
|
|
|
|
|
|
|
^[[]] ^[[]]
|
|
|
|
)",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
|
|
|
|
[[]] [[]]
|
|
|
|
)",
|
|
|
|
R"(
|
|
|
|
// insert a line
|
|
|
|
^[[]]
|
|
|
|
^[[]] ^[[]] // column is shifted.
|
|
|
|
)",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
|
|
|
|
[[]] [[]]
|
|
|
|
)",
|
|
|
|
R"(
|
|
|
|
// insert a line
|
|
|
|
[[]]
|
|
|
|
|
|
|
|
[[]] [[]] // not mapped (both line and column are changed).
|
|
|
|
)",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
|
|
|
|
}
|
|
|
|
)",
|
|
|
|
R"(
|
|
|
|
// insert a new line
|
|
|
|
^[[]]
|
|
|
|
^[[]]
|
|
|
|
[[]] // additional range
|
|
|
|
^[[]]
|
|
|
|
^[[]]
|
|
|
|
[[]] // additional range
|
|
|
|
)",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// non-distinct result (two best results), not a near miss
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
)",
|
|
|
|
R"(
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
[[]]
|
|
|
|
)",
|
|
|
|
}
|
|
|
|
};
|
|
|
|
for (const auto &T : Tests) {
|
2020-01-27 14:09:27 +01:00
|
|
|
SCOPED_TRACE(T.IndexedCode);
|
2019-12-09 17:00:51 +01:00
|
|
|
auto Lexed = Annotations(T.LexedCode);
|
|
|
|
auto LexedRanges = Lexed.ranges();
|
|
|
|
std::vector<Range> ExpectedMatches;
|
|
|
|
for (auto P : Lexed.points()) {
|
|
|
|
auto Match = llvm::find_if(LexedRanges, [&P](const Range& R) {
|
|
|
|
return R.start == P;
|
|
|
|
});
|
|
|
|
ASSERT_NE(Match, LexedRanges.end());
|
|
|
|
ExpectedMatches.push_back(*Match);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Mapped =
|
|
|
|
getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
|
|
|
|
if (!Mapped)
|
|
|
|
EXPECT_THAT(ExpectedMatches, IsEmpty());
|
|
|
|
else
|
2020-01-27 14:09:27 +01:00
|
|
|
EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
|
2019-12-09 17:00:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(CrossFileRenameTests, adjustmentCost) {
|
|
|
|
struct {
|
|
|
|
llvm::StringRef RangeCode;
|
|
|
|
size_t ExpectedCost;
|
|
|
|
} Tests[] = {
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]]$lex[[]] // diff: 0
|
|
|
|
)",
|
|
|
|
0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // line diff: +1
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // line diff: +1
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // line diff: +1
|
|
|
|
|
|
|
|
$idx[[]]
|
|
|
|
|
|
|
|
$lex[[]] // line diff: +2
|
|
|
|
)",
|
|
|
|
1 + 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // line diff: +1
|
|
|
|
$idx[[]]
|
|
|
|
|
|
|
|
$lex[[]] // line diff: +2
|
|
|
|
$idx[[]]
|
|
|
|
|
|
|
|
|
|
|
|
$lex[[]] // line diff: +3
|
|
|
|
)",
|
|
|
|
1 + 1 + 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]]
|
|
|
|
|
|
|
|
|
|
|
|
$lex[[]] // line diff: +3
|
|
|
|
$idx[[]]
|
|
|
|
|
|
|
|
$lex[[]] // line diff: +2
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // line diff: +1
|
|
|
|
)",
|
|
|
|
3 + 1 + 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // line diff: +1
|
|
|
|
$lex[[]] // line diff: -2
|
|
|
|
|
|
|
|
$idx[[]]
|
|
|
|
$idx[[]]
|
|
|
|
|
|
|
|
|
|
|
|
$lex[[]] // line diff: +3
|
|
|
|
)",
|
|
|
|
1 + 3 + 5
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]] $lex[[]] // column diff: +1
|
|
|
|
$idx[[]]$lex[[]] // diff: 0
|
|
|
|
)",
|
|
|
|
1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]]
|
|
|
|
$lex[[]] // diff: +1
|
|
|
|
$idx[[]] $lex[[]] // column diff: +1
|
|
|
|
$idx[[]]$lex[[]] // diff: 0
|
|
|
|
)",
|
|
|
|
1 + 1 + 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
$idx[[]] $lex[[]] // column diff: +1
|
|
|
|
)",
|
|
|
|
1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
R"(
|
|
|
|
// column diffs: +1, +2, +3
|
|
|
|
$idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
|
|
|
|
)",
|
|
|
|
1 + 1 + 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
for (const auto &T : Tests) {
|
2020-01-27 14:09:27 +01:00
|
|
|
SCOPED_TRACE(T.RangeCode);
|
2019-12-09 17:00:51 +01:00
|
|
|
Annotations C(T.RangeCode);
|
|
|
|
std::vector<size_t> MappedIndex;
|
|
|
|
for (size_t I = 0; I < C.ranges("lex").size(); ++I)
|
|
|
|
MappedIndex.push_back(I);
|
|
|
|
EXPECT_EQ(renameRangeAdjustmentCost(C.ranges("idx"), C.ranges("lex"),
|
|
|
|
MappedIndex),
|
2020-01-27 14:09:27 +01:00
|
|
|
T.ExpectedCost);
|
2019-12-09 17:00:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 07:11:56 +00:00
|
|
|
} // namespace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|