summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2018-08-03 09:16:07 +0000
committerEric Liu <ioeric@google.com>2018-08-03 09:16:07 +0000
commitdb887cab7a4df2c426aa5793eeb649db4942806c (patch)
tree34b435ce0572d2838f4e0645a64b1ebd461aeac5
parentcd75901de3a53db6260aeed7a66950a210a1eed6 (diff)
downloadbcm5719-llvm-db887cab7a4df2c426aa5793eeb649db4942806c.tar.gz
bcm5719-llvm-db887cab7a4df2c426aa5793eeb649db4942806c.zip
Fully qualify the renamed symbol if the shortened name is ambiguous.
Summary: For example, when renaming `a::b::x::foo` to `y::foo` below, replacing `x::foo()` with `y::foo()` can cause ambiguity. In such cases, we simply fully qualify the name with leading `::`. ``` namespace a { namespace b { namespace x { void foo() {} } namespace y { void foo() {} } } } namespace a { namespace b { void f() { x::foo(); } } } ``` Reviewers: ilya-biryukov, hokein Reviewed By: ilya-biryukov Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D50189 llvm-svn: 338832
-rw-r--r--clang/lib/Tooling/Core/Lookup.cpp44
-rw-r--r--clang/unittests/Tooling/LookupTest.cpp34
2 files changed, 75 insertions, 3 deletions
diff --git a/clang/lib/Tooling/Core/Lookup.cpp b/clang/lib/Tooling/Core/Lookup.cpp
index 6edf61b8050..cc448d144e2 100644
--- a/clang/lib/Tooling/Core/Lookup.cpp
+++ b/clang/lib/Tooling/Core/Lookup.cpp
@@ -14,6 +14,7 @@
#include "clang/Tooling/Core/Lookup.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclarationName.h"
using namespace clang;
using namespace clang::tooling;
@@ -114,6 +115,37 @@ static bool isFullyQualified(const NestedNameSpecifier *NNS) {
return false;
}
+// Returns true if spelling symbol \p QName as \p Spelling in \p UseContext is
+// ambiguous. For example, if QName is "::y::bar" and the spelling is "y::bar"
+// in `UseContext` "a" that contains a nested namespace "a::y", then "y::bar"
+// can be resolved to ::a::y::bar, which can cause compile error.
+// FIXME: consider using namespaces.
+static bool isAmbiguousNameInScope(StringRef Spelling, StringRef QName,
+ const DeclContext &UseContext) {
+ assert(QName.startswith("::"));
+ if (Spelling.startswith("::"))
+ return false;
+
+ // Lookup the first component of Spelling in all enclosing namespaces and
+ // check if there is any existing symbols with the same name but in different
+ // scope.
+ StringRef Head = Spelling.split("::").first;
+
+ llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
+ getAllNamedNamespaces(&UseContext);
+ auto &AST = UseContext.getParentASTContext();
+ StringRef TrimmedQName = QName.substr(2);
+ for (const auto *NS : UseNamespaces) {
+ auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
+ if (!LookupRes.empty()) {
+ for (const NamedDecl *Res : LookupRes)
+ if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()))
+ return true;
+ }
+ }
+ return false;
+}
+
std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
const DeclContext *UseContext,
const NamedDecl *FromDecl,
@@ -146,6 +178,14 @@ std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
// figure out how good a namespace match we have with our destination type.
// We work backwards (from most specific possible namespace to least
// specific).
- return getBestNamespaceSubstr(UseContext, ReplacementString,
- isFullyQualified(Use));
+ StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,
+ isFullyQualified(Use));
+ // Use the fully qualified name if the suggested name is ambiguous.
+ // FIXME: consider re-shortening the name until the name is not ambiguous. We
+ // are not doing this because ambiguity is pretty bad and we should not try to
+ // be clever in handling such cases. Making this noticeable to users seems to
+ // be a better option.
+ return isAmbiguousNameInScope(Suggested, ReplacementString, *UseContext)
+ ? ReplacementString
+ : Suggested;
}
diff --git a/clang/unittests/Tooling/LookupTest.cpp b/clang/unittests/Tooling/LookupTest.cpp
index f42f31e9dd9..a08b2b418f7 100644
--- a/clang/unittests/Tooling/LookupTest.cpp
+++ b/clang/unittests/Tooling/LookupTest.cpp
@@ -68,7 +68,7 @@ TEST(LookupTest, replaceNestedFunctionName) {
"namespace b { void f() { a::foo(); } }\n");
Visitor.OnCall = [&](CallExpr *Expr) {
- EXPECT_EQ("a::bar", replaceCallExpr(Expr, "::a::bar"));
+ EXPECT_EQ("::a::bar", replaceCallExpr(Expr, "::a::bar"));
};
Visitor.runOver("namespace a { void foo(); }\n"
"namespace b { namespace a { void foo(); }\n"
@@ -127,6 +127,38 @@ TEST(LookupTest, replaceNestedFunctionName) {
"namespace a { namespace b { namespace c {"
"void f() { foo(); }"
"} } }\n");
+
+ // If the shortest name is ambiguous, we need to add more qualifiers.
+ Visitor.OnCall = [&](CallExpr *Expr) {
+ EXPECT_EQ("::a::y::bar", replaceCallExpr(Expr, "::a::y::bar"));
+ };
+ Visitor.runOver(R"(
+ namespace a {
+ namespace b {
+ namespace x { void foo() {} }
+ namespace y { void foo() {} }
+ }
+ }
+
+ namespace a {
+ namespace b {
+ void f() { x::foo(); }
+ }
+ })");
+
+ Visitor.OnCall = [&](CallExpr *Expr) {
+ EXPECT_EQ("y::bar", replaceCallExpr(Expr, "::y::bar"));
+ };
+ Visitor.runOver(R"(
+ namespace a {
+ namespace b {
+ namespace x { void foo() {} }
+ namespace y { void foo() {} }
+ }
+ }
+
+ void f() { a::b::x::foo(); }
+ )");
}
TEST(LookupTest, replaceNestedClassName) {
OpenPOWER on IntegriCloud