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

- Update to reflect changes in parent revision


Repository:
  rCTE Clang Tools Extra

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

https://reviews.llvm.org/D56610

Files:
  clangd/SourceCode.cpp
  clangd/SourceCode.h
  clangd/refactor/tweaks/CMakeLists.txt
  clangd/refactor/tweaks/QualifyName.cpp
  clangd/refactor/tweaks/QualifyName.h

Index: clangd/refactor/tweaks/QualifyName.h
===================================================================
--- /dev/null
+++ clangd/refactor/tweaks/QualifyName.h
@@ -0,0 +1,42 @@
+//===--- QualifyName.h -------------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Fully qualifies a name under a cursor.
+// Before:
+//   using namespace std;
+//   ^vector<int> foo;
+// After:
+//   std::vector<int> foo;
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_TWEAKS_QUALIFYNAME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_TWEAKS_QUALIFYNAME_H
+
+#include "refactor/Tweak.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace clangd {
+
+class QualifyName : public Tweak {
+public:
+  TweakID id() const override { return llvm::StringLiteral("qualify-name"); }
+
+  bool prepare(const Selection &Inputs) override;
+  Expected<tooling::Replacements> apply(const Selection &Inputs) override;
+  std::string title() const override;
+
+private:
+  SourceLocation InsertLoc;
+  std::string Qualifier;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: clangd/refactor/tweaks/QualifyName.cpp
===================================================================
--- /dev/null
+++ clangd/refactor/tweaks/QualifyName.cpp
@@ -0,0 +1,163 @@
+//===--- QualifyName.cpp -----------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "QualifyName.h"
+#include "AST.h"
+#include "ClangdUnit.h"
+#include "SourceCode.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+
+REGISTER_TWEAK(QualifyName);
+
+namespace {
+struct Reference {
+  SourceLocation Begin;
+  NamedDecl *Target = nullptr;
+
+  operator bool() const { return Target != nullptr; }
+};
+
+NamedDecl *toReferencedDecl(NestedNameSpecifier *NNS) {
+  switch (NNS->getKind()) {
+  case clang::NestedNameSpecifier::Namespace:
+    return NNS->getAsNamespace();
+  case clang::NestedNameSpecifier::NamespaceAlias:
+    return NNS->getAsNamespaceAlias();
+  case clang::NestedNameSpecifier::TypeSpec:
+  case clang::NestedNameSpecifier::TypeSpecWithTemplate:
+    return nullptr; // FIXME: handle this situation, retrieve the thing
+                    // referenced inside a type.
+  case clang::NestedNameSpecifier::Identifier:
+  case clang::NestedNameSpecifier::Super:
+  case clang::NestedNameSpecifier::Global: // FIXME: could return
+                                           // TranslationUnitDecl.
+    return nullptr;
+    return nullptr;
+  }
+  llvm_unreachable("unhandled NestedNameSpecifier kind.");
+}
+
+class LocateInsertLoc : public RecursiveASTVisitor<LocateInsertLoc> {
+public:
+  LocateInsertLoc(ASTContext &Ctx, SourceLocation CursorLoc,
+                  Reference &UnqualRef)
+      : Ctx(Ctx), CursorLoc(CursorLoc), UnqualRef(UnqualRef) {}
+
+  bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+  // FIXME: RAT does not have VisitNestedNameSpecifierLoc. Should we add that?
+  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+    if (!RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(NNS))
+      return false;
+    return VisitNestedNameSpecifierLoc(NNS);
+  }
+
+  bool VisitNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+    if (NNS.getPrefix())
+      return true; // we want only unqualified names.
+    auto &SM = Ctx.getSourceManager();
+    auto Rng = toHalfOpenFileRange(SM, Ctx.getLangOpts(), NNS.getSourceRange());
+    if (!Rng)
+      return true;
+    if (!halfOpenRangeContains(SM, *Rng, CursorLoc))
+      return true;
+    auto *Target = toReferencedDecl(NNS.getNestedNameSpecifier());
+    if (!Target)
+      return true; // continue traversal to recurse into types, if any.
+    UnqualRef.Begin = Rng->getBegin();
+    UnqualRef.Target = Target;
+    return false; // we found the insertion point.
+  }
+
+  bool VisitDeclRefExpr(DeclRefExpr *E) {
+    if (E->hasQualifier())
+      return true; // we want only unqualified names.
+    auto &SM = Ctx.getSourceManager();
+    auto Rng = toHalfOpenFileRange(SM, Ctx.getLangOpts(), E->getSourceRange());
+    if (!Rng)
+      return true;
+    if (!halfOpenRangeContains(SM, *Rng, CursorLoc))
+      return true;
+    UnqualRef.Begin = Rng->getBegin();
+    UnqualRef.Target = E->getFoundDecl();
+    return false;
+  }
+
+  bool VisitTagTypeLoc(TagTypeLoc Loc) {
+    auto &SM = Ctx.getSourceManager();
+    auto Rng = toHalfOpenFileRange(SM, Ctx.getLangOpts(), Loc.getSourceRange());
+    if (!Rng)
+      return true;
+    if (!halfOpenRangeContains(SM, *Rng, CursorLoc))
+      return true;
+    UnqualRef.Begin = Rng->getBegin();
+    UnqualRef.Target = Loc.getDecl();
+    return false;
+  }
+
+  bool VisitTypedefTypeLoc(TypedefTypeLoc Loc) {
+    auto &SM = Ctx.getSourceManager();
+    auto Rng = toHalfOpenFileRange(SM, Ctx.getLangOpts(), Loc.getSourceRange());
+    if (!Rng)
+      return true;
+    if (!halfOpenRangeContains(SM, *Rng, CursorLoc))
+      return true;
+    UnqualRef.Begin = Rng->getBegin();
+    UnqualRef.Target = Loc.getTypedefNameDecl();
+    return false;
+  }
+
+private:
+  ASTContext &Ctx;
+  SourceLocation CursorLoc;
+  Reference &UnqualRef;
+};
+} // namespace
+
+bool QualifyName::prepare(const Selection &Inputs) {
+  auto &Ctx = Inputs.AST.getASTContext();
+  Reference Ref;
+  LocateInsertLoc(Ctx, Inputs.Cursor, Ref).TraverseAST(Ctx);
+  if (!Ref)
+    return false;
+  auto *Parent = Ref.Target->getDeclContext();
+  if (!Parent->isNamespace())
+    return false; // avoid complicated cases, qualify only with namespaces.
+  Qualifier = printNamespaceScope(*Parent);
+  if (Qualifier.empty())
+    return false; // happens for decls from global namespaces.
+  InsertLoc = Ref.Begin;
+  return true;
+}
+
+Expected<tooling::Replacements> QualifyName::apply(const Selection &Inputs) {
+  auto &Ctx = Inputs.AST.getASTContext();
+
+  tooling::Replacements R;
+  if (auto Err = R.add(tooling::Replacement(Ctx.getSourceManager(), InsertLoc,
+                                            0, Qualifier))) {
+    llvm::consumeError(std::move(Err));
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "building a replacement failed");
+  }
+  return R;
+}
+
+std::string QualifyName::title() const {
+  return llvm::formatv("Add '{0}' qualifier", Qualifier);
+}
+
+} // namespace clangd
+} // namespace clang
Index: clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clangd/refactor/tweaks/CMakeLists.txt
+++ clangd/refactor/tweaks/CMakeLists.txt
@@ -8,4 +8,5 @@
 # $<TARGET_OBJECTS:obj.clangDaemonTweaks> to a list of sources, see
 # clangd/tool/CMakeLists.txt for an example.
 add_clang_library(clangDaemonTweaks OBJECT
+  QualifyName.cpp
   )
Index: clangd/SourceCode.h
===================================================================
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H
 #include "Protocol.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Tooling/Core/Replacement.h"
@@ -60,6 +61,30 @@
 llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,
                                                         Position P);
 
+/// Turns a token range into a half-open range and checks its correctness.
+/// The resulting range will have only valid source location on both sides, both
+/// of which are file locations.
+llvm::Optional<SourceRange> toHalfOpenFileRange(const SourceManager &Mgr,
+                                                const LangOptions &LangOpts,
+                                                SourceRange R);
+
+/// Returns true iff all of the following conditions hold:
+///   - start and end locations are valid,
+///   - start and end locations are file locations from the same file
+///     (i.e. expansion locations are not taken into account).
+///   - start offset <= end offset.
+/// FIXME: introduce a type for source range with this invariant.
+bool isValidFileRange(const SourceManager &Mgr, SourceRange R);
+
+/// Returns true iff \p L is contained in \p R.
+/// Preconditions: isValidFileRange(R) == true, L is a file location.
+bool halfOpenRangeContains(const SourceManager &Mgr, SourceRange R,
+                           SourceLocation L);
+
+/// Returns the source code covered by the source range.
+/// Preconditions: isValidRange(R) == true.
+llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R);
+
 // Converts a half-open clang source range to an LSP range.
 // Note that clang also uses closed source ranges, which this can't handle!
 Range halfOpenToRange(const SourceManager &SM, CharSourceRange R);
Index: clangd/SourceCode.cpp
===================================================================
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -12,6 +12,8 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/Path.h"
@@ -142,6 +144,64 @@
   return P;
 }
 
+bool isValidFileRange(const SourceManager &Mgr, SourceRange R) {
+  if (!R.getBegin().isValid() || !R.getEnd().isValid())
+    return false;
+
+  FileID BeginFID;
+  size_t BeginOffset = 0;
+  std::tie(BeginFID, BeginOffset) = Mgr.getDecomposedLoc(R.getBegin());
+
+  FileID EndFID;
+  size_t EndOffset = 0;
+  std::tie(EndFID, EndOffset) = Mgr.getDecomposedLoc(R.getEnd());
+
+  return BeginFID.isValid() && BeginFID == EndFID && BeginOffset <= EndOffset;
+}
+
+bool halfOpenRangeContains(const SourceManager &Mgr, SourceRange R,
+                           SourceLocation L) {
+  assert(isValidFileRange(Mgr, R));
+
+  FileID BeginFID;
+  size_t BeginOffset = 0;
+  std::tie(BeginFID, BeginOffset) = Mgr.getDecomposedLoc(R.getBegin());
+  size_t EndOffset = Mgr.getFileOffset(R.getEnd());
+
+  FileID LFid;
+  size_t LOffset;
+  std::tie(LFid, LOffset) = Mgr.getDecomposedLoc(L);
+  return BeginFID == LFid && BeginOffset <= LOffset && LOffset < EndOffset;
+}
+
+llvm::Optional<SourceRange> toHalfOpenFileRange(const SourceManager &Mgr,
+                                                const LangOptions &LangOpts,
+                                                SourceRange R) {
+  auto Begin = Mgr.getFileLoc(R.getBegin());
+  if (Begin.isInvalid())
+    return llvm::None;
+  auto End = Mgr.getFileLoc(R.getEnd());
+  if (End.isInvalid())
+    return llvm::None;
+  End = Lexer::getLocForEndOfToken(End, 0, Mgr, LangOpts);
+
+  SourceRange Result(Begin, End);
+  if (!isValidFileRange(Mgr, Result))
+    return llvm::None;
+  return Result;
+}
+
+llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R) {
+  assert(isValidFileRange(SM, R));
+  bool Invalid = false;
+  auto *Buf = SM.getBuffer(SM.getFileID(R.getBegin()), &Invalid);
+  assert(!Invalid);
+
+  size_t BeginOffset = SM.getFileOffset(R.getBegin());
+  size_t EndOffset = SM.getFileOffset(R.getEnd());
+  return Buf->getBuffer().substr(BeginOffset, EndOffset - BeginOffset);
+}
+
 llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,
                                                         Position P) {
   llvm::StringRef Code = SM.getBuffer(SM.getMainFileID())->getBuffer();
@@ -170,8 +230,7 @@
   return {Lines + 1, Offset - StartOfLine + 1};
 }
 
-std::pair<llvm::StringRef, llvm::StringRef>
-splitQualifiedName(llvm::StringRef QName) {
+std::pair<StringRef, StringRef> splitQualifiedName(StringRef QName) {
   size_t Pos = QName.rfind("::");
   if (Pos == llvm::StringRef::npos)
     return {llvm::StringRef(), QName};
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to