ilya-biryukov created this revision.
ilya-biryukov added a reviewer: kadircet.
Herald added subscribers: usaxena95, arphaman, jkorous, MaskRay.
Herald added a project: clang.

Allows to simplify pending code tweaks:

- the upcoming DefineInline tweak (D66647 <https://reviews.llvm.org/D66647>)
- remove using declaration (D56612 <https://reviews.llvm.org/D56612>)
- qualify name under cursor (D56610 <https://reviews.llvm.org/D56610>)

Another potential future application is simplifying semantic highlighting.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D67826

Files:
  clang-tools-extra/clangd/FindTarget.cpp
  clang-tools-extra/clangd/FindTarget.h

Index: clang-tools-extra/clangd/FindTarget.h
===================================================================
--- clang-tools-extra/clangd/FindTarget.h
+++ clang-tools-extra/clangd/FindTarget.h
@@ -20,8 +20,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/Stmt.h"
 #include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
 
 #include <bitset>
 
@@ -69,6 +74,47 @@
 llvm::SmallVector<const Decl *, 1>
 targetDecl(const ast_type_traits::DynTypedNode &, DeclRelationSet Mask);
 
+/// Information about a reference written in the source code, independent of the
+/// actual AST node that this reference lives in.
+/// Useful for tools that are source-aware, e.g. refactorings.
+struct ReferenceLoc {
+  /// Contains qualifier written in the code, if any, e.g. 'ns::' for 'ns::foo'.
+  NestedNameSpecifierLoc Qualifier;
+  /// Start location of the last name part, i.e. 'foo' in 'ns::foo<int>'.
+  SourceLocation NameLoc;
+  // FIXME: add info about template arguments.
+  /// A list of targets referenced by this name. Normally this has a single
+  /// element, but multiple is also possible, e.g. in case of using declarations
+  /// or unresolved overloaded functions.
+  /// For dependent and unresolved references, Targets can also be empty.
+  llvm::SmallVector<const NamedDecl *, 1> Targets;
+  /// If reference comes from a MemberExpr, the LHS of the reference, e.g. 'obj'
+  /// in 'obj.foo'.
+  const Expr* MemberExprBase = nullptr;
+};
+
+/// Obtain information about a reference in \p N, if any. Note that any of the
+/// fields in the returned structure can be empty, but not all of them, e.g.
+///   - for implicitly generated nodes (e.g. MemberExpr from range-based-for),
+///     source location information may be missing,
+///   - for dependent code, targets may be empty.
+///
+/// (!) For the purposes of this function declarations are not considered to be
+///     references. However, declarations can have:wa references inside them,
+///     e.g. 'namespace foo = std' references namespace 'std' and this function
+///     will return the corresponding reference.
+llvm::Optional<ReferenceLoc>
+explicitReference(ast_type_traits::DynTypedNode N);
+
+/// Recursively traverse \p S and report all references explicitly written in
+/// the code. The main use-case is refactorings that need to process all
+/// references in some subrange of the file and apply simple edits, e.g. add
+/// qualifiers.
+/// FIXME: currently this does not report references to overloaded operators.
+/// FIXME: extend to report location information about declaration names too.
+void findExplicitReferences(Stmt *S,
+                            llvm::function_ref<void(ReferenceLoc)> Out);
+
 /// Similar to targetDecl(), however instead of applying a filter, all possible
 /// decls are returned along with their DeclRelationSets.
 /// This is suitable for indexing, where everything is recorded and filtering
Index: clang-tools-extra/clangd/FindTarget.cpp
===================================================================
--- clang-tools-extra/clangd/FindTarget.cpp
+++ clang-tools-extra/clangd/FindTarget.cpp
@@ -10,22 +10,31 @@
 #include "AST.h"
 #include "Logger.h"
 #include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
 #include "clang/AST/DeclarationName.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprObjC.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
 #include "clang/AST/TypeLocVisitor.h"
 #include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/raw_ostream.h"
+#include <utility>
 
 namespace clang {
 namespace clangd {
 namespace {
+using ast_type_traits::DynTypedNode;
 
 LLVM_ATTRIBUTE_UNUSED std::string
 nodeToString(const ast_type_traits::DynTypedNode &N) {
@@ -348,12 +357,246 @@
 llvm::SmallVector<const Decl *, 1>
 targetDecl(const ast_type_traits::DynTypedNode &N, DeclRelationSet Mask) {
   llvm::SmallVector<const Decl *, 1> Result;
-  for (const auto &Entry : allTargetDecls(N))
+  for (const auto &Entry : allTargetDecls(N)) {
     if (!(Entry.second & ~Mask))
       Result.push_back(Entry.first);
+  }
   return Result;
 }
 
+namespace {
+llvm::SmallVector<const NamedDecl *, 1>
+explicitReferenceTargets(ast_type_traits::DynTypedNode N,
+                         DeclRelationSet Mask = {}) {
+  assert(!(Mask & (DeclRelation::TemplatePattern |
+                   DeclRelation::TemplateInstantiation)) &&
+         "namedTarget handles templates on its own");
+  auto Decls = allTargetDecls(N);
+
+  // We prefer to return template instantiation, but fallback to template
+  // pattern if instantiation is not available.
+  Mask |= DeclRelation::TemplatePattern | DeclRelation::TemplateInstantiation;
+  bool SeenTemplateInstantiation =
+      llvm::find_if(Decls, [&](std::pair<const Decl *, DeclRelationSet> D) {
+        if (D.second & ~Mask)
+          return false;
+        return static_cast<bool>(D.second &
+                                 DeclRelation::TemplateInstantiation);
+      }) != Decls.end();
+  if (SeenTemplateInstantiation)
+    Mask &= ~DeclRelation::TemplatePattern;
+
+  llvm::SmallVector<const NamedDecl *, 1> Named;
+  for (auto &D : Decls) {
+    if (D.second & ~Mask)
+      continue;
+    Named.push_back(llvm::cast<NamedDecl>(D.first));
+  }
+  return Named;
+}
+
+Optional<ReferenceLoc> refInDecl(const Decl *D) {
+  struct Visitor : ConstDeclVisitor<Visitor> {
+    llvm::Optional<ReferenceLoc> Ref;
+
+    void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
+      Ref = ReferenceLoc{D->getQualifierLoc(),
+                         D->getIdentLocation(),
+                         {D->getNominatedNamespaceAsWritten()}};
+    }
+
+    void VisitUsingDecl(const UsingDecl *D) {
+      Ref = ReferenceLoc{D->getQualifierLoc(), D->getUsingLoc(),
+                         explicitReferenceTargets(DynTypedNode::create(*D),
+                                                  DeclRelation::Underlying)};
+    }
+
+    void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) {
+      Ref = ReferenceLoc{D->getQualifierLoc(),
+                         D->getTargetNameLoc(),
+                         {D->getAliasedNamespace()}};
+    }
+  };
+
+  Visitor V;
+  V.Visit(D);
+  return V.Ref;
+}
+
+Optional<ReferenceLoc> refInExpr(const Expr *E) {
+  struct Visitor : ConstStmtVisitor<Visitor> {
+    llvm::Optional<ReferenceLoc> Ref;
+
+    void VisitDeclRefExpr(const DeclRefExpr *E) {
+      Ref = ReferenceLoc{
+          E->getQualifierLoc(), E->getNameInfo().getLoc(), {E->getDecl()}};
+    }
+
+    void VisitMemberExpr(const MemberExpr *E) {
+      Ref = ReferenceLoc{E->getQualifierLoc(),
+                         E->getMemberNameInfo().getLoc(),
+                         {E->getMemberDecl()},
+                         E->getBase()};
+    }
+  };
+
+  Visitor V;
+  V.Visit(E);
+  return V.Ref;
+}
+
+Optional<ReferenceLoc> refInTypeLoc(TypeLoc L) {
+  struct Visitor : TypeLocVisitor<Visitor> {
+    llvm::Optional<ReferenceLoc> Ref;
+
+    void VisitElaboratedTypeLoc(ElaboratedTypeLoc L) {
+      // We only know about qualifier, rest if filled by inner locations.
+      Visit(L.getNamedTypeLoc().getUnqualifiedLoc());
+      // Fill in the qualifier.
+      if (!Ref)
+        return;
+      assert(!Ref->Qualifier.hasQualifier() && "qualifier already set");
+      Ref->Qualifier = L.getQualifierLoc();
+    }
+
+    void VisitDeducedTemplateSpecializationTypeLoc(
+        DeducedTemplateSpecializationTypeLoc L) {
+      Ref = ReferenceLoc{
+          NestedNameSpecifierLoc(), L.getNameLoc(),
+          explicitReferenceTargets(DynTypedNode::create(L.getType()))};
+    }
+
+    void VisitTagTypeLoc(TagTypeLoc L) {
+      Ref =
+          ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), {L.getDecl()}};
+    }
+
+    void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc L) {
+      Ref = ReferenceLoc{
+          NestedNameSpecifierLoc(), L.getTemplateNameLoc(),
+          explicitReferenceTargets(DynTypedNode::create(L.getType()))};
+    }
+
+    void VisitDependentTemplateSpecializationTypeLoc(
+        DependentTemplateSpecializationTypeLoc L) {
+      Ref = ReferenceLoc{
+          L.getQualifierLoc(), L.getTemplateNameLoc(),
+          explicitReferenceTargets(DynTypedNode::create(L.getType()))};
+    }
+
+    void VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
+      Ref = ReferenceLoc{
+          L.getQualifierLoc(), L.getNameLoc(),
+          explicitReferenceTargets(DynTypedNode::create(L.getType()))};
+    }
+
+    void VisitTypedefTypeLoc(TypedefTypeLoc L) {
+      Ref = ReferenceLoc{
+          NestedNameSpecifierLoc(), L.getNameLoc(), {L.getTypedefNameDecl()}};
+    }
+  };
+
+  Visitor V;
+  V.Visit(L.getUnqualifiedLoc());
+  return V.Ref;
+}
+
+} // namespace
+
+llvm::Optional<ReferenceLoc>
+explicitReference(ast_type_traits::DynTypedNode N) {
+  if (auto *D = N.get<Decl>())
+    return refInDecl(D);
+  if (auto *E = N.get<Expr>())
+    return refInExpr(E);
+  if (auto *NNSL = N.get<NestedNameSpecifierLoc>())
+    return ReferenceLoc{NNSL->getPrefix(), NNSL->getLocalBeginLoc(),
+                        explicitReferenceTargets(DynTypedNode::create(
+                            *NNSL->getNestedNameSpecifier()))};
+  if (const TypeLoc *TL = N.get<TypeLoc>())
+    return refInTypeLoc(*TL);
+  if (const CXXCtorInitializer *CCI = N.get<CXXCtorInitializer>()) {
+    if (CCI->isBaseInitializer())
+      return refInTypeLoc(CCI->getBaseClassLoc());
+    assert(CCI->isAnyMemberInitializer());
+    return ReferenceLoc{NestedNameSpecifierLoc(),
+                        CCI->getMemberLocation(),
+                        {CCI->getAnyMember()}};
+  }
+  // Some nodes are lacking location information.
+  return llvm::None;
+}
+
+namespace {
+
+class ExplicitReferenceColletor
+    : public RecursiveASTVisitor<ExplicitReferenceColletor> {
+public:
+  ExplicitReferenceColletor(llvm::function_ref<void(ReferenceLoc)> Out)
+      : Out(Out) {
+    assert(Out);
+  }
+
+  bool VisitTypeLoc(TypeLoc TTL) {
+    if (TypeLocsToSkip.count(TTL.getBeginLoc().getRawEncoding()))
+      return true;
+    visitNode(DynTypedNode::create(TTL));
+    return true;
+  }
+
+  bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc L) {
+    // ElaboratedTypeLoc will reports information for its inner type loc.
+    // Otherwise we loose information about inner types loc's qualifier.
+    TypeLoc Inner = L.getNamedTypeLoc().getUnqualifiedLoc();
+    TypeLocsToSkip.insert(Inner.getBeginLoc().getRawEncoding());
+    return RecursiveASTVisitor::TraverseElaboratedTypeLoc(L);
+  }
+
+  bool VisitExpr(Expr *E) {
+    visitNode(DynTypedNode::create(*E));
+    return true;
+  }
+
+  bool VisitDecl(Decl *D) {
+    visitNode(DynTypedNode::create(*D));
+    return true;
+  }
+
+  // We have to use Traverse* because there is no corresponding Visit*.
+  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc L) {
+    if (!L.getNestedNameSpecifier())
+      return true;
+    visitNode(DynTypedNode::create(L));
+    // Inner type is missing information about its qualifier, skip it.
+    if (auto TL = L.getTypeLoc())
+      TypeLocsToSkip.insert(TL.getBeginLoc().getRawEncoding());
+    return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(L);
+  }
+
+private:
+  void visitNode(DynTypedNode N) {
+    auto Ref = explicitReference(N);
+    if (!Ref)
+      return;
+    // FIXME: this should be done by nodeReference.
+    if (Ref->NameLoc.isInvalid() || Ref->NameLoc.isMacroID())
+      return;
+    Out(*Ref);
+  }
+
+  llvm::function_ref<void(ReferenceLoc)> Out;
+  /// TypeLocs starting at these locations must be skipped, see
+  /// TraverseElaboratedTypeSpecifierLoc for details.
+  llvm::DenseSet</*SourceLocation*/ unsigned> TypeLocsToSkip;
+};
+} // namespace
+
+void findExplicitReferences(Stmt *S,
+                            llvm::function_ref<void(ReferenceLoc)> Out) {
+  assert(S);
+  ExplicitReferenceColletor(Out).TraverseStmt(S);
+}
+
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, DeclRelation R) {
   switch (R) {
 #define REL_CASE(X)                                                            \
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to