147 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
///
 | 
						|
/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
 | 
						|
/// point.
 | 
						|
///
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
 | 
						|
#include "clang/AST/AST.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/AST/RecursiveASTVisitor.h"
 | 
						|
#include "clang/Basic/SourceManager.h"
 | 
						|
#include "clang/Index/USRGeneration.h"
 | 
						|
#include "clang/Lex/Lexer.h"
 | 
						|
#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
 | 
						|
#include "llvm/ADT/SmallVector.h"
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace tooling {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
/// Recursively visits each AST node to find the symbol underneath the cursor.
 | 
						|
class NamedDeclOccurrenceFindingVisitor
 | 
						|
    : public RecursiveSymbolVisitor<NamedDeclOccurrenceFindingVisitor> {
 | 
						|
public:
 | 
						|
  // Finds the NamedDecl at a point in the source.
 | 
						|
  // \param Point the location in the source to search for the NamedDecl.
 | 
						|
  explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,
 | 
						|
                                             const ASTContext &Context)
 | 
						|
      : RecursiveSymbolVisitor(Context.getSourceManager(),
 | 
						|
                               Context.getLangOpts()),
 | 
						|
        Point(Point), Context(Context) {}
 | 
						|
 | 
						|
  bool visitSymbolOccurrence(const NamedDecl *ND,
 | 
						|
                             ArrayRef<SourceRange> NameRanges) {
 | 
						|
    if (!ND)
 | 
						|
      return true;
 | 
						|
    for (const auto &Range : NameRanges) {
 | 
						|
      SourceLocation Start = Range.getBegin();
 | 
						|
      SourceLocation End = Range.getEnd();
 | 
						|
      if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
 | 
						|
          !End.isFileID() || !isPointWithin(Start, End))
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    Result = ND;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  const NamedDecl *getNamedDecl() const { return Result; }
 | 
						|
 | 
						|
private:
 | 
						|
  // Determines if the Point is within Start and End.
 | 
						|
  bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
 | 
						|
    // FIXME: Add tests for Point == End.
 | 
						|
    return Point == Start || Point == End ||
 | 
						|
           (Context.getSourceManager().isBeforeInTranslationUnit(Start,
 | 
						|
                                                                 Point) &&
 | 
						|
            Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
 | 
						|
  }
 | 
						|
 | 
						|
  const NamedDecl *Result = nullptr;
 | 
						|
  const SourceLocation Point; // The location to find the NamedDecl.
 | 
						|
  const ASTContext &Context;
 | 
						|
};
 | 
						|
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
 | 
						|
                                const SourceLocation Point) {
 | 
						|
  const SourceManager &SM = Context.getSourceManager();
 | 
						|
  NamedDeclOccurrenceFindingVisitor Visitor(Point, Context);
 | 
						|
 | 
						|
  // Try to be clever about pruning down the number of top-level declarations we
 | 
						|
  // see. If both start and end is either before or after the point we're
 | 
						|
  // looking for the point cannot be inside of this decl. Don't even look at it.
 | 
						|
  for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
 | 
						|
    SourceLocation StartLoc = CurrDecl->getBeginLoc();
 | 
						|
    SourceLocation EndLoc = CurrDecl->getEndLoc();
 | 
						|
    if (StartLoc.isValid() && EndLoc.isValid() &&
 | 
						|
        SM.isBeforeInTranslationUnit(StartLoc, Point) !=
 | 
						|
            SM.isBeforeInTranslationUnit(EndLoc, Point))
 | 
						|
      Visitor.TraverseDecl(CurrDecl);
 | 
						|
  }
 | 
						|
 | 
						|
  return Visitor.getNamedDecl();
 | 
						|
}
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
/// Recursively visits each NamedDecl node to find the declaration with a
 | 
						|
/// specific name.
 | 
						|
class NamedDeclFindingVisitor
 | 
						|
    : public RecursiveASTVisitor<NamedDeclFindingVisitor> {
 | 
						|
public:
 | 
						|
  explicit NamedDeclFindingVisitor(StringRef Name) : Name(Name) {}
 | 
						|
 | 
						|
  // We don't have to traverse the uses to find some declaration with a
 | 
						|
  // specific name, so just visit the named declarations.
 | 
						|
  bool VisitNamedDecl(const NamedDecl *ND) {
 | 
						|
    if (!ND)
 | 
						|
      return true;
 | 
						|
    // Fully qualified name is used to find the declaration.
 | 
						|
    if (Name != ND->getQualifiedNameAsString() &&
 | 
						|
        Name != "::" + ND->getQualifiedNameAsString())
 | 
						|
      return true;
 | 
						|
    Result = ND;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  const NamedDecl *getNamedDecl() const { return Result; }
 | 
						|
 | 
						|
private:
 | 
						|
  const NamedDecl *Result = nullptr;
 | 
						|
  StringRef Name;
 | 
						|
};
 | 
						|
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
 | 
						|
                                 const std::string &Name) {
 | 
						|
  NamedDeclFindingVisitor Visitor(Name);
 | 
						|
  Visitor.TraverseDecl(Context.getTranslationUnitDecl());
 | 
						|
  return Visitor.getNamedDecl();
 | 
						|
}
 | 
						|
 | 
						|
std::string getUSRForDecl(const Decl *Decl) {
 | 
						|
  llvm::SmallVector<char, 128> Buff;
 | 
						|
 | 
						|
  // FIXME: Add test for the nullptr case.
 | 
						|
  if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
 | 
						|
    return "";
 | 
						|
 | 
						|
  return std::string(Buff.data(), Buff.size());
 | 
						|
}
 | 
						|
 | 
						|
} // end namespace tooling
 | 
						|
} // end namespace clang
 |