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 @@ -751,6 +751,26 @@ auto MainFileRenameEdit = renameWithinFile(AST, RenameDecl, RInputs.NewName); if (!MainFileRenameEdit) return MainFileRenameEdit.takeError(); + + // Check the rename-triggering location is actually being renamed. + // This is a robustness check to avoid surprising rename results -- if the + // the triggering location is not actually the name of the node we identified + // (e.g. for broken code), then rename is likely not what users expect, so we + // reject this kind of rename. + auto StartOffset = positionToOffset(MainFileCode, CurrentIdentifier.start); + auto EndOffset = positionToOffset(MainFileCode, CurrentIdentifier.end); + if (!StartOffset) + return StartOffset.takeError(); + if (!EndOffset) + return EndOffset.takeError(); + if (llvm::find_if( + *MainFileRenameEdit, + [&StartOffset, &EndOffset](const clang::tooling::Replacement &R) { + return R.getOffset() == *StartOffset && + R.getLength() == *EndOffset - *StartOffset; + }) == MainFileRenameEdit->end()) { + return makeError(ReasonToReject::NoSymbolFound); + } RenameResult Result; Result.Target = CurrentIdentifier; Edit MainFileEdits = Edit(MainFileCode, std::move(*MainFileRenameEdit)); 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 @@ -1071,6 +1071,13 @@ struct B : priv^ate A {}; )cpp", "Cannot rename symbol: there is no symbol at the given location", false}, + {R"cpp(// Ensure it doesn't associate base specifier with base name. + /*error-ok*/ + struct A { + A() : inva^lid(0) {} + }; + )cpp", + "no symbol", false}, }; for (const auto& Case : Cases) {