diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -107,6 +107,7 @@ index/dex/Trigram.cpp refactor/Rename.cpp + refactor/RenameBlacklist.cpp refactor/Tweak.cpp LINK_LIBS diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -14,6 +14,7 @@ #include "Selection.h" #include "SourceCode.h" #include "index/SymbolCollector.h" +#include "refactor/RenameBlacklist.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceLocation.h" @@ -105,7 +106,7 @@ AmbiguousSymbol, }; -llvm::Optional renameable(const Decl &RenameDecl, +llvm::Optional renameable(const NamedDecl &RenameDecl, StringRef MainFilePath, const SymbolIndex *Index, bool CrossFile) { @@ -120,6 +121,9 @@ if (RenameDecl.getParentFunctionOrMethod()) return None; + if (isRenameBlacklisted(RenameDecl)) + return ReasonToReject::UnsupportedSymbol; + // Check whether the symbol being rename is indexable. auto &ASTCtx = RenameDecl.getASTContext(); bool MainFileIsHeader = isHeaderFile(MainFilePath, ASTCtx.getLangOpts()); @@ -131,11 +135,9 @@ IsMainFileOnly = false; else if (!DeclaredInMainFile) IsMainFileOnly = false; - bool IsIndexable = - isa(RenameDecl) && - SymbolCollector::shouldCollectSymbol( - cast(RenameDecl), RenameDecl.getASTContext(), - SymbolCollector::Options(), IsMainFileOnly); + bool IsIndexable = SymbolCollector::shouldCollectSymbol( + RenameDecl, RenameDecl.getASTContext(), SymbolCollector::Options(), + IsMainFileOnly); if (!IsIndexable) // If the symbol is not indexable, we disallow rename. return ReasonToReject::NonIndexable; @@ -467,13 +469,13 @@ if (DeclsUnderCursor.size() > 1) return makeError(ReasonToReject::AmbiguousSymbol); - const auto *RenameDecl = llvm::dyn_cast(*DeclsUnderCursor.begin()); + const auto *RenameDecl = llvm::dyn_cast( + (*DeclsUnderCursor.begin())->getCanonicalDecl()); if (!RenameDecl) return makeError(ReasonToReject::UnsupportedSymbol); - auto Reject = - renameable(*RenameDecl->getCanonicalDecl(), RInputs.MainFilePath, - RInputs.Index, RInputs.AllowCrossFile); + auto Reject = renameable(*RenameDecl, RInputs.MainFilePath, RInputs.Index, + RInputs.AllowCrossFile); if (Reject) return makeError(*Reject); diff --git a/clang-tools-extra/clangd/refactor/RenameBlacklist.h b/clang-tools-extra/clangd/refactor/RenameBlacklist.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/RenameBlacklist.h @@ -0,0 +1,24 @@ +//===--- RenameBlacklist.h - ------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_RENAME_BLACKLIST_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_RENAME_BLACKLIST_H + +#include "clang/AST/Decl.h" +#include "llvm/ADT/DenseSet.h" + +namespace clang { +namespace clangd { + +// Returns true if the given decl is disallowed for rename. +bool isRenameBlacklisted(const NamedDecl &RenameDecl); + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_RENAME_BLACKLIST_H diff --git a/clang-tools-extra/clangd/refactor/RenameBlacklist.cpp b/clang-tools-extra/clangd/refactor/RenameBlacklist.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/RenameBlacklist.cpp @@ -0,0 +1,24 @@ +//===--- RenameBlacklist.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 "refactor/RenameBlacklist.h" + +namespace clang { +namespace clangd { + +bool isRenameBlacklisted(const NamedDecl &RenameDecl) { + static const auto *StdSymbols = new llvm::DenseSet({ +#define SYMBOL(Name, NameSpace, Header) {#NameSpace #Name}, +#include "StdSymbolMap.inc" +#undef SYMBOL + }); + return StdSymbols->count(RenameDecl.getQualifiedNameAsString()); +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -383,12 +383,12 @@ // Typedef. R"cpp( - namespace std { + namespace ns { class basic_string {}; typedef basic_string [[s^tring]]; - } // namespace std + } // namespace ns - std::[[s^tring]] foo(); + ns::[[s^tring]] foo(); )cpp", // Variable. @@ -450,7 +450,7 @@ for (const auto &RenamePos : Code.points()) { auto RenameResult = rename({RenamePos, NewName, AST, testPath(TU.Filename)}); - ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError(); + ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << T; ASSERT_EQ(1u, RenameResult->size()); EXPECT_EQ(applyEdits(std::move(*RenameResult)).front().second, expectedResult(Code, NewName)); @@ -539,6 +539,13 @@ void fo^o() {})cpp", "used outside main file", !HeaderFile, nullptr /*no index*/}, + {R"cpp(// disallow rename on blacklisted symbols (e.g. std symbols) + namespace std { + class str^ing {}; + } + )cpp", + "not a supported kind", !HeaderFile, Index}, + {R"cpp( void foo(int); void foo(char);