On Jun 25, 2009, at 11:22 AM, Argiris Kirtzidis wrote: > Author: akirtzidis > Date: Thu Jun 25 13:22:41 2009 > New Revision: 74197 > > URL: http://llvm.org/viewvc/llvm-project?rev=74197&view=rev > Log: > Introduce the ResolveLocationInAST function which takes an > ASTContext and a SourceLocation and it resolves it into a <Decl*, > Stmt*> pair. > > Decl* is the declaration associated with this source location and > Stmt* is the statement/expression that the location points to. > If the location does not point to a statement node, Stmt* is null. > > ResolveLocationInAST (along with converting a file:line:column > triplet to a SourceLocation) will be useful for an IDE client and > for clang's test suite.
Cool! A few comments below (but not many). > Added: > cfe/trunk/lib/Frontend/ResolveLocation.cpp > Modified: > cfe/trunk/include/clang/Frontend/Utils.h > cfe/trunk/lib/Frontend/CMakeLists.txt > > Modified: cfe/trunk/include/clang/Frontend/Utils.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/Utils.h?rev=74197&r1=74196&r2=74197&view=diff > > = > = > = > = > = > = > = > = > ====================================================================== > --- cfe/trunk/include/clang/Frontend/Utils.h (original) > +++ cfe/trunk/include/clang/Frontend/Utils.h Thu Jun 25 13:22:41 2009 > @@ -32,6 +32,10 @@ > class SourceManager; > class PreprocessorFactory; > class LangOptions; > +class Decl; > +class Stmt; > +class ASTContext; > +class SourceLocation; > > /// ProcessWarningOptions - Initialize the diagnostic client and > process the > /// warning options specified on the command line. > @@ -74,6 +78,33 @@ > /// a seekable stream. > void CacheTokens(Preprocessor& PP, llvm::raw_fd_ostream* OS); > > +/// \brief Returns the AST node that a source location points to. > +/// > +/// Returns a pair of Decl* and Stmt*. If no AST node is found for > the source > +/// location, the pair will contain null pointers. > +/// > +/// If the source location points to just a declaration, the > statement part of > +/// the pair will be null, e.g., > +/// @code > +/// int foo; > +/// @endcode > +/// If the source location points at 'foo', the pair will contain > the VarDecl > +/// of foo and a null Stmt. > +/// > +/// If the source location points to a statement node, the returned > declaration > +/// will be the immediate 'parent' declaration of the statement > node, e.g., > +/// @code > +/// void f() { > +/// int foo = 100; > +/// ++foo; > +/// } > +/// @endcode > +/// Pointing at '100' will return a <VarDecl 'foo', IntegerLiteral > '100'> pair. > +/// Pointing at '++foo' will return a <FunctionDecl 'f', > UnaryOperator> pair. > +/// > +std::pair<Decl *, Stmt *> ResolveLocationInAST(ASTContext &Ctx, > + SourceLocation Loc); > + > } // end namespace clang > > #endif > > Modified: cfe/trunk/lib/Frontend/CMakeLists.txt > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CMakeLists.txt?rev=74197&r1=74196&r2=74197&view=diff > > = > = > = > = > = > = > = > = > ====================================================================== > --- cfe/trunk/lib/Frontend/CMakeLists.txt (original) > +++ cfe/trunk/lib/Frontend/CMakeLists.txt Thu Jun 25 13:22:41 2009 > @@ -26,6 +26,7 @@ > PlistDiagnostics.cpp > PrintParserCallbacks.cpp > PrintPreprocessedOutput.cpp > + ResolveLocation.cpp > RewriteBlocks.cpp > RewriteMacros.cpp > RewriteObjC.cpp > > Added: cfe/trunk/lib/Frontend/ResolveLocation.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ResolveLocation.cpp?rev=74197&view=auto > > = > = > = > = > = > = > = > = > ====================================================================== > --- cfe/trunk/lib/Frontend/ResolveLocation.cpp (added) > +++ cfe/trunk/lib/Frontend/ResolveLocation.cpp Thu Jun 25 13:22:41 > 2009 > @@ -0,0 +1,322 @@ > +//===--- 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 <Decl *, Stmt *> 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; } > + > + 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<StmtLocResolver> { > +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<DeclLocResolver> { > +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; > + } > +} Okay, so the scheme here is: check whether the source location we're looking for is within the range of the current node. If so, recurse to find which sub-node it is in. I like that, although the CheckRange/ FoundIt/Finished trio feels more complicated that it needs to be, because there's a lot of state encoded in Dcl/Stm/PassedLoc. Why not just have your Visit functions return std::pair<Decl*, Stmt*> (or something like it?). > +void DeclLocResolver::VisitDeclContext(DeclContext *DC) { > + DeclLocResolver DLR(Ctx, Loc); > + for (DeclContext::decl_iterator > + I = DC->decls_begin(Ctx), E = DC->decls_end(Ctx); 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; > + } > + } Okay, looks good. > + // 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(Ctx)); > + 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, Ctx); > + 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); > + 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<Decl *, Stmt *> > +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(); > +} > > > _______________________________________________ > cfe-commits mailing list > [email protected] > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
