aboutsummaryrefslogtreecommitdiff
path: root/lib/Index/ResolveLocation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Index/ResolveLocation.cpp')
-rw-r--r--lib/Index/ResolveLocation.cpp505
1 files changed, 505 insertions, 0 deletions
diff --git a/lib/Index/ResolveLocation.cpp b/lib/Index/ResolveLocation.cpp
new file mode 100644
index 000000000000..229669dc330b
--- /dev/null
+++ b/lib/Index/ResolveLocation.cpp
@@ -0,0 +1,505 @@
+//===--- 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 ASTLocation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/Utils.h"
+#include "clang/Index/ASTLocation.h"
+#include "clang/AST/TypeLocVisitor.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;
+using namespace idx;
+
+namespace {
+
+/// \brief Base for the LocResolver classes. Mostly does source range checking.
+class VISIBILITY_HIDDEN LocResolverBase {
+protected:
+ ASTContext &Ctx;
+ SourceLocation Loc;
+
+ ASTLocation ResolveInDeclarator(Decl *D, Stmt *Stm, DeclaratorInfo *DInfo);
+
+ enum RangePos {
+ BeforeLoc,
+ ContainsLoc,
+ AfterLoc
+ };
+
+ RangePos CheckRange(SourceRange Range);
+ RangePos CheckRange(DeclaratorInfo *DInfo);
+ RangePos CheckRange(Decl *D) {
+ if (DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D))
+ if (ContainsLocation(DD->getDeclaratorInfo()))
+ return ContainsLoc;
+
+ return CheckRange(D->getSourceRange());
+ }
+ RangePos CheckRange(Stmt *Node) { return CheckRange(Node->getSourceRange()); }
+ RangePos CheckRange(TypeLoc TL) { return CheckRange(TL.getSourceRange()); }
+
+ template <typename T>
+ bool isBeforeLocation(T Node) {
+ return CheckRange(Node) == BeforeLoc;
+ }
+
+ template <typename T>
+ bool ContainsLocation(T Node) {
+ return CheckRange(Node) == ContainsLoc;
+ }
+
+ template <typename T>
+ bool isAfterLocation(T Node) {
+ return CheckRange(Node) == AfterLoc;
+ }
+
+public:
+ LocResolverBase(ASTContext &ctx, SourceLocation loc)
+ : Ctx(ctx), Loc(loc) {}
+
+#ifndef NDEBUG
+ /// \brief Debugging output.
+ void print(Decl *D);
+ /// \brief Debugging output.
+ void print(Stmt *Node);
+#endif
+};
+
+/// \brief Searches a statement for the ASTLocation that corresponds to a source
+/// location.
+class VISIBILITY_HIDDEN StmtLocResolver : public LocResolverBase,
+ public StmtVisitor<StmtLocResolver,
+ ASTLocation > {
+ Decl * const Parent;
+
+public:
+ StmtLocResolver(ASTContext &ctx, SourceLocation loc, Decl *parent)
+ : LocResolverBase(ctx, loc), Parent(parent) {}
+
+ ASTLocation VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Node);
+ ASTLocation VisitDeclStmt(DeclStmt *Node);
+ ASTLocation VisitStmt(Stmt *Node);
+};
+
+/// \brief Searches a declaration for the ASTLocation that corresponds to a
+/// source location.
+class VISIBILITY_HIDDEN DeclLocResolver : public LocResolverBase,
+ public DeclVisitor<DeclLocResolver,
+ ASTLocation > {
+public:
+ DeclLocResolver(ASTContext &ctx, SourceLocation loc)
+ : LocResolverBase(ctx, loc) {}
+
+ ASTLocation VisitDeclContext(DeclContext *DC);
+ ASTLocation VisitTranslationUnitDecl(TranslationUnitDecl *TU);
+ ASTLocation VisitDeclaratorDecl(DeclaratorDecl *D);
+ ASTLocation VisitVarDecl(VarDecl *D);
+ ASTLocation VisitFunctionDecl(FunctionDecl *D);
+ ASTLocation VisitObjCMethodDecl(ObjCMethodDecl *D);
+ ASTLocation VisitDecl(Decl *D);
+};
+
+class TypeLocResolver : public LocResolverBase,
+ public TypeLocVisitor<TypeLocResolver, ASTLocation> {
+ Decl * const ParentDecl;
+
+public:
+ TypeLocResolver(ASTContext &ctx, SourceLocation loc, Decl *pd)
+ : LocResolverBase(ctx, loc), ParentDecl(pd) { }
+
+ ASTLocation VisitTypedefLoc(TypedefLoc TL);
+ ASTLocation VisitFunctionLoc(FunctionLoc TL);
+ ASTLocation VisitArrayLoc(ArrayLoc TL);
+ ASTLocation VisitObjCInterfaceLoc(ObjCInterfaceLoc TL);
+ ASTLocation VisitObjCProtocolListLoc(ObjCProtocolListLoc TL);
+ ASTLocation VisitTypeLoc(TypeLoc TL);
+};
+
+} // anonymous namespace
+
+ASTLocation
+StmtLocResolver::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Node) {
+ assert(ContainsLocation(Node) &&
+ "Should visit only after verifying that loc is in range");
+
+ if (Node->getNumArgs() == 1)
+ // Unary operator. Let normal child traversal handle it.
+ return VisitCallExpr(Node);
+
+ assert(Node->getNumArgs() == 2 &&
+ "Wrong args for the C++ operator call expr ?");
+
+ llvm::SmallVector<Expr *, 3> Nodes;
+ // Binary operator. Check in order of 1-left arg, 2-callee, 3-right arg.
+ Nodes.push_back(Node->getArg(0));
+ Nodes.push_back(Node->getCallee());
+ Nodes.push_back(Node->getArg(1));
+
+ for (unsigned i = 0, e = Nodes.size(); i != e; ++i) {
+ RangePos RP = CheckRange(Nodes[i]);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return Visit(Nodes[i]);
+ }
+
+ return ASTLocation(Parent, Node);
+}
+
+ASTLocation StmtLocResolver::VisitDeclStmt(DeclStmt *Node) {
+ assert(ContainsLocation(Node) &&
+ "Should visit only after verifying that loc is in range");
+
+ // Search all declarations of this DeclStmt.
+ for (DeclStmt::decl_iterator
+ I = Node->decl_begin(), E = Node->decl_end(); I != E; ++I) {
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return DeclLocResolver(Ctx, Loc).Visit(*I);
+ }
+
+ return ASTLocation(Parent, Node);
+}
+
+ASTLocation StmtLocResolver::VisitStmt(Stmt *Node) {
+ assert(ContainsLocation(Node) &&
+ "Should visit only after verifying that loc is in range");
+
+ // Search the child statements.
+ for (Stmt::child_iterator
+ I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
+ if (*I == NULL)
+ continue;
+
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return Visit(*I);
+ }
+
+ return ASTLocation(Parent, Node);
+}
+
+ASTLocation DeclLocResolver::VisitDeclContext(DeclContext *DC) {
+ for (DeclContext::decl_iterator
+ I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) {
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return Visit(*I);
+ }
+
+ return ASTLocation(cast<Decl>(DC));
+}
+
+ASTLocation DeclLocResolver::VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
+ ASTLocation ASTLoc = VisitDeclContext(TU);
+ if (ASTLoc.getParentDecl() == TU)
+ return ASTLocation();
+ return ASTLoc;
+}
+
+ASTLocation DeclLocResolver::VisitFunctionDecl(FunctionDecl *D) {
+ assert(ContainsLocation(D) &&
+ "Should visit only after verifying that loc is in range");
+
+ if (ContainsLocation(D->getDeclaratorInfo()))
+ return ResolveInDeclarator(D, 0, D->getDeclaratorInfo());
+
+ // First, search through the parameters of the function.
+ for (FunctionDecl::param_iterator
+ I = D->param_begin(), E = D->param_end(); I != E; ++I) {
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ return ASTLocation(D);
+ if (RP == ContainsLoc)
+ return Visit(*I);
+ }
+
+ // We didn't find the location in the parameters and we didn't get passed it.
+
+ if (!D->isThisDeclarationADefinition())
+ return ASTLocation(D);
+
+ // 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.
+
+ for (DeclContext::decl_iterator
+ I = D->decls_begin(), E = D->decls_end(); I != E; ++I) {
+ if (isa<ParmVarDecl>(*I))
+ continue; // We already searched through the parameters.
+
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return Visit(*I);
+ }
+
+ // We didn't find a declaration that corresponds to the source location.
+
+ // Finally, search through the body of the function.
+ Stmt *Body = D->getBody();
+ assert(Body && "Expected definition");
+ assert(!isBeforeLocation(Body) &&
+ "This function is supposed to contain the loc");
+ if (isAfterLocation(Body))
+ return ASTLocation(D);
+
+ // The body contains the location.
+ assert(ContainsLocation(Body));
+ return StmtLocResolver(Ctx, Loc, D).Visit(Body);
+}
+
+ASTLocation DeclLocResolver::VisitDeclaratorDecl(DeclaratorDecl *D) {
+ assert(ContainsLocation(D) &&
+ "Should visit only after verifying that loc is in range");
+ if (ContainsLocation(D->getDeclaratorInfo()))
+ return ResolveInDeclarator(D, /*Stmt=*/0, D->getDeclaratorInfo());
+
+ return ASTLocation(D);
+}
+
+ASTLocation DeclLocResolver::VisitVarDecl(VarDecl *D) {
+ assert(ContainsLocation(D) &&
+ "Should visit only after verifying that loc is in range");
+
+ // Check whether the location points to the init expression.
+ Expr *Init = D->getInit();
+ if (Init && ContainsLocation(Init))
+ return StmtLocResolver(Ctx, Loc, D).Visit(Init);
+
+ if (ContainsLocation(D->getDeclaratorInfo()))
+ return ResolveInDeclarator(D, 0, D->getDeclaratorInfo());
+
+ return ASTLocation(D);
+}
+
+ASTLocation DeclLocResolver::VisitObjCMethodDecl(ObjCMethodDecl *D) {
+ assert(ContainsLocation(D) &&
+ "Should visit only after verifying that loc is in range");
+
+ // First, search through the parameters of the method.
+ for (ObjCMethodDecl::param_iterator
+ I = D->param_begin(), E = D->param_end(); I != E; ++I) {
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ return ASTLocation(D);
+ if (RP == ContainsLoc)
+ return Visit(*I);
+ }
+
+ // We didn't find the location in the parameters and we didn't get passed it.
+
+ if (!D->getBody())
+ return ASTLocation(D);
+
+ // Second, search through the declarations that are part of the method.
+ // If we find he location there, we won't have to search through its body.
+
+ for (DeclContext::decl_iterator
+ I = D->decls_begin(), E = D->decls_end(); I != E; ++I) {
+ if (isa<ParmVarDecl>(*I))
+ continue; // We already searched through the parameters.
+
+ RangePos RP = CheckRange(*I);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return Visit(*I);
+ }
+
+ // We didn't find a declaration that corresponds to the source location.
+
+ // Finally, search through the body of the method.
+ Stmt *Body = D->getBody();
+ assert(Body && "Expected definition");
+ assert(!isBeforeLocation(Body) &&
+ "This method is supposed to contain the loc");
+ if (isAfterLocation(Body))
+ return ASTLocation(D);
+
+ // The body contains the location.
+ assert(ContainsLocation(Body));
+ return StmtLocResolver(Ctx, Loc, D).Visit(Body);
+}
+
+ASTLocation DeclLocResolver::VisitDecl(Decl *D) {
+ assert(ContainsLocation(D) &&
+ "Should visit only after verifying that loc is in range");
+ if (DeclContext *DC = dyn_cast<DeclContext>(D))
+ return VisitDeclContext(DC);
+ return ASTLocation(D);
+}
+
+ASTLocation TypeLocResolver::VisitTypedefLoc(TypedefLoc TL) {
+ assert(ContainsLocation(TL) &&
+ "Should visit only after verifying that loc is in range");
+ if (ContainsLocation(TL.getNameLoc()))
+ return ASTLocation(ParentDecl, TL.getTypedefDecl(), TL.getNameLoc());
+ return ASTLocation(ParentDecl, TL);
+}
+
+ASTLocation TypeLocResolver::VisitFunctionLoc(FunctionLoc TL) {
+ assert(ContainsLocation(TL) &&
+ "Should visit only after verifying that loc is in range");
+
+ for (unsigned i = 0; i != TL.getNumArgs(); ++i) {
+ ParmVarDecl *Parm = TL.getArg(i);
+ RangePos RP = CheckRange(Parm);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return DeclLocResolver(Ctx, Loc).Visit(Parm);
+ }
+
+ return ASTLocation(ParentDecl, TL);
+}
+
+ASTLocation TypeLocResolver::VisitArrayLoc(ArrayLoc TL) {
+ assert(ContainsLocation(TL) &&
+ "Should visit only after verifying that loc is in range");
+
+ Expr *E = TL.getSizeExpr();
+ if (E && ContainsLocation(E))
+ return StmtLocResolver(Ctx, Loc, ParentDecl).Visit(E);
+
+ return ASTLocation(ParentDecl, TL);
+}
+
+ASTLocation TypeLocResolver::VisitObjCInterfaceLoc(ObjCInterfaceLoc TL) {
+ assert(ContainsLocation(TL) &&
+ "Should visit only after verifying that loc is in range");
+ if (ContainsLocation(TL.getNameLoc()))
+ return ASTLocation(ParentDecl, TL.getIFaceDecl(), TL.getNameLoc());
+ return ASTLocation(ParentDecl, TL);
+}
+
+ASTLocation TypeLocResolver::VisitObjCProtocolListLoc(ObjCProtocolListLoc TL) {
+ assert(ContainsLocation(TL) &&
+ "Should visit only after verifying that loc is in range");
+
+ for (unsigned i = 0; i != TL.getNumProtocols(); ++i) {
+ SourceLocation L = TL.getProtocolLoc(i);
+ RangePos RP = CheckRange(L);
+ if (RP == AfterLoc)
+ break;
+ if (RP == ContainsLoc)
+ return ASTLocation(ParentDecl, TL.getProtocol(i), L);
+ }
+
+ return ASTLocation(ParentDecl, TL);
+}
+
+ASTLocation TypeLocResolver::VisitTypeLoc(TypeLoc TL) {
+ assert(ContainsLocation(TL) &&
+ "Should visit only after verifying that loc is in range");
+ return ASTLocation(ParentDecl, TL);
+}
+
+ASTLocation LocResolverBase::ResolveInDeclarator(Decl *D, Stmt *Stm,
+ DeclaratorInfo *DInfo) {
+ assert(ContainsLocation(DInfo) &&
+ "Should visit only after verifying that loc is in range");
+
+ TypeLocResolver(Ctx, Loc, D);
+ for (TypeLoc TL = DInfo->getTypeLoc(); TL; TL = TL.getNextTypeLoc())
+ if (ContainsLocation(TL))
+ return TypeLocResolver(Ctx, Loc, D).Visit(TL);
+
+ assert(0 && "Should have found the loc in a typeloc");
+ return ASTLocation(D, Stm);
+}
+
+LocResolverBase::RangePos LocResolverBase::CheckRange(DeclaratorInfo *DInfo) {
+ if (!DInfo)
+ return BeforeLoc; // Keep looking.
+
+ for (TypeLoc TL = DInfo->getTypeLoc(); TL; TL = TL.getNextTypeLoc())
+ if (ContainsLocation(TL))
+ return ContainsLoc;
+
+ return BeforeLoc; // Keep looking.
+}
+
+LocResolverBase::RangePos LocResolverBase::CheckRange(SourceRange Range) {
+ if (!Range.isValid())
+ return BeforeLoc; // Keep looking.
+
+ // Update the end source range to cover the full length of the token
+ // positioned at the end of the source range.
+ //
+ // e.g.,
+ // int foo
+ // ^ ^
+ //
+ // will be updated to
+ // int foo
+ // ^ ^
+ unsigned TokSize = Lexer::MeasureTokenLength(Range.getEnd(),
+ Ctx.getSourceManager(),
+ Ctx.getLangOptions());
+ Range.setEnd(Range.getEnd().getFileLocWithOffset(TokSize-1));
+
+ SourceManager &SourceMgr = Ctx.getSourceManager();
+ if (SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), Loc))
+ return BeforeLoc;
+
+ if (SourceMgr.isBeforeInTranslationUnit(Loc, Range.getBegin()))
+ return AfterLoc;
+
+ return ContainsLoc;
+}
+
+#ifndef NDEBUG
+void LocResolverBase::print(Decl *D) {
+ llvm::raw_ostream &OS = llvm::outs();
+ OS << "#### DECL " << D->getDeclKindName() << " ####\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 " << Node->getStmtClassName() << " ####\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();
+}
+#endif
+
+
+/// \brief Returns the AST node that a source location points to.
+///
+ASTLocation idx::ResolveLocationInAST(ASTContext &Ctx, SourceLocation Loc) {
+ if (Loc.isInvalid())
+ return ASTLocation();
+
+ return DeclLocResolver(Ctx, Loc).Visit(Ctx.getTranslationUnitDecl());
+}