ilya-biryukov updated this revision to Diff 182322.
ilya-biryukov added a comment.

- Remove the header file


Repository:
  rCTE Clang Tools Extra

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56612/new/

https://reviews.llvm.org/D56612

Files:
  clangd/AST.cpp
  clangd/AST.h
  clangd/refactor/tweaks/CMakeLists.txt
  clangd/refactor/tweaks/RemoveUsingNamespace.cpp

Index: clangd/refactor/tweaks/RemoveUsingNamespace.cpp
===================================================================
--- /dev/null
+++ clangd/refactor/tweaks/RemoveUsingNamespace.cpp
@@ -0,0 +1,206 @@
+//===--- RemoveUsingNamespace.cpp --------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "AST.h"
+#include "ClangdUnit.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
+#include "llvm/ADT/ScopeExit.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Removes the 'using namespace' under the cursor and qualifies all accesses in
+/// the current file. E.g.,
+///   using namespace std;
+///   vector<int> foo(std::map<int, int>);
+/// Would become:
+///   std::vector<int> foo(std::map<int, int>);
+class RemoveUsingNamespace : public Tweak {
+public:
+  TweakID id() const override {
+    return llvm::StringLiteral("remove-using-namespace");
+  }
+
+  bool prepare(const Selection &Inputs) override;
+  Expected<tooling::Replacements> apply(const Selection &Inputs) override;
+  std::string title() const override;
+
+private:
+  UsingDirectiveDecl *TargetDirective = nullptr;
+};
+REGISTER_TWEAK(RemoveUsingNamespace);
+
+class FindNodeUnderCursor : public RecursiveASTVisitor<FindNodeUnderCursor> {
+public:
+  FindNodeUnderCursor(SourceLocation SearchedLoc, UsingDirectiveDecl *&Result)
+      : SearchedLoc(SearchedLoc), Result(Result) {}
+
+  bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
+    if (D->getUsingLoc() != SearchedLoc)
+      return true;
+
+    Result = D;
+    return false;
+  }
+
+private:
+  SourceLocation SearchedLoc;
+  UsingDirectiveDecl *&Result;
+};
+
+class FindSameUsings : public RecursiveASTVisitor<FindSameUsings> {
+public:
+  FindSameUsings(UsingDirectiveDecl &Target,
+                 std::vector<UsingDirectiveDecl *> &Results)
+      : TargetNS(Target.getNominatedNamespace()),
+        TargetCtx(Target.getDeclContext()), Results(Results) {}
+
+  bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
+    if (D->getNominatedNamespace() != TargetNS ||
+        D->getDeclContext() != TargetCtx)
+      return true;
+
+    Results.push_back(D);
+    return true;
+  }
+
+private:
+  NamespaceDecl *TargetNS;
+  DeclContext *TargetCtx;
+  std::vector<UsingDirectiveDecl *> &Results;
+};
+
+class FindIdentsToQualify
+    : public tooling::RecursiveSymbolVisitor<FindIdentsToQualify> {
+public:
+  FindIdentsToQualify(ASTContext &Ctx, NamespaceDecl &TargetNS,
+                      std::vector<SourceLocation> &ResultIdents)
+      : RecursiveSymbolVisitor(Ctx.getSourceManager(), Ctx.getLangOpts()),
+        Ctx(Ctx), TargetNS(TargetNS), ResultIdents(ResultIdents) {}
+
+  bool visitSymbolOccurrence(const NamedDecl *D,
+                             llvm::ArrayRef<SourceRange> NameRanges) {
+    if (!D || D->getCanonicalDecl() == TargetNS.getCanonicalDecl())
+      return true;
+    if (!D->getDeclName().isIdentifier() ||
+        D->getDeclName().getNameKind() == DeclarationName::CXXOperatorName)
+      return true; // do not add qualifiers for non-idents, e.g. 'operator+'.
+    // Check the symbol is unqualified and references something inside our
+    // namespace.
+    // FIXME: add a check it's unqualified.
+    if (!TargetNS.InEnclosingNamespaceSetOf(D->getDeclContext()))
+      return true;
+    // FIXME: handle more tricky cases, e.g. we don't need the qualifier if we
+    //        have the using decls for some entities, we might have qualified
+    //        references that need updating too.
+    for (auto R : NameRanges) {
+      if (!Ctx.getSourceManager().isWrittenInMainFile(R.getBegin()))
+        continue; // we can't fix refences outside our file.
+                  // FIXME: this might be a conflict that we need to report.
+      ResultIdents.push_back(R.getBegin());
+    }
+    return true;
+  }
+
+  bool TraverseDecl(Decl *D) {
+    if (!Ctx.getSourceManager().isWrittenInMainFile(D->getLocation()) &&
+        !isa<TranslationUnitDecl>(D))
+      return true; // skip decls outside main file.
+    return RecursiveSymbolVisitor::TraverseDecl(D);
+  }
+
+private:
+  ASTContext &Ctx;
+  NamespaceDecl &TargetNS;
+  std::vector<SourceLocation> &ResultIdents;
+};
+
+// Produces an edit to remove 'using namespace xxx::yyy' and the trailing
+// semicolon.
+llvm::Expected<tooling::Replacement>
+removeUsingDirective(ASTContext &Ctx, UsingDirectiveDecl *D) {
+  auto &SrcMgr = Ctx.getSourceManager();
+  auto R =
+      Lexer::getAsCharRange(D->getSourceRange(), SrcMgr, Ctx.getLangOpts());
+  if (R.isInvalid())
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "invalid source range for using "
+                                   "directive");
+  SourceLocation EndLoc = R.getEnd();
+  auto NextTok = Lexer::findNextToken(EndLoc, SrcMgr, Ctx.getLangOpts());
+  if (!NextTok || NextTok->getKind() != tok::semi)
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "no semicolon after using-directive");
+  R.setEnd(NextTok->getEndLoc());
+  // FIXME: removing the semicolon may be invalid in some obscure cases, e.g.
+  //        if (x) using namespace std; else using namespace bar;
+  return tooling::Replacement(SrcMgr, R, "", Ctx.getLangOpts());
+}
+} // namespace
+  // namespace
+
+bool RemoveUsingNamespace::prepare(const Selection &Inputs) {
+  auto &Ctx = Inputs.AST.getASTContext();
+  // Find the 'using namespace' directive under the cursor.
+  TargetDirective = nullptr;
+  FindNodeUnderCursor(Inputs.Cursor, TargetDirective).TraverseAST(Ctx);
+  if (!TargetDirective)
+    return false;
+  auto *Parent = dyn_cast<Decl>(TargetDirective->getDeclContext());
+  if (!Parent)
+    return false;
+  return true;
+}
+
+Expected<tooling::Replacements>
+RemoveUsingNamespace::apply(const Selection &Inputs) {
+  auto &Ctx = Inputs.AST.getASTContext();
+  // First, collect *all* using namespace directives that redeclare the same
+  // namespace.
+  std::vector<UsingDirectiveDecl *> AllDirectives;
+  FindSameUsings(*TargetDirective, AllDirectives).TraverseAST(Ctx);
+  // Collect all references to symbols from the namespace for which we're
+  // removing the directive.
+  std::vector<SourceLocation> IdentsToQualify;
+  FindIdentsToQualify(Ctx, *TargetDirective->getNominatedNamespace(),
+                      IdentsToQualify)
+      .TraverseAST(Ctx);
+  // Remove duplicates.
+  llvm::sort(IdentsToQualify);
+  IdentsToQualify.erase(
+      std::unique(IdentsToQualify.begin(), IdentsToQualify.end()),
+      IdentsToQualify.end());
+
+  // Produce replacements to remove the using directives.
+  tooling::Replacements R;
+  for (auto *D : AllDirectives) {
+    auto RemoveUsing = removeUsingDirective(Ctx, D);
+    if (!RemoveUsing)
+      return RemoveUsing.takeError();
+    if (auto Err = R.add(*RemoveUsing))
+      return std::move(Err);
+  }
+  // Produce replacements to add the qualifiers.
+  std::string Qualifier = printUsingNamespaceName(Ctx, *TargetDirective) + "::";
+  for (auto Loc : IdentsToQualify) {
+    if (auto Err = R.add(tooling::Replacement(Ctx.getSourceManager(), Loc,
+                                              /*Length=*/0, Qualifier)))
+      return std::move(Err);
+  }
+  return R;
+}
+
+std::string RemoveUsingNamespace::title() const {
+  return llvm::formatv("Remove using namespace, add qualifiers instead");
+}
+} // namespace clangd
+} // namespace clang
Index: clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clangd/refactor/tweaks/CMakeLists.txt
+++ clangd/refactor/tweaks/CMakeLists.txt
@@ -9,5 +9,6 @@
 # clangd/tool/CMakeLists.txt for an example.
 add_clang_library(clangDaemonTweaks OBJECT
   QualifyName.cpp
+  RemoveUsingNamespace.cpp
   SwapIfBranches.cpp
   )
Index: clangd/AST.h
===================================================================
--- clangd/AST.h
+++ clangd/AST.h
@@ -42,6 +42,12 @@
 /// Returns the first enclosing namespace scope starting from \p DC.
 std::string printNamespaceScope(const DeclContext &DC);
 
+/// Returns the name of the namespace inside the 'using namespace' directive, as
+/// written in the code. E.g., passing 'using namespace ::std' will result in
+/// '::std'.
+std::string printUsingNamespaceName(const ASTContext &Ctx,
+                                    const UsingDirectiveDecl &D);
+
 /// Prints unqualified name of the decl for the purpose of displaying it to the
 /// user. Anonymous decls return names of the form "(anonymous {kind})", e.g.
 /// "(anonymous struct)" or "(anonymous namespace)".
Index: clangd/AST.cpp
===================================================================
--- clangd/AST.cpp
+++ clangd/AST.cpp
@@ -77,16 +77,25 @@
   return nullptr;
 }
 
+std::string printUsingNamespaceName(const ASTContext &Ctx,
+                                    const UsingDirectiveDecl &D) {
+  PrintingPolicy PP(Ctx.getLangOpts());
+  std::string Name;
+  llvm::raw_string_ostream Out(Name);
+
+  if (auto *Qual = D.getQualifier())
+    Qual->print(Out, PP);
+  D.getNominatedNamespaceAsWritten()->printName(Out);
+  return Out.str();
+}
+
 std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
   std::string Name;
   llvm::raw_string_ostream Out(Name);
   PrintingPolicy PP(Ctx.getLangOpts());
   // Handle 'using namespace'. They all have the same name - <using-directive>.
   if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) {
-    Out << "using namespace ";
-    if (auto *Qual = UD->getQualifier())
-      Qual->print(Out, PP);
-    UD->getNominatedNamespaceAsWritten()->printName(Out);
+    Out << "using namespace " << printUsingNamespaceName(Ctx, *UD);
     return Out.str();
   }
   ND.getDeclName().print(Out, PP);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to