//===--- ResolveLocation.cpp - Source location resolver ---------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This defines the ResolveLocationInAST function, which resolves a // source location into a pair. // //===----------------------------------------------------------------------===// #include "clang/Frontend/Utils.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/Compiler.h" using namespace clang; namespace { /// \brief Base for the LocResolver classes. Mostly does source range checking. class VISIBILITY_HIDDEN LocResolverBase { protected: ASTContext &Ctx; SourceLocation Loc; Decl *Dcl; Stmt *Stm; bool PassedLoc; /// \brief Checks whether Loc is in the source range of 'D'. /// /// If it is, updates Dcl. If Loc is passed the source range, it sets /// PassedLoc, otherwise it does nothing. void CheckRange(Decl *D); /// \brief Checks whether Loc is in the source range of 'Node'. /// /// If it is, updates Stm. If Loc is passed the source range, it sets /// PassedLoc, otherwise it does nothing. void CheckRange(Stmt *Node); /// \brief Updates the end source range to cover the full length of the token /// positioned at the end of the source range. /// /// e.g., /// @code /// int foo /// ^ ^ /// @endcode /// will be updated to /// @code /// int foo /// ^ ^ /// @endcode void FixRange(SourceRange &Range); public: LocResolverBase(ASTContext &ctx, SourceLocation loc) : Ctx(ctx), Loc(loc), Dcl(0), Stm(0), PassedLoc(0) {} /// \brief We found a AST node that corresponds to the source location. bool FoundIt() const { return Dcl != 0 || Stm != 0; } /// \brief We either found a AST node or we passed the source location while /// searching. bool Finished() const { return FoundIt() || PassedLoc; } Decl *getDecl() const { return Dcl; } Stmt *getStmt() const { return Stm; } std::pair getResult() const { return std::make_pair(getDecl(), getStmt()); } /// \brief Debugging output. void print(Decl *D); /// \brief Debugging output. void print(Stmt *Node); }; /// \brief Searches a statement for the AST node that corresponds to a source /// location. class VISIBILITY_HIDDEN StmtLocResolver : public LocResolverBase, public StmtVisitor { public: StmtLocResolver(ASTContext &ctx, SourceLocation loc) : LocResolverBase(ctx, loc) {} void VisitDeclStmt(DeclStmt *Node); void VisitStmt(Stmt *Node); }; /// \brief Searches a declaration for the AST node that corresponds to a source /// location. class VISIBILITY_HIDDEN DeclLocResolver : public LocResolverBase, public DeclVisitor { public: DeclLocResolver(ASTContext &ctx, SourceLocation loc) : LocResolverBase(ctx, loc) {} void VisitDeclContext(DeclContext *DC); void VisitTranslationUnitDecl(TranslationUnitDecl *TU); void VisitVarDecl(VarDecl *D); void VisitFunctionDecl(FunctionDecl *D); void VisitDecl(Decl *D); }; } // anonymous namespace void StmtLocResolver::VisitDeclStmt(DeclStmt *Node) { CheckRange(Node); if (!FoundIt()) return; assert(Stm == Node && "Result not updated ?"); // Search all declarations of this DeclStmt. If we found the one corresponding // to the source location, update this StmtLocResolver's result. DeclLocResolver DLR(Ctx, Loc); for (DeclStmt::decl_iterator I = Node->decl_begin(), E = Node->decl_end(); I != E; ++I) { DLR.Visit(*I); if (DLR.Finished()) { if (DLR.FoundIt()) llvm::tie(Dcl, Stm) = DLR.getResult(); return; } } } void StmtLocResolver::VisitStmt(Stmt *Node) { CheckRange(Node); if (!FoundIt()) return; assert(Stm == Node && "Result not updated ?"); // Search the child statements. StmtLocResolver SLR(Ctx, Loc); for (Stmt::child_iterator I = Node->child_begin(), E = Node->child_end(); I != E; ++I) { SLR.Visit(*I); if (!SLR.Finished()) continue; // We either found it or we passed the source location. if (SLR.FoundIt()) { // Only update Dcl if we found another more immediate 'parent' Decl for // the statement. if (SLR.getDecl()) Dcl = SLR.getDecl(); Stm = SLR.getStmt(); } return; } } void DeclLocResolver::VisitDeclContext(DeclContext *DC) { DeclLocResolver DLR(Ctx, Loc); for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { DLR.Visit(*I); if (DLR.Finished()) { if (DLR.FoundIt()) llvm::tie(Dcl, Stm) = DLR.getResult(); return; } } } void DeclLocResolver::VisitTranslationUnitDecl(TranslationUnitDecl *TU) { VisitDeclContext(TU); } void DeclLocResolver::VisitFunctionDecl(FunctionDecl *D) { CheckRange(D); if (!FoundIt()) return; assert(Dcl == D && "Result not updated ?"); // First, search through the parameters of the function. DeclLocResolver ParmRes(Ctx, Loc); for (FunctionDecl::param_iterator I = D->param_begin(), E = D->param_end(); I != E; ++I) { ParmRes.Visit(*I); if (ParmRes.Finished()) { if (ParmRes.FoundIt()) llvm::tie(Dcl, Stm) = ParmRes.getResult(); return; } } // We didn't found the location in the parameters and we didn't get passed it. // Second, search through the declarations that are part of the function. // If we find he location there, we won't have to search through its body. DeclLocResolver DLR(Ctx, Loc); DLR.VisitDeclContext(D); if (DLR.FoundIt()) { llvm::tie(Dcl, Stm) = DLR.getResult(); return; } // We didn't find a declaration that corresponds to the source location. // Finally, search through the body of the function. if (D->isThisDeclarationADefinition()) { StmtLocResolver SLR(Ctx, Loc); SLR.Visit(D->getBody()); if (SLR.FoundIt()) { llvm::tie(Dcl, Stm) = SLR.getResult(); // If we didn't find a more immediate 'parent' declaration for the // statement, set the function as the parent. if (Dcl == 0) Dcl = D; } } } void DeclLocResolver::VisitVarDecl(VarDecl *D) { CheckRange(D); if (!FoundIt()) return; assert(Dcl == D && "Result not updated ?"); // Check whether the location points to the init expression. if (D->getInit()) { StmtLocResolver SLR(Ctx, Loc); SLR.Visit(D->getInit()); Stm = SLR.getStmt(); } } void DeclLocResolver::VisitDecl(Decl *D) { CheckRange(D); } void LocResolverBase::CheckRange(Decl *D) { SourceRange Range = D->getSourceRange(); if (!Range.isValid()) return; FixRange(Range); SourceManager &SourceMgr = Ctx.getSourceManager(); if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc)) return; if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin())) PassedLoc = true; else Dcl = D; } void LocResolverBase::CheckRange(Stmt *Node) { SourceRange Range = Node->getSourceRange(); if (!Range.isValid()) return; FixRange(Range); SourceManager &SourceMgr = Ctx.getSourceManager(); if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc)) return; if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin())) PassedLoc = true; else Stm = Node; } void LocResolverBase::FixRange(SourceRange &Range) { if (!Range.isValid()) return; unsigned TokSize = Lexer::MeasureTokenLength(Range.getEnd(), Ctx.getSourceManager(), Ctx.getLangOptions()); Range.setEnd(Range.getEnd().getFileLocWithOffset(TokSize-1)); } void LocResolverBase::print(Decl *D) { llvm::raw_ostream &OS = llvm::outs(); OS << "#### DECL ####\n"; D->print(OS); OS << " <"; D->getLocStart().print(OS, Ctx.getSourceManager()); OS << " > - <"; D->getLocEnd().print(OS, Ctx.getSourceManager()); OS << ">\n\n"; OS.flush(); } void LocResolverBase::print(Stmt *Node) { llvm::raw_ostream &OS = llvm::outs(); OS << "#### STMT ####\n"; Node->printPretty(OS, Ctx, 0, PrintingPolicy(Ctx.getLangOptions())); OS << " <"; Node->getLocStart().print(OS, Ctx.getSourceManager()); OS << " > - <"; Node->getLocEnd().print(OS, Ctx.getSourceManager()); OS << ">\n\n"; OS.flush(); } /// \brief Returns the AST node that a source location points to. /// std::pair clang::ResolveLocationInAST(ASTContext &Ctx, SourceLocation Loc) { if (Loc.isInvalid()) return std::make_pair((Decl*)0, (Stmt*)0); DeclLocResolver DLR(Ctx, Loc); DLR.Visit(Ctx.getTranslationUnitDecl()); return DLR.getResult(); }