summaryrefslogtreecommitdiff
path: root/clang-tools-extra/change-namespace
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2017-01-26 15:08:44 +0000
committerEric Liu <ioeric@google.com>2017-01-26 15:08:44 +0000
commitecff79ca7aeb425c5d1c7313844307707becbac1 (patch)
tree4edbb5a6917ef6ba3a6973b61ab925ccb3ec85fa /clang-tools-extra/change-namespace
parentdff23fd75f4bcfe3a3e7af2c3caf01ba48c81776 (diff)
[change-namespace] add leading '::' to references in new namespace when name conflict is possible.
Summary: For example, when we change 'na' to "nb::nc", we need to add leading '::' to references "::nc::X" in the changed namespace. Reviewers: bkramer Reviewed By: bkramer Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D29176
Diffstat (limited to 'clang-tools-extra/change-namespace')
-rw-r--r--clang-tools-extra/change-namespace/ChangeNamespace.cpp44
1 files changed, 37 insertions, 7 deletions
diff --git a/clang-tools-extra/change-namespace/ChangeNamespace.cpp b/clang-tools-extra/change-namespace/ChangeNamespace.cpp
index 35ea707e1e4..1e60dbdf5a5 100644
--- a/clang-tools-extra/change-namespace/ChangeNamespace.cpp
+++ b/clang-tools-extra/change-namespace/ChangeNamespace.cpp
@@ -196,6 +196,8 @@ tooling::Replacement createInsertion(SourceLocation Loc,
// Returns the shortest qualified name for declaration `DeclName` in the
// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
// is "a::c::d", then "b::X" will be returned.
+// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns
+// "::b::X" instead of "b::X" since there will be a name conflict otherwise.
// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
// will have empty name.
@@ -206,14 +208,42 @@ std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
if (DeclName.find(':') == llvm::StringRef::npos)
return DeclName;
- while (!DeclName.consume_front((NsName + "::").str())) {
- const auto Pos = NsName.find_last_of(':');
- if (Pos == llvm::StringRef::npos)
- return DeclName;
- assert(Pos > 0);
- NsName = NsName.substr(0, Pos - 1);
+ llvm::SmallVector<llvm::StringRef, 4> NsNameSplitted;
+ NsName.split(NsNameSplitted, "::", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ llvm::SmallVector<llvm::StringRef, 4> DeclNsSplitted;
+ DeclName.split(DeclNsSplitted, "::", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val();
+ // If the Decl is in global namespace, there is no need to shorten it.
+ if (DeclNsSplitted.empty())
+ return UnqualifiedDeclName;
+ // If NsName is the global namespace, we can simply use the DeclName sans
+ // leading "::".
+ if (NsNameSplitted.empty())
+ return DeclName;
+
+ if (NsNameSplitted.front() != DeclNsSplitted.front()) {
+ // The DeclName must be fully-qualified, but we still need to decide if a
+ // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the
+ // `DeclName` is "b::X", then the reference must be qualified as "::b::X"
+ // to avoid conflict.
+ if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front()))
+ return ("::" + DeclName).str();
+ return DeclName;
+ }
+ // Since there is already an overlap namespace, we know that `DeclName` can be
+ // shortened, so we reduce the longest common prefix.
+ auto DeclI = DeclNsSplitted.begin();
+ auto DeclE = DeclNsSplitted.end();
+ auto NsI = NsNameSplitted.begin();
+ auto NsE = NsNameSplitted.end();
+ for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) {
}
- return DeclName;
+ return (DeclI == DeclE)
+ ? UnqualifiedDeclName.str()
+ : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName)
+ .str();
}
std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {