forked from OSchip/llvm-project
[clangd] Add a symbol-name-based blacklist for rename.
Summary: This patch adds a simple mechanism to disallow global rename on std symbols. We might extend it to other symbols, e.g. protobuf. Reviewers: kadircet Subscribers: mgorny, ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D73450
This commit is contained in:
parent
757bdc64d3
commit
0d893fda43
|
@ -75,8 +75,8 @@ llvm::Optional<std::string> getOtherRefFile(const Decl &D, StringRef MainFile,
|
|||
return OtherFile;
|
||||
}
|
||||
|
||||
llvm::DenseSet<const Decl *> locateDeclAt(ParsedAST &AST,
|
||||
SourceLocation TokenStartLoc) {
|
||||
llvm::DenseSet<const NamedDecl *> locateDeclAt(ParsedAST &AST,
|
||||
SourceLocation TokenStartLoc) {
|
||||
unsigned Offset =
|
||||
AST.getSourceManager().getDecomposedSpellingLoc(TokenStartLoc).second;
|
||||
|
||||
|
@ -85,7 +85,7 @@ llvm::DenseSet<const Decl *> locateDeclAt(ParsedAST &AST,
|
|||
if (!SelectedNode)
|
||||
return {};
|
||||
|
||||
llvm::DenseSet<const Decl *> Result;
|
||||
llvm::DenseSet<const NamedDecl *> Result;
|
||||
for (const NamedDecl *D :
|
||||
targetDecl(SelectedNode->ASTNode,
|
||||
DeclRelation::Alias | DeclRelation::TemplatePattern)) {
|
||||
|
@ -96,6 +96,15 @@ llvm::DenseSet<const Decl *> locateDeclAt(ParsedAST &AST,
|
|||
return Result;
|
||||
}
|
||||
|
||||
bool isBlacklisted(const NamedDecl &RenameDecl) {
|
||||
static const auto *StdSymbols = new llvm::DenseSet<llvm::StringRef>({
|
||||
#define SYMBOL(Name, NameSpace, Header) {#NameSpace #Name},
|
||||
#include "StdSymbolMap.inc"
|
||||
#undef SYMBOL
|
||||
});
|
||||
return StdSymbols->count(RenameDecl.getQualifiedNameAsString());
|
||||
}
|
||||
|
||||
enum ReasonToReject {
|
||||
NoSymbolFound,
|
||||
NoIndexProvided,
|
||||
|
@ -105,7 +114,7 @@ enum ReasonToReject {
|
|||
AmbiguousSymbol,
|
||||
};
|
||||
|
||||
llvm::Optional<ReasonToReject> renameable(const Decl &RenameDecl,
|
||||
llvm::Optional<ReasonToReject> renameable(const NamedDecl &RenameDecl,
|
||||
StringRef MainFilePath,
|
||||
const SymbolIndex *Index,
|
||||
bool CrossFile) {
|
||||
|
@ -120,6 +129,9 @@ llvm::Optional<ReasonToReject> renameable(const Decl &RenameDecl,
|
|||
if (RenameDecl.getParentFunctionOrMethod())
|
||||
return None;
|
||||
|
||||
if (isBlacklisted(RenameDecl))
|
||||
return ReasonToReject::UnsupportedSymbol;
|
||||
|
||||
// Check whether the symbol being rename is indexable.
|
||||
auto &ASTCtx = RenameDecl.getASTContext();
|
||||
bool MainFileIsHeader = isHeaderFile(MainFilePath, ASTCtx.getLangOpts());
|
||||
|
@ -131,12 +143,10 @@ llvm::Optional<ReasonToReject> renameable(const Decl &RenameDecl,
|
|||
IsMainFileOnly = false;
|
||||
else if (!DeclaredInMainFile)
|
||||
IsMainFileOnly = false;
|
||||
bool IsIndexable =
|
||||
isa<NamedDecl>(RenameDecl) &&
|
||||
SymbolCollector::shouldCollectSymbol(
|
||||
cast<NamedDecl>(RenameDecl), RenameDecl.getASTContext(),
|
||||
SymbolCollector::Options(), IsMainFileOnly);
|
||||
if (!IsIndexable) // If the symbol is not indexable, we disallow rename.
|
||||
// If the symbol is not indexable, we disallow rename.
|
||||
if (!SymbolCollector::shouldCollectSymbol(
|
||||
RenameDecl, RenameDecl.getASTContext(), SymbolCollector::Options(),
|
||||
IsMainFileOnly))
|
||||
return ReasonToReject::NonIndexable;
|
||||
|
||||
if (!CrossFile) {
|
||||
|
@ -467,13 +477,10 @@ llvm::Expected<FileEdits> rename(const RenameInputs &RInputs) {
|
|||
if (DeclsUnderCursor.size() > 1)
|
||||
return makeError(ReasonToReject::AmbiguousSymbol);
|
||||
|
||||
const auto *RenameDecl = llvm::dyn_cast<NamedDecl>(*DeclsUnderCursor.begin());
|
||||
if (!RenameDecl)
|
||||
return makeError(ReasonToReject::UnsupportedSymbol);
|
||||
|
||||
auto Reject =
|
||||
renameable(*RenameDecl->getCanonicalDecl(), RInputs.MainFilePath,
|
||||
RInputs.Index, RInputs.AllowCrossFile);
|
||||
const auto &RenameDecl =
|
||||
llvm::cast<NamedDecl>(*(*DeclsUnderCursor.begin())->getCanonicalDecl());
|
||||
auto Reject = renameable(RenameDecl, RInputs.MainFilePath, RInputs.Index,
|
||||
RInputs.AllowCrossFile);
|
||||
if (Reject)
|
||||
return makeError(*Reject);
|
||||
|
||||
|
@ -486,7 +493,7 @@ llvm::Expected<FileEdits> rename(const RenameInputs &RInputs) {
|
|||
// To make cross-file rename work for local symbol, we use a hybrid solution:
|
||||
// - run AST-based rename on the main file;
|
||||
// - run index-based rename on other affected files;
|
||||
auto MainFileRenameEdit = renameWithinFile(AST, *RenameDecl, RInputs.NewName);
|
||||
auto MainFileRenameEdit = renameWithinFile(AST, RenameDecl, RInputs.NewName);
|
||||
if (!MainFileRenameEdit)
|
||||
return MainFileRenameEdit.takeError();
|
||||
|
||||
|
@ -502,7 +509,7 @@ llvm::Expected<FileEdits> rename(const RenameInputs &RInputs) {
|
|||
// symbol if we don't have index.
|
||||
if (RInputs.Index) {
|
||||
auto OtherFilesEdits =
|
||||
renameOutsideFile(*RenameDecl, RInputs.MainFilePath, RInputs.NewName,
|
||||
renameOutsideFile(RenameDecl, RInputs.MainFilePath, RInputs.NewName,
|
||||
*RInputs.Index, GetFileContent);
|
||||
if (!OtherFilesEdits)
|
||||
return OtherFilesEdits.takeError();
|
||||
|
|
|
@ -383,12 +383,12 @@ TEST(RenameTest, WithinFileRename) {
|
|||
|
||||
// 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.
|
||||
|
@ -540,6 +540,13 @@ TEST(RenameTest, Renameable) {
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue