From 7b332d9ada3e2806a4d765000527f739f167791f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 5 Jul 2009 22:22:35 +0000 Subject: [PATCH] Make use of the Index library through the index-test tool. 'index-test' is now able to provide additional info for a Decl, through multiple AST files: -Find declarations -Find definitions -Find references git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@74803 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/_init.c | 2 + test/Index/find-decls.c | 7 ++ test/Index/find-defs.c | 8 ++ test/Index/find-refs.c | 16 +++ test/Index/foo.h | 4 + test/Index/t1.c | 9 ++ test/Index/t2.c | 8 ++ test/TestRunner.sh | 3 + tools/index-test/CMakeLists.txt | 1 + tools/index-test/Makefile | 2 +- tools/index-test/index-test.cpp | 204 ++++++++++++++++++++++++++------ utils/test/TestRunner.py | 2 + 12 files changed, 229 insertions(+), 37 deletions(-) create mode 100644 test/Index/_init.c create mode 100644 test/Index/find-decls.c create mode 100644 test/Index/find-defs.c create mode 100644 test/Index/find-refs.c create mode 100644 test/Index/foo.h create mode 100644 test/Index/t1.c create mode 100644 test/Index/t2.c diff --git a/test/Index/_init.c b/test/Index/_init.c new file mode 100644 index 0000000000..24caaefcb1 --- /dev/null +++ b/test/Index/_init.c @@ -0,0 +1,2 @@ +// RUN: clang-cc -emit-pch %S/t1.c -o %T/t1.ast && +// RUN: clang-cc -emit-pch %S/t2.c -o %T/t2.ast diff --git a/test/Index/find-decls.c b/test/Index/find-decls.c new file mode 100644 index 0000000000..06b52e96d7 --- /dev/null +++ b/test/Index/find-decls.c @@ -0,0 +1,7 @@ +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:8:7 -print-decls | count 3 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:8:7 -print-decls | grep 'foo.h:4:6,' | count 2 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:8:7 -print-decls | grep 't2.c:5:6,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:5:47 -print-decls | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:5:47 -print-decls | grep 't1.c:5:12,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:6:20 -print-decls | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:6:20 -print-decls | grep 't1.c:3:19,' diff --git a/test/Index/find-defs.c b/test/Index/find-defs.c new file mode 100644 index 0000000000..d17dc0b142 --- /dev/null +++ b/test/Index/find-defs.c @@ -0,0 +1,8 @@ +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:1:14 -print-defs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:1:14 -print-defs | grep 't2.c:3:5,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:3:9 -print-defs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:3:9 -print-defs | grep 't1.c:3:6,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:4:9 -print-defs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:4:9 -print-defs | grep 't2.c:5:6,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:8:7 -print-defs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:8:7 -print-defs | grep 't2.c:5:6,' diff --git a/test/Index/find-refs.c b/test/Index/find-refs.c new file mode 100644 index 0000000000..fea1f752fb --- /dev/null +++ b/test/Index/find-refs.c @@ -0,0 +1,16 @@ +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:1:14 -print-refs | count 3 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:1:14 -print-refs | grep 't1.c:4:19,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:1:14 -print-refs | grep 't2.c:6:3,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:1:14 -print-refs | grep 't2.c:7:12,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:3:9 -print-refs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:3:9 -print-refs | grep 't2.c:7:3,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:4:9 -print-refs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/foo.h:4:9 -print-refs | grep 't1.c:8:3,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:3:22 -print-refs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:3:22 -print-refs | grep 't1.c:6:17,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:4:11 -print-refs | count 1 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:4:11 -print-refs | grep 't1.c:6:5,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:5:30 -print-refs | count 3 && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:5:30 -print-refs | grep 't1.c:5:27,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:5:30 -print-refs | grep 't1.c:5:44,' && +// RUN: index-test %T/t1.ast %T/t2.ast -point-at %S/t1.c:5:30 -print-refs | grep 't1.c:6:26,' diff --git a/test/Index/foo.h b/test/Index/foo.h new file mode 100644 index 0000000000..2e3b403dea --- /dev/null +++ b/test/Index/foo.h @@ -0,0 +1,4 @@ +extern int global_var; + +void foo_func(int param1); +void bar_func(void); diff --git a/test/Index/t1.c b/test/Index/t1.c new file mode 100644 index 0000000000..1ff89ca34b --- /dev/null +++ b/test/Index/t1.c @@ -0,0 +1,9 @@ +#include "foo.h" + +void foo_func(int param1) { + int local_var = global_var; + for (int for_var = 100; for_var < 500; ++for_var) { + local_var = param1 + for_var; + } + bar_func(); +} diff --git a/test/Index/t2.c b/test/Index/t2.c new file mode 100644 index 0000000000..8e405cc367 --- /dev/null +++ b/test/Index/t2.c @@ -0,0 +1,8 @@ +#include "foo.h" + +int global_var = 10; + +void bar_func(void) { + global_var += 100; + foo_func(global_var); +} diff --git a/test/TestRunner.sh b/test/TestRunner.sh index bb20728578..07451eb485 100755 --- a/test/TestRunner.sh +++ b/test/TestRunner.sh @@ -11,6 +11,7 @@ # %S - Replaced with the directory where the input file resides # %prcontext - prcontext.tcl script # %t - temporary file name (derived from testcase name) +# %T - directory of temporary file name # FILENAME=$1 @@ -19,6 +20,7 @@ SUBST=$1 FILEDIR=`dirname $TESTNAME` OUTPUT=Output/$1.out +OUTPUTDIR=`dirname $OUTPUT` # create the output directory if it does not already exist mkdir -p `dirname $OUTPUT` > /dev/null 2>&1 @@ -86,6 +88,7 @@ grep 'RUN:' $FILENAME | \ -e "s|%s|$SUBST|g" \ -e "s|%S|$FILEDIR|g" \ -e "s|%prcontext|prcontext.tcl|g" \ + -e "s|%T|$OUTPUTDIR|g" \ -e "s|%t|$TEMPOUTPUT|g" > $SCRIPT IS_XFAIL=0 diff --git a/tools/index-test/CMakeLists.txt b/tools/index-test/CMakeLists.txt index 09f4fc12c1..880539bbe3 100644 --- a/tools/index-test/CMakeLists.txt +++ b/tools/index-test/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS + clangIndex clangFrontend clangSema clangAST diff --git a/tools/index-test/Makefile b/tools/index-test/Makefile index 4fbde29198..cec0eee286 100644 --- a/tools/index-test/Makefile +++ b/tools/index-test/Makefile @@ -18,6 +18,6 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := bitreader -USEDLIBS = clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a +USEDLIBS = clangIndex.a clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/index-test/index-test.cpp b/tools/index-test/index-test.cpp index 0fa0d5a466..5043e3e62b 100644 --- a/tools/index-test/index-test.cpp +++ b/tools/index-test/index-test.cpp @@ -21,35 +21,162 @@ // Point at a declaration/statement/expression. If no other operation is // specified, prints some info about it. // +// -print-refs +// Print ASTNodes that reference the -point-at node +// +// -print-defs +// Print ASTNodes that define the -point-at node +// +// -print-decls +// Print ASTNodes that declare the -point-at node +// //===----------------------------------------------------------------------===// +#include "clang/Index/Program.h" +#include "clang/Index/IndexProvider.h" +#include "clang/Index/Entity.h" +#include "clang/Index/TranslationUnit.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/Utils.h" #include "clang/Frontend/CommandLineSourceLoc.h" #include "clang/AST/Decl.h" -#include "clang/AST/Stmt.h" +#include "clang/AST/Expr.h" #include "clang/AST/ASTNode.h" +#include "clang/AST/DeclReferenceMap.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/raw_ostream.h" #include "llvm/System/Signals.h" using namespace clang; +using namespace idx; +class TUnit : public TranslationUnit { +public: + TUnit(ASTUnit *ast, const std::string &filename) + : AST(ast), Filename(filename) { } + + virtual ASTContext &getASTContext() { return AST->getASTContext(); } + + llvm::OwningPtr AST; + std::string Filename; +}; static llvm::cl::list PointAtLocation("point-at", llvm::cl::Optional, llvm::cl::value_desc("source-location"), llvm::cl::desc("Point at the given source location of the first AST file")); +enum ProgActions { + PrintPoint, // Just print the point-at node + PrintRefs, // Print references of the point-at node + PrintDefs, // Print definitions of the point-at node + PrintDecls // Print declarations of the point-at node +}; + +static llvm::cl::opt +ProgAction( + llvm::cl::desc("Choose action to perform on the pointed-at AST node:"), + llvm::cl::ZeroOrMore, + llvm::cl::init(PrintPoint), + llvm::cl::values( + clEnumValN(PrintRefs, "print-refs", + "Print references"), + clEnumValN(PrintDefs, "print-defs", + "Print definitions"), + clEnumValN(PrintDecls, "print-decls", + "Print declarations"), + clEnumValEnd)); + static llvm::cl::opt DisableFree("disable-free", llvm::cl::desc("Disable freeing of memory on exit"), llvm::cl::init(false)); +static void ProcessDecl(Decl *D) { + assert(D); + llvm::raw_ostream &OS = llvm::outs(); + + switch (ProgAction) { + default: assert(0); + case PrintRefs: { + NamedDecl *ND = dyn_cast(D); + if (!ND) + return; + + DeclReferenceMap RefMap(ND->getASTContext()); + for (DeclReferenceMap::astnode_iterator + I = RefMap.refs_begin(ND), E = RefMap.refs_end(ND); I != E; ++I) + I->print(OS); + break; + } + + case PrintDefs: { + const Decl *DefD = 0; + if (FunctionDecl *FD = dyn_cast(D)) { + const FunctionDecl *DFD = 0; + FD->getBody(DFD); + DefD = DFD; + } else if (VarDecl *VD = dyn_cast(D)) { + const VarDecl *DVD = 0; + VD->getDefinition(DVD); + DefD = DVD; + } + + if (DefD) + ASTNode(DefD).print(OS); + break; + } + + case PrintDecls : + if (const FunctionDecl *FD = dyn_cast(D)) { + while (FD) { + ASTNode(FD).print(OS); + FD = FD->getPreviousDeclaration(); + } + } else if (const VarDecl *VD = dyn_cast(D)) { + while (VD) { + ASTNode(VD).print(OS); + VD = VD->getPreviousDeclaration(); + } + } else + ASTNode(D).print(OS); + break; + + } +} + +static void ProcessNode(ASTNode Node, IndexProvider &IdxProvider) { + assert(Node.isValid()); + + Decl *D = 0; + if (Node.hasStmt()) { + if (DeclRefExpr *RefExpr = dyn_cast(Node.getStmt())) + D = RefExpr->getDecl(); + } else { + D = Node.getDecl(); + } + assert(D); + + Entity *Ent = Entity::get(D, IdxProvider.getProgram()); + // If there is no Entity associated with this Decl, it means that it's not + // visible to other translation units. + if (!Ent) + return ProcessDecl(D); + + // Find the "same" Decl in other translation units and print information. + for (IndexProvider::translation_unit_iterator + I = IdxProvider.translation_units_begin(Ent), + E = IdxProvider.translation_units_end(Ent); I != E; ++I) { + TUnit *TU = static_cast(*I); + Decl *OtherD = Ent->getDecl(TU->getASTContext()); + assert(OtherD && "Couldn't resolve Entity"); + ProcessDecl(OtherD); + } +} + static llvm::cl::list InputFilenames(llvm::cl::Positional, llvm::cl::desc("")); @@ -60,25 +187,36 @@ int main(int argc, char **argv) { "LLVM 'Clang' Indexing Test Bed: http://clang.llvm.org\n"); FileManager FileMgr; + + Program Prog; + IndexProvider IdxProvider(Prog); + llvm::SmallVector TUnits; // If no input was specified, read from stdin. if (InputFilenames.empty()) InputFilenames.push_back("-"); - // FIXME: Only the first AST file is used for now. + for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { + const std::string &InFile = InputFilenames[i]; + + std::string ErrMsg; + llvm::OwningPtr AST; - const std::string &InFile = InputFilenames[0]; - - std::string ErrMsg; - llvm::OwningPtr AST; + AST.reset(ASTUnit::LoadFromPCHFile(InFile, FileMgr, &ErrMsg)); + if (!AST) { + llvm::errs() << "[" << InFile << "] Error: " << ErrMsg << '\n'; + return 1; + } - AST.reset(ASTUnit::LoadFromPCHFile(InFile, FileMgr, &ErrMsg)); - if (!AST) { - llvm::errs() << "[" << InFile << "] Error: " << ErrMsg << '\n'; - return 1; + TUnit *TU = new TUnit(AST.take(), InFile); + TUnits.push_back(TU); + + IdxProvider.IndexAST(TU); } ASTNode Node; + const std::string &FirstFile = TUnits[0]->Filename; + ASTUnit *FirstAST = TUnits[0]->AST.get(); if (!PointAtLocation.empty()) { const std::string &Filename = PointAtLocation[0].FileName; @@ -87,9 +225,9 @@ int main(int argc, char **argv) { // Safety check. Using an out-of-date AST file will only lead to crashes // or incorrect results. // FIXME: Check all the source files that make up the AST file. - const FileEntry *ASTFile = FileMgr.getFile(InFile); + const FileEntry *ASTFile = FileMgr.getFile(FirstFile); if (File->getModificationTime() > ASTFile->getModificationTime()) { - llvm::errs() << "[" << InFile << "] Error: " << + llvm::errs() << "[" << FirstFile << "] Error: " << "Pointing at a source file which was modified after creating " "the AST file\n"; return 1; @@ -102,44 +240,38 @@ int main(int argc, char **argv) { unsigned Line = PointAtLocation[0].Line; unsigned Col = PointAtLocation[0].Column; - SourceLocation Loc = AST->getSourceManager().getLocation(File, Line, Col); + SourceLocation Loc = + FirstAST->getSourceManager().getLocation(File, Line, Col); if (Loc.isInvalid()) { - llvm::errs() << "[" << InFile << "] Error: " << + llvm::errs() << "[" << FirstFile << "] Error: " << "Couldn't resolve source location (invalid location)\n"; return 1; } - Node = ResolveLocationInAST(AST->getASTContext(), Loc); + Node = ResolveLocationInAST(FirstAST->getASTContext(), Loc); if (Node.isInvalid()) { - llvm::errs() << "[" << InFile << "] Error: " << + llvm::errs() << "[" << FirstFile << "] Error: " << "Couldn't resolve source location (no declaration found)\n"; return 1; } } if (Node.isValid()) { - llvm::raw_ostream &OS = llvm::outs(); - OS << "Declaration node at point: " << Node.getDecl()->getDeclKindName() - << " "; - if (NamedDecl *ND = dyn_cast(Node.getDecl())) - OS << ND->getNameAsString(); - OS << "\n"; - - if (const char *Comment = - AST->getASTContext().getCommentForDecl(Node.getDecl())) - OS << "Comment associated with this declaration:\n" << Comment << "\n"; - - if (Node.getStmt()) { - OS << "Statement node at point: " << Node.getStmt()->getStmtClassName() - << " "; - Node.getStmt()->printPretty(OS, AST->getASTContext(), 0, - PrintingPolicy(AST->getASTContext().getLangOptions())); - OS << "\n"; + if (ProgAction == PrintPoint) { + llvm::raw_ostream &OS = llvm::outs(); + Node.print(OS); + if (const char *Comment = + FirstAST->getASTContext().getCommentForDecl(Node.getDecl())) + OS << "Comment associated with this declaration:\n" << Comment << "\n"; + } else { + ProcessNode(Node, IdxProvider); } } - if (DisableFree) - AST.take(); + if (!DisableFree) { + for (int i=0, e=TUnits.size(); i != e; ++i) + delete TUnits[i]; + } // Managed static deconstruction. Useful for making things like // -time-passes usable. diff --git a/utils/test/TestRunner.py b/utils/test/TestRunner.py index 9020622350..5625cb3c5b 100755 --- a/utils/test/TestRunner.py +++ b/utils/test/TestRunner.py @@ -13,6 +13,7 @@ # %llvmgxx - llvm-g++ command # %prcontext - prcontext.tcl script # %t - temporary file name (derived from testcase name) +# %T - directory of temporary file name # import errno @@ -116,6 +117,7 @@ def runOneTest(FILENAME, SUBST, OUTPUT, TESTNAME, CLANG, CLANGCC, ('%llvmgxx','llvm-g++ -emit-llvm -w'), ('%prcontext','prcontext.tcl'), ('%t',TEMPOUTPUT), + ('%T',os.path.dirname(TEMPOUTPUT)), (' clang ', ' ' + CLANG + ' '), (' clang-cc ', ' ' + CLANGCC + ' ')] scriptLines = []