diff options
author | Eric Liu <ioeric@google.com> | 2017-01-26 15:08:44 +0000 |
---|---|---|
committer | Eric Liu <ioeric@google.com> | 2017-01-26 15:08:44 +0000 |
commit | ecff79ca7aeb425c5d1c7313844307707becbac1 (patch) | |
tree | 4edbb5a6917ef6ba3a6973b61ab925ccb3ec85fa /clang-tools-extra/change-namespace | |
parent | dff23fd75f4bcfe3a3e7af2c3caf01ba48c81776 (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.cpp | 44 |
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) { |