diff options
author | Zancanaro; Carlo <czan8762@plang3.cs.usyd.edu.au> | 2012-09-24 09:58:17 +1000 |
---|---|---|
committer | Zancanaro; Carlo <czan8762@plang3.cs.usyd.edu.au> | 2012-09-24 09:58:17 +1000 |
commit | 222e2a7620e6520ffaf4fc4e69d79c18da31542e (patch) | |
tree | 7bfbc05bfa3b41c8f9d2e56d53a0bc3e310df239 /clang/tools | |
parent | 3d206f03985b50beacae843d880bccdc91a9f424 (diff) |
Add the clang library to the repo (with some of my changes, too).
Diffstat (limited to 'clang/tools')
74 files changed, 26105 insertions, 0 deletions
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt new file mode 100644 index 0000000..ab4748d --- /dev/null +++ b/clang/tools/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(libclang) +add_subdirectory(c-index-test) +add_subdirectory(arcmt-test) +add_subdirectory(c-arcmt-test) +add_subdirectory(diagtool) +add_subdirectory(driver) +add_subdirectory(clang-check) diff --git a/clang/tools/Makefile b/clang/tools/Makefile new file mode 100644 index 0000000..5059ade --- /dev/null +++ b/clang/tools/Makefile @@ -0,0 +1,16 @@ +##===- tools/Makefile --------------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := .. +DIRS := driver libclang c-index-test arcmt-test c-arcmt-test diagtool \ + clang-check + +include $(CLANG_LEVEL)/../../Makefile.config + +include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/arcmt-test/CMakeLists.txt b/clang/tools/arcmt-test/CMakeLists.txt new file mode 100644 index 0000000..a0029b4 --- /dev/null +++ b/clang/tools/arcmt-test/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_USED_LIBS + clangARCMigrate + clangEdit + clangRewrite + ) + +set( LLVM_LINK_COMPONENTS + support + mc + ) + +add_clang_executable(arcmt-test + arcmt-test.cpp + ) diff --git a/clang/tools/arcmt-test/Makefile b/clang/tools/arcmt-test/Makefile new file mode 100644 index 0000000..57cd574 --- /dev/null +++ b/clang/tools/arcmt-test/Makefile @@ -0,0 +1,24 @@ +##===- tools/arcmt-test/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = arcmt-test + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# Don't install this. It is used for tests. +NO_INSTALL = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clangARCMigrate.a clangRewrite.a \ + clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ + clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/arcmt-test/arcmt-test.cpp b/clang/tools/arcmt-test/arcmt-test.cpp new file mode 100644 index 0000000..3983c24 --- /dev/null +++ b/clang/tools/arcmt-test/arcmt-test.cpp @@ -0,0 +1,376 @@ +//===-- arcmt-test.cpp - ARC Migration Tool testbed -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/system_error.h" + +using namespace clang; +using namespace arcmt; + +static llvm::cl::opt<bool> +CheckOnly("check-only", + llvm::cl::desc("Just check for issues that need to be handled manually")); + +//static llvm::cl::opt<bool> +//TestResultForARC("test-result", +//llvm::cl::desc("Test the result of transformations by parsing it in ARC mode")); + +static llvm::cl::opt<bool> +OutputTransformations("output-transformations", + llvm::cl::desc("Print the source transformations")); + +static llvm::cl::opt<bool> +VerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings")); + +static llvm::cl::opt<bool> +VerboseOpt("v", llvm::cl::desc("Enable verbose output")); + +static llvm::cl::opt<bool> +VerifyTransformedFiles("verify-transformed-files", +llvm::cl::desc("Read pairs of file mappings (typically the output of " + "c-arcmt-test) and compare their contents with the filenames " + "provided in command-line")); + +static llvm::cl::opt<std::string> +RemappingsFile("remappings-file", + llvm::cl::desc("Pairs of file mappings (typically the output of " + "c-arcmt-test)")); + +static llvm::cl::list<std::string> +ResultFiles(llvm::cl::Positional, llvm::cl::desc("<filename>...")); + +static llvm::cl::extrahelp extraHelp( + "\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n"); + +// This function isn't referenced outside its translation unit, but it +// can't use the "static" keyword because its address is used for +// GetMainExecutable (since some platforms don't support taking the +// address of main, and some platforms can't implement GetMainExecutable +// without being given the address of a function in the main executable). +llvm::sys::Path GetExecutablePath(const char *Argv0) { + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *MainAddr = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr); +} + +static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, + raw_ostream &OS); +static void printSourceRange(CharSourceRange range, ASTContext &Ctx, + raw_ostream &OS); + +namespace { + +class PrintTransforms : public MigrationProcess::RewriteListener { + ASTContext *Ctx; + raw_ostream &OS; + +public: + PrintTransforms(raw_ostream &OS) + : Ctx(0), OS(OS) { } + + virtual void start(ASTContext &ctx) { Ctx = &ctx; } + virtual void finish() { Ctx = 0; } + + virtual void insert(SourceLocation loc, StringRef text) { + assert(Ctx); + OS << "Insert: "; + printSourceLocation(loc, *Ctx, OS); + OS << " \"" << text << "\"\n"; + } + + virtual void remove(CharSourceRange range) { + assert(Ctx); + OS << "Remove: "; + printSourceRange(range, *Ctx, OS); + OS << '\n'; + } +}; + +} // anonymous namespace + +static bool checkForMigration(StringRef resourcesPath, + ArrayRef<const char *> Args) { + DiagnosticConsumer *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, DiagClient)); + // Chain in -verify checker, if requested. + VerifyDiagnosticConsumer *verifyDiag = 0; + if (VerifyDiags) { + verifyDiag = new VerifyDiagnosticConsumer(*Diags); + Diags->setClient(verifyDiag); + } + + CompilerInvocation CI; + if (!CompilerInvocation::CreateFromArgs(CI, Args.begin(), Args.end(), *Diags)) + return true; + + if (CI.getFrontendOpts().Inputs.empty()) { + llvm::errs() << "error: no input files\n"; + return true; + } + + if (!CI.getLangOpts()->ObjC1) + return false; + + arcmt::checkForManualIssues(CI, CI.getFrontendOpts().Inputs[0], + Diags->getClient()); + return Diags->getClient()->getNumErrors() > 0; +} + +static void printResult(FileRemapper &remapper, raw_ostream &OS) { + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); + // The changed files will be in memory buffers, print them. + for (unsigned i = 0, e = PPOpts.RemappedFileBuffers.size(); i != e; ++i) { + const llvm::MemoryBuffer *mem = PPOpts.RemappedFileBuffers[i].second; + OS << mem->getBuffer(); + } +} + +static bool performTransformations(StringRef resourcesPath, + ArrayRef<const char *> Args) { + // Check first. + if (checkForMigration(resourcesPath, Args)) + return true; + + DiagnosticConsumer *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> TopDiags( + new DiagnosticsEngine(DiagID, DiagClient)); + + CompilerInvocation origCI; + if (!CompilerInvocation::CreateFromArgs(origCI, Args.begin(), Args.end(), + *TopDiags)) + return true; + + if (origCI.getFrontendOpts().Inputs.empty()) { + llvm::errs() << "error: no input files\n"; + return true; + } + + if (!origCI.getLangOpts()->ObjC1) + return false; + + MigrationProcess migration(origCI, DiagClient); + + std::vector<TransformFn> + transforms = arcmt::getAllTransformations(origCI.getLangOpts()->getGC(), + origCI.getMigratorOpts().NoFinalizeRemoval); + assert(!transforms.empty()); + + OwningPtr<PrintTransforms> transformPrinter; + if (OutputTransformations) + transformPrinter.reset(new PrintTransforms(llvm::outs())); + + for (unsigned i=0, e = transforms.size(); i != e; ++i) { + bool err = migration.applyTransform(transforms[i], transformPrinter.get()); + if (err) return true; + + if (VerboseOpt) { + if (i == e-1) + llvm::errs() << "\n##### FINAL RESULT #####\n"; + else + llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n"; + printResult(migration.getRemapper(), llvm::errs()); + llvm::errs() << "\n##########################\n\n"; + } + } + + if (!OutputTransformations) + printResult(migration.getRemapper(), llvm::outs()); + + // FIXME: TestResultForARC + + return false; +} + +static bool filesCompareEqual(StringRef fname1, StringRef fname2) { + using namespace llvm; + + OwningPtr<MemoryBuffer> file1; + MemoryBuffer::getFile(fname1, file1); + if (!file1) + return false; + + OwningPtr<MemoryBuffer> file2; + MemoryBuffer::getFile(fname2, file2); + if (!file2) + return false; + + return file1->getBuffer() == file2->getBuffer(); +} + +static bool verifyTransformedFiles(ArrayRef<std::string> resultFiles) { + using namespace llvm; + + assert(!resultFiles.empty()); + + std::map<StringRef, StringRef> resultMap; + + for (ArrayRef<std::string>::iterator + I = resultFiles.begin(), E = resultFiles.end(); I != E; ++I) { + StringRef fname(*I); + if (!fname.endswith(".result")) { + errs() << "error: filename '" << fname + << "' does not have '.result' extension\n"; + return true; + } + resultMap[sys::path::stem(fname)] = fname; + } + + OwningPtr<MemoryBuffer> inputBuf; + if (RemappingsFile.empty()) + MemoryBuffer::getSTDIN(inputBuf); + else + MemoryBuffer::getFile(RemappingsFile, inputBuf); + if (!inputBuf) { + errs() << "error: could not read remappings input\n"; + return true; + } + + SmallVector<StringRef, 8> strs; + inputBuf->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + + if (strs.empty()) { + errs() << "error: no files to verify from stdin\n"; + return true; + } + if (strs.size() % 2 != 0) { + errs() << "error: files to verify are not original/result pairs\n"; + return true; + } + + for (unsigned i = 0, e = strs.size(); i != e; i += 2) { + StringRef inputOrigFname = strs[i]; + StringRef inputResultFname = strs[i+1]; + + std::map<StringRef, StringRef>::iterator It; + It = resultMap.find(sys::path::filename(inputOrigFname)); + if (It == resultMap.end()) { + errs() << "error: '" << inputOrigFname << "' is not in the list of " + << "transformed files to verify\n"; + return true; + } + + bool exists = false; + sys::fs::exists(It->second, exists); + if (!exists) { + errs() << "error: '" << It->second << "' does not exist\n"; + return true; + } + sys::fs::exists(inputResultFname, exists); + if (!exists) { + errs() << "error: '" << inputResultFname << "' does not exist\n"; + return true; + } + + if (!filesCompareEqual(It->second, inputResultFname)) { + errs() << "error: '" << It->second << "' is different than " + << "'" << inputResultFname << "'\n"; + return true; + } + + resultMap.erase(It); + } + + if (!resultMap.empty()) { + for (std::map<StringRef, StringRef>::iterator + I = resultMap.begin(), E = resultMap.end(); I != E; ++I) + errs() << "error: '" << I->second << "' was not verified!\n"; + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// Misc. functions. +//===----------------------------------------------------------------------===// + +static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, + raw_ostream &OS) { + SourceManager &SM = Ctx.getSourceManager(); + PresumedLoc PL = SM.getPresumedLoc(loc); + + OS << llvm::sys::path::filename(PL.getFilename()); + OS << ":" << PL.getLine() << ":" + << PL.getColumn(); +} + +static void printSourceRange(CharSourceRange range, ASTContext &Ctx, + raw_ostream &OS) { + SourceManager &SM = Ctx.getSourceManager(); + const LangOptions &langOpts = Ctx.getLangOpts(); + + PresumedLoc PL = SM.getPresumedLoc(range.getBegin()); + + OS << llvm::sys::path::filename(PL.getFilename()); + OS << " [" << PL.getLine() << ":" + << PL.getColumn(); + OS << " - "; + + SourceLocation end = range.getEnd(); + PL = SM.getPresumedLoc(end); + + unsigned endCol = PL.getColumn() - 1; + if (!range.isTokenRange()) + endCol += Lexer::MeasureTokenLength(end, SM, langOpts); + OS << PL.getLine() << ":" << endCol << "]"; +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int main(int argc, const char **argv) { + void *MainAddr = (void*) (intptr_t) GetExecutablePath; + llvm::sys::PrintStackTraceOnErrorSignal(); + + std::string + resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr); + + int optargc = 0; + for (; optargc != argc; ++optargc) { + if (StringRef(argv[optargc]) == "--args") + break; + } + llvm::cl::ParseCommandLineOptions(optargc, argv, "arcmt-test"); + + if (VerifyTransformedFiles) { + if (ResultFiles.empty()) { + llvm::cl::PrintHelpMessage(); + return 1; + } + return verifyTransformedFiles(ResultFiles); + } + + if (optargc == argc) { + llvm::cl::PrintHelpMessage(); + return 1; + } + + ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1); + + if (CheckOnly) + return checkForMigration(resourcesPath, Args); + + return performTransformations(resourcesPath, Args); +} diff --git a/clang/tools/c-arcmt-test/CMakeLists.txt b/clang/tools/c-arcmt-test/CMakeLists.txt new file mode 100644 index 0000000..351f4ad --- /dev/null +++ b/clang/tools/c-arcmt-test/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_USED_LIBS libclang) + +set( LLVM_LINK_COMPONENTS + support + mc + ) + +add_clang_executable(c-arcmt-test + c-arcmt-test.c + ) + +set_target_properties(c-arcmt-test + PROPERTIES + LINKER_LANGUAGE CXX) diff --git a/clang/tools/c-arcmt-test/Makefile b/clang/tools/c-arcmt-test/Makefile new file mode 100644 index 0000000..818f648 --- /dev/null +++ b/clang/tools/c-arcmt-test/Makefile @@ -0,0 +1,25 @@ +##===- tools/c-arcmt-test/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = c-arcmt-test + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# Don't install this. It is used for tests. +NO_INSTALL = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clang.a clangARCMigrate.a clangRewrite.a \ + clangFrontend.a clangDriver.a \ + clangSerialization.a clangParse.a clangSema.a \ + clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/c-arcmt-test/c-arcmt-test.c b/clang/tools/c-arcmt-test/c-arcmt-test.c new file mode 100644 index 0000000..56a4132 --- /dev/null +++ b/clang/tools/c-arcmt-test/c-arcmt-test.c @@ -0,0 +1,123 @@ +/* c-arcmt-test.c */ + +#include "clang-c/Index.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#if defined(_WIN32) +#include <io.h> +#include <fcntl.h> +#endif + +static int print_remappings(const char *path) { + CXRemapping remap; + unsigned i, N; + CXString origFname; + CXString transFname; + + remap = clang_getRemappings(path); + if (!remap) + return 1; + + N = clang_remap_getNumFiles(remap); + for (i = 0; i != N; ++i) { + clang_remap_getFilenames(remap, i, &origFname, &transFname); + + fprintf(stdout, "%s\n", clang_getCString(origFname)); + fprintf(stdout, "%s\n", clang_getCString(transFname)); + + clang_disposeString(origFname); + clang_disposeString(transFname); + } + + clang_remap_dispose(remap); + return 0; +} + +static int print_remappings_filelist(const char **files, unsigned numFiles) { + CXRemapping remap; + unsigned i, N; + CXString origFname; + CXString transFname; + + remap = clang_getRemappingsFromFileList(files, numFiles); + if (!remap) + return 1; + + N = clang_remap_getNumFiles(remap); + for (i = 0; i != N; ++i) { + clang_remap_getFilenames(remap, i, &origFname, &transFname); + + fprintf(stdout, "%s\n", clang_getCString(origFname)); + fprintf(stdout, "%s\n", clang_getCString(transFname)); + + clang_disposeString(origFname); + clang_disposeString(transFname); + } + + clang_remap_dispose(remap); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ + +static void print_usage(void) { + fprintf(stderr, + "usage: c-arcmt-test -mt-migrate-directory <path>\n" + " c-arcmt-test <remap-file-path1> <remap-file-path2> ...\n\n\n"); +} + +/***/ + +int carcmttest_main(int argc, const char **argv) { + clang_enableStackTraces(); + if (argc == 3 && strncmp(argv[1], "-mt-migrate-directory", 21) == 0) + return print_remappings(argv[2]); + + if (argc > 1) + return print_remappings_filelist(argv+1, argc-1); + + print_usage(); + return 1; +} + +/***/ + +/* We intentionally run in a separate thread to ensure we at least minimal + * testing of a multithreaded environment (for example, having a reduced stack + * size). */ + +typedef struct thread_info { + int argc; + const char **argv; + int result; +} thread_info; +void thread_runner(void *client_data_v) { + thread_info *client_data = client_data_v; + client_data->result = carcmttest_main(client_data->argc, client_data->argv); +#ifdef __CYGWIN__ + fflush(stdout); /* stdout is not flushed on Cygwin. */ +#endif +} + +int main(int argc, const char **argv) { + thread_info client_data; + +#if defined(_WIN32) + if (getenv("LIBCLANG_LOGGING") == NULL) + putenv("LIBCLANG_LOGGING=1"); + _setmode( _fileno(stdout), _O_BINARY ); +#else + setenv("LIBCLANG_LOGGING", "1", /*overwrite=*/0); +#endif + + if (getenv("CINDEXTEST_NOTHREADS")) + return carcmttest_main(argc, argv); + + client_data.argc = argc; + client_data.argv = argv; + clang_executeOnThread(thread_runner, &client_data, 0); + return client_data.result; +} diff --git a/clang/tools/c-index-test/CMakeLists.txt b/clang/tools/c-index-test/CMakeLists.txt new file mode 100644 index 0000000..c44b34b --- /dev/null +++ b/clang/tools/c-index-test/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_USED_LIBS libclang) + +set( LLVM_LINK_COMPONENTS + support + mc + ) + +add_clang_executable(c-index-test + c-index-test.c + ) + +set_target_properties(c-index-test + PROPERTIES + LINKER_LANGUAGE CXX) + +install(TARGETS c-index-test RUNTIME DESTINATION bin) diff --git a/clang/tools/c-index-test/Makefile b/clang/tools/c-index-test/Makefile new file mode 100644 index 0000000..03519b3 --- /dev/null +++ b/clang/tools/c-index-test/Makefile @@ -0,0 +1,25 @@ +##===- tools/index-test/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = c-index-test + +# If a separate install prefix was specified for internal tools, use it +# when installing c-index-test. +INTERNAL_TOOL = 1 + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clang.a clangFrontend.a clangDriver.a \ + clangSerialization.a clangParse.a clangSema.a \ + clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c new file mode 100644 index 0000000..eb2a406 --- /dev/null +++ b/clang/tools/c-index-test/c-index-test.c @@ -0,0 +1,2825 @@ +/* c-index-test.c */ + +#include "clang-c/Index.h" +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +/******************************************************************************/ +/* Utility functions. */ +/******************************************************************************/ + +#ifdef _MSC_VER +char *basename(const char* path) +{ + char* base1 = (char*)strrchr(path, '/'); + char* base2 = (char*)strrchr(path, '\\'); + if (base1 && base2) + return((base1 > base2) ? base1 + 1 : base2 + 1); + else if (base1) + return(base1 + 1); + else if (base2) + return(base2 + 1); + + return((char*)path); +} +#else +extern char *basename(const char *); +#endif + +/** \brief Return the default parsing options. */ +static unsigned getDefaultParsingOptions() { + unsigned options = CXTranslationUnit_DetailedPreprocessingRecord; + + if (getenv("CINDEXTEST_EDITING")) + options |= clang_defaultEditingTranslationUnitOptions(); + if (getenv("CINDEXTEST_COMPLETION_CACHING")) + options |= CXTranslationUnit_CacheCompletionResults; + if (getenv("CINDEXTEST_COMPLETION_NO_CACHING")) + options &= ~CXTranslationUnit_CacheCompletionResults; + if (getenv("CINDEXTEST_SKIP_FUNCTION_BODIES")) + options |= CXTranslationUnit_SkipFunctionBodies; + + return options; +} + +static int checkForErrors(CXTranslationUnit TU); + +static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column, + unsigned end_line, unsigned end_column) { + fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column, + end_line, end_column); +} + +static unsigned CreateTranslationUnit(CXIndex Idx, const char *file, + CXTranslationUnit *TU) { + + *TU = clang_createTranslationUnit(Idx, file); + if (!*TU) { + fprintf(stderr, "Unable to load translation unit from '%s'!\n", file); + return 0; + } + return 1; +} + +void free_remapped_files(struct CXUnsavedFile *unsaved_files, + int num_unsaved_files) { + int i; + for (i = 0; i != num_unsaved_files; ++i) { + free((char *)unsaved_files[i].Filename); + free((char *)unsaved_files[i].Contents); + } + free(unsaved_files); +} + +int parse_remapped_files(int argc, const char **argv, int start_arg, + struct CXUnsavedFile **unsaved_files, + int *num_unsaved_files) { + int i; + int arg; + int prefix_len = strlen("-remap-file="); + *unsaved_files = 0; + *num_unsaved_files = 0; + + /* Count the number of remapped files. */ + for (arg = start_arg; arg < argc; ++arg) { + if (strncmp(argv[arg], "-remap-file=", prefix_len)) + break; + + ++*num_unsaved_files; + } + + if (*num_unsaved_files == 0) + return 0; + + *unsaved_files + = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) * + *num_unsaved_files); + for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) { + struct CXUnsavedFile *unsaved = *unsaved_files + i; + const char *arg_string = argv[arg] + prefix_len; + int filename_len; + char *filename; + char *contents; + FILE *to_file; + const char *semi = strchr(arg_string, ';'); + if (!semi) { + fprintf(stderr, + "error: -remap-file=from;to argument is missing semicolon\n"); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + + /* Open the file that we're remapping to. */ + to_file = fopen(semi + 1, "rb"); + if (!to_file) { + fprintf(stderr, "error: cannot open file %s that we are remapping to\n", + semi + 1); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + + /* Determine the length of the file we're remapping to. */ + fseek(to_file, 0, SEEK_END); + unsaved->Length = ftell(to_file); + fseek(to_file, 0, SEEK_SET); + + /* Read the contents of the file we're remapping to. */ + contents = (char *)malloc(unsaved->Length + 1); + if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) { + fprintf(stderr, "error: unexpected %s reading 'to' file %s\n", + (feof(to_file) ? "EOF" : "error"), semi + 1); + fclose(to_file); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + contents[unsaved->Length] = 0; + unsaved->Contents = contents; + + /* Close the file. */ + fclose(to_file); + + /* Copy the file name that we're remapping from. */ + filename_len = semi - arg_string; + filename = (char *)malloc(filename_len + 1); + memcpy(filename, arg_string, filename_len); + filename[filename_len] = 0; + unsaved->Filename = filename; + } + + return 0; +} + +/******************************************************************************/ +/* Pretty-printing. */ +/******************************************************************************/ + +static void PrintRange(CXSourceRange R, const char *str) { + CXFile begin_file, end_file; + unsigned begin_line, begin_column, end_line, end_column; + + clang_getSpellingLocation(clang_getRangeStart(R), + &begin_file, &begin_line, &begin_column, 0); + clang_getSpellingLocation(clang_getRangeEnd(R), + &end_file, &end_line, &end_column, 0); + if (!begin_file || !end_file) + return; + + if (str) + printf(" %s=", str); + PrintExtent(stdout, begin_line, begin_column, end_line, end_column); +} + +int want_display_name = 0; + +static void PrintCursor(CXCursor Cursor) { + CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); + if (clang_isInvalid(Cursor.kind)) { + CXString ks = clang_getCursorKindSpelling(Cursor.kind); + printf("Invalid Cursor => %s", clang_getCString(ks)); + clang_disposeString(ks); + } + else { + CXString string, ks; + CXCursor Referenced; + unsigned line, column; + CXCursor SpecializationOf; + CXCursor *overridden; + unsigned num_overridden; + unsigned RefNameRangeNr; + CXSourceRange CursorExtent; + CXSourceRange RefNameRange; + + ks = clang_getCursorKindSpelling(Cursor.kind); + string = want_display_name? clang_getCursorDisplayName(Cursor) + : clang_getCursorSpelling(Cursor); + printf("%s=%s", clang_getCString(ks), + clang_getCString(string)); + clang_disposeString(ks); + clang_disposeString(string); + + Referenced = clang_getCursorReferenced(Cursor); + if (!clang_equalCursors(Referenced, clang_getNullCursor())) { + if (clang_getCursorKind(Referenced) == CXCursor_OverloadedDeclRef) { + unsigned I, N = clang_getNumOverloadedDecls(Referenced); + printf("["); + for (I = 0; I != N; ++I) { + CXCursor Ovl = clang_getOverloadedDecl(Referenced, I); + CXSourceLocation Loc; + if (I) + printf(", "); + + Loc = clang_getCursorLocation(Ovl); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf("%d:%d", line, column); + } + printf("]"); + } else { + CXSourceLocation Loc = clang_getCursorLocation(Referenced); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf(":%d:%d", line, column); + } + } + + if (clang_isCursorDefinition(Cursor)) + printf(" (Definition)"); + + switch (clang_getCursorAvailability(Cursor)) { + case CXAvailability_Available: + break; + + case CXAvailability_Deprecated: + printf(" (deprecated)"); + break; + + case CXAvailability_NotAvailable: + printf(" (unavailable)"); + break; + + case CXAvailability_NotAccessible: + printf(" (inaccessible)"); + break; + } + + if (clang_CXXMethod_isStatic(Cursor)) + printf(" (static)"); + if (clang_CXXMethod_isVirtual(Cursor)) + printf(" (virtual)"); + + if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { + CXType T = + clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor)); + CXString S = clang_getTypeKindSpelling(T.kind); + printf(" [IBOutletCollection=%s]", clang_getCString(S)); + clang_disposeString(S); + } + + if (Cursor.kind == CXCursor_CXXBaseSpecifier) { + enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); + unsigned isVirtual = clang_isVirtualBase(Cursor); + const char *accessStr = 0; + + switch (access) { + case CX_CXXInvalidAccessSpecifier: + accessStr = "invalid"; break; + case CX_CXXPublic: + accessStr = "public"; break; + case CX_CXXProtected: + accessStr = "protected"; break; + case CX_CXXPrivate: + accessStr = "private"; break; + } + + printf(" [access=%s isVirtual=%s]", accessStr, + isVirtual ? "true" : "false"); + } + + SpecializationOf = clang_getSpecializedCursorTemplate(Cursor); + if (!clang_equalCursors(SpecializationOf, clang_getNullCursor())) { + CXSourceLocation Loc = clang_getCursorLocation(SpecializationOf); + CXString Name = clang_getCursorSpelling(SpecializationOf); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf(" [Specialization of %s:%d:%d]", + clang_getCString(Name), line, column); + clang_disposeString(Name); + } + + clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); + if (num_overridden) { + unsigned I; + printf(" [Overrides "); + for (I = 0; I != num_overridden; ++I) { + CXSourceLocation Loc = clang_getCursorLocation(overridden[I]); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + if (I) + printf(", "); + printf("@%d:%d", line, column); + } + printf("]"); + clang_disposeOverriddenCursors(overridden); + } + + if (Cursor.kind == CXCursor_InclusionDirective) { + CXFile File = clang_getIncludedFile(Cursor); + CXString Included = clang_getFileName(File); + printf(" (%s)", clang_getCString(Included)); + clang_disposeString(Included); + + if (clang_isFileMultipleIncludeGuarded(TU, File)) + printf(" [multi-include guarded]"); + } + + CursorExtent = clang_getCursorExtent(Cursor); + RefNameRange = clang_getCursorReferenceNameRange(Cursor, + CXNameRange_WantQualifier + | CXNameRange_WantSinglePiece + | CXNameRange_WantTemplateArgs, + 0); + if (!clang_equalRanges(CursorExtent, RefNameRange)) + PrintRange(RefNameRange, "SingleRefName"); + + for (RefNameRangeNr = 0; 1; RefNameRangeNr++) { + RefNameRange = clang_getCursorReferenceNameRange(Cursor, + CXNameRange_WantQualifier + | CXNameRange_WantTemplateArgs, + RefNameRangeNr); + if (clang_equalRanges(clang_getNullRange(), RefNameRange)) + break; + if (!clang_equalRanges(CursorExtent, RefNameRange)) + PrintRange(RefNameRange, "RefName"); + } + } +} + +static const char* GetCursorSource(CXCursor Cursor) { + CXSourceLocation Loc = clang_getCursorLocation(Cursor); + CXString source; + CXFile file; + clang_getExpansionLocation(Loc, &file, 0, 0, 0); + source = clang_getFileName(file); + if (!clang_getCString(source)) { + clang_disposeString(source); + return "<invalid loc>"; + } + else { + const char *b = basename(clang_getCString(source)); + clang_disposeString(source); + return b; + } +} + +/******************************************************************************/ +/* Callbacks. */ +/******************************************************************************/ + +typedef void (*PostVisitTU)(CXTranslationUnit); + +void PrintDiagnostic(CXDiagnostic Diagnostic) { + FILE *out = stderr; + CXFile file; + CXString Msg; + unsigned display_opts = CXDiagnostic_DisplaySourceLocation + | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges + | CXDiagnostic_DisplayOption; + unsigned i, num_fixits; + + if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored) + return; + + Msg = clang_formatDiagnostic(Diagnostic, display_opts); + fprintf(stderr, "%s\n", clang_getCString(Msg)); + clang_disposeString(Msg); + + clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic), + &file, 0, 0, 0); + if (!file) + return; + + num_fixits = clang_getDiagnosticNumFixIts(Diagnostic); + fprintf(stderr, "Number FIX-ITs = %d\n", num_fixits); + for (i = 0; i != num_fixits; ++i) { + CXSourceRange range; + CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range); + CXSourceLocation start = clang_getRangeStart(range); + CXSourceLocation end = clang_getRangeEnd(range); + unsigned start_line, start_column, end_line, end_column; + CXFile start_file, end_file; + clang_getSpellingLocation(start, &start_file, &start_line, + &start_column, 0); + clang_getSpellingLocation(end, &end_file, &end_line, &end_column, 0); + if (clang_equalLocations(start, end)) { + /* Insertion. */ + if (start_file == file) + fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n", + clang_getCString(insertion_text), start_line, start_column); + } else if (strcmp(clang_getCString(insertion_text), "") == 0) { + /* Removal. */ + if (start_file == file && end_file == file) { + fprintf(out, "FIX-IT: Remove "); + PrintExtent(out, start_line, start_column, end_line, end_column); + fprintf(out, "\n"); + } + } else { + /* Replacement. */ + if (start_file == end_file) { + fprintf(out, "FIX-IT: Replace "); + PrintExtent(out, start_line, start_column, end_line, end_column); + fprintf(out, " with \"%s\"\n", clang_getCString(insertion_text)); + } + break; + } + clang_disposeString(insertion_text); + } +} + +void PrintDiagnosticSet(CXDiagnosticSet Set) { + int i = 0, n = clang_getNumDiagnosticsInSet(Set); + for ( ; i != n ; ++i) { + CXDiagnostic Diag = clang_getDiagnosticInSet(Set, i); + CXDiagnosticSet ChildDiags = clang_getChildDiagnostics(Diag); + PrintDiagnostic(Diag); + if (ChildDiags) + PrintDiagnosticSet(ChildDiags); + } +} + +void PrintDiagnostics(CXTranslationUnit TU) { + CXDiagnosticSet TUSet = clang_getDiagnosticSetFromTU(TU); + PrintDiagnosticSet(TUSet); + clang_disposeDiagnosticSet(TUSet); +} + +void PrintMemoryUsage(CXTranslationUnit TU) { + unsigned long total = 0; + unsigned i = 0; + CXTUResourceUsage usage = clang_getCXTUResourceUsage(TU); + fprintf(stderr, "Memory usage:\n"); + for (i = 0 ; i != usage.numEntries; ++i) { + const char *name = clang_getTUResourceUsageName(usage.entries[i].kind); + unsigned long amount = usage.entries[i].amount; + total += amount; + fprintf(stderr, " %s : %ld bytes (%f MBytes)\n", name, amount, + ((double) amount)/(1024*1024)); + } + fprintf(stderr, " TOTAL = %ld bytes (%f MBytes)\n", total, + ((double) total)/(1024*1024)); + clang_disposeCXTUResourceUsage(usage); +} + +/******************************************************************************/ +/* Logic for testing traversal. */ +/******************************************************************************/ + +static const char *FileCheckPrefix = "CHECK"; + +static void PrintCursorExtent(CXCursor C) { + CXSourceRange extent = clang_getCursorExtent(C); + PrintRange(extent, "Extent"); +} + +/* Data used by all of the visitors. */ +typedef struct { + CXTranslationUnit TU; + enum CXCursorKind *Filter; +} VisitorData; + + +enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, + CXCursor Parent, + CXClientData ClientData) { + VisitorData *Data = (VisitorData *)ClientData; + if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) { + CXSourceLocation Loc = clang_getCursorLocation(Cursor); + unsigned line, column; + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf("// %s: %s:%d:%d: ", FileCheckPrefix, + GetCursorSource(Cursor), line, column); + PrintCursor(Cursor); + PrintCursorExtent(Cursor); + printf("\n"); + return CXChildVisit_Recurse; + } + + return CXChildVisit_Continue; +} + +static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor, + CXCursor Parent, + CXClientData ClientData) { + const char *startBuf, *endBuf; + unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn; + CXCursor Ref; + VisitorData *Data = (VisitorData *)ClientData; + + if (Cursor.kind != CXCursor_FunctionDecl || + !clang_isCursorDefinition(Cursor)) + return CXChildVisit_Continue; + + clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf, + &startLine, &startColumn, + &endLine, &endColumn); + /* Probe the entire body, looking for both decls and refs. */ + curLine = startLine; + curColumn = startColumn; + + while (startBuf < endBuf) { + CXSourceLocation Loc; + CXFile file; + CXString source; + + if (*startBuf == '\n') { + startBuf++; + curLine++; + curColumn = 1; + } else if (*startBuf != '\t') + curColumn++; + + Loc = clang_getCursorLocation(Cursor); + clang_getSpellingLocation(Loc, &file, 0, 0, 0); + + source = clang_getFileName(file); + if (clang_getCString(source)) { + CXSourceLocation RefLoc + = clang_getLocation(Data->TU, file, curLine, curColumn); + Ref = clang_getCursor(Data->TU, RefLoc); + if (Ref.kind == CXCursor_NoDeclFound) { + /* Nothing found here; that's fine. */ + } else if (Ref.kind != CXCursor_FunctionDecl) { + printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref), + curLine, curColumn); + PrintCursor(Ref); + printf("\n"); + } + } + clang_disposeString(source); + startBuf++; + } + + return CXChildVisit_Continue; +} + +/******************************************************************************/ +/* USR testing. */ +/******************************************************************************/ + +enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent, + CXClientData ClientData) { + VisitorData *Data = (VisitorData *)ClientData; + if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) { + CXString USR = clang_getCursorUSR(C); + const char *cstr = clang_getCString(USR); + if (!cstr || cstr[0] == '\0') { + clang_disposeString(USR); + return CXChildVisit_Recurse; + } + printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), cstr); + + PrintCursorExtent(C); + printf("\n"); + clang_disposeString(USR); + + return CXChildVisit_Recurse; + } + + return CXChildVisit_Continue; +} + +/******************************************************************************/ +/* Inclusion stack testing. */ +/******************************************************************************/ + +void InclusionVisitor(CXFile includedFile, CXSourceLocation *includeStack, + unsigned includeStackLen, CXClientData data) { + + unsigned i; + CXString fname; + + fname = clang_getFileName(includedFile); + printf("file: %s\nincluded by:\n", clang_getCString(fname)); + clang_disposeString(fname); + + for (i = 0; i < includeStackLen; ++i) { + CXFile includingFile; + unsigned line, column; + clang_getSpellingLocation(includeStack[i], &includingFile, &line, + &column, 0); + fname = clang_getFileName(includingFile); + printf(" %s:%d:%d\n", clang_getCString(fname), line, column); + clang_disposeString(fname); + } + printf("\n"); +} + +void PrintInclusionStack(CXTranslationUnit TU) { + clang_getInclusions(TU, InclusionVisitor, NULL); +} + +/******************************************************************************/ +/* Linkage testing. */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p, + CXClientData d) { + const char *linkage = 0; + + if (clang_isInvalid(clang_getCursorKind(cursor))) + return CXChildVisit_Recurse; + + switch (clang_getCursorLinkage(cursor)) { + case CXLinkage_Invalid: break; + case CXLinkage_NoLinkage: linkage = "NoLinkage"; break; + case CXLinkage_Internal: linkage = "Internal"; break; + case CXLinkage_UniqueExternal: linkage = "UniqueExternal"; break; + case CXLinkage_External: linkage = "External"; break; + } + + if (linkage) { + PrintCursor(cursor); + printf("linkage=%s\n", linkage); + } + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Typekind testing. */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintTypeKind(CXCursor cursor, CXCursor p, + CXClientData d) { + if (!clang_isInvalid(clang_getCursorKind(cursor))) { + CXType T = clang_getCursorType(cursor); + CXString S = clang_getTypeKindSpelling(T.kind); + PrintCursor(cursor); + printf(" typekind=%s", clang_getCString(S)); + if (clang_isConstQualifiedType(T)) + printf(" const"); + if (clang_isVolatileQualifiedType(T)) + printf(" volatile"); + if (clang_isRestrictQualifiedType(T)) + printf(" restrict"); + clang_disposeString(S); + /* Print the canonical type if it is different. */ + { + CXType CT = clang_getCanonicalType(T); + if (!clang_equalTypes(T, CT)) { + CXString CS = clang_getTypeKindSpelling(CT.kind); + printf(" [canonical=%s]", clang_getCString(CS)); + clang_disposeString(CS); + } + } + /* Print the return type if it exists. */ + { + CXType RT = clang_getCursorResultType(cursor); + if (RT.kind != CXType_Invalid) { + CXString RS = clang_getTypeKindSpelling(RT.kind); + printf(" [result=%s]", clang_getCString(RS)); + clang_disposeString(RS); + } + } + /* Print the argument types if they exist. */ + { + int numArgs = clang_Cursor_getNumArguments(cursor); + if (numArgs != -1 && numArgs != 0) { + int i; + printf(" [args="); + for (i = 0; i < numArgs; ++i) { + CXType T = clang_getCursorType(clang_Cursor_getArgument(cursor, i)); + if (T.kind != CXType_Invalid) { + CXString S = clang_getTypeKindSpelling(T.kind); + printf(" %s", clang_getCString(S)); + clang_disposeString(S); + } + } + printf("]"); + } + } + /* Print if this is a non-POD type. */ + printf(" [isPOD=%d]", clang_isPODType(T)); + + printf("\n"); + } + return CXChildVisit_Recurse; +} + + +/******************************************************************************/ +/* Loading ASTs/source. */ +/******************************************************************************/ + +static int perform_test_load(CXIndex Idx, CXTranslationUnit TU, + const char *filter, const char *prefix, + CXCursorVisitor Visitor, + PostVisitTU PV) { + + if (prefix) + FileCheckPrefix = prefix; + + if (Visitor) { + enum CXCursorKind K = CXCursor_NotImplemented; + enum CXCursorKind *ck = &K; + VisitorData Data; + + /* Perform some simple filtering. */ + if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL; + else if (!strcmp(filter, "all-display") || + !strcmp(filter, "local-display")) { + ck = NULL; + want_display_name = 1; + } + else if (!strcmp(filter, "none")) K = (enum CXCursorKind) ~0; + else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl; + else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl; + else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl; + else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl; + else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl; + else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor; + else { + fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter); + return 1; + } + + Data.TU = TU; + Data.Filter = ck; + clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data); + } + + if (PV) + PV(TU); + + PrintDiagnostics(TU); + if (checkForErrors(TU) != 0) { + clang_disposeTranslationUnit(TU); + return -1; + } + + clang_disposeTranslationUnit(TU); + return 0; +} + +int perform_test_load_tu(const char *file, const char *filter, + const char *prefix, CXCursorVisitor Visitor, + PostVisitTU PV) { + CXIndex Idx; + CXTranslationUnit TU; + int result; + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + !strcmp(filter, "local") ? 1 : 0, + /* displayDiagnosics=*/1); + + if (!CreateTranslationUnit(Idx, file, &TU)) { + clang_disposeIndex(Idx); + return 1; + } + + result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV); + clang_disposeIndex(Idx); + return result; +} + +int perform_test_load_source(int argc, const char **argv, + const char *filter, CXCursorVisitor Visitor, + PostVisitTU PV) { + CXIndex Idx; + CXTranslationUnit TU; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + int result; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + (!strcmp(filter, "local") || + !strcmp(filter, "local-display"))? 1 : 0, + /* displayDiagnosics=*/0); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + clang_disposeIndex(Idx); + return -1; + } + + TU = clang_parseTranslationUnit(Idx, 0, + argv + num_unsaved_files, + argc - num_unsaved_files, + unsaved_files, num_unsaved_files, + getDefaultParsingOptions()); + if (!TU) { + fprintf(stderr, "Unable to load translation unit!\n"); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return 1; + } + + result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return result; +} + +int perform_test_reparse_source(int argc, const char **argv, int trials, + const char *filter, CXCursorVisitor Visitor, + PostVisitTU PV) { + CXIndex Idx; + CXTranslationUnit TU; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + int result; + int trial; + int remap_after_trial = 0; + char *endptr = 0; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + !strcmp(filter, "local") ? 1 : 0, + /* displayDiagnosics=*/0); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + clang_disposeIndex(Idx); + return -1; + } + + /* Load the initial translation unit -- we do this without honoring remapped + * files, so that we have a way to test results after changing the source. */ + TU = clang_parseTranslationUnit(Idx, 0, + argv + num_unsaved_files, + argc - num_unsaved_files, + 0, 0, getDefaultParsingOptions()); + if (!TU) { + fprintf(stderr, "Unable to load translation unit!\n"); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return 1; + } + + if (checkForErrors(TU) != 0) + return -1; + + if (getenv("CINDEXTEST_REMAP_AFTER_TRIAL")) { + remap_after_trial = + strtol(getenv("CINDEXTEST_REMAP_AFTER_TRIAL"), &endptr, 10); + } + + for (trial = 0; trial < trials; ++trial) { + if (clang_reparseTranslationUnit(TU, + trial >= remap_after_trial ? num_unsaved_files : 0, + trial >= remap_after_trial ? unsaved_files : 0, + clang_defaultReparseOptions(TU))) { + fprintf(stderr, "Unable to reparse translation unit!\n"); + clang_disposeTranslationUnit(TU); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + } + + result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV); + + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return result; +} + +/******************************************************************************/ +/* Logic for testing clang_getCursor(). */ +/******************************************************************************/ + +static void print_cursor_file_scan(CXTranslationUnit TU, CXCursor cursor, + unsigned start_line, unsigned start_col, + unsigned end_line, unsigned end_col, + const char *prefix) { + printf("// %s: ", FileCheckPrefix); + if (prefix) + printf("-%s", prefix); + PrintExtent(stdout, start_line, start_col, end_line, end_col); + printf(" "); + PrintCursor(cursor); + printf("\n"); +} + +static int perform_file_scan(const char *ast_file, const char *source_file, + const char *prefix) { + CXIndex Idx; + CXTranslationUnit TU; + FILE *fp; + CXCursor prevCursor = clang_getNullCursor(); + CXFile file; + unsigned line = 1, col = 1; + unsigned start_line = 1, start_col = 1; + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnosics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + + if (!CreateTranslationUnit(Idx, ast_file, &TU)) + return 1; + + if ((fp = fopen(source_file, "r")) == NULL) { + fprintf(stderr, "Could not open '%s'\n", source_file); + return 1; + } + + file = clang_getFile(TU, source_file); + for (;;) { + CXCursor cursor; + int c = fgetc(fp); + + if (c == '\n') { + ++line; + col = 1; + } else + ++col; + + /* Check the cursor at this position, and dump the previous one if we have + * found something new. + */ + cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, col)); + if ((c == EOF || !clang_equalCursors(cursor, prevCursor)) && + prevCursor.kind != CXCursor_InvalidFile) { + print_cursor_file_scan(TU, prevCursor, start_line, start_col, + line, col, prefix); + start_line = line; + start_col = col; + } + if (c == EOF) + break; + + prevCursor = cursor; + } + + fclose(fp); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(Idx); + return 0; +} + +/******************************************************************************/ +/* Logic for testing clang code completion. */ +/******************************************************************************/ + +/* Parse file:line:column from the input string. Returns 0 on success, non-zero + on failure. If successful, the pointer *filename will contain newly-allocated + memory (that will be owned by the caller) to store the file name. */ +int parse_file_line_column(const char *input, char **filename, unsigned *line, + unsigned *column, unsigned *second_line, + unsigned *second_column) { + /* Find the second colon. */ + const char *last_colon = strrchr(input, ':'); + unsigned values[4], i; + unsigned num_values = (second_line && second_column)? 4 : 2; + + char *endptr = 0; + if (!last_colon || last_colon == input) { + if (num_values == 4) + fprintf(stderr, "could not parse filename:line:column:line:column in " + "'%s'\n", input); + else + fprintf(stderr, "could not parse filename:line:column in '%s'\n", input); + return 1; + } + + for (i = 0; i != num_values; ++i) { + const char *prev_colon; + + /* Parse the next line or column. */ + values[num_values - i - 1] = strtol(last_colon + 1, &endptr, 10); + if (*endptr != 0 && *endptr != ':') { + fprintf(stderr, "could not parse %s in '%s'\n", + (i % 2 ? "column" : "line"), input); + return 1; + } + + if (i + 1 == num_values) + break; + + /* Find the previous colon. */ + prev_colon = last_colon - 1; + while (prev_colon != input && *prev_colon != ':') + --prev_colon; + if (prev_colon == input) { + fprintf(stderr, "could not parse %s in '%s'\n", + (i % 2 == 0? "column" : "line"), input); + return 1; + } + + last_colon = prev_colon; + } + + *line = values[0]; + *column = values[1]; + + if (second_line && second_column) { + *second_line = values[2]; + *second_column = values[3]; + } + + /* Copy the file name. */ + *filename = (char*)malloc(last_colon - input + 1); + memcpy(*filename, input, last_colon - input); + (*filename)[last_colon - input] = 0; + return 0; +} + +const char * +clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) { + switch (Kind) { + case CXCompletionChunk_Optional: return "Optional"; + case CXCompletionChunk_TypedText: return "TypedText"; + case CXCompletionChunk_Text: return "Text"; + case CXCompletionChunk_Placeholder: return "Placeholder"; + case CXCompletionChunk_Informative: return "Informative"; + case CXCompletionChunk_CurrentParameter: return "CurrentParameter"; + case CXCompletionChunk_LeftParen: return "LeftParen"; + case CXCompletionChunk_RightParen: return "RightParen"; + case CXCompletionChunk_LeftBracket: return "LeftBracket"; + case CXCompletionChunk_RightBracket: return "RightBracket"; + case CXCompletionChunk_LeftBrace: return "LeftBrace"; + case CXCompletionChunk_RightBrace: return "RightBrace"; + case CXCompletionChunk_LeftAngle: return "LeftAngle"; + case CXCompletionChunk_RightAngle: return "RightAngle"; + case CXCompletionChunk_Comma: return "Comma"; + case CXCompletionChunk_ResultType: return "ResultType"; + case CXCompletionChunk_Colon: return "Colon"; + case CXCompletionChunk_SemiColon: return "SemiColon"; + case CXCompletionChunk_Equal: return "Equal"; + case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace"; + case CXCompletionChunk_VerticalSpace: return "VerticalSpace"; + } + + return "Unknown"; +} + +static int checkForErrors(CXTranslationUnit TU) { + unsigned Num, i; + CXDiagnostic Diag; + CXString DiagStr; + + if (!getenv("CINDEXTEST_FAILONERROR")) + return 0; + + Num = clang_getNumDiagnostics(TU); + for (i = 0; i != Num; ++i) { + Diag = clang_getDiagnostic(TU, i); + if (clang_getDiagnosticSeverity(Diag) >= CXDiagnostic_Error) { + DiagStr = clang_formatDiagnostic(Diag, + clang_defaultDiagnosticDisplayOptions()); + fprintf(stderr, "%s\n", clang_getCString(DiagStr)); + clang_disposeString(DiagStr); + clang_disposeDiagnostic(Diag); + return -1; + } + clang_disposeDiagnostic(Diag); + } + + return 0; +} + +void print_completion_string(CXCompletionString completion_string, FILE *file) { + int I, N; + + N = clang_getNumCompletionChunks(completion_string); + for (I = 0; I != N; ++I) { + CXString text; + const char *cstr; + enum CXCompletionChunkKind Kind + = clang_getCompletionChunkKind(completion_string, I); + + if (Kind == CXCompletionChunk_Optional) { + fprintf(file, "{Optional "); + print_completion_string( + clang_getCompletionChunkCompletionString(completion_string, I), + file); + fprintf(file, "}"); + continue; + } + + if (Kind == CXCompletionChunk_VerticalSpace) { + fprintf(file, "{VerticalSpace }"); + continue; + } + + text = clang_getCompletionChunkText(completion_string, I); + cstr = clang_getCString(text); + fprintf(file, "{%s %s}", + clang_getCompletionChunkKindSpelling(Kind), + cstr ? cstr : ""); + clang_disposeString(text); + } + +} + +void print_completion_result(CXCompletionResult *completion_result, + CXClientData client_data) { + FILE *file = (FILE *)client_data; + CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind); + unsigned annotationCount; + enum CXCursorKind ParentKind; + CXString ParentName; + + fprintf(file, "%s:", clang_getCString(ks)); + clang_disposeString(ks); + + print_completion_string(completion_result->CompletionString, file); + fprintf(file, " (%u)", + clang_getCompletionPriority(completion_result->CompletionString)); + switch (clang_getCompletionAvailability(completion_result->CompletionString)){ + case CXAvailability_Available: + break; + + case CXAvailability_Deprecated: + fprintf(file, " (deprecated)"); + break; + + case CXAvailability_NotAvailable: + fprintf(file, " (unavailable)"); + break; + + case CXAvailability_NotAccessible: + fprintf(file, " (inaccessible)"); + break; + } + + annotationCount = clang_getCompletionNumAnnotations( + completion_result->CompletionString); + if (annotationCount) { + unsigned i; + fprintf(file, " ("); + for (i = 0; i < annotationCount; ++i) { + if (i != 0) + fprintf(file, ", "); + fprintf(file, "\"%s\"", + clang_getCString(clang_getCompletionAnnotation( + completion_result->CompletionString, i))); + } + fprintf(file, ")"); + } + + if (!getenv("CINDEXTEST_NO_COMPLETION_PARENTS")) { + ParentName = clang_getCompletionParent(completion_result->CompletionString, + &ParentKind); + if (ParentKind != CXCursor_NotImplemented) { + CXString KindSpelling = clang_getCursorKindSpelling(ParentKind); + fprintf(file, " (parent: %s '%s')", + clang_getCString(KindSpelling), + clang_getCString(ParentName)); + clang_disposeString(KindSpelling); + } + clang_disposeString(ParentName); + } + + fprintf(file, "\n"); +} + +void print_completion_contexts(unsigned long long contexts, FILE *file) { + fprintf(file, "Completion contexts:\n"); + if (contexts == CXCompletionContext_Unknown) { + fprintf(file, "Unknown\n"); + } + if (contexts & CXCompletionContext_AnyType) { + fprintf(file, "Any type\n"); + } + if (contexts & CXCompletionContext_AnyValue) { + fprintf(file, "Any value\n"); + } + if (contexts & CXCompletionContext_ObjCObjectValue) { + fprintf(file, "Objective-C object value\n"); + } + if (contexts & CXCompletionContext_ObjCSelectorValue) { + fprintf(file, "Objective-C selector value\n"); + } + if (contexts & CXCompletionContext_CXXClassTypeValue) { + fprintf(file, "C++ class type value\n"); + } + if (contexts & CXCompletionContext_DotMemberAccess) { + fprintf(file, "Dot member access\n"); + } + if (contexts & CXCompletionContext_ArrowMemberAccess) { + fprintf(file, "Arrow member access\n"); + } + if (contexts & CXCompletionContext_ObjCPropertyAccess) { + fprintf(file, "Objective-C property access\n"); + } + if (contexts & CXCompletionContext_EnumTag) { + fprintf(file, "Enum tag\n"); + } + if (contexts & CXCompletionContext_UnionTag) { + fprintf(file, "Union tag\n"); + } + if (contexts & CXCompletionContext_StructTag) { + fprintf(file, "Struct tag\n"); + } + if (contexts & CXCompletionContext_ClassTag) { + fprintf(file, "Class name\n"); + } + if (contexts & CXCompletionContext_Namespace) { + fprintf(file, "Namespace or namespace alias\n"); + } + if (contexts & CXCompletionContext_NestedNameSpecifier) { + fprintf(file, "Nested name specifier\n"); + } + if (contexts & CXCompletionContext_ObjCInterface) { + fprintf(file, "Objective-C interface\n"); + } + if (contexts & CXCompletionContext_ObjCProtocol) { + fprintf(file, "Objective-C protocol\n"); + } + if (contexts & CXCompletionContext_ObjCCategory) { + fprintf(file, "Objective-C category\n"); + } + if (contexts & CXCompletionContext_ObjCInstanceMessage) { + fprintf(file, "Objective-C instance method\n"); + } + if (contexts & CXCompletionContext_ObjCClassMessage) { + fprintf(file, "Objective-C class method\n"); + } + if (contexts & CXCompletionContext_ObjCSelectorName) { + fprintf(file, "Objective-C selector name\n"); + } + if (contexts & CXCompletionContext_MacroName) { + fprintf(file, "Macro name\n"); + } + if (contexts & CXCompletionContext_NaturalLanguage) { + fprintf(file, "Natural language\n"); + } +} + +int my_stricmp(const char *s1, const char *s2) { + while (*s1 && *s2) { + int c1 = tolower((unsigned char)*s1), c2 = tolower((unsigned char)*s2); + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + + ++s1; + ++s2; + } + + if (*s1) + return 1; + else if (*s2) + return -1; + return 0; +} + +int perform_code_completion(int argc, const char **argv, int timing_only) { + const char *input = argv[1]; + char *filename = 0; + unsigned line; + unsigned column; + CXIndex CIdx; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + CXCodeCompleteResults *results = 0; + CXTranslationUnit TU = 0; + unsigned I, Repeats = 1; + unsigned completionOptions = clang_defaultCodeCompleteOptions(); + + if (getenv("CINDEXTEST_CODE_COMPLETE_PATTERNS")) + completionOptions |= CXCodeComplete_IncludeCodePatterns; + + if (timing_only) + input += strlen("-code-completion-timing="); + else + input += strlen("-code-completion-at="); + + if ((errorCode = parse_file_line_column(input, &filename, &line, &column, + 0, 0))) + return errorCode; + + if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) + return -1; + + CIdx = clang_createIndex(0, 0); + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + TU = clang_parseTranslationUnit(CIdx, 0, + argv + num_unsaved_files + 2, + argc - num_unsaved_files - 2, + 0, 0, getDefaultParsingOptions()); + if (!TU) { + fprintf(stderr, "Unable to load translation unit!\n"); + return 1; + } + + if (clang_reparseTranslationUnit(TU, 0, 0, clang_defaultReparseOptions(TU))) { + fprintf(stderr, "Unable to reparse translation init!\n"); + return 1; + } + + for (I = 0; I != Repeats; ++I) { + results = clang_codeCompleteAt(TU, filename, line, column, + unsaved_files, num_unsaved_files, + completionOptions); + if (!results) { + fprintf(stderr, "Unable to perform code completion!\n"); + return 1; + } + if (I != Repeats-1) + clang_disposeCodeCompleteResults(results); + } + + if (results) { + unsigned i, n = results->NumResults, containerIsIncomplete = 0; + unsigned long long contexts; + enum CXCursorKind containerKind; + CXString objCSelector; + const char *selectorString; + if (!timing_only) { + /* Sort the code-completion results based on the typed text. */ + clang_sortCodeCompletionResults(results->Results, results->NumResults); + + for (i = 0; i != n; ++i) + print_completion_result(results->Results + i, stdout); + } + n = clang_codeCompleteGetNumDiagnostics(results); + for (i = 0; i != n; ++i) { + CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); + PrintDiagnostic(diag); + clang_disposeDiagnostic(diag); + } + + contexts = clang_codeCompleteGetContexts(results); + print_completion_contexts(contexts, stdout); + + containerKind = clang_codeCompleteGetContainerKind(results, + &containerIsIncomplete); + + if (containerKind != CXCursor_InvalidCode) { + /* We have found a container */ + CXString containerUSR, containerKindSpelling; + containerKindSpelling = clang_getCursorKindSpelling(containerKind); + printf("Container Kind: %s\n", clang_getCString(containerKindSpelling)); + clang_disposeString(containerKindSpelling); + + if (containerIsIncomplete) { + printf("Container is incomplete\n"); + } + else { + printf("Container is complete\n"); + } + + containerUSR = clang_codeCompleteGetContainerUSR(results); + printf("Container USR: %s\n", clang_getCString(containerUSR)); + clang_disposeString(containerUSR); + } + + objCSelector = clang_codeCompleteGetObjCSelector(results); + selectorString = clang_getCString(objCSelector); + if (selectorString && strlen(selectorString) > 0) { + printf("Objective-C selector: %s\n", selectorString); + } + clang_disposeString(objCSelector); + + clang_disposeCodeCompleteResults(results); + } + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(filename); + + free_remapped_files(unsaved_files, num_unsaved_files); + + return 0; +} + +typedef struct { + char *filename; + unsigned line; + unsigned column; +} CursorSourceLocation; + +static int inspect_cursor_at(int argc, const char **argv) { + CXIndex CIdx; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + CXTranslationUnit TU; + CXCursor Cursor; + CursorSourceLocation *Locations = 0; + unsigned NumLocations = 0, Loc; + unsigned Repeats = 1; + unsigned I; + + /* Count the number of locations. */ + while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1]) + ++NumLocations; + + /* Parse the locations. */ + assert(NumLocations > 0 && "Unable to count locations?"); + Locations = (CursorSourceLocation *)malloc( + NumLocations * sizeof(CursorSourceLocation)); + for (Loc = 0; Loc < NumLocations; ++Loc) { + const char *input = argv[Loc + 1] + strlen("-cursor-at="); + if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, + &Locations[Loc].line, + &Locations[Loc].column, 0, 0))) + return errorCode; + } + + if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, + &num_unsaved_files)) + return -1; + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + /* Parse the translation unit. When we're testing clang_getCursor() after + reparsing, don't remap unsaved files until the second parse. */ + CIdx = clang_createIndex(1, 1); + TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], + argv + num_unsaved_files + 1 + NumLocations, + argc - num_unsaved_files - 2 - NumLocations, + unsaved_files, + Repeats > 1? 0 : num_unsaved_files, + getDefaultParsingOptions()); + + if (!TU) { + fprintf(stderr, "unable to parse input\n"); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (I = 0; I != Repeats; ++I) { + if (Repeats > 1 && + clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU))) { + clang_disposeTranslationUnit(TU); + return 1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (Loc = 0; Loc < NumLocations; ++Loc) { + CXFile file = clang_getFile(TU, Locations[Loc].filename); + if (!file) + continue; + + Cursor = clang_getCursor(TU, + clang_getLocation(TU, file, Locations[Loc].line, + Locations[Loc].column)); + + if (checkForErrors(TU) != 0) + return -1; + + if (I + 1 == Repeats) { + CXCompletionString completionString = clang_getCursorCompletionString( + Cursor); + CXSourceLocation CursorLoc = clang_getCursorLocation(Cursor); + CXString Spelling; + const char *cspell; + unsigned line, column; + clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0); + printf("%d:%d ", line, column); + PrintCursor(Cursor); + PrintCursorExtent(Cursor); + Spelling = clang_getCursorSpelling(Cursor); + cspell = clang_getCString(Spelling); + if (cspell && strlen(cspell) != 0) { + unsigned pieceIndex; + printf(" Spelling=%s (", cspell); + for (pieceIndex = 0; ; ++pieceIndex) { + CXSourceRange range = + clang_Cursor_getSpellingNameRange(Cursor, pieceIndex, 0); + if (clang_Range_isNull(range)) + break; + PrintRange(range, 0); + } + printf(")"); + } + clang_disposeString(Spelling); + if (clang_Cursor_getObjCSelectorIndex(Cursor) != -1) + printf(" Selector index=%d",clang_Cursor_getObjCSelectorIndex(Cursor)); + if (completionString != NULL) { + printf("\nCompletion string: "); + print_completion_string(completionString, stdout); + } + printf("\n"); + free(Locations[Loc].filename); + } + } + } + + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(Locations); + free_remapped_files(unsaved_files, num_unsaved_files); + return 0; +} + +static enum CXVisitorResult findFileRefsVisit(void *context, + CXCursor cursor, CXSourceRange range) { + if (clang_Range_isNull(range)) + return CXVisit_Continue; + + PrintCursor(cursor); + PrintRange(range, ""); + printf("\n"); + return CXVisit_Continue; +} + +static int find_file_refs_at(int argc, const char **argv) { + CXIndex CIdx; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + CXTranslationUnit TU; + CXCursor Cursor; + CursorSourceLocation *Locations = 0; + unsigned NumLocations = 0, Loc; + unsigned Repeats = 1; + unsigned I; + + /* Count the number of locations. */ + while (strstr(argv[NumLocations+1], "-file-refs-at=") == argv[NumLocations+1]) + ++NumLocations; + + /* Parse the locations. */ + assert(NumLocations > 0 && "Unable to count locations?"); + Locations = (CursorSourceLocation *)malloc( + NumLocations * sizeof(CursorSourceLocation)); + for (Loc = 0; Loc < NumLocations; ++Loc) { + const char *input = argv[Loc + 1] + strlen("-file-refs-at="); + if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, + &Locations[Loc].line, + &Locations[Loc].column, 0, 0))) + return errorCode; + } + + if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, + &num_unsaved_files)) + return -1; + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + /* Parse the translation unit. When we're testing clang_getCursor() after + reparsing, don't remap unsaved files until the second parse. */ + CIdx = clang_createIndex(1, 1); + TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], + argv + num_unsaved_files + 1 + NumLocations, + argc - num_unsaved_files - 2 - NumLocations, + unsaved_files, + Repeats > 1? 0 : num_unsaved_files, + getDefaultParsingOptions()); + + if (!TU) { + fprintf(stderr, "unable to parse input\n"); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (I = 0; I != Repeats; ++I) { + if (Repeats > 1 && + clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU))) { + clang_disposeTranslationUnit(TU); + return 1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (Loc = 0; Loc < NumLocations; ++Loc) { + CXFile file = clang_getFile(TU, Locations[Loc].filename); + if (!file) + continue; + + Cursor = clang_getCursor(TU, + clang_getLocation(TU, file, Locations[Loc].line, + Locations[Loc].column)); + + if (checkForErrors(TU) != 0) + return -1; + + if (I + 1 == Repeats) { + CXCursorAndRangeVisitor visitor = { 0, findFileRefsVisit }; + PrintCursor(Cursor); + printf("\n"); + clang_findReferencesInFile(Cursor, file, visitor); + free(Locations[Loc].filename); + + if (checkForErrors(TU) != 0) + return -1; + } + } + } + + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(Locations); + free_remapped_files(unsaved_files, num_unsaved_files); + return 0; +} + +typedef struct { + const char *check_prefix; + int first_check_printed; + int fail_for_error; + int abort; + const char *main_filename; +} IndexData; + +static void printCheck(IndexData *data) { + if (data->check_prefix) { + if (data->first_check_printed) { + printf("// %s-NEXT: ", data->check_prefix); + } else { + printf("// %s : ", data->check_prefix); + data->first_check_printed = 1; + } + } +} + +static void printCXIndexFile(CXIdxClientFile file) { + CXString filename = clang_getFileName((CXFile)file); + printf("%s", clang_getCString(filename)); + clang_disposeString(filename); +} + +static void printCXIndexLoc(CXIdxLoc loc, CXClientData client_data) { + IndexData *index_data; + CXString filename; + const char *cname; + CXIdxClientFile file; + unsigned line, column; + int isMainFile; + + index_data = (IndexData *)client_data; + clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); + if (line == 0) { + printf("<null loc>"); + return; + } + if (!file) { + printf("<no idxfile>"); + return; + } + filename = clang_getFileName((CXFile)file); + cname = clang_getCString(filename); + if (strcmp(cname, index_data->main_filename) == 0) + isMainFile = 1; + else + isMainFile = 0; + clang_disposeString(filename); + + if (!isMainFile) { + printCXIndexFile(file); + printf(":"); + } + printf("%d:%d", line, column); +} + +static unsigned digitCount(unsigned val) { + unsigned c = 1; + while (1) { + if (val < 10) + return c; + ++c; + val /= 10; + } +} + +static CXIdxClientContainer makeClientContainer(const CXIdxEntityInfo *info, + CXIdxLoc loc) { + const char *name; + char *newStr; + CXIdxClientFile file; + unsigned line, column; + + name = info->name; + if (!name) + name = "<anon-tag>"; + + clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); + /* FIXME: free these.*/ + newStr = (char *)malloc(strlen(name) + + digitCount(line) + digitCount(column) + 3); + sprintf(newStr, "%s:%d:%d", name, line, column); + return (CXIdxClientContainer)newStr; +} + +static void printCXIndexContainer(const CXIdxContainerInfo *info) { + CXIdxClientContainer container; + container = clang_index_getClientContainer(info); + if (!container) + printf("[<<NULL>>]"); + else + printf("[%s]", (const char *)container); +} + +static const char *getEntityKindString(CXIdxEntityKind kind) { + switch (kind) { + case CXIdxEntity_Unexposed: return "<<UNEXPOSED>>"; + case CXIdxEntity_Typedef: return "typedef"; + case CXIdxEntity_Function: return "function"; + case CXIdxEntity_Variable: return "variable"; + case CXIdxEntity_Field: return "field"; + case CXIdxEntity_EnumConstant: return "enumerator"; + case CXIdxEntity_ObjCClass: return "objc-class"; + case CXIdxEntity_ObjCProtocol: return "objc-protocol"; + case CXIdxEntity_ObjCCategory: return "objc-category"; + case CXIdxEntity_ObjCInstanceMethod: return "objc-instance-method"; + case CXIdxEntity_ObjCClassMethod: return "objc-class-method"; + case CXIdxEntity_ObjCProperty: return "objc-property"; + case CXIdxEntity_ObjCIvar: return "objc-ivar"; + case CXIdxEntity_Enum: return "enum"; + case CXIdxEntity_Struct: return "struct"; + case CXIdxEntity_Union: return "union"; + case CXIdxEntity_CXXClass: return "c++-class"; + case CXIdxEntity_CXXNamespace: return "namespace"; + case CXIdxEntity_CXXNamespaceAlias: return "namespace-alias"; + case CXIdxEntity_CXXStaticVariable: return "c++-static-var"; + case CXIdxEntity_CXXStaticMethod: return "c++-static-method"; + case CXIdxEntity_CXXInstanceMethod: return "c++-instance-method"; + case CXIdxEntity_CXXConstructor: return "constructor"; + case CXIdxEntity_CXXDestructor: return "destructor"; + case CXIdxEntity_CXXConversionFunction: return "conversion-func"; + case CXIdxEntity_CXXTypeAlias: return "type-alias"; + } + assert(0 && "Garbage entity kind"); + return 0; +} + +static const char *getEntityTemplateKindString(CXIdxEntityCXXTemplateKind kind) { + switch (kind) { + case CXIdxEntity_NonTemplate: return ""; + case CXIdxEntity_Template: return "-template"; + case CXIdxEntity_TemplatePartialSpecialization: + return "-template-partial-spec"; + case CXIdxEntity_TemplateSpecialization: return "-template-spec"; + } + assert(0 && "Garbage entity kind"); + return 0; +} + +static const char *getEntityLanguageString(CXIdxEntityLanguage kind) { + switch (kind) { + case CXIdxEntityLang_None: return "<none>"; + case CXIdxEntityLang_C: return "C"; + case CXIdxEntityLang_ObjC: return "ObjC"; + case CXIdxEntityLang_CXX: return "C++"; + } + assert(0 && "Garbage language kind"); + return 0; +} + +static void printEntityInfo(const char *cb, + CXClientData client_data, + const CXIdxEntityInfo *info) { + const char *name; + IndexData *index_data; + unsigned i; + index_data = (IndexData *)client_data; + printCheck(index_data); + + if (!info) { + printf("%s: <<NULL>>", cb); + return; + } + + name = info->name; + if (!name) + name = "<anon-tag>"; + + printf("%s: kind: %s%s", cb, getEntityKindString(info->kind), + getEntityTemplateKindString(info->templateKind)); + printf(" | name: %s", name); + printf(" | USR: %s", info->USR); + printf(" | lang: %s", getEntityLanguageString(info->lang)); + + for (i = 0; i != info->numAttributes; ++i) { + const CXIdxAttrInfo *Attr = info->attributes[i]; + printf(" <attribute>: "); + PrintCursor(Attr->cursor); + } +} + +static void printBaseClassInfo(CXClientData client_data, + const CXIdxBaseClassInfo *info) { + printEntityInfo(" <base>", client_data, info->base); + printf(" | cursor: "); + PrintCursor(info->cursor); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); +} + +static void printProtocolList(const CXIdxObjCProtocolRefListInfo *ProtoInfo, + CXClientData client_data) { + unsigned i; + for (i = 0; i < ProtoInfo->numProtocols; ++i) { + printEntityInfo(" <protocol>", client_data, + ProtoInfo->protocols[i]->protocol); + printf(" | cursor: "); + PrintCursor(ProtoInfo->protocols[i]->cursor); + printf(" | loc: "); + printCXIndexLoc(ProtoInfo->protocols[i]->loc, client_data); + printf("\n"); + } +} + +static void index_diagnostic(CXClientData client_data, + CXDiagnosticSet diagSet, void *reserved) { + CXString str; + const char *cstr; + unsigned numDiags, i; + CXDiagnostic diag; + IndexData *index_data; + index_data = (IndexData *)client_data; + printCheck(index_data); + + numDiags = clang_getNumDiagnosticsInSet(diagSet); + for (i = 0; i != numDiags; ++i) { + diag = clang_getDiagnosticInSet(diagSet, i); + str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); + cstr = clang_getCString(str); + printf("[diagnostic]: %s\n", cstr); + clang_disposeString(str); + + if (getenv("CINDEXTEST_FAILONERROR") && + clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error) { + index_data->fail_for_error = 1; + } + } +} + +static CXIdxClientFile index_enteredMainFile(CXClientData client_data, + CXFile file, void *reserved) { + IndexData *index_data; + CXString filename; + + index_data = (IndexData *)client_data; + printCheck(index_data); + + filename = clang_getFileName(file); + index_data->main_filename = clang_getCString(filename); + clang_disposeString(filename); + + printf("[enteredMainFile]: "); + printCXIndexFile((CXIdxClientFile)file); + printf("\n"); + + return (CXIdxClientFile)file; +} + +static CXIdxClientFile index_ppIncludedFile(CXClientData client_data, + const CXIdxIncludedFileInfo *info) { + IndexData *index_data; + index_data = (IndexData *)client_data; + printCheck(index_data); + + printf("[ppIncludedFile]: "); + printCXIndexFile((CXIdxClientFile)info->file); + printf(" | name: \"%s\"", info->filename); + printf(" | hash loc: "); + printCXIndexLoc(info->hashLoc, client_data); + printf(" | isImport: %d | isAngled: %d\n", info->isImport, info->isAngled); + + return (CXIdxClientFile)info->file; +} + +static CXIdxClientContainer index_startedTranslationUnit(CXClientData client_data, + void *reserved) { + IndexData *index_data; + index_data = (IndexData *)client_data; + printCheck(index_data); + + printf("[startedTranslationUnit]\n"); + return (CXIdxClientContainer)"TU"; +} + +static void index_indexDeclaration(CXClientData client_data, + const CXIdxDeclInfo *info) { + IndexData *index_data; + const CXIdxObjCCategoryDeclInfo *CatInfo; + const CXIdxObjCInterfaceDeclInfo *InterInfo; + const CXIdxObjCProtocolRefListInfo *ProtoInfo; + const CXIdxObjCPropertyDeclInfo *PropInfo; + const CXIdxCXXClassDeclInfo *CXXClassInfo; + unsigned i; + index_data = (IndexData *)client_data; + + printEntityInfo("[indexDeclaration]", client_data, info->entityInfo); + printf(" | cursor: "); + PrintCursor(info->cursor); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); + printf(" | semantic-container: "); + printCXIndexContainer(info->semanticContainer); + printf(" | lexical-container: "); + printCXIndexContainer(info->lexicalContainer); + printf(" | isRedecl: %d", info->isRedeclaration); + printf(" | isDef: %d", info->isDefinition); + printf(" | isContainer: %d", info->isContainer); + printf(" | isImplicit: %d\n", info->isImplicit); + + for (i = 0; i != info->numAttributes; ++i) { + const CXIdxAttrInfo *Attr = info->attributes[i]; + printf(" <attribute>: "); + PrintCursor(Attr->cursor); + printf("\n"); + } + + if (clang_index_isEntityObjCContainerKind(info->entityInfo->kind)) { + const char *kindName = 0; + CXIdxObjCContainerKind K = clang_index_getObjCContainerDeclInfo(info)->kind; + switch (K) { + case CXIdxObjCContainer_ForwardRef: + kindName = "forward-ref"; break; + case CXIdxObjCContainer_Interface: + kindName = "interface"; break; + case CXIdxObjCContainer_Implementation: + kindName = "implementation"; break; + } + printCheck(index_data); + printf(" <ObjCContainerInfo>: kind: %s\n", kindName); + } + + if ((CatInfo = clang_index_getObjCCategoryDeclInfo(info))) { + printEntityInfo(" <ObjCCategoryInfo>: class", client_data, + CatInfo->objcClass); + printf(" | cursor: "); + PrintCursor(CatInfo->classCursor); + printf(" | loc: "); + printCXIndexLoc(CatInfo->classLoc, client_data); + printf("\n"); + } + + if ((InterInfo = clang_index_getObjCInterfaceDeclInfo(info))) { + if (InterInfo->superInfo) { + printBaseClassInfo(client_data, InterInfo->superInfo); + printf("\n"); + } + } + + if ((ProtoInfo = clang_index_getObjCProtocolRefListInfo(info))) { + printProtocolList(ProtoInfo, client_data); + } + + if ((PropInfo = clang_index_getObjCPropertyDeclInfo(info))) { + if (PropInfo->getter) { + printEntityInfo(" <getter>", client_data, PropInfo->getter); + printf("\n"); + } + if (PropInfo->setter) { + printEntityInfo(" <setter>", client_data, PropInfo->setter); + printf("\n"); + } + } + + if ((CXXClassInfo = clang_index_getCXXClassDeclInfo(info))) { + for (i = 0; i != CXXClassInfo->numBases; ++i) { + printBaseClassInfo(client_data, CXXClassInfo->bases[i]); + printf("\n"); + } + } + + if (info->declAsContainer) + clang_index_setClientContainer(info->declAsContainer, + makeClientContainer(info->entityInfo, info->loc)); +} + +static void index_indexEntityReference(CXClientData client_data, + const CXIdxEntityRefInfo *info) { + printEntityInfo("[indexEntityReference]", client_data, info->referencedEntity); + printf(" | cursor: "); + PrintCursor(info->cursor); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); + printEntityInfo(" | <parent>:", client_data, info->parentEntity); + printf(" | container: "); + printCXIndexContainer(info->container); + printf(" | refkind: "); + switch (info->kind) { + case CXIdxEntityRef_Direct: printf("direct"); break; + case CXIdxEntityRef_Implicit: printf("implicit"); break; + } + printf("\n"); +} + +static int index_abortQuery(CXClientData client_data, void *reserved) { + IndexData *index_data; + index_data = (IndexData *)client_data; + return index_data->abort; +} + +static IndexerCallbacks IndexCB = { + index_abortQuery, + index_diagnostic, + index_enteredMainFile, + index_ppIncludedFile, + 0, /*importedASTFile*/ + index_startedTranslationUnit, + index_indexDeclaration, + index_indexEntityReference +}; + +static unsigned getIndexOptions(void) { + unsigned index_opts; + index_opts = 0; + if (getenv("CINDEXTEST_SUPPRESSREFS")) + index_opts |= CXIndexOpt_SuppressRedundantRefs; + if (getenv("CINDEXTEST_INDEXLOCALSYMBOLS")) + index_opts |= CXIndexOpt_IndexFunctionLocalSymbols; + + return index_opts; +} + +static int index_file(int argc, const char **argv) { + const char *check_prefix; + CXIndex Idx; + CXIndexAction idxAction; + IndexData index_data; + unsigned index_opts; + int result; + + check_prefix = 0; + if (argc > 0) { + if (strstr(argv[0], "-check-prefix=") == argv[0]) { + check_prefix = argv[0] + strlen("-check-prefix="); + ++argv; + --argc; + } + } + + if (argc == 0) { + fprintf(stderr, "no compiler arguments\n"); + return -1; + } + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnosics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + idxAction = 0; + result = 1; + + index_data.check_prefix = check_prefix; + index_data.first_check_printed = 0; + index_data.fail_for_error = 0; + index_data.abort = 0; + + index_opts = getIndexOptions(); + idxAction = clang_IndexAction_create(Idx); + result = clang_indexSourceFile(idxAction, &index_data, + &IndexCB,sizeof(IndexCB), index_opts, + 0, argv, argc, 0, 0, 0, 0); + if (index_data.fail_for_error) + result = -1; + + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(Idx); + return result; +} + +static int index_tu(int argc, const char **argv) { + CXIndex Idx; + CXIndexAction idxAction; + CXTranslationUnit TU; + const char *check_prefix; + IndexData index_data; + unsigned index_opts; + int result; + + check_prefix = 0; + if (argc > 0) { + if (strstr(argv[0], "-check-prefix=") == argv[0]) { + check_prefix = argv[0] + strlen("-check-prefix="); + ++argv; + --argc; + } + } + + if (argc == 0) { + fprintf(stderr, "no ast file\n"); + return -1; + } + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnosics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + idxAction = 0; + result = 1; + + if (!CreateTranslationUnit(Idx, argv[0], &TU)) + goto finished; + + index_data.check_prefix = check_prefix; + index_data.first_check_printed = 0; + index_data.fail_for_error = 0; + index_data.abort = 0; + + index_opts = getIndexOptions(); + idxAction = clang_IndexAction_create(Idx); + result = clang_indexTranslationUnit(idxAction, &index_data, + &IndexCB,sizeof(IndexCB), + index_opts, TU); + if (index_data.fail_for_error) + goto finished; + + finished: + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(Idx); + + return result; +} + +int perform_token_annotation(int argc, const char **argv) { + const char *input = argv[1]; + char *filename = 0; + unsigned line, second_line; + unsigned column, second_column; + CXIndex CIdx; + CXTranslationUnit TU = 0; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + CXToken *tokens; + unsigned num_tokens; + CXSourceRange range; + CXSourceLocation startLoc, endLoc; + CXFile file = 0; + CXCursor *cursors = 0; + unsigned i; + + input += strlen("-test-annotate-tokens="); + if ((errorCode = parse_file_line_column(input, &filename, &line, &column, + &second_line, &second_column))) + return errorCode; + + if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) + return -1; + + CIdx = clang_createIndex(0, 1); + TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], + argv + num_unsaved_files + 2, + argc - num_unsaved_files - 3, + unsaved_files, + num_unsaved_files, + getDefaultParsingOptions()); + if (!TU) { + fprintf(stderr, "unable to parse input\n"); + clang_disposeIndex(CIdx); + free(filename); + free_remapped_files(unsaved_files, num_unsaved_files); + return -1; + } + errorCode = 0; + + if (checkForErrors(TU) != 0) + return -1; + + if (getenv("CINDEXTEST_EDITING")) { + for (i = 0; i < 5; ++i) { + if (clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU))) { + fprintf(stderr, "Unable to reparse translation unit!\n"); + errorCode = -1; + goto teardown; + } + } + } + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + file = clang_getFile(TU, filename); + if (!file) { + fprintf(stderr, "file %s is not in this translation unit\n", filename); + errorCode = -1; + goto teardown; + } + + startLoc = clang_getLocation(TU, file, line, column); + if (clang_equalLocations(clang_getNullLocation(), startLoc)) { + fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line, + column); + errorCode = -1; + goto teardown; + } + + endLoc = clang_getLocation(TU, file, second_line, second_column); + if (clang_equalLocations(clang_getNullLocation(), endLoc)) { + fprintf(stderr, "invalid source location %s:%d:%d\n", filename, + second_line, second_column); + errorCode = -1; + goto teardown; + } + + range = clang_getRange(startLoc, endLoc); + clang_tokenize(TU, range, &tokens, &num_tokens); + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor)); + clang_annotateTokens(TU, tokens, num_tokens, cursors); + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + for (i = 0; i != num_tokens; ++i) { + const char *kind = "<unknown>"; + CXString spelling = clang_getTokenSpelling(TU, tokens[i]); + CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]); + unsigned start_line, start_column, end_line, end_column; + + switch (clang_getTokenKind(tokens[i])) { + case CXToken_Punctuation: kind = "Punctuation"; break; + case CXToken_Keyword: kind = "Keyword"; break; + case CXToken_Identifier: kind = "Identifier"; break; + case CXToken_Literal: kind = "Literal"; break; + case CXToken_Comment: kind = "Comment"; break; + } + clang_getSpellingLocation(clang_getRangeStart(extent), + 0, &start_line, &start_column, 0); + clang_getSpellingLocation(clang_getRangeEnd(extent), + 0, &end_line, &end_column, 0); + printf("%s: \"%s\" ", kind, clang_getCString(spelling)); + clang_disposeString(spelling); + PrintExtent(stdout, start_line, start_column, end_line, end_column); + if (!clang_isInvalid(cursors[i].kind)) { + printf(" "); + PrintCursor(cursors[i]); + } + printf("\n"); + } + free(cursors); + clang_disposeTokens(TU, tokens, num_tokens); + + teardown: + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(filename); + free_remapped_files(unsaved_files, num_unsaved_files); + return errorCode; +} + +/******************************************************************************/ +/* USR printing. */ +/******************************************************************************/ + +static int insufficient_usr(const char *kind, const char *usage) { + fprintf(stderr, "USR for '%s' requires: %s\n", kind, usage); + return 1; +} + +static unsigned isUSR(const char *s) { + return s[0] == 'c' && s[1] == ':'; +} + +static int not_usr(const char *s, const char *arg) { + fprintf(stderr, "'%s' argument ('%s') is not a USR\n", s, arg); + return 1; +} + +static void print_usr(CXString usr) { + const char *s = clang_getCString(usr); + printf("%s\n", s); + clang_disposeString(usr); +} + +static void display_usrs() { + fprintf(stderr, "-print-usrs options:\n" + " ObjCCategory <class name> <category name>\n" + " ObjCClass <class name>\n" + " ObjCIvar <ivar name> <class USR>\n" + " ObjCMethod <selector> [0=class method|1=instance method] " + "<class USR>\n" + " ObjCProperty <property name> <class USR>\n" + " ObjCProtocol <protocol name>\n"); +} + +int print_usrs(const char **I, const char **E) { + while (I != E) { + const char *kind = *I; + unsigned len = strlen(kind); + switch (len) { + case 8: + if (memcmp(kind, "ObjCIvar", 8) == 0) { + if (I + 2 >= E) + return insufficient_usr(kind, "<ivar name> <class USR>"); + if (!isUSR(I[2])) + return not_usr("<class USR>", I[2]); + else { + CXString x; + x.data = (void*) I[2]; + x.private_flags = 0; + print_usr(clang_constructUSR_ObjCIvar(I[1], x)); + } + + I += 3; + continue; + } + break; + case 9: + if (memcmp(kind, "ObjCClass", 9) == 0) { + if (I + 1 >= E) + return insufficient_usr(kind, "<class name>"); + print_usr(clang_constructUSR_ObjCClass(I[1])); + I += 2; + continue; + } + break; + case 10: + if (memcmp(kind, "ObjCMethod", 10) == 0) { + if (I + 3 >= E) + return insufficient_usr(kind, "<method selector> " + "[0=class method|1=instance method] <class USR>"); + if (!isUSR(I[3])) + return not_usr("<class USR>", I[3]); + else { + CXString x; + x.data = (void*) I[3]; + x.private_flags = 0; + print_usr(clang_constructUSR_ObjCMethod(I[1], atoi(I[2]), x)); + } + I += 4; + continue; + } + break; + case 12: + if (memcmp(kind, "ObjCCategory", 12) == 0) { + if (I + 2 >= E) + return insufficient_usr(kind, "<class name> <category name>"); + print_usr(clang_constructUSR_ObjCCategory(I[1], I[2])); + I += 3; + continue; + } + if (memcmp(kind, "ObjCProtocol", 12) == 0) { + if (I + 1 >= E) + return insufficient_usr(kind, "<protocol name>"); + print_usr(clang_constructUSR_ObjCProtocol(I[1])); + I += 2; + continue; + } + if (memcmp(kind, "ObjCProperty", 12) == 0) { + if (I + 2 >= E) + return insufficient_usr(kind, "<property name> <class USR>"); + if (!isUSR(I[2])) + return not_usr("<class USR>", I[2]); + else { + CXString x; + x.data = (void*) I[2]; + x.private_flags = 0; + print_usr(clang_constructUSR_ObjCProperty(I[1], x)); + } + I += 3; + continue; + } + break; + default: + break; + } + break; + } + + if (I != E) { + fprintf(stderr, "Invalid USR kind: %s\n", *I); + display_usrs(); + return 1; + } + return 0; +} + +int print_usrs_file(const char *file_name) { + char line[2048]; + const char *args[128]; + unsigned numChars = 0; + + FILE *fp = fopen(file_name, "r"); + if (!fp) { + fprintf(stderr, "error: cannot open '%s'\n", file_name); + return 1; + } + + /* This code is not really all that safe, but it works fine for testing. */ + while (!feof(fp)) { + char c = fgetc(fp); + if (c == '\n') { + unsigned i = 0; + const char *s = 0; + + if (numChars == 0) + continue; + + line[numChars] = '\0'; + numChars = 0; + + if (line[0] == '/' && line[1] == '/') + continue; + + s = strtok(line, " "); + while (s) { + args[i] = s; + ++i; + s = strtok(0, " "); + } + if (print_usrs(&args[0], &args[i])) + return 1; + } + else + line[numChars++] = c; + } + + fclose(fp); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ +int write_pch_file(const char *filename, int argc, const char *argv[]) { + CXIndex Idx; + CXTranslationUnit TU; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + int result = 0; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnosics=*/1); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + clang_disposeIndex(Idx); + return -1; + } + + TU = clang_parseTranslationUnit(Idx, 0, + argv + num_unsaved_files, + argc - num_unsaved_files, + unsaved_files, + num_unsaved_files, + CXTranslationUnit_Incomplete); + if (!TU) { + fprintf(stderr, "Unable to load translation unit!\n"); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return 1; + } + + switch (clang_saveTranslationUnit(TU, filename, + clang_defaultSaveOptions(TU))) { + case CXSaveError_None: + break; + + case CXSaveError_TranslationErrors: + fprintf(stderr, "Unable to write PCH file %s: translation errors\n", + filename); + result = 2; + break; + + case CXSaveError_InvalidTU: + fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n", + filename); + result = 3; + break; + + case CXSaveError_Unknown: + default: + fprintf(stderr, "Unable to write PCH file %s: unknown error \n", filename); + result = 1; + break; + } + + clang_disposeTranslationUnit(TU); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return result; +} + +/******************************************************************************/ +/* Serialized diagnostics. */ +/******************************************************************************/ + +static const char *getDiagnosticCodeStr(enum CXLoadDiag_Error error) { + switch (error) { + case CXLoadDiag_CannotLoad: return "Cannot Load File"; + case CXLoadDiag_None: break; + case CXLoadDiag_Unknown: return "Unknown"; + case CXLoadDiag_InvalidFile: return "Invalid File"; + } + return "None"; +} + +static const char *getSeverityString(enum CXDiagnosticSeverity severity) { + switch (severity) { + case CXDiagnostic_Note: return "note"; + case CXDiagnostic_Error: return "error"; + case CXDiagnostic_Fatal: return "fatal"; + case CXDiagnostic_Ignored: return "ignored"; + case CXDiagnostic_Warning: return "warning"; + } + return "unknown"; +} + +static void printIndent(unsigned indent) { + if (indent == 0) + return; + fprintf(stderr, "+"); + --indent; + while (indent > 0) { + fprintf(stderr, "-"); + --indent; + } +} + +static void printLocation(CXSourceLocation L) { + CXFile File; + CXString FileName; + unsigned line, column, offset; + + clang_getExpansionLocation(L, &File, &line, &column, &offset); + FileName = clang_getFileName(File); + + fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column); + clang_disposeString(FileName); +} + +static void printRanges(CXDiagnostic D, unsigned indent) { + unsigned i, n = clang_getDiagnosticNumRanges(D); + + for (i = 0; i < n; ++i) { + CXSourceLocation Start, End; + CXSourceRange SR = clang_getDiagnosticRange(D, i); + Start = clang_getRangeStart(SR); + End = clang_getRangeEnd(SR); + + printIndent(indent); + fprintf(stderr, "Range: "); + printLocation(Start); + fprintf(stderr, " "); + printLocation(End); + fprintf(stderr, "\n"); + } +} + +static void printFixIts(CXDiagnostic D, unsigned indent) { + unsigned i, n = clang_getDiagnosticNumFixIts(D); + fprintf(stderr, "Number FIXITs = %d\n", n); + for (i = 0 ; i < n; ++i) { + CXSourceRange ReplacementRange; + CXString text; + text = clang_getDiagnosticFixIt(D, i, &ReplacementRange); + + printIndent(indent); + fprintf(stderr, "FIXIT: ("); + printLocation(clang_getRangeStart(ReplacementRange)); + fprintf(stderr, " - "); + printLocation(clang_getRangeEnd(ReplacementRange)); + fprintf(stderr, "): \"%s\"\n", clang_getCString(text)); + clang_disposeString(text); + } +} + +static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) { + unsigned i, n; + + if (!Diags) + return; + + n = clang_getNumDiagnosticsInSet(Diags); + for (i = 0; i < n; ++i) { + CXSourceLocation DiagLoc; + CXDiagnostic D; + CXFile File; + CXString FileName, DiagSpelling, DiagOption, DiagCat; + unsigned line, column, offset; + const char *DiagOptionStr = 0, *DiagCatStr = 0; + + D = clang_getDiagnosticInSet(Diags, i); + DiagLoc = clang_getDiagnosticLocation(D); + clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset); + FileName = clang_getFileName(File); + DiagSpelling = clang_getDiagnosticSpelling(D); + + printIndent(indent); + + fprintf(stderr, "%s:%d:%d: %s: %s", + clang_getCString(FileName), + line, + column, + getSeverityString(clang_getDiagnosticSeverity(D)), + clang_getCString(DiagSpelling)); + + DiagOption = clang_getDiagnosticOption(D, 0); + DiagOptionStr = clang_getCString(DiagOption); + if (DiagOptionStr) { + fprintf(stderr, " [%s]", DiagOptionStr); + } + + DiagCat = clang_getDiagnosticCategoryText(D); + DiagCatStr = clang_getCString(DiagCat); + if (DiagCatStr) { + fprintf(stderr, " [%s]", DiagCatStr); + } + + fprintf(stderr, "\n"); + + printRanges(D, indent); + printFixIts(D, indent); + + /* Print subdiagnostics. */ + printDiagnosticSet(clang_getChildDiagnostics(D), indent+2); + + clang_disposeString(FileName); + clang_disposeString(DiagSpelling); + clang_disposeString(DiagOption); + } +} + +static int read_diagnostics(const char *filename) { + enum CXLoadDiag_Error error; + CXString errorString; + CXDiagnosticSet Diags = 0; + + Diags = clang_loadDiagnostics(filename, &error, &errorString); + if (!Diags) { + fprintf(stderr, "Trouble deserializing file (%s): %s\n", + getDiagnosticCodeStr(error), + clang_getCString(errorString)); + clang_disposeString(errorString); + return 1; + } + + printDiagnosticSet(Diags, 0); + fprintf(stderr, "Number of diagnostics: %d\n", + clang_getNumDiagnosticsInSet(Diags)); + clang_disposeDiagnosticSet(Diags); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ + +static CXCursorVisitor GetVisitor(const char *s) { + if (s[0] == '\0') + return FilteredPrintingVisitor; + if (strcmp(s, "-usrs") == 0) + return USRVisitor; + if (strncmp(s, "-memory-usage", 13) == 0) + return GetVisitor(s + 13); + return NULL; +} + +static void print_usage(void) { + fprintf(stderr, + "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n" + " c-index-test -code-completion-timing=<site> <compiler arguments>\n" + " c-index-test -cursor-at=<site> <compiler arguments>\n" + " c-index-test -file-refs-at=<site> <compiler arguments>\n" + " c-index-test -index-file [-check-prefix=<FileCheck prefix>] <compiler arguments>\n" + " c-index-test -index-tu [-check-prefix=<FileCheck prefix>] <AST file>\n" + " c-index-test -test-file-scan <AST file> <source file> " + "[FileCheck prefix]\n"); + fprintf(stderr, + " c-index-test -test-load-tu <AST file> <symbol filter> " + "[FileCheck prefix]\n" + " c-index-test -test-load-tu-usrs <AST file> <symbol filter> " + "[FileCheck prefix]\n" + " c-index-test -test-load-source <symbol filter> {<args>}*\n"); + fprintf(stderr, + " c-index-test -test-load-source-memory-usage " + "<symbol filter> {<args>}*\n" + " c-index-test -test-load-source-reparse <trials> <symbol filter> " + " {<args>}*\n" + " c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n" + " c-index-test -test-load-source-usrs-memory-usage " + "<symbol filter> {<args>}*\n" + " c-index-test -test-annotate-tokens=<range> {<args>}*\n" + " c-index-test -test-inclusion-stack-source {<args>}*\n" + " c-index-test -test-inclusion-stack-tu <AST file>\n"); + fprintf(stderr, + " c-index-test -test-print-linkage-source {<args>}*\n" + " c-index-test -test-print-typekind {<args>}*\n" + " c-index-test -print-usr [<CursorKind> {<args>}]*\n" + " c-index-test -print-usr-file <file>\n" + " c-index-test -write-pch <file> <compiler arguments>\n"); + fprintf(stderr, + " c-index-test -read-diagnostics <file>\n\n"); + fprintf(stderr, + " <symbol filter> values:\n%s", + " all - load all symbols, including those from PCH\n" + " local - load all symbols except those in PCH\n" + " category - only load ObjC categories (non-PCH)\n" + " interface - only load ObjC interfaces (non-PCH)\n" + " protocol - only load ObjC protocols (non-PCH)\n" + " function - only load functions (non-PCH)\n" + " typedef - only load typdefs (non-PCH)\n" + " scan-function - scan function bodies (non-PCH)\n\n"); +} + +/***/ + +int cindextest_main(int argc, const char **argv) { + clang_enableStackTraces(); + if (argc > 2 && strcmp(argv[1], "-read-diagnostics") == 0) + return read_diagnostics(argv[2]); + if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) + return perform_code_completion(argc, argv, 0); + if (argc > 2 && strstr(argv[1], "-code-completion-timing=") == argv[1]) + return perform_code_completion(argc, argv, 1); + if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1]) + return inspect_cursor_at(argc, argv); + if (argc > 2 && strstr(argv[1], "-file-refs-at=") == argv[1]) + return find_file_refs_at(argc, argv); + if (argc > 2 && strcmp(argv[1], "-index-file") == 0) + return index_file(argc - 2, argv + 2); + if (argc > 2 && strcmp(argv[1], "-index-tu") == 0) + return index_tu(argc - 2, argv + 2); + else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) { + CXCursorVisitor I = GetVisitor(argv[1] + 13); + if (I) + return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I, + NULL); + } + else if (argc >= 5 && strncmp(argv[1], "-test-load-source-reparse", 25) == 0){ + CXCursorVisitor I = GetVisitor(argv[1] + 25); + if (I) { + int trials = atoi(argv[2]); + return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I, + NULL); + } + } + else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) { + CXCursorVisitor I = GetVisitor(argv[1] + 17); + + PostVisitTU postVisit = 0; + if (strstr(argv[1], "-memory-usage")) + postVisit = PrintMemoryUsage; + + if (I) + return perform_test_load_source(argc - 3, argv + 3, argv[2], I, + postVisit); + } + else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0) + return perform_file_scan(argv[2], argv[3], + argc >= 5 ? argv[4] : 0); + else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1]) + return perform_token_annotation(argc, argv); + else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-source") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", NULL, + PrintInclusionStack); + else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-tu") == 0) + return perform_test_load_tu(argv[2], "all", NULL, NULL, + PrintInclusionStack); + else if (argc > 2 && strcmp(argv[1], "-test-print-linkage-source") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", PrintLinkage, + NULL); + else if (argc > 2 && strcmp(argv[1], "-test-print-typekind") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintTypeKind, 0); + else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) { + if (argc > 2) + return print_usrs(argv + 2, argv + argc); + else { + display_usrs(); + return 1; + } + } + else if (argc > 2 && strcmp(argv[1], "-print-usr-file") == 0) + return print_usrs_file(argv[2]); + else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0) + return write_pch_file(argv[2], argc - 3, argv + 3); + + print_usage(); + return 1; +} + +/***/ + +/* We intentionally run in a separate thread to ensure we at least minimal + * testing of a multithreaded environment (for example, having a reduced stack + * size). */ + +typedef struct thread_info { + int argc; + const char **argv; + int result; +} thread_info; +void thread_runner(void *client_data_v) { + thread_info *client_data = client_data_v; + client_data->result = cindextest_main(client_data->argc, client_data->argv); +#ifdef __CYGWIN__ + fflush(stdout); /* stdout is not flushed on Cygwin. */ +#endif +} + +int main(int argc, const char **argv) { + thread_info client_data; + + if (getenv("CINDEXTEST_NOTHREADS")) + return cindextest_main(argc, argv); + + client_data.argc = argc; + client_data.argv = argv; + clang_executeOnThread(thread_runner, &client_data, 0); + return client_data.result; +} diff --git a/clang/tools/clang-check/CMakeLists.txt b/clang/tools/clang-check/CMakeLists.txt new file mode 100644 index 0000000..851d6cd --- /dev/null +++ b/clang/tools/clang-check/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_USED_LIBS clangTooling clangBasic) + +add_clang_executable(clang-check + ClangCheck.cpp + ) diff --git a/clang/tools/clang-check/ClangCheck.cpp b/clang/tools/clang-check/ClangCheck.cpp new file mode 100644 index 0000000..d68e282 --- /dev/null +++ b/clang/tools/clang-check/ClangCheck.cpp @@ -0,0 +1,66 @@ +//===- examples/Tooling/ClangCheck.cpp - Clang check tool -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a clang-check tool that runs the +// clang::SyntaxOnlyAction over a number of translation units. +// +// Usage: +// clang-check <cmake-output-dir> <file1> <file2> ... +// +// Where <cmake-output-dir> is a CMake build directory in which a file named +// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in +// CMake to get this output). +// +// <file1> ... specify the paths of files in the CMake source tree. This path +// is looked up in the compile command database. If the path of a file is +// absolute, it needs to point into CMake's source tree. If the path is +// relative, the current working directory needs to be in the CMake source +// tree and the file must be in a subdirectory of the current working +// directory. "./" prefixes in the relative files will be automatically +// removed, but the rest of a relative path must be a suffix of a path in +// the compile command line database. +// +// For example, to use clang-check on all files in a subtree of the source +// tree, use: +// +// /path/in/subtree $ find . -name '*.cpp'| xargs clang-check /path/to/source +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CommandLine.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" + +using namespace clang::tooling; +using namespace llvm; + +cl::opt<std::string> BuildPath( + cl::Positional, + cl::desc("<build-path>")); + +cl::list<std::string> SourcePaths( + cl::Positional, + cl::desc("<source0> [... <sourceN>]"), + cl::OneOrMore); + +int main(int argc, const char **argv) { + llvm::OwningPtr<CompilationDatabase> Compilations( + FixedCompilationDatabase::loadFromCommandLine(argc, argv)); + cl::ParseCommandLineOptions(argc, argv); + if (!Compilations) { + std::string ErrorMessage; + Compilations.reset(CompilationDatabase::loadFromDirectory(BuildPath, + ErrorMessage)); + if (!Compilations) + llvm::report_fatal_error(ErrorMessage); + } + ClangTool Tool(*Compilations, SourcePaths); + return Tool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>()); +} diff --git a/clang/tools/clang-check/Makefile b/clang/tools/clang-check/Makefile new file mode 100644 index 0000000..49b1ada --- /dev/null +++ b/clang/tools/clang-check/Makefile @@ -0,0 +1,24 @@ +##===- tools/clang-check/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. + +TOOLNAME = clang-check +NO_INSTALL = 1 + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \ + clangTooling.a clangParse.a clangSema.a clangAnalysis.a \ + clangEdit.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile + diff --git a/clang/tools/diagtool/CMakeLists.txt b/clang/tools/diagtool/CMakeLists.txt new file mode 100644 index 0000000..f1fd9de --- /dev/null +++ b/clang/tools/diagtool/CMakeLists.txt @@ -0,0 +1,24 @@ +set( LLVM_LINK_COMPONENTS + support + ) + +set( LLVM_USED_LIBS + clangBasic + clangLex + clangSema + ) + +add_clang_executable(diagtool + diagtool_main.cpp + DiagTool.cpp + ListWarnings.cpp +) + +if(UNIX) + set(CLANGXX_LINK_OR_COPY create_symlink) +else() + set(CLANGXX_LINK_OR_COPY copy) +endif() + +install(TARGETS diagtool + RUNTIME DESTINATION bin) diff --git a/clang/tools/diagtool/DiagTool.cpp b/clang/tools/diagtool/DiagTool.cpp new file mode 100644 index 0000000..36e72a2 --- /dev/null +++ b/clang/tools/diagtool/DiagTool.cpp @@ -0,0 +1,68 @@ +//===- DiagTool.cpp - Classes for defining diagtool tools -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the boilerplate for defining diagtool tools. +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/SmallString.h" +#include <vector> + +using namespace diagtool; + +DiagTool::DiagTool(llvm::StringRef toolCmd, + llvm::StringRef toolDesc) + : cmd(toolCmd), description(toolDesc) {} + +DiagTool::~DiagTool() {} + +typedef llvm::StringMap<DiagTool *> ToolMap; +static inline ToolMap *getTools(void *v) { return static_cast<ToolMap*>(v); } + +DiagTools::DiagTools() : tools(new ToolMap()) {} +DiagTools::~DiagTools() { delete getTools(tools); } + +DiagTool *DiagTools::getTool(llvm::StringRef toolCmd) { + ToolMap::iterator it = getTools(tools)->find(toolCmd); + return (it == getTools(tools)->end()) ? 0 : it->getValue(); +} + +void DiagTools::registerTool(DiagTool *tool) { + getTools(tools)->GetOrCreateValue(tool->getName(), tool); +} + +void DiagTools::printCommands(llvm::raw_ostream &out) { + std::vector<llvm::StringRef> toolNames; + unsigned maxName = 0; + for (ToolMap::iterator it = getTools(tools)->begin(), + ei = getTools(tools)->end(); it != ei; ++it) { + toolNames.push_back(it->getKey()); + unsigned len = it->getKey().size(); + if (len > maxName) + maxName = len; + } + std::sort(toolNames.begin(), toolNames.end()); + + for (std::vector<llvm::StringRef>::iterator it = toolNames.begin(), + ei = toolNames.end(); it != ei; ++it) { + + out << " " << (*it); + unsigned spaces = (maxName + 3) - (it->size()); + for (unsigned i = 0; i < spaces; ++i) + out << ' '; + + out << getTool(*it)->getDescription() << '\n'; + } +} + +namespace diagtool { + llvm::ManagedStatic<DiagTools> diagTools; +} diff --git a/clang/tools/diagtool/DiagTool.h b/clang/tools/diagtool/DiagTool.h new file mode 100644 index 0000000..dcb6ac7 --- /dev/null +++ b/clang/tools/diagtool/DiagTool.h @@ -0,0 +1,70 @@ +//===- DiagTool.h - Classes for defining diagtool tools -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the boilerplate for defining diagtool tools. +// +//===----------------------------------------------------------------------===// + +#ifndef DIAGTOOL_DIAGTOOL_H +#define DIAGTOOL_DIAGTOOL_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ManagedStatic.h" +#include <string> + + +namespace diagtool { + +class DiagTool { + const std::string cmd; + const std::string description; +public: + DiagTool(llvm::StringRef toolCmd, llvm::StringRef toolDesc); + virtual ~DiagTool(); + + llvm::StringRef getName() const { return cmd; } + llvm::StringRef getDescription() const { return description; } + + virtual int run(unsigned argc, char *argv[], llvm::raw_ostream &out) = 0; +}; + +class DiagTools { + void *tools; +public: + DiagTools(); + ~DiagTools(); + + DiagTool *getTool(llvm::StringRef toolCmd); + void registerTool(DiagTool *tool); + void printCommands(llvm::raw_ostream &out); +}; + +extern llvm::ManagedStatic<DiagTools> diagTools; + +template <typename DIAGTOOL> +class RegisterDiagTool { +public: + RegisterDiagTool() { diagTools->registerTool(new DIAGTOOL()); } +}; + +} // end diagtool namespace + +#define DEF_DIAGTOOL(NAME, DESC, CLSNAME)\ +namespace {\ +class CLSNAME : public diagtool::DiagTool {\ +public:\ + CLSNAME() : DiagTool(NAME, DESC) {}\ + virtual ~CLSNAME() {}\ + virtual int run(unsigned argc, char *argv[], llvm::raw_ostream &out);\ +};\ +diagtool::RegisterDiagTool<CLSNAME> Register##CLSNAME;\ +} + +#endif diff --git a/clang/tools/diagtool/ListWarnings.cpp b/clang/tools/diagtool/ListWarnings.cpp new file mode 100644 index 0000000..2bbeca8 --- /dev/null +++ b/clang/tools/diagtool/ListWarnings.cpp @@ -0,0 +1,126 @@ +//===- ListWarnings.h - diagtool tool for printing warning flags ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a diagtool tool that displays warning flags for +// diagnostics. +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/Support/Format.h" +#include "llvm/ADT/StringMap.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/Basic/AllDiagnostics.h" + +DEF_DIAGTOOL("list-warnings", + "List warnings and their corresponding flags", + ListWarnings) + +using namespace clang; + +namespace { +struct StaticDiagNameIndexRec { + const char *NameStr; + unsigned short DiagID; + uint8_t NameLen; + + StringRef getName() const { + return StringRef(NameStr, NameLen); + } +}; +} + +static const StaticDiagNameIndexRec StaticDiagNameIndex[] = { +#define DIAG_NAME_INDEX(ENUM) { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, +#include "clang/Basic/DiagnosticIndexName.inc" +#undef DIAG_NAME_INDEX + { 0, 0, 0 } +}; + +static const unsigned StaticDiagNameIndexSize = + sizeof(StaticDiagNameIndex)/sizeof(StaticDiagNameIndex[0])-1; + +namespace { +struct Entry { + llvm::StringRef DiagName; + llvm::StringRef Flag; + + Entry(llvm::StringRef diagN, llvm::StringRef flag) + : DiagName(diagN), Flag(flag) {} + + bool operator<(const Entry &x) const { return DiagName < x.DiagName; } +}; +} + +static void printEntries(std::vector<Entry> &entries, llvm::raw_ostream &out) { + for (std::vector<Entry>::iterator it = entries.begin(), ei = entries.end(); + it != ei; ++it) { + out << " " << it->DiagName; + if (!it->Flag.empty()) + out << " [-W" << it->Flag << "]"; + out << '\n'; + } +} + +int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { + std::vector<Entry> Flagged, Unflagged; + llvm::StringMap<std::vector<unsigned> > flagHistogram; + + for (const StaticDiagNameIndexRec *di = StaticDiagNameIndex, *de = StaticDiagNameIndex + StaticDiagNameIndexSize; + di != de; ++di) { + + unsigned diagID = di->DiagID; + + if (DiagnosticIDs::isBuiltinNote(diagID)) + continue; + + if (!DiagnosticIDs::isBuiltinWarningOrExtension(diagID)) + continue; + + Entry entry(di->getName(), + DiagnosticIDs::getWarningOptionForDiag(diagID)); + + if (entry.Flag.empty()) + Unflagged.push_back(entry); + else { + Flagged.push_back(entry); + flagHistogram.GetOrCreateValue(entry.Flag).getValue().push_back(diagID); + } + } + + std::sort(Flagged.begin(), Flagged.end()); + std::sort(Unflagged.begin(), Unflagged.end()); + + out << "Warnings with flags (" << Flagged.size() << "):\n"; + printEntries(Flagged, out); + + out << "Warnings without flags (" << Unflagged.size() << "):\n"; + printEntries(Unflagged, out); + + out << "\nSTATISTICS:\n\n"; + + double percentFlagged = ((double) Flagged.size()) + / (Flagged.size() + Unflagged.size()) * 100.0; + + out << " Percentage of warnings with flags: " + << llvm::format("%.4g",percentFlagged) << "%\n"; + + out << " Number of unique flags: " + << flagHistogram.size() << '\n'; + + double avgDiagsPerFlag = (double) Flagged.size() / flagHistogram.size(); + out << " Average number of diagnostics per flag: " + << llvm::format("%.4g", avgDiagsPerFlag) << '\n'; + + out << '\n'; + + return 0; +} + diff --git a/clang/tools/diagtool/Makefile b/clang/tools/diagtool/Makefile new file mode 100644 index 0000000..6e3bcfc --- /dev/null +++ b/clang/tools/diagtool/Makefile @@ -0,0 +1,24 @@ +##===- tools/driver/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = diagtool + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +# Don't install this. +NO_INSTALL = 1 + +LINK_COMPONENTS := support + +USEDLIBS = clangBasic.a + +include $(CLANG_LEVEL)/Makefile + diff --git a/clang/tools/diagtool/diagtool_main.cpp b/clang/tools/diagtool/diagtool_main.cpp new file mode 100644 index 0000000..e34f0dc --- /dev/null +++ b/clang/tools/diagtool/diagtool_main.cpp @@ -0,0 +1,26 @@ +//===- diagtool_main.h - Entry point for invoking all diagnostic tools ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the main function for diagtool. +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" + +using namespace diagtool; + +int main(int argc, char *argv[]) { + if (argc > 1) + if (DiagTool *tool = diagTools->getTool(argv[1])) + return tool->run(argc - 1, &argv[2], llvm::errs()); + + llvm::errs() << "usage: diagtool <command> [<args>]\n\n"; + diagTools->printCommands(llvm::errs()); + return 1; +} diff --git a/clang/tools/driver/CMakeLists.txt b/clang/tools/driver/CMakeLists.txt new file mode 100644 index 0000000..ae49ac1 --- /dev/null +++ b/clang/tools/driver/CMakeLists.txt @@ -0,0 +1,63 @@ +set( LLVM_USED_LIBS + clangFrontendTool + clangAST + clangAnalysis + clangBasic + clangCodeGen + clangDriver + clangEdit + clangFrontend + clangLex + clangParse + clangEdit + clangARCMigrate + clangRewrite + clangSema + clangSerialization + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore + ) + +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + asmparser + bitreader + bitwriter + codegen + instrumentation + ipo + linker + selectiondag + ) + +add_clang_executable(clang + driver.cpp + cc1_main.cpp + cc1as_main.cpp + ) + +set_target_properties(clang PROPERTIES VERSION ${CLANG_EXECUTABLE_VERSION}) + +if(UNIX) + set(CLANGXX_LINK_OR_COPY create_symlink) +# Create a relative symlink + set(clang_binary "clang${CMAKE_EXECUTABLE_SUFFIX}") +else() + set(CLANGXX_LINK_OR_COPY copy) + set(clang_binary "${LLVM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/clang${CMAKE_EXECUTABLE_SUFFIX}") +endif() + +# Create the clang++ symlink in the build directory. +set(clang_pp "${LLVM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/clang++${CMAKE_EXECUTABLE_SUFFIX}") +add_custom_command(TARGET clang POST_BUILD + COMMAND ${CMAKE_COMMAND} -E ${CLANGXX_LINK_OR_COPY} "${clang_binary}" "${clang_pp}") + +set_property(DIRECTORY APPEND + PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${clang_pp}) + +install(TARGETS clang + RUNTIME DESTINATION bin) + +# Create the clang++ symlink at installation time. +install(SCRIPT clang_symlink.cmake -DCMAKE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\") diff --git a/clang/tools/driver/Info.plist.in b/clang/tools/driver/Info.plist.in new file mode 100644 index 0000000..c938fb0 --- /dev/null +++ b/clang/tools/driver/Info.plist.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleIdentifier</key> + <string>@TOOL_INFO_UTI@</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>@TOOL_INFO_NAME</string> + <key>CFBundleShortVersionString</key> + <string>@TOOL_INFO_VERSION@</string> + <key>CFBundleVersion</key> + <string>@TOOL_INFO_BUILD_VERSION@</string> + <key>CFBundleSignature</key> + <string>????</string> +</dict> +</plist> diff --git a/clang/tools/driver/Makefile b/clang/tools/driver/Makefile new file mode 100644 index 0000000..270d4fd --- /dev/null +++ b/clang/tools/driver/Makefile @@ -0,0 +1,69 @@ +##===- tools/driver/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = clang +TOOLALIAS = clang++ + +# We don't currently expect production Clang builds to be interested in +# plugins. This is important for startup performance. +ifdef CLANG_IS_PRODUCTION +TOOL_NO_EXPORTS := 1 +endif + +ifdef CLANG_ORDER_FILE +TOOL_ORDER_FILE := $(CLANG_ORDER_FILE) +endif + +# Include tool version information on OS X. +TOOL_INFO_PLIST := Info.plist + +# Include this here so we can get the configuration of the targets that have +# been configured for construction. We have to do this early so we can set up +# LINK_COMPONENTS before including Makefile.rules +include $(CLANG_LEVEL)/../../Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ + instrumentation ipo linker selectiondag +USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ + clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ + clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ + clangStaticAnalyzerCore.a \ + clangAnalysis.a clangARCMigrate.a clangRewrite.a \ + clangEdit.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile + +# Set the tool version information values. +ifeq ($(HOST_OS),Darwin) +ifdef CLANG_VENDOR +TOOL_INFO_NAME := $(CLANG_VENDOR) clang +else +TOOL_INFO_NAME := clang +endif + +ifdef CLANG_VENDOR_UTI +TOOL_INFO_UTI := $(CLANG_VENDOR_UTI) +else +TOOL_INFO_UTI := org.llvm.clang +endif + +TOOL_INFO_VERSION := $(word 3,$(shell grep "CLANG_VERSION " \ + $(PROJ_OBJ_DIR)/$(CLANG_LEVEL)/include/clang/Basic/Version.inc)) +ifdef LLVM_SUBMIT_VERSION +TOOL_INFO_BUILD_VERSION := $(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) +else +TOOL_INFO_BUILD_VERSION := +endif +endif + +# Translate make variable to define when building a "production" clang. +ifdef CLANG_IS_PRODUCTION +CPP.Defines += -DCLANG_IS_PRODUCTION +endif diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp new file mode 100644 index 0000000..a211090 --- /dev/null +++ b/clang/tools/driver/cc1_main.cpp @@ -0,0 +1,189 @@ +//===-- cc1_main.cpp - Clang CC1 Compiler Frontend ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1 functionality, which implements the +// core compiler functionality along with a number of additional tools for +// demonstration and testing purposes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/OptTable.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/FrontendTool/Utils.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/LinkAllPasses.h" +#include <cstdio> +using namespace clang; + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +static void LLVMErrorHandler(void *UserData, const std::string &Message) { + DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + exit(1); +} + +// FIXME: Define the need for this testing away. +static int cc1_test(DiagnosticsEngine &Diags, + const char **ArgBegin, const char **ArgEnd) { + using namespace clang::driver; + + llvm::errs() << "cc1 argv:"; + for (const char **i = ArgBegin; i != ArgEnd; ++i) + llvm::errs() << " \"" << *i << '"'; + llvm::errs() << "\n"; + + // Parse the arguments. + OptTable *Opts = createCC1OptTable(); + unsigned MissingArgIndex, MissingArgCount; + InputArgList *Args = Opts->ParseArgs(ArgBegin, ArgEnd, + MissingArgIndex, MissingArgCount); + + // Check for missing argument error. + if (MissingArgCount) + Diags.Report(clang::diag::err_drv_missing_argument) + << Args->getArgString(MissingArgIndex) << MissingArgCount; + + // Dump the parsed arguments. + llvm::errs() << "cc1 parsed options:\n"; + for (ArgList::const_iterator it = Args->begin(), ie = Args->end(); + it != ie; ++it) + (*it)->dump(); + + // Create a compiler invocation. + llvm::errs() << "cc1 creating invocation.\n"; + CompilerInvocation Invocation; + if (!CompilerInvocation::CreateFromArgs(Invocation, ArgBegin, ArgEnd, Diags)) + return 1; + + // Convert the invocation back to argument strings. + std::vector<std::string> InvocationArgs; + Invocation.toArgs(InvocationArgs); + + // Dump the converted arguments. + SmallVector<const char*, 32> Invocation2Args; + llvm::errs() << "invocation argv :"; + for (unsigned i = 0, e = InvocationArgs.size(); i != e; ++i) { + Invocation2Args.push_back(InvocationArgs[i].c_str()); + llvm::errs() << " \"" << InvocationArgs[i] << '"'; + } + llvm::errs() << "\n"; + + // Convert those arguments to another invocation, and check that we got the + // same thing. + CompilerInvocation Invocation2; + if (!CompilerInvocation::CreateFromArgs(Invocation2, Invocation2Args.begin(), + Invocation2Args.end(), Diags)) + return 1; + + // FIXME: Implement CompilerInvocation comparison. + if (true) { + //llvm::errs() << "warning: Invocations differ!\n"; + + std::vector<std::string> Invocation2Args; + Invocation2.toArgs(Invocation2Args); + llvm::errs() << "invocation2 argv:"; + for (unsigned i = 0, e = Invocation2Args.size(); i != e; ++i) + llvm::errs() << " \"" << Invocation2Args[i] << '"'; + llvm::errs() << "\n"; + } + + return 0; +} + +int cc1_main(const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr) { + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + + // Run clang -cc1 test. + if (ArgBegin != ArgEnd && StringRef(ArgBegin[0]) == "-cc1test") { + DiagnosticsEngine Diags(DiagID, new TextDiagnosticPrinter(llvm::errs(), + DiagnosticOptions())); + return cc1_test(Diags, ArgBegin + 1, ArgEnd); + } + + // Initialize targets first, so that --version shows registered targets. + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); + + // Buffer diagnostics from argument parsing so that we can output them using a + // well formed diagnostic object. + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, DiagsBuffer); + bool Success; + Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(), + ArgBegin, ArgEnd, Diags); + + // Infer the builtin include path if unspecified. + if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && + Clang->getHeaderSearchOpts().ResourceDir.empty()) + Clang->getHeaderSearchOpts().ResourceDir = + CompilerInvocation::GetResourcesPath(Argv0, MainAddr); + + // Create the actual diagnostics engine. + Clang->createDiagnostics(ArgEnd - ArgBegin, const_cast<char**>(ArgBegin)); + if (!Clang->hasDiagnostics()) + return 1; + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + llvm::install_fatal_error_handler(LLVMErrorHandler, + static_cast<void*>(&Clang->getDiagnostics())); + + DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); + if (!Success) + return 1; + + // Execute the frontend actions. + Success = ExecuteCompilerInvocation(Clang.get()); + + // If any timers were active but haven't been destroyed yet, print their + // results now. This happens in -disable-free mode. + llvm::TimerGroup::printAll(llvm::errs()); + + // Our error handler depends on the Diagnostics object, which we're + // potentially about to delete. Uninstall the handler now so that any + // later errors use the default handling behavior instead. + llvm::remove_fatal_error_handler(); + + // When running with -disable-free, don't do any destruction or shutdown. + if (Clang->getFrontendOpts().DisableFree) { + if (llvm::AreStatisticsEnabled() || Clang->getFrontendOpts().ShowStats) + llvm::PrintStatistics(); + Clang.take(); + return !Success; + } + + // Managed static deconstruction. Useful for making things like + // -time-passes usable. + llvm::llvm_shutdown(); + + return !Success; +} diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp new file mode 100644 index 0000000..508d6da --- /dev/null +++ b/clang/tools/driver/cc1as_main.cpp @@ -0,0 +1,451 @@ +//===-- cc1as_main.cpp - Clang Assembler ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1as functionality, which implements +// the direct interface to the LLVM MC based assembler. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/CC1AsOptions.h" +#include "clang/Driver/OptTable.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCTargetAsmParser.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" +#include "llvm/Target/TargetData.h" +using namespace clang; +using namespace clang::driver; +using namespace llvm; + +namespace { + +/// \brief Helper class for representing a single invocation of the assembler. +struct AssemblerInvocation { + /// @name Target Options + /// @{ + + /// The name of the target triple to assemble for. + std::string Triple; + + /// If given, the name of the target CPU to determine which instructions + /// are legal. + std::string CPU; + + /// The list of target specific features to enable or disable -- this should + /// be a list of strings starting with '+' or '-'. + std::vector<std::string> Features; + + /// @} + /// @name Language Options + /// @{ + + std::vector<std::string> IncludePaths; + unsigned NoInitialTextSection : 1; + unsigned SaveTemporaryLabels : 1; + unsigned GenDwarfForAssembly : 1; + std::string DwarfDebugFlags; + + /// @} + /// @name Frontend Options + /// @{ + + std::string InputFile; + std::vector<std::string> LLVMArgs; + std::string OutputPath; + enum FileType { + FT_Asm, ///< Assembly (.s) output, transliterate mode. + FT_Null, ///< No output, for timing purposes. + FT_Obj ///< Object file output. + }; + FileType OutputType; + unsigned ShowHelp : 1; + unsigned ShowVersion : 1; + + /// @} + /// @name Transliterate Options + /// @{ + + unsigned OutputAsmVariant; + unsigned ShowEncoding : 1; + unsigned ShowInst : 1; + + /// @} + /// @name Assembler Options + /// @{ + + unsigned RelaxAll : 1; + unsigned NoExecStack : 1; + + /// @} + +public: + AssemblerInvocation() { + Triple = ""; + NoInitialTextSection = 0; + InputFile = "-"; + OutputPath = "-"; + OutputType = FT_Asm; + OutputAsmVariant = 0; + ShowInst = 0; + ShowEncoding = 0; + RelaxAll = 0; + NoExecStack = 0; + } + + static bool CreateFromArgs(AssemblerInvocation &Res, const char **ArgBegin, + const char **ArgEnd, DiagnosticsEngine &Diags); +}; + +} + +bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, + const char **ArgBegin, + const char **ArgEnd, + DiagnosticsEngine &Diags) { + using namespace clang::driver::cc1asoptions; + bool Success = true; + + // Parse the arguments. + OwningPtr<OptTable> OptTbl(createCC1AsOptTable()); + unsigned MissingArgIndex, MissingArgCount; + OwningPtr<InputArgList> Args( + OptTbl->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount)); + + // Check for missing argument error. + if (MissingArgCount) { + Diags.Report(diag::err_drv_missing_argument) + << Args->getArgString(MissingArgIndex) << MissingArgCount; + Success = false; + } + + // Issue errors on unknown arguments. + for (arg_iterator it = Args->filtered_begin(cc1asoptions::OPT_UNKNOWN), + ie = Args->filtered_end(); it != ie; ++it) { + Diags.Report(diag::err_drv_unknown_argument) << (*it) ->getAsString(*Args); + Success = false; + } + + // Construct the invocation. + + // Target Options + Opts.Triple = llvm::Triple::normalize(Args->getLastArgValue(OPT_triple)); + Opts.CPU = Args->getLastArgValue(OPT_target_cpu); + Opts.Features = Args->getAllArgValues(OPT_target_feature); + + // Use the default target triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getDefaultTargetTriple(); + + // Language Options + Opts.IncludePaths = Args->getAllArgValues(OPT_I); + Opts.NoInitialTextSection = Args->hasArg(OPT_n); + Opts.SaveTemporaryLabels = Args->hasArg(OPT_L); + Opts.GenDwarfForAssembly = Args->hasArg(OPT_g); + Opts.DwarfDebugFlags = Args->getLastArgValue(OPT_dwarf_debug_flags); + + // Frontend Options + if (Args->hasArg(OPT_INPUT)) { + bool First = true; + for (arg_iterator it = Args->filtered_begin(OPT_INPUT), + ie = Args->filtered_end(); it != ie; ++it, First=false) { + const Arg *A = it; + if (First) + Opts.InputFile = A->getValue(*Args); + else { + Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(*Args); + Success = false; + } + } + } + Opts.LLVMArgs = Args->getAllArgValues(OPT_mllvm); + if (Args->hasArg(OPT_fatal_warnings)) + Opts.LLVMArgs.push_back("-fatal-assembler-warnings"); + Opts.OutputPath = Args->getLastArgValue(OPT_o); + if (Arg *A = Args->getLastArg(OPT_filetype)) { + StringRef Name = A->getValue(*Args); + unsigned OutputType = StringSwitch<unsigned>(Name) + .Case("asm", FT_Asm) + .Case("null", FT_Null) + .Case("obj", FT_Obj) + .Default(~0U); + if (OutputType == ~0U) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(*Args) << Name; + Success = false; + } else + Opts.OutputType = FileType(OutputType); + } + Opts.ShowHelp = Args->hasArg(OPT_help); + Opts.ShowVersion = Args->hasArg(OPT_version); + + // Transliterate Options + Opts.OutputAsmVariant = Args->getLastArgIntValue(OPT_output_asm_variant, + 0, Diags); + Opts.ShowEncoding = Args->hasArg(OPT_show_encoding); + Opts.ShowInst = Args->hasArg(OPT_show_inst); + + // Assemble Options + Opts.RelaxAll = Args->hasArg(OPT_relax_all); + Opts.NoExecStack = Args->hasArg(OPT_no_exec_stack); + + return Success; +} + +static formatted_raw_ostream *GetOutputStream(AssemblerInvocation &Opts, + DiagnosticsEngine &Diags, + bool Binary) { + if (Opts.OutputPath.empty()) + Opts.OutputPath = "-"; + + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT. + if (Opts.OutputPath != "-") + sys::RemoveFileOnSignal(sys::Path(Opts.OutputPath)); + + std::string Error; + raw_fd_ostream *Out = + new raw_fd_ostream(Opts.OutputPath.c_str(), Error, + (Binary ? raw_fd_ostream::F_Binary : 0)); + if (!Error.empty()) { + Diags.Report(diag::err_fe_unable_to_open_output) + << Opts.OutputPath << Error; + return 0; + } + + return new formatted_raw_ostream(*Out, formatted_raw_ostream::DELETE_STREAM); +} + +static bool ExecuteAssembler(AssemblerInvocation &Opts, + DiagnosticsEngine &Diags) { + // Get the target specific parser. + std::string Error; + const Target *TheTarget(TargetRegistry::lookupTarget(Opts.Triple, Error)); + if (!TheTarget) { + Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; + return false; + } + + OwningPtr<MemoryBuffer> BufferPtr; + if (error_code ec = MemoryBuffer::getFileOrSTDIN(Opts.InputFile, BufferPtr)) { + Error = ec.message(); + Diags.Report(diag::err_fe_error_reading) << Opts.InputFile; + return false; + } + MemoryBuffer *Buffer = BufferPtr.take(); + + SourceMgr SrcMgr; + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + SrcMgr.AddNewSourceBuffer(Buffer, SMLoc()); + + // Record the location of the include directories so that the lexer can find + // it later. + SrcMgr.setIncludeDirs(Opts.IncludePaths); + + OwningPtr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(Opts.Triple)); + assert(MAI && "Unable to create target asm info!"); + + OwningPtr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple)); + assert(MRI && "Unable to create target register info!"); + + bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; + formatted_raw_ostream *Out = GetOutputStream(Opts, Diags, IsBinary); + if (!Out) + return false; + + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and + // MCObjectFileInfo needs a MCContext reference in order to initialize itself. + OwningPtr<MCObjectFileInfo> MOFI(new MCObjectFileInfo()); + MCContext Ctx(*MAI, *MRI, MOFI.get(), &SrcMgr); + // FIXME: Assembler behavior can change with -static. + MOFI->InitMCObjectFileInfo(Opts.Triple, + Reloc::Default, CodeModel::Default, Ctx); + if (Opts.SaveTemporaryLabels) + Ctx.setAllowTemporaryLabels(false); + if (Opts.GenDwarfForAssembly) + Ctx.setGenDwarfForAssembly(true); + if (!Opts.DwarfDebugFlags.empty()) + Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags)); + + // Build up the feature string from the target feature list. + std::string FS; + if (!Opts.Features.empty()) { + FS = Opts.Features[0]; + for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i) + FS += "," + Opts.Features[i]; + } + + OwningPtr<MCStreamer> Str; + + OwningPtr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); + OwningPtr<MCSubtargetInfo> + STI(TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS)); + + // FIXME: There is a bit of code duplication with addPassesToEmitFile. + if (Opts.OutputType == AssemblerInvocation::FT_Asm) { + MCInstPrinter *IP = + TheTarget->createMCInstPrinter(Opts.OutputAsmVariant, *MAI, *MCII, *MRI, + *STI); + MCCodeEmitter *CE = 0; + MCAsmBackend *MAB = 0; + if (Opts.ShowEncoding) { + CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx); + MAB = TheTarget->createMCAsmBackend(Opts.Triple); + } + Str.reset(TheTarget->createAsmStreamer(Ctx, *Out, /*asmverbose*/true, + /*useLoc*/ true, + /*useCFI*/ true, + /*useDwarfDirectory*/ true, + IP, CE, MAB, + Opts.ShowInst)); + } else if (Opts.OutputType == AssemblerInvocation::FT_Null) { + Str.reset(createNullStreamer(Ctx)); + } else { + assert(Opts.OutputType == AssemblerInvocation::FT_Obj && + "Invalid file type!"); + MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *STI, Ctx); + MCAsmBackend *MAB = TheTarget->createMCAsmBackend(Opts.Triple); + Str.reset(TheTarget->createMCObjectStreamer(Opts.Triple, Ctx, *MAB, *Out, + CE, Opts.RelaxAll, + Opts.NoExecStack)); + Str.get()->InitSections(); + } + + OwningPtr<MCAsmParser> Parser(createMCAsmParser(SrcMgr, Ctx, + *Str.get(), *MAI)); + OwningPtr<MCTargetAsmParser> TAP(TheTarget->createMCAsmParser(*STI, *Parser)); + if (!TAP) { + Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; + return false; + } + + Parser->setTargetParser(*TAP.get()); + + bool Success = !Parser->Run(Opts.NoInitialTextSection); + + // Close the output. + delete Out; + + // Delete output on errors. + if (!Success && Opts.OutputPath != "-") + sys::Path(Opts.OutputPath).eraseFromDisk(); + + return Success; +} + +static void LLVMErrorHandler(void *UserData, const std::string &Message) { + DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + exit(1); +} + +int cc1as_main(const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(ArgEnd - ArgBegin, ArgBegin); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Initialize targets and assembly printers/parsers. + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + + // Construct our diagnostic client. + TextDiagnosticPrinter *DiagClient + = new TextDiagnosticPrinter(errs(), DiagnosticOptions()); + DiagClient->setPrefix("clang -cc1as"); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticsEngine Diags(DiagID, DiagClient); + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + ScopedFatalErrorHandler FatalErrorHandler + (LLVMErrorHandler, static_cast<void*>(&Diags)); + + // Parse the arguments. + AssemblerInvocation Asm; + if (!AssemblerInvocation::CreateFromArgs(Asm, ArgBegin, ArgEnd, Diags)) + return 1; + + // Honor -help. + if (Asm.ShowHelp) { + OwningPtr<driver::OptTable> Opts(driver::createCC1AsOptTable()); + Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler"); + return 0; + } + + // Honor -version. + // + // FIXME: Use a better -version message? + if (Asm.ShowVersion) { + llvm::cl::PrintVersionMessage(); + return 0; + } + + // Honor -mllvm. + // + // FIXME: Remove this, one day. + if (!Asm.LLVMArgs.empty()) { + unsigned NumArgs = Asm.LLVMArgs.size(); + const char **Args = new const char*[NumArgs + 2]; + Args[0] = "clang (LLVM option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = Asm.LLVMArgs[i].c_str(); + Args[NumArgs + 1] = 0; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args); + } + + // Execute the invocation, unless there were parsing errors. + bool Success = false; + if (!Diags.hasErrorOccurred()) + Success = ExecuteAssembler(Asm, Diags); + + // If any timers were active but haven't been destroyed yet, print their + // results now. + TimerGroup::printAll(errs()); + + return !Success; +} diff --git a/clang/tools/driver/clang_symlink.cmake b/clang/tools/driver/clang_symlink.cmake new file mode 100644 index 0000000..c7341cb --- /dev/null +++ b/clang/tools/driver/clang_symlink.cmake @@ -0,0 +1,27 @@ +# We need to execute this script at installation time because the +# DESTDIR environment variable may be unset at configuration time. +# See PR8397. + +if(UNIX) + set(CLANGXX_LINK_OR_COPY create_symlink) + set(CLANGXX_DESTDIR $ENV{DESTDIR}) +else() + set(CLANGXX_LINK_OR_COPY copy) +endif() + +# CMAKE_EXECUTABLE_SUFFIX is undefined on cmake scripts. See PR9286. +if( WIN32 ) + set(EXECUTABLE_SUFFIX ".exe") +else() + set(EXECUTABLE_SUFFIX "") +endif() + +set(bindir "${CLANGXX_DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/") +set(clang "clang${EXECUTABLE_SUFFIX}") +set(clangxx "clang++${EXECUTABLE_SUFFIX}") + +message("Creating clang++ executable based on ${clang}") + +execute_process( + COMMAND "${CMAKE_COMMAND}" -E ${CLANGXX_LINK_OR_COPY} "${clang}" "${clangxx}" + WORKING_DIRECTORY "${bindir}") diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp new file mode 100644 index 0000000..8c05fff --- /dev/null +++ b/clang/tools/driver/driver.cpp @@ -0,0 +1,490 @@ +//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang driver; it is a thin wrapper +// for functionality in the Driver clang library. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/ArgList.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Option.h" +#include "clang/Driver/OptTable.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/system_error.h" +#include <cctype> +using namespace clang; +using namespace clang::driver; + +llvm::sys::Path GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { + if (!CanonicalPrefixes) + return llvm::sys::Path(Argv0); + + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *P = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::Path::GetMainExecutable(Argv0, P); +} + +static const char *SaveStringInSet(std::set<std::string> &SavedStrings, + StringRef S) { + return SavedStrings.insert(S).first->c_str(); +} + +/// ApplyQAOverride - Apply a list of edits to the input argument lists. +/// +/// The input string is a space separate list of edits to perform, +/// they are applied in order to the input argument lists. Edits +/// should be one of the following forms: +/// +/// '#': Silence information about the changes to the command line arguments. +/// +/// '^': Add FOO as a new argument at the beginning of the command line. +/// +/// '+': Add FOO as a new argument at the end of the command line. +/// +/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command +/// line. +/// +/// 'xOPTION': Removes all instances of the literal argument OPTION. +/// +/// 'XOPTION': Removes all instances of the literal argument OPTION, +/// and the following argument. +/// +/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox' +/// at the end of the command line. +/// +/// \param OS - The stream to write edit information to. +/// \param Args - The vector of command line arguments. +/// \param Edit - The override command to perform. +/// \param SavedStrings - Set to use for storing string representations. +static void ApplyOneQAOverride(raw_ostream &OS, + SmallVectorImpl<const char*> &Args, + StringRef Edit, + std::set<std::string> &SavedStrings) { + // This does not need to be efficient. + + if (Edit[0] == '^') { + const char *Str = + SaveStringInSet(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at beginning\n"; + Args.insert(Args.begin() + 1, Str); + } else if (Edit[0] == '+') { + const char *Str = + SaveStringInSet(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at end\n"; + Args.push_back(Str); + } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") && + Edit.slice(2, Edit.size()-1).find('/') != StringRef::npos) { + StringRef MatchPattern = Edit.substr(2).split('/').first; + StringRef ReplPattern = Edit.substr(2).split('/').second; + ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1); + + for (unsigned i = 1, e = Args.size(); i != e; ++i) { + std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]); + + if (Repl != Args[i]) { + OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n"; + Args[i] = SaveStringInSet(SavedStrings, Repl); + } + } + } else if (Edit[0] == 'x' || Edit[0] == 'X') { + std::string Option = Edit.substr(1, std::string::npos); + for (unsigned i = 1; i < Args.size();) { + if (Option == Args[i]) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + if (Edit[0] == 'X') { + if (i < Args.size()) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + OS << "### Invalid X edit, end of command line!\n"; + } + } else + ++i; + } + } else if (Edit[0] == 'O') { + for (unsigned i = 1; i < Args.size();) { + const char *A = Args[i]; + if (A[0] == '-' && A[1] == 'O' && + (A[2] == '\0' || + (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || + ('0' <= A[2] && A[2] <= '9'))))) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + ++i; + } + OS << "### Adding argument " << Edit << " at end\n"; + Args.push_back(SaveStringInSet(SavedStrings, '-' + Edit.str())); + } else { + OS << "### Unrecognized edit: " << Edit << "\n"; + } +} + +/// ApplyQAOverride - Apply a comma separate list of edits to the +/// input argument lists. See ApplyOneQAOverride. +static void ApplyQAOverride(SmallVectorImpl<const char*> &Args, + const char *OverrideStr, + std::set<std::string> &SavedStrings) { + raw_ostream *OS = &llvm::errs(); + + if (OverrideStr[0] == '#') { + ++OverrideStr; + OS = &llvm::nulls(); + } + + *OS << "### QA_OVERRIDE_GCC3_OPTIONS: " << OverrideStr << "\n"; + + // This does not need to be efficient. + + const char *S = OverrideStr; + while (*S) { + const char *End = ::strchr(S, ' '); + if (!End) + End = S + strlen(S); + if (End != S) + ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings); + S = End; + if (*S != '\0') + ++S; + } +} + +extern int cc1_main(const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr); +extern int cc1as_main(const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr); + +static void ExpandArgsFromBuf(const char *Arg, + SmallVectorImpl<const char*> &ArgVector, + std::set<std::string> &SavedStrings) { + const char *FName = Arg + 1; + OwningPtr<llvm::MemoryBuffer> MemBuf; + if (llvm::MemoryBuffer::getFile(FName, MemBuf)) { + ArgVector.push_back(SaveStringInSet(SavedStrings, Arg)); + return; + } + + const char *Buf = MemBuf->getBufferStart(); + char InQuote = ' '; + std::string CurArg; + + for (const char *P = Buf; ; ++P) { + if (*P == '\0' || (isspace(*P) && InQuote == ' ')) { + if (!CurArg.empty()) { + + if (CurArg[0] != '@') { + ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg)); + } else { + ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings); + } + + CurArg = ""; + } + if (*P == '\0') + break; + else + continue; + } + + if (isspace(*P)) { + if (InQuote != ' ') + CurArg.push_back(*P); + continue; + } + + if (*P == '"' || *P == '\'') { + if (InQuote == *P) + InQuote = ' '; + else if (InQuote == ' ') + InQuote = *P; + else + CurArg.push_back(*P); + continue; + } + + if (*P == '\\') { + ++P; + if (*P != '\0') + CurArg.push_back(*P); + continue; + } + CurArg.push_back(*P); + } +} + +static void ExpandArgv(int argc, const char **argv, + SmallVectorImpl<const char*> &ArgVector, + std::set<std::string> &SavedStrings) { + for (int i = 0; i < argc; ++i) { + const char *Arg = argv[i]; + if (Arg[0] != '@') { + ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg))); + continue; + } + + ExpandArgsFromBuf(Arg, ArgVector, SavedStrings); + } +} + +static void ParseProgName(SmallVectorImpl<const char *> &ArgVector, + std::set<std::string> &SavedStrings, + Driver &TheDriver) +{ + // Try to infer frontend type and default target from the program name. + + // suffixes[] contains the list of known driver suffixes. + // Suffixes are compared against the program name in order. + // If there is a match, the frontend type is updated as necessary (CPP/C++). + // If there is no match, a second round is done after stripping the last + // hyphen and everything following it. This allows using something like + // "clang++-2.9". + + // If there is a match in either the first or second round, + // the function tries to identify a target as prefix. E.g. + // "x86_64-linux-clang" as interpreted as suffix "clang" with + // target prefix "x86_64-linux". If such a target prefix is found, + // is gets added via -target as implicit first argument. + static const struct { + const char *Suffix; + bool IsCXX; + bool IsCPP; + } suffixes [] = { + { "clang", false, false }, + { "clang++", true, false }, + { "clang-c++", true, false }, + { "clang-cc", false, false }, + { "clang-cpp", false, true }, + { "clang-g++", true, false }, + { "clang-gcc", false, false }, + { "cc", false, false }, + { "cpp", false, true }, + { "++", true, false }, + }; + std::string ProgName(llvm::sys::path::stem(ArgVector[0])); + StringRef ProgNameRef(ProgName); + StringRef Prefix; + + for (int Components = 2; Components; --Components) { + bool FoundMatch = false; + size_t i; + + for (i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { + if (ProgNameRef.endswith(suffixes[i].Suffix)) { + FoundMatch = true; + if (suffixes[i].IsCXX) + TheDriver.CCCIsCXX = true; + if (suffixes[i].IsCPP) + TheDriver.CCCIsCPP = true; + break; + } + } + + if (FoundMatch) { + StringRef::size_type LastComponent = ProgNameRef.rfind('-', + ProgNameRef.size() - strlen(suffixes[i].Suffix)); + if (LastComponent != StringRef::npos) + Prefix = ProgNameRef.slice(0, LastComponent); + break; + } + + StringRef::size_type LastComponent = ProgNameRef.rfind('-'); + if (LastComponent == StringRef::npos) + break; + ProgNameRef = ProgNameRef.slice(0, LastComponent); + } + + if (Prefix.empty()) + return; + + std::string IgnoredError; + if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { + SmallVectorImpl<const char *>::iterator it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + ArgVector.insert(it, SaveStringInSet(SavedStrings, Prefix)); + ArgVector.insert(it, + SaveStringInSet(SavedStrings, std::string("-target"))); + } +} + +int main(int argc_, const char **argv_) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc_, argv_); + + std::set<std::string> SavedStrings; + SmallVector<const char*, 256> argv; + + ExpandArgv(argc_, argv_, argv, SavedStrings); + + // Handle -cc1 integrated tools. + if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) { + StringRef Tool = argv[1] + 4; + + if (Tool == "") + return cc1_main(argv.data()+2, argv.data()+argv.size(), argv[0], + (void*) (intptr_t) GetExecutablePath); + if (Tool == "as") + return cc1as_main(argv.data()+2, argv.data()+argv.size(), argv[0], + (void*) (intptr_t) GetExecutablePath); + + // Reject unknown tools. + llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n"; + return 1; + } + + bool CanonicalPrefixes = true; + for (int i = 1, size = argv.size(); i < size; ++i) { + if (StringRef(argv[i]) == "-no-canonical-prefixes") { + CanonicalPrefixes = false; + break; + } + } + + llvm::sys::Path Path = GetExecutablePath(argv[0], CanonicalPrefixes); + + DiagnosticOptions DiagOpts; + { + // Note that ParseDiagnosticArgs() uses the cc1 option table. + OwningPtr<OptTable> CC1Opts(createCC1OptTable()); + unsigned MissingArgIndex, MissingArgCount; + OwningPtr<InputArgList> Args(CC1Opts->ParseArgs(argv.begin()+1, argv.end(), + MissingArgIndex, MissingArgCount)); + // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. + // Any errors that would be diagnosed here will also be diagnosed later, + // when the DiagnosticsEngine actually exists. + (void) ParseDiagnosticArgs(DiagOpts, *Args); + } + // Now we can create the DiagnosticsEngine with a properly-filled-out + // DiagnosticOptions instance. + TextDiagnosticPrinter *DiagClient + = new TextDiagnosticPrinter(llvm::errs(), DiagOpts); + DiagClient->setPrefix(llvm::sys::path::stem(Path.str())); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + + DiagnosticsEngine Diags(DiagID, DiagClient); + ProcessWarningOptions(Diags, DiagOpts); + +#ifdef CLANG_IS_PRODUCTION + const bool IsProduction = true; +#else + const bool IsProduction = false; +#endif + Driver TheDriver(Path.str(), llvm::sys::getDefaultTargetTriple(), + "a.out", IsProduction, Diags); + + // Attempt to find the original path used to invoke the driver, to determine + // the installed path. We do this manually, because we want to support that + // path being a symlink. + { + SmallString<128> InstalledPath(argv[0]); + + // Do a PATH lookup, if there are no directory components. + if (llvm::sys::path::filename(InstalledPath) == InstalledPath) { + llvm::sys::Path Tmp = llvm::sys::Program::FindProgramByName( + llvm::sys::path::filename(InstalledPath.str())); + if (!Tmp.empty()) + InstalledPath = Tmp.str(); + } + llvm::sys::fs::make_absolute(InstalledPath); + InstalledPath = llvm::sys::path::parent_path(InstalledPath); + bool exists; + if (!llvm::sys::fs::exists(InstalledPath.str(), exists) && exists) + TheDriver.setInstalledDir(InstalledPath); + } + + llvm::InitializeAllTargets(); + ParseProgName(argv, SavedStrings, TheDriver); + + // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. + TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); + if (TheDriver.CCPrintOptions) + TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE"); + + // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE. + TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS"); + if (TheDriver.CCPrintHeaders) + TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE"); + + // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE. + TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); + if (TheDriver.CCLogDiagnostics) + TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); + + // Handle QA_OVERRIDE_GCC3_OPTIONS and CCC_ADD_ARGS, used for editing a + // command line behind the scenes. + if (const char *OverrideStr = ::getenv("QA_OVERRIDE_GCC3_OPTIONS")) { + // FIXME: Driver shouldn't take extra initial argument. + ApplyQAOverride(argv, OverrideStr, SavedStrings); + } else if (const char *Cur = ::getenv("CCC_ADD_ARGS")) { + // FIXME: Driver shouldn't take extra initial argument. + std::vector<const char*> ExtraArgs; + + for (;;) { + const char *Next = strchr(Cur, ','); + + if (Next) { + ExtraArgs.push_back(SaveStringInSet(SavedStrings, + std::string(Cur, Next))); + Cur = Next + 1; + } else { + if (*Cur != '\0') + ExtraArgs.push_back(SaveStringInSet(SavedStrings, Cur)); + break; + } + } + + argv.insert(&argv[1], ExtraArgs.begin(), ExtraArgs.end()); + } + + OwningPtr<Compilation> C(TheDriver.BuildCompilation(argv)); + int Res = 0; + const Command *FailingCommand = 0; + if (C.get()) + Res = TheDriver.ExecuteCompilation(*C, FailingCommand); + + // If result status is < 0, then the driver command signalled an error. + // In this case, generate additional diagnostic information if possible. + if (Res < 0) + TheDriver.generateCompilationDiagnostics(*C, FailingCommand); + + // If any timers were active but haven't been destroyed yet, print their + // results now. This happens in -disable-free mode. + llvm::TimerGroup::printAll(llvm::errs()); + + llvm::llvm_shutdown(); + + return Res; +} diff --git a/clang/tools/libclang/ARCMigrate.cpp b/clang/tools/libclang/ARCMigrate.cpp new file mode 100644 index 0000000..5ee5cf6 --- /dev/null +++ b/clang/tools/libclang/ARCMigrate.cpp @@ -0,0 +1,139 @@ +//===- ARCMigrate.cpp - Clang-C ARC Migration Library ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the main API hooks in the Clang-C ARC Migration library. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/Index.h" + +#include "CXString.h" +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "llvm/Support/FileSystem.h" + +using namespace clang; +using namespace arcmt; + +namespace { + +struct Remap { + std::vector<std::pair<std::string, std::string> > Vec; +}; + +} // anonymous namespace. + +//===----------------------------------------------------------------------===// +// libClang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { + +CXRemapping clang_getRemappings(const char *migrate_dir_path) { + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + if (!migrate_dir_path) { + if (Logging) + llvm::errs() << "clang_getRemappings was called with NULL parameter\n"; + return 0; + } + + bool exists = false; + llvm::sys::fs::exists(migrate_dir_path, exists); + if (!exists) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappings(\"" << migrate_dir_path + << "\")\n"; + llvm::errs() << "\"" << migrate_dir_path << "\" does not exist\n"; + } + return 0; + } + + TextDiagnosticBuffer diagBuffer; + OwningPtr<Remap> remap(new Remap()); + + bool err = arcmt::getFileRemappings(remap->Vec, migrate_dir_path,&diagBuffer); + + if (err) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappings(\"" << migrate_dir_path + << "\")\n"; + for (TextDiagnosticBuffer::const_iterator + I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I) + llvm::errs() << I->second << '\n'; + } + return 0; + } + + return remap.take(); +} + +CXRemapping clang_getRemappingsFromFileList(const char **filePaths, + unsigned numFiles) { + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + OwningPtr<Remap> remap(new Remap()); + + if (numFiles == 0) { + if (Logging) + llvm::errs() << "clang_getRemappingsFromFileList was called with " + "numFiles=0\n"; + return remap.take(); + } + + if (!filePaths) { + if (Logging) + llvm::errs() << "clang_getRemappingsFromFileList was called with " + "NULL filePaths\n"; + return 0; + } + + TextDiagnosticBuffer diagBuffer; + SmallVector<StringRef, 32> Files; + for (unsigned i = 0; i != numFiles; ++i) + Files.push_back(filePaths[i]); + + bool err = arcmt::getFileRemappingsFromFileList(remap->Vec, Files, + &diagBuffer); + + if (err) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappingsFromFileList\n"; + for (TextDiagnosticBuffer::const_iterator + I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I) + llvm::errs() << I->second << '\n'; + } + return remap.take(); + } + + return remap.take(); +} + +unsigned clang_remap_getNumFiles(CXRemapping map) { + return static_cast<Remap *>(map)->Vec.size(); + +} + +void clang_remap_getFilenames(CXRemapping map, unsigned index, + CXString *original, CXString *transformed) { + if (original) + *original = cxstring::createCXString( + static_cast<Remap *>(map)->Vec[index].first, + /*DupString =*/ true); + if (transformed) + *transformed = cxstring::createCXString( + static_cast<Remap *>(map)->Vec[index].second, + /*DupString =*/ true); +} + +void clang_remap_dispose(CXRemapping map) { + delete static_cast<Remap *>(map); +} + +} // end: extern "C" diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp new file mode 100644 index 0000000..605cc8b --- /dev/null +++ b/clang/tools/libclang/CIndex.cpp @@ -0,0 +1,5876 @@ +//===- CIndex.cpp - Clang-C Source Indexing Library -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the main API hooks in the Clang-C Source Indexing +// library. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "CXTranslationUnit.h" +#include "CXString.h" +#include "CXType.h" +#include "CXSourceLocation.h" +#include "CIndexDiagnostic.h" +#include "CursorVisitor.h" + +#include "clang/Basic/Version.h" + +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; +using namespace clang::cxcursor; +using namespace clang::cxstring; +using namespace clang::cxtu; +using namespace clang::cxindex; + +CXTranslationUnit cxtu::MakeCXTranslationUnit(CIndexer *CIdx, ASTUnit *TU) { + if (!TU) + return 0; + CXTranslationUnit D = new CXTranslationUnitImpl(); + D->CIdx = CIdx; + D->TUData = TU; + D->StringPool = createCXStringPool(); + D->Diagnostics = 0; + return D; +} + +cxtu::CXTUOwner::~CXTUOwner() { + if (TU) + clang_disposeTranslationUnit(TU); +} + +/// \brief Compare two source ranges to determine their relative position in +/// the translation unit. +static RangeComparisonResult RangeCompare(SourceManager &SM, + SourceRange R1, + SourceRange R2) { + assert(R1.isValid() && "First range is invalid?"); + assert(R2.isValid() && "Second range is invalid?"); + if (R1.getEnd() != R2.getBegin() && + SM.isBeforeInTranslationUnit(R1.getEnd(), R2.getBegin())) + return RangeBefore; + if (R2.getEnd() != R1.getBegin() && + SM.isBeforeInTranslationUnit(R2.getEnd(), R1.getBegin())) + return RangeAfter; + return RangeOverlap; +} + +/// \brief Determine if a source location falls within, before, or after a +/// a given source range. +static RangeComparisonResult LocationCompare(SourceManager &SM, + SourceLocation L, SourceRange R) { + assert(R.isValid() && "First range is invalid?"); + assert(L.isValid() && "Second range is invalid?"); + if (L == R.getBegin() || L == R.getEnd()) + return RangeOverlap; + if (SM.isBeforeInTranslationUnit(L, R.getBegin())) + return RangeBefore; + if (SM.isBeforeInTranslationUnit(R.getEnd(), L)) + return RangeAfter; + return RangeOverlap; +} + +/// \brief Translate a Clang source range into a CIndex source range. +/// +/// Clang internally represents ranges where the end location points to the +/// start of the token at the end. However, for external clients it is more +/// useful to have a CXSourceRange be a proper half-open interval. This routine +/// does the appropriate translation. +CXSourceRange cxloc::translateSourceRange(const SourceManager &SM, + const LangOptions &LangOpts, + const CharSourceRange &R) { + // We want the last character in this location, so we will adjust the + // location accordingly. + SourceLocation EndLoc = R.getEnd(); + if (EndLoc.isValid() && EndLoc.isMacroID() && !SM.isMacroArgExpansion(EndLoc)) + EndLoc = SM.getExpansionRange(EndLoc).second; + if (R.isTokenRange() && !EndLoc.isInvalid()) { + unsigned Length = Lexer::MeasureTokenLength(SM.getSpellingLoc(EndLoc), + SM, LangOpts); + EndLoc = EndLoc.getLocWithOffset(Length); + } + + CXSourceRange Result = { { (void *)&SM, (void *)&LangOpts }, + R.getBegin().getRawEncoding(), + EndLoc.getRawEncoding() }; + return Result; +} + +//===----------------------------------------------------------------------===// +// Cursor visitor. +//===----------------------------------------------------------------------===// + +static SourceRange getRawCursorExtent(CXCursor C); +static SourceRange getFullCursorExtent(CXCursor C, SourceManager &SrcMgr); + + +RangeComparisonResult CursorVisitor::CompareRegionOfInterest(SourceRange R) { + return RangeCompare(AU->getSourceManager(), R, RegionOfInterest); +} + +/// \brief Visit the given cursor and, if requested by the visitor, +/// its children. +/// +/// \param Cursor the cursor to visit. +/// +/// \param CheckRegionOfInterest if true, then the caller already checked that +/// this cursor is within the region of interest. +/// +/// \returns true if the visitation should be aborted, false if it +/// should continue. +bool CursorVisitor::Visit(CXCursor Cursor, bool CheckedRegionOfInterest) { + if (clang_isInvalid(Cursor.kind)) + return false; + + if (clang_isDeclaration(Cursor.kind)) { + Decl *D = getCursorDecl(Cursor); + if (!D) { + assert(0 && "Invalid declaration cursor"); + return true; // abort. + } + + // Ignore implicit declarations, unless it's an objc method because + // currently we should report implicit methods for properties when indexing. + if (D->isImplicit() && !isa<ObjCMethodDecl>(D)) + return false; + } + + // If we have a range of interest, and this cursor doesn't intersect with it, + // we're done. + if (RegionOfInterest.isValid() && !CheckedRegionOfInterest) { + SourceRange Range = getRawCursorExtent(Cursor); + if (Range.isInvalid() || CompareRegionOfInterest(Range)) + return false; + } + + switch (Visitor(Cursor, Parent, ClientData)) { + case CXChildVisit_Break: + return true; + + case CXChildVisit_Continue: + return false; + + case CXChildVisit_Recurse: + return VisitChildren(Cursor); + } + + llvm_unreachable("Invalid CXChildVisitResult!"); +} + +static bool visitPreprocessedEntitiesInRange(SourceRange R, + PreprocessingRecord &PPRec, + CursorVisitor &Visitor) { + SourceManager &SM = Visitor.getASTUnit()->getSourceManager(); + FileID FID; + + if (!Visitor.shouldVisitIncludedEntities()) { + // If the begin/end of the range lie in the same FileID, do the optimization + // where we skip preprocessed entities that do not come from the same FileID. + FID = SM.getFileID(SM.getFileLoc(R.getBegin())); + if (FID != SM.getFileID(SM.getFileLoc(R.getEnd()))) + FID = FileID(); + } + + std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator> + Entities = PPRec.getPreprocessedEntitiesInRange(R); + return Visitor.visitPreprocessedEntities(Entities.first, Entities.second, + PPRec, FID); +} + +void CursorVisitor::visitFileRegion() { + if (RegionOfInterest.isInvalid()) + return; + + ASTUnit *Unit = static_cast<ASTUnit *>(TU->TUData); + SourceManager &SM = Unit->getSourceManager(); + + std::pair<FileID, unsigned> + Begin = SM.getDecomposedLoc(SM.getFileLoc(RegionOfInterest.getBegin())), + End = SM.getDecomposedLoc(SM.getFileLoc(RegionOfInterest.getEnd())); + + if (End.first != Begin.first) { + // If the end does not reside in the same file, try to recover by + // picking the end of the file of begin location. + End.first = Begin.first; + End.second = SM.getFileIDSize(Begin.first); + } + + assert(Begin.first == End.first); + if (Begin.second > End.second) + return; + + FileID File = Begin.first; + unsigned Offset = Begin.second; + unsigned Length = End.second - Begin.second; + + if (!VisitDeclsOnly && !VisitPreprocessorLast) + if (visitPreprocessedEntitiesInRegion()) + return; // visitation break. + + visitDeclsFromFileRegion(File, Offset, Length); + + if (!VisitDeclsOnly && VisitPreprocessorLast) + visitPreprocessedEntitiesInRegion(); +} + +static bool isInLexicalContext(Decl *D, DeclContext *DC) { + if (!DC) + return false; + + for (DeclContext *DeclDC = D->getLexicalDeclContext(); + DeclDC; DeclDC = DeclDC->getLexicalParent()) { + if (DeclDC == DC) + return true; + } + return false; +} + +void CursorVisitor::visitDeclsFromFileRegion(FileID File, + unsigned Offset, unsigned Length) { + ASTUnit *Unit = static_cast<ASTUnit *>(TU->TUData); + SourceManager &SM = Unit->getSourceManager(); + SourceRange Range = RegionOfInterest; + + SmallVector<Decl *, 16> Decls; + Unit->findFileRegionDecls(File, Offset, Length, Decls); + + // If we didn't find any file level decls for the file, try looking at the + // file that it was included from. + while (Decls.empty() || Decls.front()->isTopLevelDeclInObjCContainer()) { + bool Invalid = false; + const SrcMgr::SLocEntry &SLEntry = SM.getSLocEntry(File, &Invalid); + if (Invalid) + return; + + SourceLocation Outer; + if (SLEntry.isFile()) + Outer = SLEntry.getFile().getIncludeLoc(); + else + Outer = SLEntry.getExpansion().getExpansionLocStart(); + if (Outer.isInvalid()) + return; + + llvm::tie(File, Offset) = SM.getDecomposedExpansionLoc(Outer); + Length = 0; + Unit->findFileRegionDecls(File, Offset, Length, Decls); + } + + assert(!Decls.empty()); + + bool VisitedAtLeastOnce = false; + DeclContext *CurDC = 0; + SmallVector<Decl *, 16>::iterator DIt = Decls.begin(); + for (SmallVector<Decl *, 16>::iterator DE = Decls.end(); DIt != DE; ++DIt) { + Decl *D = *DIt; + if (D->getSourceRange().isInvalid()) + continue; + + if (isInLexicalContext(D, CurDC)) + continue; + + CurDC = dyn_cast<DeclContext>(D); + + if (TagDecl *TD = dyn_cast<TagDecl>(D)) + if (!TD->isFreeStanding()) + continue; + + RangeComparisonResult CompRes = RangeCompare(SM, D->getSourceRange(),Range); + if (CompRes == RangeBefore) + continue; + if (CompRes == RangeAfter) + break; + + assert(CompRes == RangeOverlap); + VisitedAtLeastOnce = true; + + if (isa<ObjCContainerDecl>(D)) { + FileDI_current = &DIt; + FileDE_current = DE; + } else { + FileDI_current = 0; + } + + if (Visit(MakeCXCursor(D, TU, Range), /*CheckedRegionOfInterest=*/true)) + break; + } + + if (VisitedAtLeastOnce) + return; + + // No Decls overlapped with the range. Move up the lexical context until there + // is a context that contains the range or we reach the translation unit + // level. + DeclContext *DC = DIt == Decls.begin() ? (*DIt)->getLexicalDeclContext() + : (*(DIt-1))->getLexicalDeclContext(); + + while (DC && !DC->isTranslationUnit()) { + Decl *D = cast<Decl>(DC); + SourceRange CurDeclRange = D->getSourceRange(); + if (CurDeclRange.isInvalid()) + break; + + if (RangeCompare(SM, CurDeclRange, Range) == RangeOverlap) { + Visit(MakeCXCursor(D, TU, Range), /*CheckedRegionOfInterest=*/true); + break; + } + + DC = D->getLexicalDeclContext(); + } +} + +bool CursorVisitor::visitPreprocessedEntitiesInRegion() { + if (!AU->getPreprocessor().getPreprocessingRecord()) + return false; + + PreprocessingRecord &PPRec + = *AU->getPreprocessor().getPreprocessingRecord(); + SourceManager &SM = AU->getSourceManager(); + + if (RegionOfInterest.isValid()) { + SourceRange MappedRange = AU->mapRangeToPreamble(RegionOfInterest); + SourceLocation B = MappedRange.getBegin(); + SourceLocation E = MappedRange.getEnd(); + + if (AU->isInPreambleFileID(B)) { + if (SM.isLoadedSourceLocation(E)) + return visitPreprocessedEntitiesInRange(SourceRange(B, E), + PPRec, *this); + + // Beginning of range lies in the preamble but it also extends beyond + // it into the main file. Split the range into 2 parts, one covering + // the preamble and another covering the main file. This allows subsequent + // calls to visitPreprocessedEntitiesInRange to accept a source range that + // lies in the same FileID, allowing it to skip preprocessed entities that + // do not come from the same FileID. + bool breaked = + visitPreprocessedEntitiesInRange( + SourceRange(B, AU->getEndOfPreambleFileID()), + PPRec, *this); + if (breaked) return true; + return visitPreprocessedEntitiesInRange( + SourceRange(AU->getStartOfMainFileID(), E), + PPRec, *this); + } + + return visitPreprocessedEntitiesInRange(SourceRange(B, E), PPRec, *this); + } + + bool OnlyLocalDecls + = !AU->isMainFileAST() && AU->getOnlyLocalDecls(); + + if (OnlyLocalDecls) + return visitPreprocessedEntities(PPRec.local_begin(), PPRec.local_end(), + PPRec); + + return visitPreprocessedEntities(PPRec.begin(), PPRec.end(), PPRec); +} + +template<typename InputIterator> +bool CursorVisitor::visitPreprocessedEntities(InputIterator First, + InputIterator Last, + PreprocessingRecord &PPRec, + FileID FID) { + for (; First != Last; ++First) { + if (!FID.isInvalid() && !PPRec.isEntityInFileID(First, FID)) + continue; + + PreprocessedEntity *PPE = *First; + if (MacroExpansion *ME = dyn_cast<MacroExpansion>(PPE)) { + if (Visit(MakeMacroExpansionCursor(ME, TU))) + return true; + + continue; + } + + if (MacroDefinition *MD = dyn_cast<MacroDefinition>(PPE)) { + if (Visit(MakeMacroDefinitionCursor(MD, TU))) + return true; + + continue; + } + + if (InclusionDirective *ID = dyn_cast<InclusionDirective>(PPE)) { + if (Visit(MakeInclusionDirectiveCursor(ID, TU))) + return true; + + continue; + } + } + + return false; +} + +/// \brief Visit the children of the given cursor. +/// +/// \returns true if the visitation should be aborted, false if it +/// should continue. +bool CursorVisitor::VisitChildren(CXCursor Cursor) { + if (clang_isReference(Cursor.kind) && + Cursor.kind != CXCursor_CXXBaseSpecifier) { + // By definition, references have no children. + return false; + } + + // Set the Parent field to Cursor, then back to its old value once we're + // done. + SetParentRAII SetParent(Parent, StmtParent, Cursor); + + if (clang_isDeclaration(Cursor.kind)) { + Decl *D = getCursorDecl(Cursor); + if (!D) + return false; + + return VisitAttributes(D) || Visit(D); + } + + if (clang_isStatement(Cursor.kind)) { + if (Stmt *S = getCursorStmt(Cursor)) + return Visit(S); + + return false; + } + + if (clang_isExpression(Cursor.kind)) { + if (Expr *E = getCursorExpr(Cursor)) + return Visit(E); + + return false; + } + + if (clang_isTranslationUnit(Cursor.kind)) { + CXTranslationUnit tu = getCursorTU(Cursor); + ASTUnit *CXXUnit = static_cast<ASTUnit*>(tu->TUData); + + int VisitOrder[2] = { VisitPreprocessorLast, !VisitPreprocessorLast }; + for (unsigned I = 0; I != 2; ++I) { + if (VisitOrder[I]) { + if (!CXXUnit->isMainFileAST() && CXXUnit->getOnlyLocalDecls() && + RegionOfInterest.isInvalid()) { + for (ASTUnit::top_level_iterator TL = CXXUnit->top_level_begin(), + TLEnd = CXXUnit->top_level_end(); + TL != TLEnd; ++TL) { + if (Visit(MakeCXCursor(*TL, tu, RegionOfInterest), true)) + return true; + } + } else if (VisitDeclContext( + CXXUnit->getASTContext().getTranslationUnitDecl())) + return true; + continue; + } + + // Walk the preprocessing record. + if (CXXUnit->getPreprocessor().getPreprocessingRecord()) + visitPreprocessedEntitiesInRegion(); + } + + return false; + } + + if (Cursor.kind == CXCursor_CXXBaseSpecifier) { + if (CXXBaseSpecifier *Base = getCursorCXXBaseSpecifier(Cursor)) { + if (TypeSourceInfo *BaseTSInfo = Base->getTypeSourceInfo()) { + return Visit(BaseTSInfo->getTypeLoc()); + } + } + } + + if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { + IBOutletCollectionAttr *A = + cast<IBOutletCollectionAttr>(cxcursor::getCursorAttr(Cursor)); + if (const ObjCInterfaceType *InterT = A->getInterface()->getAs<ObjCInterfaceType>()) + return Visit(cxcursor::MakeCursorObjCClassRef(InterT->getInterface(), + A->getInterfaceLoc(), TU)); + } + + // Nothing to visit at the moment. + return false; +} + +bool CursorVisitor::VisitBlockDecl(BlockDecl *B) { + if (TypeSourceInfo *TSInfo = B->getSignatureAsWritten()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + if (Stmt *Body = B->getBody()) + return Visit(MakeCXCursor(Body, StmtParent, TU, RegionOfInterest)); + + return false; +} + +llvm::Optional<bool> CursorVisitor::shouldVisitCursor(CXCursor Cursor) { + if (RegionOfInterest.isValid()) { + SourceRange Range = getFullCursorExtent(Cursor, AU->getSourceManager()); + if (Range.isInvalid()) + return llvm::Optional<bool>(); + + switch (CompareRegionOfInterest(Range)) { + case RangeBefore: + // This declaration comes before the region of interest; skip it. + return llvm::Optional<bool>(); + + case RangeAfter: + // This declaration comes after the region of interest; we're done. + return false; + + case RangeOverlap: + // This declaration overlaps the region of interest; visit it. + break; + } + } + return true; +} + +bool CursorVisitor::VisitDeclContext(DeclContext *DC) { + DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); + + // FIXME: Eventually remove. This part of a hack to support proper + // iteration over all Decls contained lexically within an ObjC container. + SaveAndRestore<DeclContext::decl_iterator*> DI_saved(DI_current, &I); + SaveAndRestore<DeclContext::decl_iterator> DE_saved(DE_current, E); + + for ( ; I != E; ++I) { + Decl *D = *I; + if (D->getLexicalDeclContext() != DC) + continue; + CXCursor Cursor = MakeCXCursor(D, TU, RegionOfInterest); + + // FIXME: ObjCClassRef/ObjCProtocolRef for forward class/protocol + // declarations is a mismatch with the compiler semantics. + if (Cursor.kind == CXCursor_ObjCInterfaceDecl) { + ObjCInterfaceDecl *ID = cast<ObjCInterfaceDecl>(D); + if (!ID->isThisDeclarationADefinition()) + Cursor = MakeCursorObjCClassRef(ID, ID->getLocation(), TU); + + } else if (Cursor.kind == CXCursor_ObjCProtocolDecl) { + ObjCProtocolDecl *PD = cast<ObjCProtocolDecl>(D); + if (!PD->isThisDeclarationADefinition()) + Cursor = MakeCursorObjCProtocolRef(PD, PD->getLocation(), TU); + } + + const llvm::Optional<bool> &V = shouldVisitCursor(Cursor); + if (!V.hasValue()) + continue; + if (!V.getValue()) + return false; + if (Visit(Cursor, true)) + return true; + } + return false; +} + +bool CursorVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) { + llvm_unreachable("Translation units are visited directly by Visit()"); +} + +bool CursorVisitor::VisitTypeAliasDecl(TypeAliasDecl *D) { + if (TypeSourceInfo *TSInfo = D->getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitTypedefDecl(TypedefDecl *D) { + if (TypeSourceInfo *TSInfo = D->getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitTagDecl(TagDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitClassTemplateSpecializationDecl( + ClassTemplateSpecializationDecl *D) { + bool ShouldVisitBody = false; + switch (D->getSpecializationKind()) { + case TSK_Undeclared: + case TSK_ImplicitInstantiation: + // Nothing to visit + return false; + + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + break; + + case TSK_ExplicitSpecialization: + ShouldVisitBody = true; + break; + } + + // Visit the template arguments used in the specialization. + if (TypeSourceInfo *SpecType = D->getTypeAsWritten()) { + TypeLoc TL = SpecType->getTypeLoc(); + if (TemplateSpecializationTypeLoc *TSTLoc + = dyn_cast<TemplateSpecializationTypeLoc>(&TL)) { + for (unsigned I = 0, N = TSTLoc->getNumArgs(); I != N; ++I) + if (VisitTemplateArgumentLoc(TSTLoc->getArgLoc(I))) + return true; + } + } + + if (ShouldVisitBody && VisitCXXRecordDecl(D)) + return true; + + return false; +} + +bool CursorVisitor::VisitClassTemplatePartialSpecializationDecl( + ClassTemplatePartialSpecializationDecl *D) { + // FIXME: Visit the "outer" template parameter lists on the TagDecl + // before visiting these template parameters. + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + // Visit the partial specialization arguments. + const TemplateArgumentLoc *TemplateArgs = D->getTemplateArgsAsWritten(); + for (unsigned I = 0, N = D->getNumTemplateArgsAsWritten(); I != N; ++I) + if (VisitTemplateArgumentLoc(TemplateArgs[I])) + return true; + + return VisitCXXRecordDecl(D); +} + +bool CursorVisitor::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + // Visit the default argument. + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) + if (TypeSourceInfo *DefArg = D->getDefaultArgumentInfo()) + if (Visit(DefArg->getTypeLoc())) + return true; + + return false; +} + +bool CursorVisitor::VisitEnumConstantDecl(EnumConstantDecl *D) { + if (Expr *Init = D->getInitExpr()) + return Visit(MakeCXCursor(Init, StmtParent, TU, RegionOfInterest)); + return false; +} + +bool CursorVisitor::VisitDeclaratorDecl(DeclaratorDecl *DD) { + if (TypeSourceInfo *TSInfo = DD->getTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + // Visit the nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = DD->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return false; +} + +/// \brief Compare two base or member initializers based on their source order. +static int CompareCXXCtorInitializers(const void* Xp, const void *Yp) { + CXXCtorInitializer const * const *X + = static_cast<CXXCtorInitializer const * const *>(Xp); + CXXCtorInitializer const * const *Y + = static_cast<CXXCtorInitializer const * const *>(Yp); + + if ((*X)->getSourceOrder() < (*Y)->getSourceOrder()) + return -1; + else if ((*X)->getSourceOrder() > (*Y)->getSourceOrder()) + return 1; + else + return 0; +} + +bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { + if (TypeSourceInfo *TSInfo = ND->getTypeSourceInfo()) { + // Visit the function declaration's syntactic components in the order + // written. This requires a bit of work. + TypeLoc TL = TSInfo->getTypeLoc().IgnoreParens(); + FunctionTypeLoc *FTL = dyn_cast<FunctionTypeLoc>(&TL); + + // If we have a function declared directly (without the use of a typedef), + // visit just the return type. Otherwise, just visit the function's type + // now. + if ((FTL && !isa<CXXConversionDecl>(ND) && Visit(FTL->getResultLoc())) || + (!FTL && Visit(TL))) + return true; + + // Visit the nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = ND->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + // Visit the declaration name. + if (VisitDeclarationNameInfo(ND->getNameInfo())) + return true; + + // FIXME: Visit explicitly-specified template arguments! + + // Visit the function parameters, if we have a function type. + if (FTL && VisitFunctionTypeLoc(*FTL, true)) + return true; + + // FIXME: Attributes? + } + + if (ND->doesThisDeclarationHaveABody() && !ND->isLateTemplateParsed()) { + if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(ND)) { + // Find the initializers that were written in the source. + SmallVector<CXXCtorInitializer *, 4> WrittenInits; + for (CXXConstructorDecl::init_iterator I = Constructor->init_begin(), + IEnd = Constructor->init_end(); + I != IEnd; ++I) { + if (!(*I)->isWritten()) + continue; + + WrittenInits.push_back(*I); + } + + // Sort the initializers in source order + llvm::array_pod_sort(WrittenInits.begin(), WrittenInits.end(), + &CompareCXXCtorInitializers); + + // Visit the initializers in source order + for (unsigned I = 0, N = WrittenInits.size(); I != N; ++I) { + CXXCtorInitializer *Init = WrittenInits[I]; + if (Init->isAnyMemberInitializer()) { + if (Visit(MakeCursorMemberRef(Init->getAnyMember(), + Init->getMemberLocation(), TU))) + return true; + } else if (TypeSourceInfo *TInfo = Init->getTypeSourceInfo()) { + if (Visit(TInfo->getTypeLoc())) + return true; + } + + // Visit the initializer value. + if (Expr *Initializer = Init->getInit()) + if (Visit(MakeCXCursor(Initializer, ND, TU, RegionOfInterest))) + return true; + } + } + + if (Visit(MakeCXCursor(ND->getBody(), StmtParent, TU, RegionOfInterest))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitFieldDecl(FieldDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (Expr *BitWidth = D->getBitWidth()) + return Visit(MakeCXCursor(BitWidth, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitVarDecl(VarDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (Expr *Init = D->getInit()) + return Visit(MakeCXCursor(Init, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) + if (Expr *DefArg = D->getDefaultArgument()) + return Visit(MakeCXCursor(DefArg, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { + // FIXME: Visit the "outer" template parameter lists on the FunctionDecl + // before visiting these template parameters. + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + return VisitFunctionDecl(D->getTemplatedDecl()); +} + +bool CursorVisitor::VisitClassTemplateDecl(ClassTemplateDecl *D) { + // FIXME: Visit the "outer" template parameter lists on the TagDecl + // before visiting these template parameters. + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + return VisitCXXRecordDecl(D->getTemplatedDecl()); +} + +bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited() && + VisitTemplateArgumentLoc(D->getDefaultArgument())) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) { + if (TypeSourceInfo *TSInfo = ND->getResultTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + for (ObjCMethodDecl::param_iterator P = ND->param_begin(), + PEnd = ND->param_end(); + P != PEnd; ++P) { + if (Visit(MakeCXCursor(*P, TU, RegionOfInterest))) + return true; + } + + if (ND->isThisDeclarationADefinition() && + Visit(MakeCXCursor(ND->getBody(), StmtParent, TU, RegionOfInterest))) + return true; + + return false; +} + +template <typename DeclIt> +static void addRangedDeclsInContainer(DeclIt *DI_current, DeclIt DE_current, + SourceManager &SM, SourceLocation EndLoc, + SmallVectorImpl<Decl *> &Decls) { + DeclIt next = *DI_current; + while (++next != DE_current) { + Decl *D_next = *next; + if (!D_next) + break; + SourceLocation L = D_next->getLocStart(); + if (!L.isValid()) + break; + if (SM.isBeforeInTranslationUnit(L, EndLoc)) { + *DI_current = next; + Decls.push_back(D_next); + continue; + } + break; + } +} + +namespace { + struct ContainerDeclsSort { + SourceManager &SM; + ContainerDeclsSort(SourceManager &sm) : SM(sm) {} + bool operator()(Decl *A, Decl *B) { + SourceLocation L_A = A->getLocStart(); + SourceLocation L_B = B->getLocStart(); + assert(L_A.isValid() && L_B.isValid()); + return SM.isBeforeInTranslationUnit(L_A, L_B); + } + }; +} + +bool CursorVisitor::VisitObjCContainerDecl(ObjCContainerDecl *D) { + // FIXME: Eventually convert back to just 'VisitDeclContext()'. Essentially + // an @implementation can lexically contain Decls that are not properly + // nested in the AST. When we identify such cases, we need to retrofit + // this nesting here. + if (!DI_current && !FileDI_current) + return VisitDeclContext(D); + + // Scan the Decls that immediately come after the container + // in the current DeclContext. If any fall within the + // container's lexical region, stash them into a vector + // for later processing. + SmallVector<Decl *, 24> DeclsInContainer; + SourceLocation EndLoc = D->getSourceRange().getEnd(); + SourceManager &SM = AU->getSourceManager(); + if (EndLoc.isValid()) { + if (DI_current) { + addRangedDeclsInContainer(DI_current, DE_current, SM, EndLoc, + DeclsInContainer); + } else { + addRangedDeclsInContainer(FileDI_current, FileDE_current, SM, EndLoc, + DeclsInContainer); + } + } + + // The common case. + if (DeclsInContainer.empty()) + return VisitDeclContext(D); + + // Get all the Decls in the DeclContext, and sort them with the + // additional ones we've collected. Then visit them. + for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); + I!=E; ++I) { + Decl *subDecl = *I; + if (!subDecl || subDecl->getLexicalDeclContext() != D || + subDecl->getLocStart().isInvalid()) + continue; + DeclsInContainer.push_back(subDecl); + } + + // Now sort the Decls so that they appear in lexical order. + std::sort(DeclsInContainer.begin(), DeclsInContainer.end(), + ContainerDeclsSort(SM)); + + // Now visit the decls. + for (SmallVectorImpl<Decl*>::iterator I = DeclsInContainer.begin(), + E = DeclsInContainer.end(); I != E; ++I) { + CXCursor Cursor = MakeCXCursor(*I, TU, RegionOfInterest); + const llvm::Optional<bool> &V = shouldVisitCursor(Cursor); + if (!V.hasValue()) + continue; + if (!V.getValue()) + return false; + if (Visit(Cursor, true)) + return true; + } + return false; +} + +bool CursorVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { + if (Visit(MakeCursorObjCClassRef(ND->getClassInterface(), ND->getLocation(), + TU))) + return true; + + ObjCCategoryDecl::protocol_loc_iterator PL = ND->protocol_loc_begin(); + for (ObjCCategoryDecl::protocol_iterator I = ND->protocol_begin(), + E = ND->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(ND); +} + +bool CursorVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) { + if (!PID->isThisDeclarationADefinition()) + return Visit(MakeCursorObjCProtocolRef(PID, PID->getLocation(), TU)); + + ObjCProtocolDecl::protocol_loc_iterator PL = PID->protocol_loc_begin(); + for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), + E = PID->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(PID); +} + +bool CursorVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *PD) { + if (PD->getTypeSourceInfo() && Visit(PD->getTypeSourceInfo()->getTypeLoc())) + return true; + + // FIXME: This implements a workaround with @property declarations also being + // installed in the DeclContext for the @interface. Eventually this code + // should be removed. + ObjCCategoryDecl *CDecl = dyn_cast<ObjCCategoryDecl>(PD->getDeclContext()); + if (!CDecl || !CDecl->IsClassExtension()) + return false; + + ObjCInterfaceDecl *ID = CDecl->getClassInterface(); + if (!ID) + return false; + + IdentifierInfo *PropertyId = PD->getIdentifier(); + ObjCPropertyDecl *prevDecl = + ObjCPropertyDecl::findPropertyDecl(cast<DeclContext>(ID), PropertyId); + + if (!prevDecl) + return false; + + // Visit synthesized methods since they will be skipped when visiting + // the @interface. + if (ObjCMethodDecl *MD = prevDecl->getGetterMethodDecl()) + if (MD->isSynthesized() && MD->getLexicalDeclContext() == CDecl) + if (Visit(MakeCXCursor(MD, TU, RegionOfInterest))) + return true; + + if (ObjCMethodDecl *MD = prevDecl->getSetterMethodDecl()) + if (MD->isSynthesized() && MD->getLexicalDeclContext() == CDecl) + if (Visit(MakeCXCursor(MD, TU, RegionOfInterest))) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { + if (!D->isThisDeclarationADefinition()) { + // Forward declaration is treated like a reference. + return Visit(MakeCursorObjCClassRef(D, D->getLocation(), TU)); + } + + // Issue callbacks for super class. + if (D->getSuperClass() && + Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(), + D->getSuperClassLoc(), + TU))) + return true; + + ObjCInterfaceDecl::protocol_loc_iterator PL = D->protocol_loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator I = D->protocol_begin(), + E = D->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(D); +} + +bool CursorVisitor::VisitObjCImplDecl(ObjCImplDecl *D) { + return VisitObjCContainerDecl(D); +} + +bool CursorVisitor::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { + // 'ID' could be null when dealing with invalid code. + if (ObjCInterfaceDecl *ID = D->getClassInterface()) + if (Visit(MakeCursorObjCClassRef(ID, D->getLocation(), TU))) + return true; + + return VisitObjCImplDecl(D); +} + +bool CursorVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { +#if 0 + // Issue callbacks for super class. + // FIXME: No source location information! + if (D->getSuperClass() && + Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(), + D->getSuperClassLoc(), + TU))) + return true; +#endif + + return VisitObjCImplDecl(D); +} + +bool CursorVisitor::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *PD) { + if (ObjCIvarDecl *Ivar = PD->getPropertyIvarDecl()) + return Visit(MakeCursorMemberRef(Ivar, PD->getPropertyIvarDeclLoc(), TU)); + + return false; +} + +bool CursorVisitor::VisitNamespaceDecl(NamespaceDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return Visit(MakeCursorNamespaceRef(D->getAliasedNamespace(), + D->getTargetNameLoc(), TU)); +} + +bool CursorVisitor::VisitUsingDecl(UsingDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) { + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + } + + if (Visit(MakeCursorOverloadedDeclRef(D, D->getLocation(), TU))) + return true; + + return VisitDeclarationNameInfo(D->getNameInfo()); +} + +bool CursorVisitor::VisitUsingDirectiveDecl(UsingDirectiveDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return Visit(MakeCursorNamespaceRef(D->getNominatedNamespaceAsWritten(), + D->getIdentLocation(), TU)); +} + +bool CursorVisitor::VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) { + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + } + + return VisitDeclarationNameInfo(D->getNameInfo()); +} + +bool CursorVisitor::VisitUnresolvedUsingTypenameDecl( + UnresolvedUsingTypenameDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return false; +} + +bool CursorVisitor::VisitDeclarationNameInfo(DeclarationNameInfo Name) { + switch (Name.getName().getNameKind()) { + case clang::DeclarationName::Identifier: + case clang::DeclarationName::CXXLiteralOperatorName: + case clang::DeclarationName::CXXOperatorName: + case clang::DeclarationName::CXXUsingDirective: + return false; + + case clang::DeclarationName::CXXConstructorName: + case clang::DeclarationName::CXXDestructorName: + case clang::DeclarationName::CXXConversionFunctionName: + if (TypeSourceInfo *TSInfo = Name.getNamedTypeInfo()) + return Visit(TSInfo->getTypeLoc()); + return false; + + case clang::DeclarationName::ObjCZeroArgSelector: + case clang::DeclarationName::ObjCOneArgSelector: + case clang::DeclarationName::ObjCMultiArgSelector: + // FIXME: Per-identifier location info? + return false; + } + + llvm_unreachable("Invalid DeclarationName::Kind!"); +} + +bool CursorVisitor::VisitNestedNameSpecifier(NestedNameSpecifier *NNS, + SourceRange Range) { + // FIXME: This whole routine is a hack to work around the lack of proper + // source information in nested-name-specifiers (PR5791). Since we do have + // a beginning source location, we can visit the first component of the + // nested-name-specifier, if it's a single-token component. + if (!NNS) + return false; + + // Get the first component in the nested-name-specifier. + while (NestedNameSpecifier *Prefix = NNS->getPrefix()) + NNS = Prefix; + + switch (NNS->getKind()) { + case NestedNameSpecifier::Namespace: + return Visit(MakeCursorNamespaceRef(NNS->getAsNamespace(), Range.getBegin(), + TU)); + + case NestedNameSpecifier::NamespaceAlias: + return Visit(MakeCursorNamespaceRef(NNS->getAsNamespaceAlias(), + Range.getBegin(), TU)); + + case NestedNameSpecifier::TypeSpec: { + // If the type has a form where we know that the beginning of the source + // range matches up with a reference cursor. Visit the appropriate reference + // cursor. + const Type *T = NNS->getAsType(); + if (const TypedefType *Typedef = dyn_cast<TypedefType>(T)) + return Visit(MakeCursorTypeRef(Typedef->getDecl(), Range.getBegin(), TU)); + if (const TagType *Tag = dyn_cast<TagType>(T)) + return Visit(MakeCursorTypeRef(Tag->getDecl(), Range.getBegin(), TU)); + if (const TemplateSpecializationType *TST + = dyn_cast<TemplateSpecializationType>(T)) + return VisitTemplateName(TST->getTemplateName(), Range.getBegin()); + break; + } + + case NestedNameSpecifier::TypeSpecWithTemplate: + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Identifier: + break; + } + + return false; +} + +bool +CursorVisitor::VisitNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier) { + SmallVector<NestedNameSpecifierLoc, 4> Qualifiers; + for (; Qualifier; Qualifier = Qualifier.getPrefix()) + Qualifiers.push_back(Qualifier); + + while (!Qualifiers.empty()) { + NestedNameSpecifierLoc Q = Qualifiers.pop_back_val(); + NestedNameSpecifier *NNS = Q.getNestedNameSpecifier(); + switch (NNS->getKind()) { + case NestedNameSpecifier::Namespace: + if (Visit(MakeCursorNamespaceRef(NNS->getAsNamespace(), + Q.getLocalBeginLoc(), + TU))) + return true; + + break; + + case NestedNameSpecifier::NamespaceAlias: + if (Visit(MakeCursorNamespaceRef(NNS->getAsNamespaceAlias(), + Q.getLocalBeginLoc(), + TU))) + return true; + + break; + + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + if (Visit(Q.getTypeLoc())) + return true; + + break; + + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Identifier: + break; + } + } + + return false; +} + +bool CursorVisitor::VisitTemplateParameters( + const TemplateParameterList *Params) { + if (!Params) + return false; + + for (TemplateParameterList::const_iterator P = Params->begin(), + PEnd = Params->end(); + P != PEnd; ++P) { + if (Visit(MakeCXCursor(*P, TU, RegionOfInterest))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) { + switch (Name.getKind()) { + case TemplateName::Template: + return Visit(MakeCursorTemplateRef(Name.getAsTemplateDecl(), Loc, TU)); + + case TemplateName::OverloadedTemplate: + // Visit the overloaded template set. + if (Visit(MakeCursorOverloadedDeclRef(Name, Loc, TU))) + return true; + + return false; + + case TemplateName::DependentTemplate: + // FIXME: Visit nested-name-specifier. + return false; + + case TemplateName::QualifiedTemplate: + // FIXME: Visit nested-name-specifier. + return Visit(MakeCursorTemplateRef( + Name.getAsQualifiedTemplateName()->getDecl(), + Loc, TU)); + + case TemplateName::SubstTemplateTemplateParm: + return Visit(MakeCursorTemplateRef( + Name.getAsSubstTemplateTemplateParm()->getParameter(), + Loc, TU)); + + case TemplateName::SubstTemplateTemplateParmPack: + return Visit(MakeCursorTemplateRef( + Name.getAsSubstTemplateTemplateParmPack()->getParameterPack(), + Loc, TU)); + } + + llvm_unreachable("Invalid TemplateName::Kind!"); +} + +bool CursorVisitor::VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL) { + switch (TAL.getArgument().getKind()) { + case TemplateArgument::Null: + case TemplateArgument::Integral: + case TemplateArgument::Pack: + return false; + + case TemplateArgument::Type: + if (TypeSourceInfo *TSInfo = TAL.getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + return false; + + case TemplateArgument::Declaration: + if (Expr *E = TAL.getSourceDeclExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + + case TemplateArgument::Expression: + if (Expr *E = TAL.getSourceExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + if (VisitNestedNameSpecifierLoc(TAL.getTemplateQualifierLoc())) + return true; + + return VisitTemplateName(TAL.getArgument().getAsTemplateOrTemplatePattern(), + TAL.getTemplateNameLoc()); + } + + llvm_unreachable("Invalid TemplateArgument::Kind!"); +} + +bool CursorVisitor::VisitLinkageSpecDecl(LinkageSpecDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitQualifiedTypeLoc(QualifiedTypeLoc TL) { + return Visit(TL.getUnqualifiedLoc()); +} + +bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) { + ASTContext &Context = AU->getASTContext(); + + // Some builtin types (such as Objective-C's "id", "sel", and + // "Class") have associated declarations. Create cursors for those. + QualType VisitType; + switch (TL.getTypePtr()->getKind()) { + + case BuiltinType::Void: + case BuiltinType::NullPtr: + case BuiltinType::Dependent: +#define BUILTIN_TYPE(Id, SingletonId) +#define SIGNED_TYPE(Id, SingletonId) case BuiltinType::Id: +#define UNSIGNED_TYPE(Id, SingletonId) case BuiltinType::Id: +#define FLOATING_TYPE(Id, SingletonId) case BuiltinType::Id: +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#include "clang/AST/BuiltinTypes.def" + break; + + case BuiltinType::ObjCId: + VisitType = Context.getObjCIdType(); + break; + + case BuiltinType::ObjCClass: + VisitType = Context.getObjCClassType(); + break; + + case BuiltinType::ObjCSel: + VisitType = Context.getObjCSelType(); + break; + } + + if (!VisitType.isNull()) { + if (const TypedefType *Typedef = VisitType->getAs<TypedefType>()) + return Visit(MakeCursorTypeRef(Typedef->getDecl(), TL.getBuiltinLoc(), + TU)); + } + + return false; +} + +bool CursorVisitor::VisitTypedefTypeLoc(TypedefTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getTypedefNameDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitUnresolvedUsingTypeLoc(UnresolvedUsingTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitTagTypeLoc(TagTypeLoc TL) { + if (TL.isDefinition()) + return Visit(MakeCXCursor(TL.getDecl(), TU, RegionOfInterest)); + + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { + if (Visit(MakeCursorObjCClassRef(TL.getIFaceDecl(), TL.getNameLoc(), TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { + if (TL.hasBaseTypeAsWritten() && Visit(TL.getBaseLoc())) + return true; + + for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) { + if (Visit(MakeCursorObjCProtocolRef(TL.getProtocol(I), TL.getProtocolLoc(I), + TU))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitObjCObjectPointerTypeLoc(ObjCObjectPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitParenTypeLoc(ParenTypeLoc TL) { + return Visit(TL.getInnerLoc()); +} + +bool CursorVisitor::VisitPointerTypeLoc(PointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitLValueReferenceTypeLoc(LValueReferenceTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitRValueReferenceTypeLoc(RValueReferenceTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitAttributedTypeLoc(AttributedTypeLoc TL) { + return Visit(TL.getModifiedLoc()); +} + +bool CursorVisitor::VisitFunctionTypeLoc(FunctionTypeLoc TL, + bool SkipResultType) { + if (!SkipResultType && Visit(TL.getResultLoc())) + return true; + + for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) + if (Decl *D = TL.getArg(I)) + if (Visit(MakeCXCursor(D, TU, RegionOfInterest))) + return true; + + return false; +} + +bool CursorVisitor::VisitArrayTypeLoc(ArrayTypeLoc TL) { + if (Visit(TL.getElementLoc())) + return true; + + if (Expr *Size = TL.getSizeExpr()) + return Visit(MakeCXCursor(Size, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitTemplateSpecializationTypeLoc( + TemplateSpecializationTypeLoc TL) { + // Visit the template name. + if (VisitTemplateName(TL.getTypePtr()->getTemplateName(), + TL.getTemplateNameLoc())) + return true; + + // Visit the template arguments. + for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) + if (VisitTemplateArgumentLoc(TL.getArgLoc(I))) + return true; + + return false; +} + +bool CursorVisitor::VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) { + return Visit(MakeCXCursor(TL.getUnderlyingExpr(), StmtParent, TU)); +} + +bool CursorVisitor::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) { + if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { + if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { + if (VisitNestedNameSpecifierLoc(TL.getQualifierLoc())) + return true; + + return false; +} + +bool CursorVisitor::VisitDependentTemplateSpecializationTypeLoc( + DependentTemplateSpecializationTypeLoc TL) { + // Visit the nested-name-specifier, if there is one. + if (TL.getQualifierLoc() && + VisitNestedNameSpecifierLoc(TL.getQualifierLoc())) + return true; + + // Visit the template arguments. + for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) + if (VisitTemplateArgumentLoc(TL.getArgLoc(I))) + return true; + + return false; +} + +bool CursorVisitor::VisitElaboratedTypeLoc(ElaboratedTypeLoc TL) { + if (VisitNestedNameSpecifierLoc(TL.getQualifierLoc())) + return true; + + return Visit(TL.getNamedTypeLoc()); +} + +bool CursorVisitor::VisitPackExpansionTypeLoc(PackExpansionTypeLoc TL) { + return Visit(TL.getPatternLoc()); +} + +bool CursorVisitor::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { + if (Expr *E = TL.getUnderlyingExpr()) + return Visit(MakeCXCursor(E, StmtParent, TU)); + + return false; +} + +bool CursorVisitor::VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitAtomicTypeLoc(AtomicTypeLoc TL) { + return Visit(TL.getValueLoc()); +} + +#define DEFAULT_TYPELOC_IMPL(CLASS, PARENT) \ +bool CursorVisitor::Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { \ + return Visit##PARENT##Loc(TL); \ +} + +DEFAULT_TYPELOC_IMPL(Complex, Type) +DEFAULT_TYPELOC_IMPL(ConstantArray, ArrayType) +DEFAULT_TYPELOC_IMPL(IncompleteArray, ArrayType) +DEFAULT_TYPELOC_IMPL(VariableArray, ArrayType) +DEFAULT_TYPELOC_IMPL(DependentSizedArray, ArrayType) +DEFAULT_TYPELOC_IMPL(DependentSizedExtVector, Type) +DEFAULT_TYPELOC_IMPL(Vector, Type) +DEFAULT_TYPELOC_IMPL(ExtVector, VectorType) +DEFAULT_TYPELOC_IMPL(FunctionProto, FunctionType) +DEFAULT_TYPELOC_IMPL(FunctionNoProto, FunctionType) +DEFAULT_TYPELOC_IMPL(Record, TagType) +DEFAULT_TYPELOC_IMPL(Enum, TagType) +DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParm, Type) +DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParmPack, Type) +DEFAULT_TYPELOC_IMPL(Auto, Type) + +bool CursorVisitor::VisitCXXRecordDecl(CXXRecordDecl *D) { + // Visit the nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + if (D->isCompleteDefinition()) { + for (CXXRecordDecl::base_class_iterator I = D->bases_begin(), + E = D->bases_end(); I != E; ++I) { + if (Visit(cxcursor::MakeCursorCXXBaseSpecifier(I, TU))) + return true; + } + } + + return VisitTagDecl(D); +} + +bool CursorVisitor::VisitAttributes(Decl *D) { + for (AttrVec::const_iterator i = D->attr_begin(), e = D->attr_end(); + i != e; ++i) + if (Visit(MakeCXCursor(*i, D, TU))) + return true; + + return false; +} + +//===----------------------------------------------------------------------===// +// Data-recursive visitor methods. +//===----------------------------------------------------------------------===// + +namespace { +#define DEF_JOB(NAME, DATA, KIND)\ +class NAME : public VisitorJob {\ +public:\ + NAME(DATA *d, CXCursor parent) : VisitorJob(parent, VisitorJob::KIND, d) {} \ + static bool classof(const VisitorJob *VJ) { return VJ->getKind() == KIND; }\ + DATA *get() const { return static_cast<DATA*>(data[0]); }\ +}; + +DEF_JOB(StmtVisit, Stmt, StmtVisitKind) +DEF_JOB(MemberExprParts, MemberExpr, MemberExprPartsKind) +DEF_JOB(DeclRefExprParts, DeclRefExpr, DeclRefExprPartsKind) +DEF_JOB(OverloadExprParts, OverloadExpr, OverloadExprPartsKind) +DEF_JOB(ExplicitTemplateArgsVisit, ASTTemplateArgumentListInfo, + ExplicitTemplateArgsVisitKind) +DEF_JOB(SizeOfPackExprParts, SizeOfPackExpr, SizeOfPackExprPartsKind) +DEF_JOB(LambdaExprParts, LambdaExpr, LambdaExprPartsKind) +#undef DEF_JOB + +class DeclVisit : public VisitorJob { +public: + DeclVisit(Decl *d, CXCursor parent, bool isFirst) : + VisitorJob(parent, VisitorJob::DeclVisitKind, + d, isFirst ? (void*) 1 : (void*) 0) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == DeclVisitKind; + } + Decl *get() const { return static_cast<Decl*>(data[0]); } + bool isFirst() const { return data[1] ? true : false; } +}; +class TypeLocVisit : public VisitorJob { +public: + TypeLocVisit(TypeLoc tl, CXCursor parent) : + VisitorJob(parent, VisitorJob::TypeLocVisitKind, + tl.getType().getAsOpaquePtr(), tl.getOpaqueData()) {} + + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == TypeLocVisitKind; + } + + TypeLoc get() const { + QualType T = QualType::getFromOpaquePtr(data[0]); + return TypeLoc(T, data[1]); + } +}; + +class LabelRefVisit : public VisitorJob { +public: + LabelRefVisit(LabelDecl *LD, SourceLocation labelLoc, CXCursor parent) + : VisitorJob(parent, VisitorJob::LabelRefVisitKind, LD, + labelLoc.getPtrEncoding()) {} + + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::LabelRefVisitKind; + } + LabelDecl *get() const { return static_cast<LabelDecl*>(data[0]); } + SourceLocation getLoc() const { + return SourceLocation::getFromPtrEncoding(data[1]); } +}; + +class NestedNameSpecifierLocVisit : public VisitorJob { +public: + NestedNameSpecifierLocVisit(NestedNameSpecifierLoc Qualifier, CXCursor parent) + : VisitorJob(parent, VisitorJob::NestedNameSpecifierLocVisitKind, + Qualifier.getNestedNameSpecifier(), + Qualifier.getOpaqueData()) { } + + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::NestedNameSpecifierLocVisitKind; + } + + NestedNameSpecifierLoc get() const { + return NestedNameSpecifierLoc(static_cast<NestedNameSpecifier*>(data[0]), + data[1]); + } +}; + +class DeclarationNameInfoVisit : public VisitorJob { +public: + DeclarationNameInfoVisit(Stmt *S, CXCursor parent) + : VisitorJob(parent, VisitorJob::DeclarationNameInfoVisitKind, S) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::DeclarationNameInfoVisitKind; + } + DeclarationNameInfo get() const { + Stmt *S = static_cast<Stmt*>(data[0]); + switch (S->getStmtClass()) { + default: + llvm_unreachable("Unhandled Stmt"); + case clang::Stmt::MSDependentExistsStmtClass: + return cast<MSDependentExistsStmt>(S)->getNameInfo(); + case Stmt::CXXDependentScopeMemberExprClass: + return cast<CXXDependentScopeMemberExpr>(S)->getMemberNameInfo(); + case Stmt::DependentScopeDeclRefExprClass: + return cast<DependentScopeDeclRefExpr>(S)->getNameInfo(); + } + } +}; +class MemberRefVisit : public VisitorJob { +public: + MemberRefVisit(FieldDecl *D, SourceLocation L, CXCursor parent) + : VisitorJob(parent, VisitorJob::MemberRefVisitKind, D, + L.getPtrEncoding()) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::MemberRefVisitKind; + } + FieldDecl *get() const { + return static_cast<FieldDecl*>(data[0]); + } + SourceLocation getLoc() const { + return SourceLocation::getFromRawEncoding((unsigned)(uintptr_t) data[1]); + } +}; +class EnqueueVisitor : public StmtVisitor<EnqueueVisitor, void> { + VisitorWorkList &WL; + CXCursor Parent; +public: + EnqueueVisitor(VisitorWorkList &wl, CXCursor parent) + : WL(wl), Parent(parent) {} + + void VisitAddrLabelExpr(AddrLabelExpr *E); + void VisitBlockExpr(BlockExpr *B); + void VisitCompoundLiteralExpr(CompoundLiteralExpr *E); + void VisitCompoundStmt(CompoundStmt *S); + void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { /* Do nothing. */ } + void VisitMSDependentExistsStmt(MSDependentExistsStmt *S); + void VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E); + void VisitCXXNewExpr(CXXNewExpr *E); + void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E); + void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); + void VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E); + void VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E); + void VisitCXXTypeidExpr(CXXTypeidExpr *E); + void VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E); + void VisitCXXUuidofExpr(CXXUuidofExpr *E); + void VisitCXXCatchStmt(CXXCatchStmt *S); + void VisitDeclRefExpr(DeclRefExpr *D); + void VisitDeclStmt(DeclStmt *S); + void VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E); + void VisitDesignatedInitExpr(DesignatedInitExpr *E); + void VisitExplicitCastExpr(ExplicitCastExpr *E); + void VisitForStmt(ForStmt *FS); + void VisitGotoStmt(GotoStmt *GS); + void VisitIfStmt(IfStmt *If); + void VisitInitListExpr(InitListExpr *IE); + void VisitMemberExpr(MemberExpr *M); + void VisitOffsetOfExpr(OffsetOfExpr *E); + void VisitObjCEncodeExpr(ObjCEncodeExpr *E); + void VisitObjCMessageExpr(ObjCMessageExpr *M); + void VisitOverloadExpr(OverloadExpr *E); + void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); + void VisitStmt(Stmt *S); + void VisitSwitchStmt(SwitchStmt *S); + void VisitWhileStmt(WhileStmt *W); + void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E); + void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E); + void VisitTypeTraitExpr(TypeTraitExpr *E); + void VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *E); + void VisitExpressionTraitExpr(ExpressionTraitExpr *E); + void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U); + void VisitVAArgExpr(VAArgExpr *E); + void VisitSizeOfPackExpr(SizeOfPackExpr *E); + void VisitPseudoObjectExpr(PseudoObjectExpr *E); + void VisitOpaqueValueExpr(OpaqueValueExpr *E); + void VisitLambdaExpr(LambdaExpr *E); + +private: + void AddDeclarationNameInfo(Stmt *S); + void AddNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier); + void AddExplicitTemplateArgs(const ASTTemplateArgumentListInfo *A); + void AddMemberRef(FieldDecl *D, SourceLocation L); + void AddStmt(Stmt *S); + void AddDecl(Decl *D, bool isFirst = true); + void AddTypeLoc(TypeSourceInfo *TI); + void EnqueueChildren(Stmt *S); +}; +} // end anonyous namespace + +void EnqueueVisitor::AddDeclarationNameInfo(Stmt *S) { + // 'S' should always be non-null, since it comes from the + // statement we are visiting. + WL.push_back(DeclarationNameInfoVisit(S, Parent)); +} + +void +EnqueueVisitor::AddNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier) { + if (Qualifier) + WL.push_back(NestedNameSpecifierLocVisit(Qualifier, Parent)); +} + +void EnqueueVisitor::AddStmt(Stmt *S) { + if (S) + WL.push_back(StmtVisit(S, Parent)); +} +void EnqueueVisitor::AddDecl(Decl *D, bool isFirst) { + if (D) + WL.push_back(DeclVisit(D, Parent, isFirst)); +} +void EnqueueVisitor:: + AddExplicitTemplateArgs(const ASTTemplateArgumentListInfo *A) { + if (A) + WL.push_back(ExplicitTemplateArgsVisit( + const_cast<ASTTemplateArgumentListInfo*>(A), Parent)); +} +void EnqueueVisitor::AddMemberRef(FieldDecl *D, SourceLocation L) { + if (D) + WL.push_back(MemberRefVisit(D, L, Parent)); +} +void EnqueueVisitor::AddTypeLoc(TypeSourceInfo *TI) { + if (TI) + WL.push_back(TypeLocVisit(TI->getTypeLoc(), Parent)); + } +void EnqueueVisitor::EnqueueChildren(Stmt *S) { + unsigned size = WL.size(); + for (Stmt::child_range Child = S->children(); Child; ++Child) { + AddStmt(*Child); + } + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} +void EnqueueVisitor::VisitAddrLabelExpr(AddrLabelExpr *E) { + WL.push_back(LabelRefVisit(E->getLabel(), E->getLabelLoc(), Parent)); +} +void EnqueueVisitor::VisitBlockExpr(BlockExpr *B) { + AddDecl(B->getBlockDecl()); +} +void EnqueueVisitor::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCompoundStmt(CompoundStmt *S) { + for (CompoundStmt::reverse_body_iterator I = S->body_rbegin(), + E = S->body_rend(); I != E; ++I) { + AddStmt(*I); + } +} +void EnqueueVisitor:: +VisitMSDependentExistsStmt(MSDependentExistsStmt *S) { + AddStmt(S->getSubStmt()); + AddDeclarationNameInfo(S); + if (NestedNameSpecifierLoc QualifierLoc = S->getQualifierLoc()) + AddNestedNameSpecifierLoc(QualifierLoc); +} + +void EnqueueVisitor:: +VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { + AddExplicitTemplateArgs(E->getOptionalExplicitTemplateArgs()); + AddDeclarationNameInfo(E); + if (NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc()) + AddNestedNameSpecifierLoc(QualifierLoc); + if (!E->isImplicitAccess()) + AddStmt(E->getBase()); +} +void EnqueueVisitor::VisitCXXNewExpr(CXXNewExpr *E) { + // Enqueue the initializer , if any. + AddStmt(E->getInitializer()); + // Enqueue the array size, if any. + AddStmt(E->getArraySize()); + // Enqueue the allocated type. + AddTypeLoc(E->getAllocatedTypeSourceInfo()); + // Enqueue the placement arguments. + for (unsigned I = E->getNumPlacementArgs(); I > 0; --I) + AddStmt(E->getPlacementArg(I-1)); +} +void EnqueueVisitor::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *CE) { + for (unsigned I = CE->getNumArgs(); I > 1 /* Yes, this is 1 */; --I) + AddStmt(CE->getArg(I-1)); + AddStmt(CE->getCallee()); + AddStmt(CE->getArg(0)); +} +void EnqueueVisitor::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E) { + // Visit the name of the type being destroyed. + AddTypeLoc(E->getDestroyedTypeInfo()); + // Visit the scope type that looks disturbingly like the nested-name-specifier + // but isn't. + AddTypeLoc(E->getScopeTypeInfo()); + // Visit the nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc()) + AddNestedNameSpecifierLoc(QualifierLoc); + // Visit base expression. + AddStmt(E->getBase()); +} +void EnqueueVisitor::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) { + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCXXTypeidExpr(CXXTypeidExpr *E) { + EnqueueChildren(E); + if (E->isTypeOperand()) + AddTypeLoc(E->getTypeOperandSourceInfo()); +} + +void EnqueueVisitor::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr + *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCXXUuidofExpr(CXXUuidofExpr *E) { + EnqueueChildren(E); + if (E->isTypeOperand()) + AddTypeLoc(E->getTypeOperandSourceInfo()); +} + +void EnqueueVisitor::VisitCXXCatchStmt(CXXCatchStmt *S) { + EnqueueChildren(S); + AddDecl(S->getExceptionDecl()); +} + +void EnqueueVisitor::VisitDeclRefExpr(DeclRefExpr *DR) { + if (DR->hasExplicitTemplateArgs()) { + AddExplicitTemplateArgs(&DR->getExplicitTemplateArgs()); + } + WL.push_back(DeclRefExprParts(DR, Parent)); +} +void EnqueueVisitor::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { + AddExplicitTemplateArgs(E->getOptionalExplicitTemplateArgs()); + AddDeclarationNameInfo(E); + AddNestedNameSpecifierLoc(E->getQualifierLoc()); +} +void EnqueueVisitor::VisitDeclStmt(DeclStmt *S) { + unsigned size = WL.size(); + bool isFirst = true; + for (DeclStmt::decl_iterator D = S->decl_begin(), DEnd = S->decl_end(); + D != DEnd; ++D) { + AddDecl(*D, isFirst); + isFirst = false; + } + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} +void EnqueueVisitor::VisitDesignatedInitExpr(DesignatedInitExpr *E) { + AddStmt(E->getInit()); + typedef DesignatedInitExpr::Designator Designator; + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) { + if (FieldDecl *Field = D->getField()) + AddMemberRef(Field, D->getFieldLoc()); + continue; + } + if (D->isArrayDesignator()) { + AddStmt(E->getArrayIndex(*D)); + continue; + } + assert(D->isArrayRangeDesignator() && "Unknown designator kind"); + AddStmt(E->getArrayRangeEnd(*D)); + AddStmt(E->getArrayRangeStart(*D)); + } +} +void EnqueueVisitor::VisitExplicitCastExpr(ExplicitCastExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeInfoAsWritten()); +} +void EnqueueVisitor::VisitForStmt(ForStmt *FS) { + AddStmt(FS->getBody()); + AddStmt(FS->getInc()); + AddStmt(FS->getCond()); + AddDecl(FS->getConditionVariable()); + AddStmt(FS->getInit()); +} +void EnqueueVisitor::VisitGotoStmt(GotoStmt *GS) { + WL.push_back(LabelRefVisit(GS->getLabel(), GS->getLabelLoc(), Parent)); +} +void EnqueueVisitor::VisitIfStmt(IfStmt *If) { + AddStmt(If->getElse()); + AddStmt(If->getThen()); + AddStmt(If->getCond()); + AddDecl(If->getConditionVariable()); +} +void EnqueueVisitor::VisitInitListExpr(InitListExpr *IE) { + // We care about the syntactic form of the initializer list, only. + if (InitListExpr *Syntactic = IE->getSyntacticForm()) + IE = Syntactic; + EnqueueChildren(IE); +} +void EnqueueVisitor::VisitMemberExpr(MemberExpr *M) { + WL.push_back(MemberExprParts(M, Parent)); + + // If the base of the member access expression is an implicit 'this', don't + // visit it. + // FIXME: If we ever want to show these implicit accesses, this will be + // unfortunate. However, clang_getCursor() relies on this behavior. + if (!M->isImplicitAccess()) + AddStmt(M->getBase()); +} +void EnqueueVisitor::VisitObjCEncodeExpr(ObjCEncodeExpr *E) { + AddTypeLoc(E->getEncodedTypeSourceInfo()); +} +void EnqueueVisitor::VisitObjCMessageExpr(ObjCMessageExpr *M) { + EnqueueChildren(M); + AddTypeLoc(M->getClassReceiverTypeInfo()); +} +void EnqueueVisitor::VisitOffsetOfExpr(OffsetOfExpr *E) { + // Visit the components of the offsetof expression. + for (unsigned N = E->getNumComponents(), I = N; I > 0; --I) { + typedef OffsetOfExpr::OffsetOfNode OffsetOfNode; + const OffsetOfNode &Node = E->getComponent(I-1); + switch (Node.getKind()) { + case OffsetOfNode::Array: + AddStmt(E->getIndexExpr(Node.getArrayExprIndex())); + break; + case OffsetOfNode::Field: + AddMemberRef(Node.getField(), Node.getSourceRange().getEnd()); + break; + case OffsetOfNode::Identifier: + case OffsetOfNode::Base: + continue; + } + } + // Visit the type into which we're computing the offset. + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitOverloadExpr(OverloadExpr *E) { + AddExplicitTemplateArgs(E->getOptionalExplicitTemplateArgs()); + WL.push_back(OverloadExprParts(E, Parent)); +} +void EnqueueVisitor::VisitUnaryExprOrTypeTraitExpr( + UnaryExprOrTypeTraitExpr *E) { + EnqueueChildren(E); + if (E->isArgumentType()) + AddTypeLoc(E->getArgumentTypeInfo()); +} +void EnqueueVisitor::VisitStmt(Stmt *S) { + EnqueueChildren(S); +} +void EnqueueVisitor::VisitSwitchStmt(SwitchStmt *S) { + AddStmt(S->getBody()); + AddStmt(S->getCond()); + AddDecl(S->getConditionVariable()); +} + +void EnqueueVisitor::VisitWhileStmt(WhileStmt *W) { + AddStmt(W->getBody()); + AddStmt(W->getCond()); + AddDecl(W->getConditionVariable()); +} + +void EnqueueVisitor::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) { + AddTypeLoc(E->getQueriedTypeSourceInfo()); +} + +void EnqueueVisitor::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E) { + AddTypeLoc(E->getRhsTypeSourceInfo()); + AddTypeLoc(E->getLhsTypeSourceInfo()); +} + +void EnqueueVisitor::VisitTypeTraitExpr(TypeTraitExpr *E) { + for (unsigned I = E->getNumArgs(); I > 0; --I) + AddTypeLoc(E->getArg(I-1)); +} + +void EnqueueVisitor::VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *E) { + AddTypeLoc(E->getQueriedTypeSourceInfo()); +} + +void EnqueueVisitor::VisitExpressionTraitExpr(ExpressionTraitExpr *E) { + EnqueueChildren(E); +} + +void EnqueueVisitor::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U) { + VisitOverloadExpr(U); + if (!U->isImplicitAccess()) + AddStmt(U->getBase()); +} +void EnqueueVisitor::VisitVAArgExpr(VAArgExpr *E) { + AddStmt(E->getSubExpr()); + AddTypeLoc(E->getWrittenTypeInfo()); +} +void EnqueueVisitor::VisitSizeOfPackExpr(SizeOfPackExpr *E) { + WL.push_back(SizeOfPackExprParts(E, Parent)); +} +void EnqueueVisitor::VisitOpaqueValueExpr(OpaqueValueExpr *E) { + // If the opaque value has a source expression, just transparently + // visit that. This is useful for (e.g.) pseudo-object expressions. + if (Expr *SourceExpr = E->getSourceExpr()) + return Visit(SourceExpr); +} +void EnqueueVisitor::VisitLambdaExpr(LambdaExpr *E) { + AddStmt(E->getBody()); + WL.push_back(LambdaExprParts(E, Parent)); +} +void EnqueueVisitor::VisitPseudoObjectExpr(PseudoObjectExpr *E) { + // Treat the expression like its syntactic form. + Visit(E->getSyntacticForm()); +} + +void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, Stmt *S) { + EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S); +} + +bool CursorVisitor::IsInRegionOfInterest(CXCursor C) { + if (RegionOfInterest.isValid()) { + SourceRange Range = getRawCursorExtent(C); + if (Range.isInvalid() || CompareRegionOfInterest(Range)) + return false; + } + return true; +} + +bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { + while (!WL.empty()) { + // Dequeue the worklist item. + VisitorJob LI = WL.back(); + WL.pop_back(); + + // Set the Parent field, then back to its old value once we're done. + SetParentRAII SetParent(Parent, StmtParent, LI.getParent()); + + switch (LI.getKind()) { + case VisitorJob::DeclVisitKind: { + Decl *D = cast<DeclVisit>(&LI)->get(); + if (!D) + continue; + + // For now, perform default visitation for Decls. + if (Visit(MakeCXCursor(D, TU, RegionOfInterest, + cast<DeclVisit>(&LI)->isFirst()))) + return true; + + continue; + } + case VisitorJob::ExplicitTemplateArgsVisitKind: { + const ASTTemplateArgumentListInfo *ArgList = + cast<ExplicitTemplateArgsVisit>(&LI)->get(); + for (const TemplateArgumentLoc *Arg = ArgList->getTemplateArgs(), + *ArgEnd = Arg + ArgList->NumTemplateArgs; + Arg != ArgEnd; ++Arg) { + if (VisitTemplateArgumentLoc(*Arg)) + return true; + } + continue; + } + case VisitorJob::TypeLocVisitKind: { + // Perform default visitation for TypeLocs. + if (Visit(cast<TypeLocVisit>(&LI)->get())) + return true; + continue; + } + case VisitorJob::LabelRefVisitKind: { + LabelDecl *LS = cast<LabelRefVisit>(&LI)->get(); + if (LabelStmt *stmt = LS->getStmt()) { + if (Visit(MakeCursorLabelRef(stmt, cast<LabelRefVisit>(&LI)->getLoc(), + TU))) { + return true; + } + } + continue; + } + + case VisitorJob::NestedNameSpecifierLocVisitKind: { + NestedNameSpecifierLocVisit *V = cast<NestedNameSpecifierLocVisit>(&LI); + if (VisitNestedNameSpecifierLoc(V->get())) + return true; + continue; + } + + case VisitorJob::DeclarationNameInfoVisitKind: { + if (VisitDeclarationNameInfo(cast<DeclarationNameInfoVisit>(&LI) + ->get())) + return true; + continue; + } + case VisitorJob::MemberRefVisitKind: { + MemberRefVisit *V = cast<MemberRefVisit>(&LI); + if (Visit(MakeCursorMemberRef(V->get(), V->getLoc(), TU))) + return true; + continue; + } + case VisitorJob::StmtVisitKind: { + Stmt *S = cast<StmtVisit>(&LI)->get(); + if (!S) + continue; + + // Update the current cursor. + CXCursor Cursor = MakeCXCursor(S, StmtParent, TU, RegionOfInterest); + if (!IsInRegionOfInterest(Cursor)) + continue; + switch (Visitor(Cursor, Parent, ClientData)) { + case CXChildVisit_Break: return true; + case CXChildVisit_Continue: break; + case CXChildVisit_Recurse: + EnqueueWorkList(WL, S); + break; + } + continue; + } + case VisitorJob::MemberExprPartsKind: { + // Handle the other pieces in the MemberExpr besides the base. + MemberExpr *M = cast<MemberExprParts>(&LI)->get(); + + // Visit the nested-name-specifier + if (NestedNameSpecifierLoc QualifierLoc = M->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + // Visit the declaration name. + if (VisitDeclarationNameInfo(M->getMemberNameInfo())) + return true; + + // Visit the explicitly-specified template arguments, if any. + if (M->hasExplicitTemplateArgs()) { + for (const TemplateArgumentLoc *Arg = M->getTemplateArgs(), + *ArgEnd = Arg + M->getNumTemplateArgs(); + Arg != ArgEnd; ++Arg) { + if (VisitTemplateArgumentLoc(*Arg)) + return true; + } + } + continue; + } + case VisitorJob::DeclRefExprPartsKind: { + DeclRefExpr *DR = cast<DeclRefExprParts>(&LI)->get(); + // Visit nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = DR->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + // Visit declaration name. + if (VisitDeclarationNameInfo(DR->getNameInfo())) + return true; + continue; + } + case VisitorJob::OverloadExprPartsKind: { + OverloadExpr *O = cast<OverloadExprParts>(&LI)->get(); + // Visit the nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = O->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + // Visit the declaration name. + if (VisitDeclarationNameInfo(O->getNameInfo())) + return true; + // Visit the overloaded declaration reference. + if (Visit(MakeCursorOverloadedDeclRef(O, TU))) + return true; + continue; + } + case VisitorJob::SizeOfPackExprPartsKind: { + SizeOfPackExpr *E = cast<SizeOfPackExprParts>(&LI)->get(); + NamedDecl *Pack = E->getPack(); + if (isa<TemplateTypeParmDecl>(Pack)) { + if (Visit(MakeCursorTypeRef(cast<TemplateTypeParmDecl>(Pack), + E->getPackLoc(), TU))) + return true; + + continue; + } + + if (isa<TemplateTemplateParmDecl>(Pack)) { + if (Visit(MakeCursorTemplateRef(cast<TemplateTemplateParmDecl>(Pack), + E->getPackLoc(), TU))) + return true; + + continue; + } + + // Non-type template parameter packs and function parameter packs are + // treated like DeclRefExpr cursors. + continue; + } + + case VisitorJob::LambdaExprPartsKind: { + // Visit captures. + LambdaExpr *E = cast<LambdaExprParts>(&LI)->get(); + for (LambdaExpr::capture_iterator C = E->explicit_capture_begin(), + CEnd = E->explicit_capture_end(); + C != CEnd; ++C) { + if (C->capturesThis()) + continue; + + if (Visit(MakeCursorVariableRef(C->getCapturedVar(), + C->getLocation(), + TU))) + return true; + } + + // Visit parameters and return type, if present. + if (E->hasExplicitParameters() || E->hasExplicitResultType()) { + TypeLoc TL = E->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + if (E->hasExplicitParameters() && E->hasExplicitResultType()) { + // Visit the whole type. + if (Visit(TL)) + return true; + } else if (isa<FunctionProtoTypeLoc>(TL)) { + FunctionProtoTypeLoc Proto = cast<FunctionProtoTypeLoc>(TL); + if (E->hasExplicitParameters()) { + // Visit parameters. + for (unsigned I = 0, N = Proto.getNumArgs(); I != N; ++I) + if (Visit(MakeCXCursor(Proto.getArg(I), TU))) + return true; + } else { + // Visit result type. + if (Visit(Proto.getResultLoc())) + return true; + } + } + } + break; + } + } + } + return false; +} + +bool CursorVisitor::Visit(Stmt *S) { + VisitorWorkList *WL = 0; + if (!WorkListFreeList.empty()) { + WL = WorkListFreeList.back(); + WL->clear(); + WorkListFreeList.pop_back(); + } + else { + WL = new VisitorWorkList(); + WorkListCache.push_back(WL); + } + EnqueueWorkList(*WL, S); + bool result = RunVisitorWorkList(*WL); + WorkListFreeList.push_back(WL); + return result; +} + +namespace { +typedef llvm::SmallVector<SourceRange, 4> RefNamePieces; +RefNamePieces buildPieces(unsigned NameFlags, bool IsMemberRefExpr, + const DeclarationNameInfo &NI, + const SourceRange &QLoc, + const ASTTemplateArgumentListInfo *TemplateArgs = 0){ + const bool WantQualifier = NameFlags & CXNameRange_WantQualifier; + const bool WantTemplateArgs = NameFlags & CXNameRange_WantTemplateArgs; + const bool WantSinglePiece = NameFlags & CXNameRange_WantSinglePiece; + + const DeclarationName::NameKind Kind = NI.getName().getNameKind(); + + RefNamePieces Pieces; + + if (WantQualifier && QLoc.isValid()) + Pieces.push_back(QLoc); + + if (Kind != DeclarationName::CXXOperatorName || IsMemberRefExpr) + Pieces.push_back(NI.getLoc()); + + if (WantTemplateArgs && TemplateArgs) + Pieces.push_back(SourceRange(TemplateArgs->LAngleLoc, + TemplateArgs->RAngleLoc)); + + if (Kind == DeclarationName::CXXOperatorName) { + Pieces.push_back(SourceLocation::getFromRawEncoding( + NI.getInfo().CXXOperatorName.BeginOpNameLoc)); + Pieces.push_back(SourceLocation::getFromRawEncoding( + NI.getInfo().CXXOperatorName.EndOpNameLoc)); + } + + if (WantSinglePiece) { + SourceRange R(Pieces.front().getBegin(), Pieces.back().getEnd()); + Pieces.clear(); + Pieces.push_back(R); + } + + return Pieces; +} +} + +//===----------------------------------------------------------------------===// +// Misc. API hooks. +//===----------------------------------------------------------------------===// + +static llvm::sys::Mutex EnableMultithreadingMutex; +static bool EnabledMultithreading; + +static void fatal_error_handler(void *user_data, const std::string& reason) { + // Write the result out to stderr avoiding errs() because raw_ostreams can + // call report_fatal_error. + fprintf(stderr, "LIBCLANG FATAL ERROR: %s\n", reason.c_str()); + ::abort(); +} + +extern "C" { +CXIndex clang_createIndex(int excludeDeclarationsFromPCH, + int displayDiagnostics) { + // Disable pretty stack trace functionality, which will otherwise be a very + // poor citizen of the world and set up all sorts of signal handlers. + llvm::DisablePrettyStackTrace = true; + + // We use crash recovery to make some of our APIs more reliable, implicitly + // enable it. + llvm::CrashRecoveryContext::Enable(); + + // Enable support for multithreading in LLVM. + { + llvm::sys::ScopedLock L(EnableMultithreadingMutex); + if (!EnabledMultithreading) { + llvm::install_fatal_error_handler(fatal_error_handler, 0); + llvm::llvm_start_multithreaded(); + EnabledMultithreading = true; + } + } + + CIndexer *CIdxr = new CIndexer(); + if (excludeDeclarationsFromPCH) + CIdxr->setOnlyLocalDecls(); + if (displayDiagnostics) + CIdxr->setDisplayDiagnostics(); + + if (getenv("LIBCLANG_BGPRIO_INDEX")) + CIdxr->setCXGlobalOptFlags(CIdxr->getCXGlobalOptFlags() | + CXGlobalOpt_ThreadBackgroundPriorityForIndexing); + if (getenv("LIBCLANG_BGPRIO_EDIT")) + CIdxr->setCXGlobalOptFlags(CIdxr->getCXGlobalOptFlags() | + CXGlobalOpt_ThreadBackgroundPriorityForEditing); + + return CIdxr; +} + +void clang_disposeIndex(CXIndex CIdx) { + if (CIdx) + delete static_cast<CIndexer *>(CIdx); +} + +void clang_CXIndex_setGlobalOptions(CXIndex CIdx, unsigned options) { + if (CIdx) + static_cast<CIndexer *>(CIdx)->setCXGlobalOptFlags(options); +} + +unsigned clang_CXIndex_getGlobalOptions(CXIndex CIdx) { + if (CIdx) + return static_cast<CIndexer *>(CIdx)->getCXGlobalOptFlags(); + return 0; +} + +void clang_toggleCrashRecovery(unsigned isEnabled) { + if (isEnabled) + llvm::CrashRecoveryContext::Enable(); + else + llvm::CrashRecoveryContext::Disable(); +} + +CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, + const char *ast_filename) { + if (!CIdx) + return 0; + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + FileSystemOptions FileSystemOpts; + FileSystemOpts.WorkingDir = CXXIdx->getWorkingDirectory(); + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags; + ASTUnit *TU = ASTUnit::LoadFromASTFile(ast_filename, Diags, FileSystemOpts, + CXXIdx->getOnlyLocalDecls(), + 0, 0, + /*CaptureDiagnostics=*/true, + /*AllowPCHWithCompilerErrors=*/true); + return MakeCXTranslationUnit(CXXIdx, TU); +} + +unsigned clang_defaultEditingTranslationUnitOptions() { + return CXTranslationUnit_PrecompiledPreamble | + CXTranslationUnit_CacheCompletionResults; +} + +CXTranslationUnit +clang_createTranslationUnitFromSourceFile(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char * const *command_line_args, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files) { + unsigned Options = CXTranslationUnit_DetailedPreprocessingRecord; + return clang_parseTranslationUnit(CIdx, source_filename, + command_line_args, num_command_line_args, + unsaved_files, num_unsaved_files, + Options); +} + +struct ParseTranslationUnitInfo { + CXIndex CIdx; + const char *source_filename; + const char *const *command_line_args; + int num_command_line_args; + struct CXUnsavedFile *unsaved_files; + unsigned num_unsaved_files; + unsigned options; + CXTranslationUnit result; +}; +static void clang_parseTranslationUnit_Impl(void *UserData) { + ParseTranslationUnitInfo *PTUI = + static_cast<ParseTranslationUnitInfo*>(UserData); + CXIndex CIdx = PTUI->CIdx; + const char *source_filename = PTUI->source_filename; + const char * const *command_line_args = PTUI->command_line_args; + int num_command_line_args = PTUI->num_command_line_args; + struct CXUnsavedFile *unsaved_files = PTUI->unsaved_files; + unsigned num_unsaved_files = PTUI->num_unsaved_files; + unsigned options = PTUI->options; + PTUI->result = 0; + + if (!CIdx) + return; + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + bool PrecompilePreamble = options & CXTranslationUnit_PrecompiledPreamble; + // FIXME: Add a flag for modules. + TranslationUnitKind TUKind + = (options & CXTranslationUnit_Incomplete)? TU_Prefix : TU_Complete; + bool CacheCodeCompetionResults + = options & CXTranslationUnit_CacheCompletionResults; + bool SkipFunctionBodies = options & CXTranslationUnit_SkipFunctionBodies; + + // Configure the diagnostics. + DiagnosticOptions DiagOpts; + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(DiagOpts, num_command_line_args, + command_line_args)); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.getPtr()); + + OwningPtr<std::vector<ASTUnit::RemappedFile> > + RemappedFiles(new std::vector<ASTUnit::RemappedFile>()); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar< + std::vector<ASTUnit::RemappedFile> > RemappedCleanup(RemappedFiles.get()); + + for (unsigned I = 0; I != num_unsaved_files; ++I) { + StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); + RemappedFiles->push_back(std::make_pair(unsaved_files[I].Filename, + Buffer)); + } + + OwningPtr<std::vector<const char *> > + Args(new std::vector<const char*>()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<std::vector<const char*> > + ArgsCleanup(Args.get()); + + // Since the Clang C library is primarily used by batch tools dealing with + // (often very broken) source code, where spell-checking can have a + // significant negative impact on performance (particularly when + // precompiled headers are involved), we disable it by default. + // Only do this if we haven't found a spell-checking-related argument. + bool FoundSpellCheckingArgument = false; + for (int I = 0; I != num_command_line_args; ++I) { + if (strcmp(command_line_args[I], "-fno-spell-checking") == 0 || + strcmp(command_line_args[I], "-fspell-checking") == 0) { + FoundSpellCheckingArgument = true; + break; + } + } + if (!FoundSpellCheckingArgument) + Args->push_back("-fno-spell-checking"); + + Args->insert(Args->end(), command_line_args, + command_line_args + num_command_line_args); + + // The 'source_filename' argument is optional. If the caller does not + // specify it then it is assumed that the source file is specified + // in the actual argument list. + // Put the source file after command_line_args otherwise if '-x' flag is + // present it will be unused. + if (source_filename) + Args->push_back(source_filename); + + // Do we need the detailed preprocessing record? + if (options & CXTranslationUnit_DetailedPreprocessingRecord) { + Args->push_back("-Xclang"); + Args->push_back("-detailed-preprocessing-record"); + } + + unsigned NumErrors = Diags->getClient()->getNumErrors(); + OwningPtr<ASTUnit> ErrUnit; + OwningPtr<ASTUnit> Unit( + ASTUnit::LoadFromCommandLine(Args->size() ? &(*Args)[0] : 0 + /* vector::data() not portable */, + Args->size() ? (&(*Args)[0] + Args->size()) :0, + Diags, + CXXIdx->getClangResourcesPath(), + CXXIdx->getOnlyLocalDecls(), + /*CaptureDiagnostics=*/true, + RemappedFiles->size() ? &(*RemappedFiles)[0]:0, + RemappedFiles->size(), + /*RemappedFilesKeepOriginalName=*/true, + PrecompilePreamble, + TUKind, + CacheCodeCompetionResults, + /*AllowPCHWithCompilerErrors=*/true, + SkipFunctionBodies, + &ErrUnit)); + + if (NumErrors != Diags->getClient()->getNumErrors()) { + // Make sure to check that 'Unit' is non-NULL. + if (CXXIdx->getDisplayDiagnostics()) + printDiagsToStderr(Unit ? Unit.get() : ErrUnit.get()); + } + + PTUI->result = MakeCXTranslationUnit(CXXIdx, Unit.take()); +} +CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx, + const char *source_filename, + const char * const *command_line_args, + int num_command_line_args, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + unsigned options) { + ParseTranslationUnitInfo PTUI = { CIdx, source_filename, command_line_args, + num_command_line_args, unsaved_files, + num_unsaved_files, options, 0 }; + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, clang_parseTranslationUnit_Impl, &PTUI)) { + fprintf(stderr, "libclang: crash detected during parsing: {\n"); + fprintf(stderr, " 'source_filename' : '%s'\n", source_filename); + fprintf(stderr, " 'command_line_args' : ["); + for (int i = 0; i != num_command_line_args; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "'%s'", command_line_args[i]); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'unsaved_files' : ["); + for (unsigned i = 0; i != num_unsaved_files; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "('%s', '...', %ld)", unsaved_files[i].Filename, + unsaved_files[i].Length); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'options' : %d,\n", options); + fprintf(stderr, "}\n"); + + return 0; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) { + PrintLibclangResourceUsage(PTUI.result); + } + + return PTUI.result; +} + +unsigned clang_defaultSaveOptions(CXTranslationUnit TU) { + return CXSaveTranslationUnit_None; +} + +namespace { + +struct SaveTranslationUnitInfo { + CXTranslationUnit TU; + const char *FileName; + unsigned options; + CXSaveError result; +}; + +} + +static void clang_saveTranslationUnit_Impl(void *UserData) { + SaveTranslationUnitInfo *STUI = + static_cast<SaveTranslationUnitInfo*>(UserData); + + CIndexer *CXXIdx = (CIndexer*)STUI->TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + STUI->result = static_cast<ASTUnit *>(STUI->TU->TUData)->Save(STUI->FileName); +} + +int clang_saveTranslationUnit(CXTranslationUnit TU, const char *FileName, + unsigned options) { + if (!TU) + return CXSaveError_InvalidTU; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SaveTranslationUnitInfo STUI = { TU, FileName, options, CXSaveError_None }; + + if (!CXXUnit->getDiagnostics().hasUnrecoverableErrorOccurred() || + getenv("LIBCLANG_NOTHREADS")) { + clang_saveTranslationUnit_Impl(&STUI); + + if (getenv("LIBCLANG_RESOURCE_USAGE")) + PrintLibclangResourceUsage(TU); + + return STUI.result; + } + + // We have an AST that has invalid nodes due to compiler errors. + // Use a crash recovery thread for protection. + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, clang_saveTranslationUnit_Impl, &STUI)) { + fprintf(stderr, "libclang: crash detected during AST saving: {\n"); + fprintf(stderr, " 'filename' : '%s'\n", FileName); + fprintf(stderr, " 'options' : %d,\n", options); + fprintf(stderr, "}\n"); + + return CXSaveError_Unknown; + + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) { + PrintLibclangResourceUsage(TU); + } + + return STUI.result; +} + +void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { + if (CTUnit) { + // If the translation unit has been marked as unsafe to free, just discard + // it. + if (static_cast<ASTUnit *>(CTUnit->TUData)->isUnsafeToFree()) + return; + + delete static_cast<ASTUnit *>(CTUnit->TUData); + disposeCXStringPool(CTUnit->StringPool); + delete static_cast<CXDiagnosticSetImpl *>(CTUnit->Diagnostics); + delete CTUnit; + } +} + +unsigned clang_defaultReparseOptions(CXTranslationUnit TU) { + return CXReparse_None; +} + +struct ReparseTranslationUnitInfo { + CXTranslationUnit TU; + unsigned num_unsaved_files; + struct CXUnsavedFile *unsaved_files; + unsigned options; + int result; +}; + +static void clang_reparseTranslationUnit_Impl(void *UserData) { + ReparseTranslationUnitInfo *RTUI = + static_cast<ReparseTranslationUnitInfo*>(UserData); + CXTranslationUnit TU = RTUI->TU; + + // Reset the associated diagnostics. + delete static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); + TU->Diagnostics = 0; + + unsigned num_unsaved_files = RTUI->num_unsaved_files; + struct CXUnsavedFile *unsaved_files = RTUI->unsaved_files; + unsigned options = RTUI->options; + (void) options; + RTUI->result = 1; + + if (!TU) + return; + + CIndexer *CXXIdx = (CIndexer*)TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) + setThreadBackgroundPriority(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + OwningPtr<std::vector<ASTUnit::RemappedFile> > + RemappedFiles(new std::vector<ASTUnit::RemappedFile>()); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar< + std::vector<ASTUnit::RemappedFile> > RemappedCleanup(RemappedFiles.get()); + + for (unsigned I = 0; I != num_unsaved_files; ++I) { + StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); + RemappedFiles->push_back(std::make_pair(unsaved_files[I].Filename, + Buffer)); + } + + if (!CXXUnit->Reparse(RemappedFiles->size() ? &(*RemappedFiles)[0] : 0, + RemappedFiles->size())) + RTUI->result = 0; +} + +int clang_reparseTranslationUnit(CXTranslationUnit TU, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + unsigned options) { + ReparseTranslationUnitInfo RTUI = { TU, num_unsaved_files, unsaved_files, + options, 0 }; + + if (getenv("LIBCLANG_NOTHREADS")) { + clang_reparseTranslationUnit_Impl(&RTUI); + return RTUI.result; + } + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, clang_reparseTranslationUnit_Impl, &RTUI)) { + fprintf(stderr, "libclang: crash detected during reparsing\n"); + static_cast<ASTUnit *>(TU->TUData)->setUnsafeToFree(true); + return 1; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) + PrintLibclangResourceUsage(TU); + + return RTUI.result; +} + + +CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) { + if (!CTUnit) + return createCXString(""); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit->TUData); + return createCXString(CXXUnit->getOriginalSourceFileName(), true); +} + +CXCursor clang_getTranslationUnitCursor(CXTranslationUnit TU) { + CXCursor Result = { CXCursor_TranslationUnit, 0, { 0, 0, TU } }; + return Result; +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// CXFile Operations. +//===----------------------------------------------------------------------===// + +extern "C" { +CXString clang_getFileName(CXFile SFile) { + if (!SFile) + return createCXString((const char*)NULL); + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return createCXString(FEnt->getName()); +} + +time_t clang_getFileTime(CXFile SFile) { + if (!SFile) + return 0; + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return FEnt->getModificationTime(); +} + +CXFile clang_getFile(CXTranslationUnit tu, const char *file_name) { + if (!tu) + return 0; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData); + + FileManager &FMgr = CXXUnit->getFileManager(); + return const_cast<FileEntry *>(FMgr.getFile(file_name)); +} + +unsigned clang_isFileMultipleIncludeGuarded(CXTranslationUnit tu, CXFile file) { + if (!tu || !file) + return 0; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData); + FileEntry *FEnt = static_cast<FileEntry *>(file); + return CXXUnit->getPreprocessor().getHeaderSearchInfo() + .isFileMultipleIncludeGuarded(FEnt); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// CXCursor Operations. +//===----------------------------------------------------------------------===// + +static Decl *getDeclFromExpr(Stmt *E) { + if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) + return getDeclFromExpr(CE->getSubExpr()); + + if (DeclRefExpr *RefExpr = dyn_cast<DeclRefExpr>(E)) + return RefExpr->getDecl(); + if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(E)) + return RE->getDecl(); + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { + if (PRE->isExplicitProperty()) + return PRE->getExplicitProperty(); + // It could be messaging both getter and setter as in: + // ++myobj.myprop; + // in which case prefer to associate the setter since it is less obvious + // from inspecting the source that the setter is going to get called. + if (PRE->isMessagingSetter()) + return PRE->getImplicitPropertySetter(); + return PRE->getImplicitPropertyGetter(); + } + if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) + return getDeclFromExpr(POE->getSyntacticForm()); + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) + if (Expr *Src = OVE->getSourceExpr()) + return getDeclFromExpr(Src); + + if (CallExpr *CE = dyn_cast<CallExpr>(E)) + return getDeclFromExpr(CE->getCallee()); + if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) + if (!CE->isElidable()) + return CE->getConstructor(); + if (ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>(E)) + return OME->getMethodDecl(); + + if (ObjCProtocolExpr *PE = dyn_cast<ObjCProtocolExpr>(E)) + return PE->getProtocol(); + if (SubstNonTypeTemplateParmPackExpr *NTTP + = dyn_cast<SubstNonTypeTemplateParmPackExpr>(E)) + return NTTP->getParameterPack(); + if (SizeOfPackExpr *SizeOfPack = dyn_cast<SizeOfPackExpr>(E)) + if (isa<NonTypeTemplateParmDecl>(SizeOfPack->getPack()) || + isa<ParmVarDecl>(SizeOfPack->getPack())) + return SizeOfPack->getPack(); + + return 0; +} + +static SourceLocation getLocationFromExpr(Expr *E) { + if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) + return getLocationFromExpr(CE->getSubExpr()); + + if (ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) + return /*FIXME:*/Msg->getLeftLoc(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getLocation(); + if (MemberExpr *Member = dyn_cast<MemberExpr>(E)) + return Member->getMemberLoc(); + if (ObjCIvarRefExpr *Ivar = dyn_cast<ObjCIvarRefExpr>(E)) + return Ivar->getLocation(); + if (SizeOfPackExpr *SizeOfPack = dyn_cast<SizeOfPackExpr>(E)) + return SizeOfPack->getPackLoc(); + if (ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) + return PropRef->getLocation(); + + return E->getLocStart(); +} + +extern "C" { + +unsigned clang_visitChildren(CXCursor parent, + CXCursorVisitor visitor, + CXClientData client_data) { + CursorVisitor CursorVis(getCursorTU(parent), visitor, client_data, + /*VisitPreprocessorLast=*/false); + return CursorVis.VisitChildren(parent); +} + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#if __has_feature(blocks) +typedef enum CXChildVisitResult + (^CXCursorVisitorBlock)(CXCursor cursor, CXCursor parent); + +static enum CXChildVisitResult visitWithBlock(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + CXCursorVisitorBlock block = (CXCursorVisitorBlock)client_data; + return block(cursor, parent); +} +#else +// If we are compiled with a compiler that doesn't have native blocks support, +// define and call the block manually, so the +typedef struct _CXChildVisitResult +{ + void *isa; + int flags; + int reserved; + enum CXChildVisitResult(*invoke)(struct _CXChildVisitResult*, CXCursor, + CXCursor); +} *CXCursorVisitorBlock; + +static enum CXChildVisitResult visitWithBlock(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + CXCursorVisitorBlock block = (CXCursorVisitorBlock)client_data; + return block->invoke(block, cursor, parent); +} +#endif + + +unsigned clang_visitChildrenWithBlock(CXCursor parent, + CXCursorVisitorBlock block) { + return clang_visitChildren(parent, visitWithBlock, block); +} + +static CXString getDeclSpelling(Decl *D) { + if (!D) + return createCXString(""); + + NamedDecl *ND = dyn_cast<NamedDecl>(D); + if (!ND) { + if (ObjCPropertyImplDecl *PropImpl =dyn_cast<ObjCPropertyImplDecl>(D)) + if (ObjCPropertyDecl *Property = PropImpl->getPropertyDecl()) + return createCXString(Property->getIdentifier()->getName()); + + return createCXString(""); + } + + if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(ND)) + return createCXString(OMD->getSelector().getAsString()); + + if (ObjCCategoryImplDecl *CIMP = dyn_cast<ObjCCategoryImplDecl>(ND)) + // No, this isn't the same as the code below. getIdentifier() is non-virtual + // and returns different names. NamedDecl returns the class name and + // ObjCCategoryImplDecl returns the category name. + return createCXString(CIMP->getIdentifier()->getNameStart()); + + if (isa<UsingDirectiveDecl>(D)) + return createCXString(""); + + SmallString<1024> S; + llvm::raw_svector_ostream os(S); + ND->printName(os); + + return createCXString(os.str()); +} + +CXString clang_getCursorSpelling(CXCursor C) { + if (clang_isTranslationUnit(C.kind)) + return clang_getTranslationUnitSpelling( + static_cast<CXTranslationUnit>(C.data[2])); + + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + ObjCInterfaceDecl *Super = getCursorObjCSuperClassRef(C).first; + return createCXString(Super->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCClassRef: { + ObjCInterfaceDecl *Class = getCursorObjCClassRef(C).first; + return createCXString(Class->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCProtocolRef: { + ObjCProtocolDecl *OID = getCursorObjCProtocolRef(C).first; + assert(OID && "getCursorSpelling(): Missing protocol decl"); + return createCXString(OID->getIdentifier()->getNameStart()); + } + case CXCursor_CXXBaseSpecifier: { + CXXBaseSpecifier *B = getCursorCXXBaseSpecifier(C); + return createCXString(B->getType().getAsString()); + } + case CXCursor_TypeRef: { + TypeDecl *Type = getCursorTypeRef(C).first; + assert(Type && "Missing type decl"); + + return createCXString(getCursorContext(C).getTypeDeclType(Type). + getAsString()); + } + case CXCursor_TemplateRef: { + TemplateDecl *Template = getCursorTemplateRef(C).first; + assert(Template && "Missing template decl"); + + return createCXString(Template->getNameAsString()); + } + + case CXCursor_NamespaceRef: { + NamedDecl *NS = getCursorNamespaceRef(C).first; + assert(NS && "Missing namespace decl"); + + return createCXString(NS->getNameAsString()); + } + + case CXCursor_MemberRef: { + FieldDecl *Field = getCursorMemberRef(C).first; + assert(Field && "Missing member decl"); + + return createCXString(Field->getNameAsString()); + } + + case CXCursor_LabelRef: { + LabelStmt *Label = getCursorLabelRef(C).first; + assert(Label && "Missing label"); + + return createCXString(Label->getName()); + } + + case CXCursor_OverloadedDeclRef: { + OverloadedDeclRefStorage Storage = getCursorOverloadedDeclRef(C).first; + if (Decl *D = Storage.dyn_cast<Decl *>()) { + if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) + return createCXString(ND->getNameAsString()); + return createCXString(""); + } + if (OverloadExpr *E = Storage.dyn_cast<OverloadExpr *>()) + return createCXString(E->getName().getAsString()); + OverloadedTemplateStorage *Ovl + = Storage.get<OverloadedTemplateStorage*>(); + if (Ovl->size() == 0) + return createCXString(""); + return createCXString((*Ovl->begin())->getNameAsString()); + } + + case CXCursor_VariableRef: { + VarDecl *Var = getCursorVariableRef(C).first; + assert(Var && "Missing variable decl"); + + return createCXString(Var->getNameAsString()); + } + + default: + return createCXString("<not implemented>"); + } + } + + if (clang_isExpression(C.kind)) { + Decl *D = getDeclFromExpr(getCursorExpr(C)); + if (D) + return getDeclSpelling(D); + return createCXString(""); + } + + if (clang_isStatement(C.kind)) { + Stmt *S = getCursorStmt(C); + if (LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S)) + return createCXString(Label->getName()); + + return createCXString(""); + } + + if (C.kind == CXCursor_MacroExpansion) + return createCXString(getCursorMacroExpansion(C)->getName() + ->getNameStart()); + + if (C.kind == CXCursor_MacroDefinition) + return createCXString(getCursorMacroDefinition(C)->getName() + ->getNameStart()); + + if (C.kind == CXCursor_InclusionDirective) + return createCXString(getCursorInclusionDirective(C)->getFileName()); + + if (clang_isDeclaration(C.kind)) + return getDeclSpelling(getCursorDecl(C)); + + if (C.kind == CXCursor_AnnotateAttr) { + AnnotateAttr *AA = cast<AnnotateAttr>(cxcursor::getCursorAttr(C)); + return createCXString(AA->getAnnotation()); + } + + if (C.kind == CXCursor_AsmLabelAttr) { + AsmLabelAttr *AA = cast<AsmLabelAttr>(cxcursor::getCursorAttr(C)); + return createCXString(AA->getLabel()); + } + + return createCXString(""); +} + +CXSourceRange clang_Cursor_getSpellingNameRange(CXCursor C, + unsigned pieceIndex, + unsigned options) { + if (clang_Cursor_isNull(C)) + return clang_getNullRange(); + + ASTContext &Ctx = getCursorContext(C); + + if (clang_isStatement(C.kind)) { + Stmt *S = getCursorStmt(C); + if (LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S)) { + if (pieceIndex > 0) + return clang_getNullRange(); + return cxloc::translateSourceRange(Ctx, Label->getIdentLoc()); + } + + return clang_getNullRange(); + } + + if (C.kind == CXCursor_ObjCMessageExpr) { + if (ObjCMessageExpr * + ME = dyn_cast_or_null<ObjCMessageExpr>(getCursorExpr(C))) { + if (pieceIndex >= ME->getNumSelectorLocs()) + return clang_getNullRange(); + return cxloc::translateSourceRange(Ctx, ME->getSelectorLoc(pieceIndex)); + } + } + + if (C.kind == CXCursor_ObjCInstanceMethodDecl || + C.kind == CXCursor_ObjCClassMethodDecl) { + if (ObjCMethodDecl * + MD = dyn_cast_or_null<ObjCMethodDecl>(getCursorDecl(C))) { + if (pieceIndex >= MD->getNumSelectorLocs()) + return clang_getNullRange(); + return cxloc::translateSourceRange(Ctx, MD->getSelectorLoc(pieceIndex)); + } + } + + if (C.kind == CXCursor_ObjCCategoryDecl || + C.kind == CXCursor_ObjCCategoryImplDecl) { + if (pieceIndex > 0) + return clang_getNullRange(); + if (ObjCCategoryDecl * + CD = dyn_cast_or_null<ObjCCategoryDecl>(getCursorDecl(C))) + return cxloc::translateSourceRange(Ctx, CD->getCategoryNameLoc()); + if (ObjCCategoryImplDecl * + CID = dyn_cast_or_null<ObjCCategoryImplDecl>(getCursorDecl(C))) + return cxloc::translateSourceRange(Ctx, CID->getCategoryNameLoc()); + } + + // FIXME: A CXCursor_InclusionDirective should give the location of the + // filename, but we don't keep track of this. + + // FIXME: A CXCursor_AnnotateAttr should give the location of the annotation + // but we don't keep track of this. + + // FIXME: A CXCursor_AsmLabelAttr should give the location of the label + // but we don't keep track of this. + + // Default handling, give the location of the cursor. + + if (pieceIndex > 0) + return clang_getNullRange(); + + CXSourceLocation CXLoc = clang_getCursorLocation(C); + SourceLocation Loc = cxloc::translateSourceLocation(CXLoc); + return cxloc::translateSourceRange(Ctx, Loc); +} + +CXString clang_getCursorDisplayName(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return clang_getCursorSpelling(C); + + Decl *D = getCursorDecl(C); + if (!D) + return createCXString(""); + + PrintingPolicy Policy = getCursorContext(C).getPrintingPolicy(); + if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D)) + D = FunTmpl->getTemplatedDecl(); + + if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) { + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << *Function; + if (Function->getPrimaryTemplate()) + OS << "<>"; + OS << "("; + for (unsigned I = 0, N = Function->getNumParams(); I != N; ++I) { + if (I) + OS << ", "; + OS << Function->getParamDecl(I)->getType().getAsString(Policy); + } + + if (Function->isVariadic()) { + if (Function->getNumParams()) + OS << ", "; + OS << "..."; + } + OS << ")"; + return createCXString(OS.str()); + } + + if (ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(D)) { + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << *ClassTemplate; + OS << "<"; + TemplateParameterList *Params = ClassTemplate->getTemplateParameters(); + for (unsigned I = 0, N = Params->size(); I != N; ++I) { + if (I) + OS << ", "; + + NamedDecl *Param = Params->getParam(I); + if (Param->getIdentifier()) { + OS << Param->getIdentifier()->getName(); + continue; + } + + // There is no parameter name, which makes this tricky. Try to come up + // with something useful that isn't too long. + if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + OS << (TTP->wasDeclaredWithTypename()? "typename" : "class"); + else if (NonTypeTemplateParmDecl *NTTP + = dyn_cast<NonTypeTemplateParmDecl>(Param)) + OS << NTTP->getType().getAsString(Policy); + else + OS << "template<...> class"; + } + + OS << ">"; + return createCXString(OS.str()); + } + + if (ClassTemplateSpecializationDecl *ClassSpec + = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + // If the type was explicitly written, use that. + if (TypeSourceInfo *TSInfo = ClassSpec->getTypeAsWritten()) + return createCXString(TSInfo->getType().getAsString(Policy)); + + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << *ClassSpec; + OS << TemplateSpecializationType::PrintTemplateArgumentList( + ClassSpec->getTemplateArgs().data(), + ClassSpec->getTemplateArgs().size(), + Policy); + return createCXString(OS.str()); + } + + return clang_getCursorSpelling(C); +} + +CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { + switch (Kind) { + case CXCursor_FunctionDecl: + return createCXString("FunctionDecl"); + case CXCursor_TypedefDecl: + return createCXString("TypedefDecl"); + case CXCursor_EnumDecl: + return createCXString("EnumDecl"); + case CXCursor_EnumConstantDecl: + return createCXString("EnumConstantDecl"); + case CXCursor_StructDecl: + return createCXString("StructDecl"); + case CXCursor_UnionDecl: + return createCXString("UnionDecl"); + case CXCursor_ClassDecl: + return createCXString("ClassDecl"); + case CXCursor_FieldDecl: + return createCXString("FieldDecl"); + case CXCursor_VarDecl: + return createCXString("VarDecl"); + case CXCursor_ParmDecl: + return createCXString("ParmDecl"); + case CXCursor_ObjCInterfaceDecl: + return createCXString("ObjCInterfaceDecl"); + case CXCursor_ObjCCategoryDecl: + return createCXString("ObjCCategoryDecl"); + case CXCursor_ObjCProtocolDecl: + return createCXString("ObjCProtocolDecl"); + case CXCursor_ObjCPropertyDecl: + return createCXString("ObjCPropertyDecl"); + case CXCursor_ObjCIvarDecl: + return createCXString("ObjCIvarDecl"); + case CXCursor_ObjCInstanceMethodDecl: + return createCXString("ObjCInstanceMethodDecl"); + case CXCursor_ObjCClassMethodDecl: + return createCXString("ObjCClassMethodDecl"); + case CXCursor_ObjCImplementationDecl: + return createCXString("ObjCImplementationDecl"); + case CXCursor_ObjCCategoryImplDecl: + return createCXString("ObjCCategoryImplDecl"); + case CXCursor_CXXMethod: + return createCXString("CXXMethod"); + case CXCursor_UnexposedDecl: + return createCXString("UnexposedDecl"); + case CXCursor_ObjCSuperClassRef: + return createCXString("ObjCSuperClassRef"); + case CXCursor_ObjCProtocolRef: + return createCXString("ObjCProtocolRef"); + case CXCursor_ObjCClassRef: + return createCXString("ObjCClassRef"); + case CXCursor_TypeRef: + return createCXString("TypeRef"); + case CXCursor_TemplateRef: + return createCXString("TemplateRef"); + case CXCursor_NamespaceRef: + return createCXString("NamespaceRef"); + case CXCursor_MemberRef: + return createCXString("MemberRef"); + case CXCursor_LabelRef: + return createCXString("LabelRef"); + case CXCursor_OverloadedDeclRef: + return createCXString("OverloadedDeclRef"); + case CXCursor_VariableRef: + return createCXString("VariableRef"); + case CXCursor_IntegerLiteral: + return createCXString("IntegerLiteral"); + case CXCursor_FloatingLiteral: + return createCXString("FloatingLiteral"); + case CXCursor_ImaginaryLiteral: + return createCXString("ImaginaryLiteral"); + case CXCursor_StringLiteral: + return createCXString("StringLiteral"); + case CXCursor_CharacterLiteral: + return createCXString("CharacterLiteral"); + case CXCursor_ParenExpr: + return createCXString("ParenExpr"); + case CXCursor_UnaryOperator: + return createCXString("UnaryOperator"); + case CXCursor_ArraySubscriptExpr: + return createCXString("ArraySubscriptExpr"); + case CXCursor_BinaryOperator: + return createCXString("BinaryOperator"); + case CXCursor_CompoundAssignOperator: + return createCXString("CompoundAssignOperator"); + case CXCursor_ConditionalOperator: + return createCXString("ConditionalOperator"); + case CXCursor_CStyleCastExpr: + return createCXString("CStyleCastExpr"); + case CXCursor_CompoundLiteralExpr: + return createCXString("CompoundLiteralExpr"); + case CXCursor_InitListExpr: + return createCXString("InitListExpr"); + case CXCursor_AddrLabelExpr: + return createCXString("AddrLabelExpr"); + case CXCursor_StmtExpr: + return createCXString("StmtExpr"); + case CXCursor_GenericSelectionExpr: + return createCXString("GenericSelectionExpr"); + case CXCursor_GNUNullExpr: + return createCXString("GNUNullExpr"); + case CXCursor_CXXStaticCastExpr: + return createCXString("CXXStaticCastExpr"); + case CXCursor_CXXDynamicCastExpr: + return createCXString("CXXDynamicCastExpr"); + case CXCursor_CXXReinterpretCastExpr: + return createCXString("CXXReinterpretCastExpr"); + case CXCursor_CXXConstCastExpr: + return createCXString("CXXConstCastExpr"); + case CXCursor_CXXFunctionalCastExpr: + return createCXString("CXXFunctionalCastExpr"); + case CXCursor_CXXTypeidExpr: + return createCXString("CXXTypeidExpr"); + case CXCursor_CXXBoolLiteralExpr: + return createCXString("CXXBoolLiteralExpr"); + case CXCursor_CXXNullPtrLiteralExpr: + return createCXString("CXXNullPtrLiteralExpr"); + case CXCursor_CXXThisExpr: + return createCXString("CXXThisExpr"); + case CXCursor_CXXThrowExpr: + return createCXString("CXXThrowExpr"); + case CXCursor_CXXNewExpr: + return createCXString("CXXNewExpr"); + case CXCursor_CXXDeleteExpr: + return createCXString("CXXDeleteExpr"); + case CXCursor_UnaryExpr: + return createCXString("UnaryExpr"); + case CXCursor_ObjCStringLiteral: + return createCXString("ObjCStringLiteral"); + case CXCursor_ObjCBoolLiteralExpr: + return createCXString("ObjCBoolLiteralExpr"); + case CXCursor_ObjCEncodeExpr: + return createCXString("ObjCEncodeExpr"); + case CXCursor_ObjCSelectorExpr: + return createCXString("ObjCSelectorExpr"); + case CXCursor_ObjCProtocolExpr: + return createCXString("ObjCProtocolExpr"); + case CXCursor_ObjCBridgedCastExpr: + return createCXString("ObjCBridgedCastExpr"); + case CXCursor_BlockExpr: + return createCXString("BlockExpr"); + case CXCursor_PackExpansionExpr: + return createCXString("PackExpansionExpr"); + case CXCursor_SizeOfPackExpr: + return createCXString("SizeOfPackExpr"); + case CXCursor_LambdaExpr: + return createCXString("LambdaExpr"); + case CXCursor_UnexposedExpr: + return createCXString("UnexposedExpr"); + case CXCursor_DeclRefExpr: + return createCXString("DeclRefExpr"); + case CXCursor_MemberRefExpr: + return createCXString("MemberRefExpr"); + case CXCursor_CallExpr: + return createCXString("CallExpr"); + case CXCursor_ObjCMessageExpr: + return createCXString("ObjCMessageExpr"); + case CXCursor_UnexposedStmt: + return createCXString("UnexposedStmt"); + case CXCursor_DeclStmt: + return createCXString("DeclStmt"); + case CXCursor_LabelStmt: + return createCXString("LabelStmt"); + case CXCursor_CompoundStmt: + return createCXString("CompoundStmt"); + case CXCursor_CaseStmt: + return createCXString("CaseStmt"); + case CXCursor_DefaultStmt: + return createCXString("DefaultStmt"); + case CXCursor_IfStmt: + return createCXString("IfStmt"); + case CXCursor_SwitchStmt: + return createCXString("SwitchStmt"); + case CXCursor_WhileStmt: + return createCXString("WhileStmt"); + case CXCursor_DoStmt: + return createCXString("DoStmt"); + case CXCursor_ForStmt: + return createCXString("ForStmt"); + case CXCursor_GotoStmt: + return createCXString("GotoStmt"); + case CXCursor_IndirectGotoStmt: + return createCXString("IndirectGotoStmt"); + case CXCursor_ContinueStmt: + return createCXString("ContinueStmt"); + case CXCursor_BreakStmt: + return createCXString("BreakStmt"); + case CXCursor_ReturnStmt: + return createCXString("ReturnStmt"); + case CXCursor_AsmStmt: + return createCXString("AsmStmt"); + case CXCursor_ObjCAtTryStmt: + return createCXString("ObjCAtTryStmt"); + case CXCursor_ObjCAtCatchStmt: + return createCXString("ObjCAtCatchStmt"); + case CXCursor_ObjCAtFinallyStmt: + return createCXString("ObjCAtFinallyStmt"); + case CXCursor_ObjCAtThrowStmt: + return createCXString("ObjCAtThrowStmt"); + case CXCursor_ObjCAtSynchronizedStmt: + return createCXString("ObjCAtSynchronizedStmt"); + case CXCursor_ObjCAutoreleasePoolStmt: + return createCXString("ObjCAutoreleasePoolStmt"); + case CXCursor_ObjCForCollectionStmt: + return createCXString("ObjCForCollectionStmt"); + case CXCursor_CXXCatchStmt: + return createCXString("CXXCatchStmt"); + case CXCursor_CXXTryStmt: + return createCXString("CXXTryStmt"); + case CXCursor_CXXForRangeStmt: + return createCXString("CXXForRangeStmt"); + case CXCursor_SEHTryStmt: + return createCXString("SEHTryStmt"); + case CXCursor_SEHExceptStmt: + return createCXString("SEHExceptStmt"); + case CXCursor_SEHFinallyStmt: + return createCXString("SEHFinallyStmt"); + case CXCursor_NullStmt: + return createCXString("NullStmt"); + case CXCursor_InvalidFile: + return createCXString("InvalidFile"); + case CXCursor_InvalidCode: + return createCXString("InvalidCode"); + case CXCursor_NoDeclFound: + return createCXString("NoDeclFound"); + case CXCursor_NotImplemented: + return createCXString("NotImplemented"); + case CXCursor_TranslationUnit: + return createCXString("TranslationUnit"); + case CXCursor_UnexposedAttr: + return createCXString("UnexposedAttr"); + case CXCursor_IBActionAttr: + return createCXString("attribute(ibaction)"); + case CXCursor_IBOutletAttr: + return createCXString("attribute(iboutlet)"); + case CXCursor_IBOutletCollectionAttr: + return createCXString("attribute(iboutletcollection)"); + case CXCursor_CXXFinalAttr: + return createCXString("attribute(final)"); + case CXCursor_CXXOverrideAttr: + return createCXString("attribute(override)"); + case CXCursor_AnnotateAttr: + return createCXString("attribute(annotate)"); + case CXCursor_AsmLabelAttr: + return createCXString("asm label"); + case CXCursor_PreprocessingDirective: + return createCXString("preprocessing directive"); + case CXCursor_MacroDefinition: + return createCXString("macro definition"); + case CXCursor_MacroExpansion: + return createCXString("macro expansion"); + case CXCursor_InclusionDirective: + return createCXString("inclusion directive"); + case CXCursor_Namespace: + return createCXString("Namespace"); + case CXCursor_LinkageSpec: + return createCXString("LinkageSpec"); + case CXCursor_CXXBaseSpecifier: + return createCXString("C++ base class specifier"); + case CXCursor_Constructor: + return createCXString("CXXConstructor"); + case CXCursor_Destructor: + return createCXString("CXXDestructor"); + case CXCursor_ConversionFunction: + return createCXString("CXXConversion"); + case CXCursor_TemplateTypeParameter: + return createCXString("TemplateTypeParameter"); + case CXCursor_NonTypeTemplateParameter: + return createCXString("NonTypeTemplateParameter"); + case CXCursor_TemplateTemplateParameter: + return createCXString("TemplateTemplateParameter"); + case CXCursor_FunctionTemplate: + return createCXString("FunctionTemplate"); + case CXCursor_ClassTemplate: + return createCXString("ClassTemplate"); + case CXCursor_ClassTemplatePartialSpecialization: + return createCXString("ClassTemplatePartialSpecialization"); + case CXCursor_NamespaceAlias: + return createCXString("NamespaceAlias"); + case CXCursor_UsingDirective: + return createCXString("UsingDirective"); + case CXCursor_UsingDeclaration: + return createCXString("UsingDeclaration"); + case CXCursor_TypeAliasDecl: + return createCXString("TypeAliasDecl"); + case CXCursor_ObjCSynthesizeDecl: + return createCXString("ObjCSynthesizeDecl"); + case CXCursor_ObjCDynamicDecl: + return createCXString("ObjCDynamicDecl"); + case CXCursor_CXXAccessSpecifier: + return createCXString("CXXAccessSpecifier"); + } + + llvm_unreachable("Unhandled CXCursorKind"); +} + +struct GetCursorData { + SourceLocation TokenBeginLoc; + bool PointsAtMacroArgExpansion; + CXCursor &BestCursor; + + GetCursorData(SourceManager &SM, + SourceLocation tokenBegin, CXCursor &outputCursor) + : TokenBeginLoc(tokenBegin), BestCursor(outputCursor) { + PointsAtMacroArgExpansion = SM.isMacroArgExpansion(tokenBegin); + } +}; + +static enum CXChildVisitResult GetCursorVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + GetCursorData *Data = static_cast<GetCursorData *>(client_data); + CXCursor *BestCursor = &Data->BestCursor; + + // If we point inside a macro argument we should provide info of what the + // token is so use the actual cursor, don't replace it with a macro expansion + // cursor. + if (cursor.kind == CXCursor_MacroExpansion && Data->PointsAtMacroArgExpansion) + return CXChildVisit_Recurse; + + if (clang_isDeclaration(cursor.kind)) { + // Avoid having the implicit methods override the property decls. + if (ObjCMethodDecl *MD + = dyn_cast_or_null<ObjCMethodDecl>(getCursorDecl(cursor))) { + if (MD->isImplicit()) + return CXChildVisit_Break; + + } else if (ObjCInterfaceDecl *ID + = dyn_cast_or_null<ObjCInterfaceDecl>(getCursorDecl(cursor))) { + // Check that when we have multiple @class references in the same line, + // that later ones do not override the previous ones. + // If we have: + // @class Foo, Bar; + // source ranges for both start at '@', so 'Bar' will end up overriding + // 'Foo' even though the cursor location was at 'Foo'. + if (BestCursor->kind == CXCursor_ObjCInterfaceDecl || + BestCursor->kind == CXCursor_ObjCClassRef) + if (ObjCInterfaceDecl *PrevID + = dyn_cast_or_null<ObjCInterfaceDecl>(getCursorDecl(*BestCursor))){ + if (PrevID != ID && + !PrevID->isThisDeclarationADefinition() && + !ID->isThisDeclarationADefinition()) + return CXChildVisit_Break; + } + } + } + + if (clang_isExpression(cursor.kind) && + clang_isDeclaration(BestCursor->kind)) { + if (Decl *D = getCursorDecl(*BestCursor)) { + // Avoid having the cursor of an expression replace the declaration cursor + // when the expression source range overlaps the declaration range. + // This can happen for C++ constructor expressions whose range generally + // include the variable declaration, e.g.: + // MyCXXClass foo; // Make sure pointing at 'foo' returns a VarDecl cursor. + if (D->getLocation().isValid() && Data->TokenBeginLoc.isValid() && + D->getLocation() == Data->TokenBeginLoc) + return CXChildVisit_Break; + } + } + + // If our current best cursor is the construction of a temporary object, + // don't replace that cursor with a type reference, because we want + // clang_getCursor() to point at the constructor. + if (clang_isExpression(BestCursor->kind) && + isa<CXXTemporaryObjectExpr>(getCursorExpr(*BestCursor)) && + cursor.kind == CXCursor_TypeRef) { + // Keep the cursor pointing at CXXTemporaryObjectExpr but also mark it + // as having the actual point on the type reference. + *BestCursor = getTypeRefedCallExprCursor(*BestCursor); + return CXChildVisit_Recurse; + } + + *BestCursor = cursor; + return CXChildVisit_Recurse; +} + +CXCursor clang_getCursor(CXTranslationUnit TU, CXSourceLocation Loc) { + if (!TU) + return clang_getNullCursor(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SourceLocation SLoc = cxloc::translateSourceLocation(Loc); + CXCursor Result = cxcursor::getCursor(TU, SLoc); + + bool Logging = getenv("LIBCLANG_LOGGING"); + if (Logging) { + CXFile SearchFile; + unsigned SearchLine, SearchColumn; + CXFile ResultFile; + unsigned ResultLine, ResultColumn; + CXString SearchFileName, ResultFileName, KindSpelling, USR; + const char *IsDef = clang_isCursorDefinition(Result)? " (Definition)" : ""; + CXSourceLocation ResultLoc = clang_getCursorLocation(Result); + + clang_getExpansionLocation(Loc, &SearchFile, &SearchLine, &SearchColumn, 0); + clang_getExpansionLocation(ResultLoc, &ResultFile, &ResultLine, + &ResultColumn, 0); + SearchFileName = clang_getFileName(SearchFile); + ResultFileName = clang_getFileName(ResultFile); + KindSpelling = clang_getCursorKindSpelling(Result.kind); + USR = clang_getCursorUSR(Result); + fprintf(stderr, "clang_getCursor(%s:%d:%d) = %s(%s:%d:%d):%s%s\n", + clang_getCString(SearchFileName), SearchLine, SearchColumn, + clang_getCString(KindSpelling), + clang_getCString(ResultFileName), ResultLine, ResultColumn, + clang_getCString(USR), IsDef); + clang_disposeString(SearchFileName); + clang_disposeString(ResultFileName); + clang_disposeString(KindSpelling); + clang_disposeString(USR); + + CXCursor Definition = clang_getCursorDefinition(Result); + if (!clang_equalCursors(Definition, clang_getNullCursor())) { + CXSourceLocation DefinitionLoc = clang_getCursorLocation(Definition); + CXString DefinitionKindSpelling + = clang_getCursorKindSpelling(Definition.kind); + CXFile DefinitionFile; + unsigned DefinitionLine, DefinitionColumn; + clang_getExpansionLocation(DefinitionLoc, &DefinitionFile, + &DefinitionLine, &DefinitionColumn, 0); + CXString DefinitionFileName = clang_getFileName(DefinitionFile); + fprintf(stderr, " -> %s(%s:%d:%d)\n", + clang_getCString(DefinitionKindSpelling), + clang_getCString(DefinitionFileName), + DefinitionLine, DefinitionColumn); + clang_disposeString(DefinitionFileName); + clang_disposeString(DefinitionKindSpelling); + } + } + + return Result; +} + +CXCursor clang_getNullCursor(void) { + return MakeCXCursorInvalid(CXCursor_InvalidFile); +} + +unsigned clang_equalCursors(CXCursor X, CXCursor Y) { + return X == Y; +} + +unsigned clang_hashCursor(CXCursor C) { + unsigned Index = 0; + if (clang_isExpression(C.kind) || clang_isStatement(C.kind)) + Index = 1; + + return llvm::DenseMapInfo<std::pair<unsigned, void*> >::getHashValue( + std::make_pair(C.kind, C.data[Index])); +} + +unsigned clang_isInvalid(enum CXCursorKind K) { + return K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid; +} + +unsigned clang_isDeclaration(enum CXCursorKind K) { + return K >= CXCursor_FirstDecl && K <= CXCursor_LastDecl; +} + +unsigned clang_isReference(enum CXCursorKind K) { + return K >= CXCursor_FirstRef && K <= CXCursor_LastRef; +} + +unsigned clang_isExpression(enum CXCursorKind K) { + return K >= CXCursor_FirstExpr && K <= CXCursor_LastExpr; +} + +unsigned clang_isStatement(enum CXCursorKind K) { + return K >= CXCursor_FirstStmt && K <= CXCursor_LastStmt; +} + +unsigned clang_isAttribute(enum CXCursorKind K) { + return K >= CXCursor_FirstAttr && K <= CXCursor_LastAttr; +} + +unsigned clang_isTranslationUnit(enum CXCursorKind K) { + return K == CXCursor_TranslationUnit; +} + +unsigned clang_isPreprocessing(enum CXCursorKind K) { + return K >= CXCursor_FirstPreprocessing && K <= CXCursor_LastPreprocessing; +} + +unsigned clang_isUnexposed(enum CXCursorKind K) { + switch (K) { + case CXCursor_UnexposedDecl: + case CXCursor_UnexposedExpr: + case CXCursor_UnexposedStmt: + case CXCursor_UnexposedAttr: + return true; + default: + return false; + } +} + +CXCursorKind clang_getCursorKind(CXCursor C) { + return C.kind; +} + +CXSourceLocation clang_getCursorLocation(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + std::pair<ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCSuperClassRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCProtocolRef: { + std::pair<ObjCProtocolDecl *, SourceLocation> P + = getCursorObjCProtocolRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCClassRef: { + std::pair<ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCClassRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_TypeRef: { + std::pair<TypeDecl *, SourceLocation> P = getCursorTypeRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_TemplateRef: { + std::pair<TemplateDecl *, SourceLocation> P = getCursorTemplateRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_NamespaceRef: { + std::pair<NamedDecl *, SourceLocation> P = getCursorNamespaceRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_MemberRef: { + std::pair<FieldDecl *, SourceLocation> P = getCursorMemberRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_VariableRef: { + std::pair<VarDecl *, SourceLocation> P = getCursorVariableRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_CXXBaseSpecifier: { + CXXBaseSpecifier *BaseSpec = getCursorCXXBaseSpecifier(C); + if (!BaseSpec) + return clang_getNullLocation(); + + if (TypeSourceInfo *TSInfo = BaseSpec->getTypeSourceInfo()) + return cxloc::translateSourceLocation(getCursorContext(C), + TSInfo->getTypeLoc().getBeginLoc()); + + return cxloc::translateSourceLocation(getCursorContext(C), + BaseSpec->getLocStart()); + } + + case CXCursor_LabelRef: { + std::pair<LabelStmt *, SourceLocation> P = getCursorLabelRef(C); + return cxloc::translateSourceLocation(getCursorContext(C), P.second); + } + + case CXCursor_OverloadedDeclRef: + return cxloc::translateSourceLocation(getCursorContext(C), + getCursorOverloadedDeclRef(C).second); + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (clang_isExpression(C.kind)) + return cxloc::translateSourceLocation(getCursorContext(C), + getLocationFromExpr(getCursorExpr(C))); + + if (clang_isStatement(C.kind)) + return cxloc::translateSourceLocation(getCursorContext(C), + getCursorStmt(C)->getLocStart()); + + if (C.kind == CXCursor_PreprocessingDirective) { + SourceLocation L = cxcursor::getCursorPreprocessingDirective(C).getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_MacroExpansion) { + SourceLocation L + = cxcursor::getCursorMacroExpansion(C)->getSourceRange().getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_MacroDefinition) { + SourceLocation L = cxcursor::getCursorMacroDefinition(C)->getLocation(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_InclusionDirective) { + SourceLocation L + = cxcursor::getCursorInclusionDirective(C)->getSourceRange().getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind < CXCursor_FirstDecl || C.kind > CXCursor_LastDecl) + return clang_getNullLocation(); + + Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullLocation(); + + SourceLocation Loc = D->getLocation(); + // FIXME: Multiple variables declared in a single declaration + // currently lack the information needed to correctly determine their + // ranges when accounting for the type-specifier. We use context + // stored in the CXCursor to determine if the VarDecl is in a DeclGroup, + // and if so, whether it is the first decl. + if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!cxcursor::isFirstInDeclGroup(C)) + Loc = VD->getLocation(); + } + + // For ObjC methods, give the start location of the method name. + if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + Loc = MD->getSelectorStartLoc(); + + return cxloc::translateSourceLocation(getCursorContext(C), Loc); +} + +} // end extern "C" + +CXCursor cxcursor::getCursor(CXTranslationUnit TU, SourceLocation SLoc) { + assert(TU); + + // Guard against an invalid SourceLocation, or we may assert in one + // of the following calls. + if (SLoc.isInvalid()) + return clang_getNullCursor(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + + // Translate the given source location to make it point at the beginning of + // the token under the cursor. + SLoc = Lexer::GetBeginningOfToken(SLoc, CXXUnit->getSourceManager(), + CXXUnit->getASTContext().getLangOpts()); + + CXCursor Result = MakeCXCursorInvalid(CXCursor_NoDeclFound); + if (SLoc.isValid()) { + GetCursorData ResultData(CXXUnit->getSourceManager(), SLoc, Result); + CursorVisitor CursorVis(TU, GetCursorVisitor, &ResultData, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + SourceLocation(SLoc)); + CursorVis.visitFileRegion(); + } + + return Result; +} + +static SourceRange getRawCursorExtent(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: + return getCursorObjCSuperClassRef(C).second; + + case CXCursor_ObjCProtocolRef: + return getCursorObjCProtocolRef(C).second; + + case CXCursor_ObjCClassRef: + return getCursorObjCClassRef(C).second; + + case CXCursor_TypeRef: + return getCursorTypeRef(C).second; + + case CXCursor_TemplateRef: + return getCursorTemplateRef(C).second; + + case CXCursor_NamespaceRef: + return getCursorNamespaceRef(C).second; + + case CXCursor_MemberRef: + return getCursorMemberRef(C).second; + + case CXCursor_CXXBaseSpecifier: + return getCursorCXXBaseSpecifier(C)->getSourceRange(); + + case CXCursor_LabelRef: + return getCursorLabelRef(C).second; + + case CXCursor_OverloadedDeclRef: + return getCursorOverloadedDeclRef(C).second; + + case CXCursor_VariableRef: + return getCursorVariableRef(C).second; + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (clang_isExpression(C.kind)) + return getCursorExpr(C)->getSourceRange(); + + if (clang_isStatement(C.kind)) + return getCursorStmt(C)->getSourceRange(); + + if (clang_isAttribute(C.kind)) + return getCursorAttr(C)->getRange(); + + if (C.kind == CXCursor_PreprocessingDirective) + return cxcursor::getCursorPreprocessingDirective(C); + + if (C.kind == CXCursor_MacroExpansion) { + ASTUnit *TU = getCursorASTUnit(C); + SourceRange Range = cxcursor::getCursorMacroExpansion(C)->getSourceRange(); + return TU->mapRangeFromPreamble(Range); + } + + if (C.kind == CXCursor_MacroDefinition) { + ASTUnit *TU = getCursorASTUnit(C); + SourceRange Range = cxcursor::getCursorMacroDefinition(C)->getSourceRange(); + return TU->mapRangeFromPreamble(Range); + } + + if (C.kind == CXCursor_InclusionDirective) { + ASTUnit *TU = getCursorASTUnit(C); + SourceRange Range = cxcursor::getCursorInclusionDirective(C)->getSourceRange(); + return TU->mapRangeFromPreamble(Range); + } + + if (C.kind == CXCursor_TranslationUnit) { + ASTUnit *TU = getCursorASTUnit(C); + FileID MainID = TU->getSourceManager().getMainFileID(); + SourceLocation Start = TU->getSourceManager().getLocForStartOfFile(MainID); + SourceLocation End = TU->getSourceManager().getLocForEndOfFile(MainID); + return SourceRange(Start, End); + } + + if (C.kind >= CXCursor_FirstDecl && C.kind <= CXCursor_LastDecl) { + Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return SourceRange(); + + SourceRange R = D->getSourceRange(); + // FIXME: Multiple variables declared in a single declaration + // currently lack the information needed to correctly determine their + // ranges when accounting for the type-specifier. We use context + // stored in the CXCursor to determine if the VarDecl is in a DeclGroup, + // and if so, whether it is the first decl. + if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!cxcursor::isFirstInDeclGroup(C)) + R.setBegin(VD->getLocation()); + } + return R; + } + return SourceRange(); +} + +/// \brief Retrieves the "raw" cursor extent, which is then extended to include +/// the decl-specifier-seq for declarations. +static SourceRange getFullCursorExtent(CXCursor C, SourceManager &SrcMgr) { + if (C.kind >= CXCursor_FirstDecl && C.kind <= CXCursor_LastDecl) { + Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return SourceRange(); + + SourceRange R = D->getSourceRange(); + + // Adjust the start of the location for declarations preceded by + // declaration specifiers. + SourceLocation StartLoc; + if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { + if (TypeSourceInfo *TI = DD->getTypeSourceInfo()) + StartLoc = TI->getTypeLoc().getLocStart(); + } else if (TypedefDecl *Typedef = dyn_cast<TypedefDecl>(D)) { + if (TypeSourceInfo *TI = Typedef->getTypeSourceInfo()) + StartLoc = TI->getTypeLoc().getLocStart(); + } + + if (StartLoc.isValid() && R.getBegin().isValid() && + SrcMgr.isBeforeInTranslationUnit(StartLoc, R.getBegin())) + R.setBegin(StartLoc); + + // FIXME: Multiple variables declared in a single declaration + // currently lack the information needed to correctly determine their + // ranges when accounting for the type-specifier. We use context + // stored in the CXCursor to determine if the VarDecl is in a DeclGroup, + // and if so, whether it is the first decl. + if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!cxcursor::isFirstInDeclGroup(C)) + R.setBegin(VD->getLocation()); + } + + return R; + } + + return getRawCursorExtent(C); +} + +extern "C" { + +CXSourceRange clang_getCursorExtent(CXCursor C) { + SourceRange R = getRawCursorExtent(C); + if (R.isInvalid()) + return clang_getNullRange(); + + return cxloc::translateSourceRange(getCursorContext(C), R); +} + +CXCursor clang_getCursorReferenced(CXCursor C) { + if (clang_isInvalid(C.kind)) + return clang_getNullCursor(); + + CXTranslationUnit tu = getCursorTU(C); + if (clang_isDeclaration(C.kind)) { + Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + if (UsingDecl *Using = dyn_cast<UsingDecl>(D)) + return MakeCursorOverloadedDeclRef(Using, D->getLocation(), tu); + if (ObjCPropertyImplDecl *PropImpl =dyn_cast<ObjCPropertyImplDecl>(D)) + if (ObjCPropertyDecl *Property = PropImpl->getPropertyDecl()) + return MakeCXCursor(Property, tu); + + return C; + } + + if (clang_isExpression(C.kind)) { + Expr *E = getCursorExpr(C); + Decl *D = getDeclFromExpr(E); + if (D) { + CXCursor declCursor = MakeCXCursor(D, tu); + declCursor = getSelectorIdentifierCursor(getSelectorIdentifierIndex(C), + declCursor); + return declCursor; + } + + if (OverloadExpr *Ovl = dyn_cast_or_null<OverloadExpr>(E)) + return MakeCursorOverloadedDeclRef(Ovl, tu); + + return clang_getNullCursor(); + } + + if (clang_isStatement(C.kind)) { + Stmt *S = getCursorStmt(C); + if (GotoStmt *Goto = dyn_cast_or_null<GotoStmt>(S)) + if (LabelDecl *label = Goto->getLabel()) + if (LabelStmt *labelS = label->getStmt()) + return MakeCXCursor(labelS, getCursorDecl(C), tu); + + return clang_getNullCursor(); + } + + if (C.kind == CXCursor_MacroExpansion) { + if (MacroDefinition *Def = getCursorMacroExpansion(C)->getDefinition()) + return MakeMacroDefinitionCursor(Def, tu); + } + + if (!clang_isReference(C.kind)) + return clang_getNullCursor(); + + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: + return MakeCXCursor(getCursorObjCSuperClassRef(C).first, tu); + + case CXCursor_ObjCProtocolRef: { + ObjCProtocolDecl *Prot = getCursorObjCProtocolRef(C).first; + if (ObjCProtocolDecl *Def = Prot->getDefinition()) + return MakeCXCursor(Def, tu); + + return MakeCXCursor(Prot, tu); + } + + case CXCursor_ObjCClassRef: { + ObjCInterfaceDecl *Class = getCursorObjCClassRef(C).first; + if (ObjCInterfaceDecl *Def = Class->getDefinition()) + return MakeCXCursor(Def, tu); + + return MakeCXCursor(Class, tu); + } + + case CXCursor_TypeRef: + return MakeCXCursor(getCursorTypeRef(C).first, tu ); + + case CXCursor_TemplateRef: + return MakeCXCursor(getCursorTemplateRef(C).first, tu ); + + case CXCursor_NamespaceRef: + return MakeCXCursor(getCursorNamespaceRef(C).first, tu ); + + case CXCursor_MemberRef: + return MakeCXCursor(getCursorMemberRef(C).first, tu ); + + case CXCursor_CXXBaseSpecifier: { + CXXBaseSpecifier *B = cxcursor::getCursorCXXBaseSpecifier(C); + return clang_getTypeDeclaration(cxtype::MakeCXType(B->getType(), + tu )); + } + + case CXCursor_LabelRef: + // FIXME: We end up faking the "parent" declaration here because we + // don't want to make CXCursor larger. + return MakeCXCursor(getCursorLabelRef(C).first, + static_cast<ASTUnit*>(tu->TUData)->getASTContext() + .getTranslationUnitDecl(), + tu); + + case CXCursor_OverloadedDeclRef: + return C; + + case CXCursor_VariableRef: + return MakeCXCursor(getCursorVariableRef(C).first, tu); + + default: + // We would prefer to enumerate all non-reference cursor kinds here. + llvm_unreachable("Unhandled reference cursor kind"); + } +} + +CXCursor clang_getCursorDefinition(CXCursor C) { + if (clang_isInvalid(C.kind)) + return clang_getNullCursor(); + + CXTranslationUnit TU = getCursorTU(C); + + bool WasReference = false; + if (clang_isReference(C.kind) || clang_isExpression(C.kind)) { + C = clang_getCursorReferenced(C); + WasReference = true; + } + + if (C.kind == CXCursor_MacroExpansion) + return clang_getCursorReferenced(C); + + if (!clang_isDeclaration(C.kind)) + return clang_getNullCursor(); + + Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + + switch (D->getKind()) { + // Declaration kinds that don't really separate the notions of + // declaration and definition. + case Decl::Namespace: + case Decl::Typedef: + case Decl::TypeAlias: + case Decl::TypeAliasTemplate: + case Decl::TemplateTypeParm: + case Decl::EnumConstant: + case Decl::Field: + case Decl::IndirectField: + case Decl::ObjCIvar: + case Decl::ObjCAtDefsField: + case Decl::ImplicitParam: + case Decl::ParmVar: + case Decl::NonTypeTemplateParm: + case Decl::TemplateTemplateParm: + case Decl::ObjCCategoryImpl: + case Decl::ObjCImplementation: + case Decl::AccessSpec: + case Decl::LinkageSpec: + case Decl::ObjCPropertyImpl: + case Decl::FileScopeAsm: + case Decl::StaticAssert: + case Decl::Block: + case Decl::Label: // FIXME: Is this right?? + case Decl::ClassScopeFunctionSpecialization: + case Decl::Import: + return C; + + // Declaration kinds that don't make any sense here, but are + // nonetheless harmless. + case Decl::TranslationUnit: + break; + + // Declaration kinds for which the definition is not resolvable. + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + break; + + case Decl::UsingDirective: + return MakeCXCursor(cast<UsingDirectiveDecl>(D)->getNominatedNamespace(), + TU); + + case Decl::NamespaceAlias: + return MakeCXCursor(cast<NamespaceAliasDecl>(D)->getNamespace(), TU); + + case Decl::Enum: + case Decl::Record: + case Decl::CXXRecord: + case Decl::ClassTemplateSpecialization: + case Decl::ClassTemplatePartialSpecialization: + if (TagDecl *Def = cast<TagDecl>(D)->getDefinition()) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: { + const FunctionDecl *Def = 0; + if (cast<FunctionDecl>(D)->getBody(Def)) + return MakeCXCursor(const_cast<FunctionDecl *>(Def), TU); + return clang_getNullCursor(); + } + + case Decl::Var: { + // Ask the variable if it has a definition. + if (VarDecl *Def = cast<VarDecl>(D)->getDefinition()) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + } + + case Decl::FunctionTemplate: { + const FunctionDecl *Def = 0; + if (cast<FunctionTemplateDecl>(D)->getTemplatedDecl()->getBody(Def)) + return MakeCXCursor(Def->getDescribedFunctionTemplate(), TU); + return clang_getNullCursor(); + } + + case Decl::ClassTemplate: { + if (RecordDecl *Def = cast<ClassTemplateDecl>(D)->getTemplatedDecl() + ->getDefinition()) + return MakeCXCursor(cast<CXXRecordDecl>(Def)->getDescribedClassTemplate(), + TU); + return clang_getNullCursor(); + } + + case Decl::Using: + return MakeCursorOverloadedDeclRef(cast<UsingDecl>(D), + D->getLocation(), TU); + + case Decl::UsingShadow: + return clang_getCursorDefinition( + MakeCXCursor(cast<UsingShadowDecl>(D)->getTargetDecl(), + TU)); + + case Decl::ObjCMethod: { + ObjCMethodDecl *Method = cast<ObjCMethodDecl>(D); + if (Method->isThisDeclarationADefinition()) + return C; + + // Dig out the method definition in the associated + // @implementation, if we have it. + // FIXME: The ASTs should make finding the definition easier. + if (ObjCInterfaceDecl *Class + = dyn_cast<ObjCInterfaceDecl>(Method->getDeclContext())) + if (ObjCImplementationDecl *ClassImpl = Class->getImplementation()) + if (ObjCMethodDecl *Def = ClassImpl->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + if (Def->isThisDeclarationADefinition()) + return MakeCXCursor(Def, TU); + + return clang_getNullCursor(); + } + + case Decl::ObjCCategory: + if (ObjCCategoryImplDecl *Impl + = cast<ObjCCategoryDecl>(D)->getImplementation()) + return MakeCXCursor(Impl, TU); + return clang_getNullCursor(); + + case Decl::ObjCProtocol: + if (ObjCProtocolDecl *Def = cast<ObjCProtocolDecl>(D)->getDefinition()) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + + case Decl::ObjCInterface: { + // There are two notions of a "definition" for an Objective-C + // class: the interface and its implementation. When we resolved a + // reference to an Objective-C class, produce the @interface as + // the definition; when we were provided with the interface, + // produce the @implementation as the definition. + ObjCInterfaceDecl *IFace = cast<ObjCInterfaceDecl>(D); + if (WasReference) { + if (ObjCInterfaceDecl *Def = IFace->getDefinition()) + return MakeCXCursor(Def, TU); + } else if (ObjCImplementationDecl *Impl = IFace->getImplementation()) + return MakeCXCursor(Impl, TU); + return clang_getNullCursor(); + } + + case Decl::ObjCProperty: + // FIXME: We don't really know where to find the + // ObjCPropertyImplDecls that implement this property. + return clang_getNullCursor(); + + case Decl::ObjCCompatibleAlias: + if (ObjCInterfaceDecl *Class + = cast<ObjCCompatibleAliasDecl>(D)->getClassInterface()) + if (ObjCInterfaceDecl *Def = Class->getDefinition()) + return MakeCXCursor(Def, TU); + + return clang_getNullCursor(); + + case Decl::Friend: + if (NamedDecl *Friend = cast<FriendDecl>(D)->getFriendDecl()) + return clang_getCursorDefinition(MakeCXCursor(Friend, TU)); + return clang_getNullCursor(); + + case Decl::FriendTemplate: + if (NamedDecl *Friend = cast<FriendTemplateDecl>(D)->getFriendDecl()) + return clang_getCursorDefinition(MakeCXCursor(Friend, TU)); + return clang_getNullCursor(); + } + + return clang_getNullCursor(); +} + +unsigned clang_isCursorDefinition(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + return clang_getCursorDefinition(C) == C; +} + +CXCursor clang_getCanonicalCursor(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return C; + + if (Decl *D = getCursorDecl(C)) { + if (ObjCCategoryImplDecl *CatImplD = dyn_cast<ObjCCategoryImplDecl>(D)) + if (ObjCCategoryDecl *CatD = CatImplD->getCategoryDecl()) + return MakeCXCursor(CatD, getCursorTU(C)); + + if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) + if (ObjCInterfaceDecl *IFD = ImplD->getClassInterface()) + return MakeCXCursor(IFD, getCursorTU(C)); + + return MakeCXCursor(D->getCanonicalDecl(), getCursorTU(C)); + } + + return C; +} + +int clang_Cursor_getObjCSelectorIndex(CXCursor cursor) { + return cxcursor::getSelectorIdentifierIndexAndLoc(cursor).first; +} + +unsigned clang_getNumOverloadedDecls(CXCursor C) { + if (C.kind != CXCursor_OverloadedDeclRef) + return 0; + + OverloadedDeclRefStorage Storage = getCursorOverloadedDeclRef(C).first; + if (OverloadExpr *E = Storage.dyn_cast<OverloadExpr *>()) + return E->getNumDecls(); + + if (OverloadedTemplateStorage *S + = Storage.dyn_cast<OverloadedTemplateStorage*>()) + return S->size(); + + Decl *D = Storage.get<Decl*>(); + if (UsingDecl *Using = dyn_cast<UsingDecl>(D)) + return Using->shadow_size(); + + return 0; +} + +CXCursor clang_getOverloadedDecl(CXCursor cursor, unsigned index) { + if (cursor.kind != CXCursor_OverloadedDeclRef) + return clang_getNullCursor(); + + if (index >= clang_getNumOverloadedDecls(cursor)) + return clang_getNullCursor(); + + CXTranslationUnit TU = getCursorTU(cursor); + OverloadedDeclRefStorage Storage = getCursorOverloadedDeclRef(cursor).first; + if (OverloadExpr *E = Storage.dyn_cast<OverloadExpr *>()) + return MakeCXCursor(E->decls_begin()[index], TU); + + if (OverloadedTemplateStorage *S + = Storage.dyn_cast<OverloadedTemplateStorage*>()) + return MakeCXCursor(S->begin()[index], TU); + + Decl *D = Storage.get<Decl*>(); + if (UsingDecl *Using = dyn_cast<UsingDecl>(D)) { + // FIXME: This is, unfortunately, linear time. + UsingDecl::shadow_iterator Pos = Using->shadow_begin(); + std::advance(Pos, index); + return MakeCXCursor(cast<UsingShadowDecl>(*Pos)->getTargetDecl(), TU); + } + + return clang_getNullCursor(); +} + +void clang_getDefinitionSpellingAndExtent(CXCursor C, + const char **startBuf, + const char **endBuf, + unsigned *startLine, + unsigned *startColumn, + unsigned *endLine, + unsigned *endColumn) { + assert(getCursorDecl(C) && "CXCursor has null decl"); + NamedDecl *ND = static_cast<NamedDecl *>(getCursorDecl(C)); + FunctionDecl *FD = dyn_cast<FunctionDecl>(ND); + CompoundStmt *Body = dyn_cast<CompoundStmt>(FD->getBody()); + + SourceManager &SM = FD->getASTContext().getSourceManager(); + *startBuf = SM.getCharacterData(Body->getLBracLoc()); + *endBuf = SM.getCharacterData(Body->getRBracLoc()); + *startLine = SM.getSpellingLineNumber(Body->getLBracLoc()); + *startColumn = SM.getSpellingColumnNumber(Body->getLBracLoc()); + *endLine = SM.getSpellingLineNumber(Body->getRBracLoc()); + *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc()); +} + + +CXSourceRange clang_getCursorReferenceNameRange(CXCursor C, unsigned NameFlags, + unsigned PieceIndex) { + RefNamePieces Pieces; + + switch (C.kind) { + case CXCursor_MemberRefExpr: + if (MemberExpr *E = dyn_cast<MemberExpr>(getCursorExpr(C))) + Pieces = buildPieces(NameFlags, true, E->getMemberNameInfo(), + E->getQualifierLoc().getSourceRange()); + break; + + case CXCursor_DeclRefExpr: + if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(getCursorExpr(C))) + Pieces = buildPieces(NameFlags, false, E->getNameInfo(), + E->getQualifierLoc().getSourceRange(), + E->getOptionalExplicitTemplateArgs()); + break; + + case CXCursor_CallExpr: + if (CXXOperatorCallExpr *OCE = + dyn_cast<CXXOperatorCallExpr>(getCursorExpr(C))) { + Expr *Callee = OCE->getCallee(); + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Callee)) + Callee = ICE->getSubExpr(); + + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Callee)) + Pieces = buildPieces(NameFlags, false, DRE->getNameInfo(), + DRE->getQualifierLoc().getSourceRange()); + } + break; + + default: + break; + } + + if (Pieces.empty()) { + if (PieceIndex == 0) + return clang_getCursorExtent(C); + } else if (PieceIndex < Pieces.size()) { + SourceRange R = Pieces[PieceIndex]; + if (R.isValid()) + return cxloc::translateSourceRange(getCursorContext(C), R); + } + + return clang_getNullRange(); +} + +void clang_enableStackTraces(void) { + llvm::sys::PrintStackTraceOnErrorSignal(); +} + +void clang_executeOnThread(void (*fn)(void*), void *user_data, + unsigned stack_size) { + llvm::llvm_execute_on_thread(fn, user_data, stack_size); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Token-based Operations. +//===----------------------------------------------------------------------===// + +/* CXToken layout: + * int_data[0]: a CXTokenKind + * int_data[1]: starting token location + * int_data[2]: token length + * int_data[3]: reserved + * ptr_data: for identifiers and keywords, an IdentifierInfo*. + * otherwise unused. + */ +extern "C" { + +CXTokenKind clang_getTokenKind(CXToken CXTok) { + return static_cast<CXTokenKind>(CXTok.int_data[0]); +} + +CXString clang_getTokenSpelling(CXTranslationUnit TU, CXToken CXTok) { + switch (clang_getTokenKind(CXTok)) { + case CXToken_Identifier: + case CXToken_Keyword: + // We know we have an IdentifierInfo*, so use that. + return createCXString(static_cast<IdentifierInfo *>(CXTok.ptr_data) + ->getNameStart()); + + case CXToken_Literal: { + // We have stashed the starting pointer in the ptr_data field. Use it. + const char *Text = static_cast<const char *>(CXTok.ptr_data); + return createCXString(StringRef(Text, CXTok.int_data[2])); + } + + case CXToken_Punctuation: + case CXToken_Comment: + break; + } + + // We have to find the starting buffer pointer the hard way, by + // deconstructing the source location. + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + if (!CXXUnit) + return createCXString(""); + + SourceLocation Loc = SourceLocation::getFromRawEncoding(CXTok.int_data[1]); + std::pair<FileID, unsigned> LocInfo + = CXXUnit->getSourceManager().getDecomposedSpellingLoc(Loc); + bool Invalid = false; + StringRef Buffer + = CXXUnit->getSourceManager().getBufferData(LocInfo.first, &Invalid); + if (Invalid) + return createCXString(""); + + return createCXString(Buffer.substr(LocInfo.second, CXTok.int_data[2])); +} + +CXSourceLocation clang_getTokenLocation(CXTranslationUnit TU, CXToken CXTok) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + if (!CXXUnit) + return clang_getNullLocation(); + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), + SourceLocation::getFromRawEncoding(CXTok.int_data[1])); +} + +CXSourceRange clang_getTokenExtent(CXTranslationUnit TU, CXToken CXTok) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + if (!CXXUnit) + return clang_getNullRange(); + + return cxloc::translateSourceRange(CXXUnit->getASTContext(), + SourceLocation::getFromRawEncoding(CXTok.int_data[1])); +} + +static void getTokens(ASTUnit *CXXUnit, SourceRange Range, + SmallVectorImpl<CXToken> &CXTokens) { + SourceManager &SourceMgr = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> BeginLocInfo + = SourceMgr.getDecomposedLoc(Range.getBegin()); + std::pair<FileID, unsigned> EndLocInfo + = SourceMgr.getDecomposedLoc(Range.getEnd()); + + // Cannot tokenize across files. + if (BeginLocInfo.first != EndLocInfo.first) + return; + + // Create a lexer + bool Invalid = false; + StringRef Buffer + = SourceMgr.getBufferData(BeginLocInfo.first, &Invalid); + if (Invalid) + return; + + Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first), + CXXUnit->getASTContext().getLangOpts(), + Buffer.begin(), Buffer.data() + BeginLocInfo.second, Buffer.end()); + Lex.SetCommentRetentionState(true); + + // Lex tokens until we hit the end of the range. + const char *EffectiveBufferEnd = Buffer.data() + EndLocInfo.second; + Token Tok; + bool previousWasAt = false; + do { + // Lex the next token + Lex.LexFromRawLexer(Tok); + if (Tok.is(tok::eof)) + break; + + // Initialize the CXToken. + CXToken CXTok; + + // - Common fields + CXTok.int_data[1] = Tok.getLocation().getRawEncoding(); + CXTok.int_data[2] = Tok.getLength(); + CXTok.int_data[3] = 0; + + // - Kind-specific fields + if (Tok.isLiteral()) { + CXTok.int_data[0] = CXToken_Literal; + CXTok.ptr_data = (void *)Tok.getLiteralData(); + } else if (Tok.is(tok::raw_identifier)) { + // Lookup the identifier to determine whether we have a keyword. + IdentifierInfo *II + = CXXUnit->getPreprocessor().LookUpIdentifierInfo(Tok); + + if ((II->getObjCKeywordID() != tok::objc_not_keyword) && previousWasAt) { + CXTok.int_data[0] = CXToken_Keyword; + } + else { + CXTok.int_data[0] = Tok.is(tok::identifier) + ? CXToken_Identifier + : CXToken_Keyword; + } + CXTok.ptr_data = II; + } else if (Tok.is(tok::comment)) { + CXTok.int_data[0] = CXToken_Comment; + CXTok.ptr_data = 0; + } else { + CXTok.int_data[0] = CXToken_Punctuation; + CXTok.ptr_data = 0; + } + CXTokens.push_back(CXTok); + previousWasAt = Tok.is(tok::at); + } while (Lex.getBufferLocation() <= EffectiveBufferEnd); +} + +void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range, + CXToken **Tokens, unsigned *NumTokens) { + if (Tokens) + *Tokens = 0; + if (NumTokens) + *NumTokens = 0; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + if (!CXXUnit || !Tokens || !NumTokens) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SourceRange R = cxloc::translateCXSourceRange(Range); + if (R.isInvalid()) + return; + + SmallVector<CXToken, 32> CXTokens; + getTokens(CXXUnit, R, CXTokens); + + if (CXTokens.empty()) + return; + + *Tokens = (CXToken *)malloc(sizeof(CXToken) * CXTokens.size()); + memmove(*Tokens, CXTokens.data(), sizeof(CXToken) * CXTokens.size()); + *NumTokens = CXTokens.size(); +} + +void clang_disposeTokens(CXTranslationUnit TU, + CXToken *Tokens, unsigned NumTokens) { + free(Tokens); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Token annotation APIs. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap<unsigned, CXCursor> AnnotateTokensData; +static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data); +namespace { +class AnnotateTokensWorker { + AnnotateTokensData &Annotated; + CXToken *Tokens; + CXCursor *Cursors; + unsigned NumTokens; + unsigned TokIdx; + unsigned PreprocessingTokIdx; + CursorVisitor AnnotateVis; + SourceManager &SrcMgr; + bool HasContextSensitiveKeywords; + + bool MoreTokens() const { return TokIdx < NumTokens; } + unsigned NextToken() const { return TokIdx; } + void AdvanceToken() { ++TokIdx; } + SourceLocation GetTokenLoc(unsigned tokI) { + return SourceLocation::getFromRawEncoding(Tokens[tokI].int_data[1]); + } + bool isFunctionMacroToken(unsigned tokI) const { + return Tokens[tokI].int_data[3] != 0; + } + SourceLocation getFunctionMacroTokenLoc(unsigned tokI) const { + return SourceLocation::getFromRawEncoding(Tokens[tokI].int_data[3]); + } + + void annotateAndAdvanceTokens(CXCursor, RangeComparisonResult, SourceRange); + void annotateAndAdvanceFunctionMacroTokens(CXCursor, RangeComparisonResult, + SourceRange); + +public: + AnnotateTokensWorker(AnnotateTokensData &annotated, + CXToken *tokens, CXCursor *cursors, unsigned numTokens, + CXTranslationUnit tu, SourceRange RegionOfInterest) + : Annotated(annotated), Tokens(tokens), Cursors(cursors), + NumTokens(numTokens), TokIdx(0), PreprocessingTokIdx(0), + AnnotateVis(tu, + AnnotateTokensVisitor, this, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + RegionOfInterest), + SrcMgr(static_cast<ASTUnit*>(tu->TUData)->getSourceManager()), + HasContextSensitiveKeywords(false) { } + + void VisitChildren(CXCursor C) { AnnotateVis.VisitChildren(C); } + enum CXChildVisitResult Visit(CXCursor cursor, CXCursor parent); + void AnnotateTokens(); + + /// \brief Determine whether the annotator saw any cursors that have + /// context-sensitive keywords. + bool hasContextSensitiveKeywords() const { + return HasContextSensitiveKeywords; + } +}; +} + +void AnnotateTokensWorker::AnnotateTokens() { + // Walk the AST within the region of interest, annotating tokens + // along the way. + AnnotateVis.visitFileRegion(); + + for (unsigned I = 0 ; I < TokIdx ; ++I) { + AnnotateTokensData::iterator Pos = Annotated.find(Tokens[I].int_data[1]); + if (Pos != Annotated.end() && + (clang_isInvalid(Cursors[I].kind) || + Pos->second.kind != CXCursor_PreprocessingDirective)) + Cursors[I] = Pos->second; + } + + // Finish up annotating any tokens left. + if (!MoreTokens()) + return; + + const CXCursor &C = clang_getNullCursor(); + for (unsigned I = TokIdx ; I < NumTokens ; ++I) { + if (I < PreprocessingTokIdx && clang_isPreprocessing(Cursors[I].kind)) + continue; + + AnnotateTokensData::iterator Pos = Annotated.find(Tokens[I].int_data[1]); + Cursors[I] = (Pos == Annotated.end()) ? C : Pos->second; + } +} + +/// \brief It annotates and advances tokens with a cursor until the comparison +//// between the cursor location and the source range is the same as +/// \arg compResult. +/// +/// Pass RangeBefore to annotate tokens with a cursor until a range is reached. +/// Pass RangeOverlap to annotate tokens inside a range. +void AnnotateTokensWorker::annotateAndAdvanceTokens(CXCursor updateC, + RangeComparisonResult compResult, + SourceRange range) { + while (MoreTokens()) { + const unsigned I = NextToken(); + if (isFunctionMacroToken(I)) + return annotateAndAdvanceFunctionMacroTokens(updateC, compResult, range); + + SourceLocation TokLoc = GetTokenLoc(I); + if (LocationCompare(SrcMgr, TokLoc, range) == compResult) { + Cursors[I] = updateC; + AdvanceToken(); + continue; + } + break; + } +} + +/// \brief Special annotation handling for macro argument tokens. +void AnnotateTokensWorker::annotateAndAdvanceFunctionMacroTokens( + CXCursor updateC, + RangeComparisonResult compResult, + SourceRange range) { + assert(MoreTokens()); + assert(isFunctionMacroToken(NextToken()) && + "Should be called only for macro arg tokens"); + + // This works differently than annotateAndAdvanceTokens; because expanded + // macro arguments can have arbitrary translation-unit source order, we do not + // advance the token index one by one until a token fails the range test. + // We only advance once past all of the macro arg tokens if all of them + // pass the range test. If one of them fails we keep the token index pointing + // at the start of the macro arg tokens so that the failing token will be + // annotated by a subsequent annotation try. + + bool atLeastOneCompFail = false; + + unsigned I = NextToken(); + for (; I < NumTokens && isFunctionMacroToken(I); ++I) { + SourceLocation TokLoc = getFunctionMacroTokenLoc(I); + if (TokLoc.isFileID()) + continue; // not macro arg token, it's parens or comma. + if (LocationCompare(SrcMgr, TokLoc, range) == compResult) { + if (clang_isInvalid(clang_getCursorKind(Cursors[I]))) + Cursors[I] = updateC; + } else + atLeastOneCompFail = true; + } + + if (!atLeastOneCompFail) + TokIdx = I; // All of the tokens were handled, advance beyond all of them. +} + +enum CXChildVisitResult +AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) { + CXSourceLocation Loc = clang_getCursorLocation(cursor); + SourceRange cursorRange = getRawCursorExtent(cursor); + if (cursorRange.isInvalid()) + return CXChildVisit_Recurse; + + if (!HasContextSensitiveKeywords) { + // Objective-C properties can have context-sensitive keywords. + if (cursor.kind == CXCursor_ObjCPropertyDecl) { + if (ObjCPropertyDecl *Property + = dyn_cast_or_null<ObjCPropertyDecl>(getCursorDecl(cursor))) + HasContextSensitiveKeywords = Property->getPropertyAttributesAsWritten() != 0; + } + // Objective-C methods can have context-sensitive keywords. + else if (cursor.kind == CXCursor_ObjCInstanceMethodDecl || + cursor.kind == CXCursor_ObjCClassMethodDecl) { + if (ObjCMethodDecl *Method + = dyn_cast_or_null<ObjCMethodDecl>(getCursorDecl(cursor))) { + if (Method->getObjCDeclQualifier()) + HasContextSensitiveKeywords = true; + else { + for (ObjCMethodDecl::param_iterator P = Method->param_begin(), + PEnd = Method->param_end(); + P != PEnd; ++P) { + if ((*P)->getObjCDeclQualifier()) { + HasContextSensitiveKeywords = true; + break; + } + } + } + } + } + // C++ methods can have context-sensitive keywords. + else if (cursor.kind == CXCursor_CXXMethod) { + if (CXXMethodDecl *Method + = dyn_cast_or_null<CXXMethodDecl>(getCursorDecl(cursor))) { + if (Method->hasAttr<FinalAttr>() || Method->hasAttr<OverrideAttr>()) + HasContextSensitiveKeywords = true; + } + } + // C++ classes can have context-sensitive keywords. + else if (cursor.kind == CXCursor_StructDecl || + cursor.kind == CXCursor_ClassDecl || + cursor.kind == CXCursor_ClassTemplate || + cursor.kind == CXCursor_ClassTemplatePartialSpecialization) { + if (Decl *D = getCursorDecl(cursor)) + if (D->hasAttr<FinalAttr>()) + HasContextSensitiveKeywords = true; + } + } + + if (clang_isPreprocessing(cursor.kind)) { + // For macro expansions, just note where the beginning of the macro + // expansion occurs. + if (cursor.kind == CXCursor_MacroExpansion) { + Annotated[Loc.int_data] = cursor; + return CXChildVisit_Recurse; + } + + // Items in the preprocessing record are kept separate from items in + // declarations, so we keep a separate token index. + unsigned SavedTokIdx = TokIdx; + TokIdx = PreprocessingTokIdx; + + // Skip tokens up until we catch up to the beginning of the preprocessing + // entry. + while (MoreTokens()) { + const unsigned I = NextToken(); + SourceLocation TokLoc = GetTokenLoc(I); + switch (LocationCompare(SrcMgr, TokLoc, cursorRange)) { + case RangeBefore: + AdvanceToken(); + continue; + case RangeAfter: + case RangeOverlap: + break; + } + break; + } + + // Look at all of the tokens within this range. + while (MoreTokens()) { + const unsigned I = NextToken(); + SourceLocation TokLoc = GetTokenLoc(I); + switch (LocationCompare(SrcMgr, TokLoc, cursorRange)) { + case RangeBefore: + llvm_unreachable("Infeasible"); + case RangeAfter: + break; + case RangeOverlap: + Cursors[I] = cursor; + AdvanceToken(); + continue; + } + break; + } + + // Save the preprocessing token index; restore the non-preprocessing + // token index. + PreprocessingTokIdx = TokIdx; + TokIdx = SavedTokIdx; + return CXChildVisit_Recurse; + } + + if (cursorRange.isInvalid()) + return CXChildVisit_Continue; + + SourceLocation L = SourceLocation::getFromRawEncoding(Loc.int_data); + + // Adjust the annotated range based specific declarations. + const enum CXCursorKind cursorK = clang_getCursorKind(cursor); + if (cursorK >= CXCursor_FirstDecl && cursorK <= CXCursor_LastDecl) { + Decl *D = cxcursor::getCursorDecl(cursor); + + SourceLocation StartLoc; + if (const DeclaratorDecl *DD = dyn_cast_or_null<DeclaratorDecl>(D)) { + if (TypeSourceInfo *TI = DD->getTypeSourceInfo()) + StartLoc = TI->getTypeLoc().getLocStart(); + } else if (TypedefDecl *Typedef = dyn_cast_or_null<TypedefDecl>(D)) { + if (TypeSourceInfo *TI = Typedef->getTypeSourceInfo()) + StartLoc = TI->getTypeLoc().getLocStart(); + } + + if (StartLoc.isValid() && L.isValid() && + SrcMgr.isBeforeInTranslationUnit(StartLoc, L)) + cursorRange.setBegin(StartLoc); + } + + // If the location of the cursor occurs within a macro instantiation, record + // the spelling location of the cursor in our annotation map. We can then + // paper over the token labelings during a post-processing step to try and + // get cursor mappings for tokens that are the *arguments* of a macro + // instantiation. + if (L.isMacroID()) { + unsigned rawEncoding = SrcMgr.getSpellingLoc(L).getRawEncoding(); + // Only invalidate the old annotation if it isn't part of a preprocessing + // directive. Here we assume that the default construction of CXCursor + // results in CXCursor.kind being an initialized value (i.e., 0). If + // this isn't the case, we can fix by doing lookup + insertion. + + CXCursor &oldC = Annotated[rawEncoding]; + if (!clang_isPreprocessing(oldC.kind)) + oldC = cursor; + } + + const enum CXCursorKind K = clang_getCursorKind(parent); + const CXCursor updateC = + (clang_isInvalid(K) || K == CXCursor_TranslationUnit) + ? clang_getNullCursor() : parent; + + annotateAndAdvanceTokens(updateC, RangeBefore, cursorRange); + + // Avoid having the cursor of an expression "overwrite" the annotation of the + // variable declaration that it belongs to. + // This can happen for C++ constructor expressions whose range generally + // include the variable declaration, e.g.: + // MyCXXClass foo; // Make sure we don't annotate 'foo' as a CallExpr cursor. + if (clang_isExpression(cursorK)) { + Expr *E = getCursorExpr(cursor); + if (Decl *D = getCursorParentDecl(cursor)) { + const unsigned I = NextToken(); + if (E->getLocStart().isValid() && D->getLocation().isValid() && + E->getLocStart() == D->getLocation() && + E->getLocStart() == GetTokenLoc(I)) { + Cursors[I] = updateC; + AdvanceToken(); + } + } + } + + // Visit children to get their cursor information. + const unsigned BeforeChildren = NextToken(); + VisitChildren(cursor); + const unsigned AfterChildren = NextToken(); + + // Scan the tokens that are at the end of the cursor, but are not captured + // but the child cursors. + annotateAndAdvanceTokens(cursor, RangeOverlap, cursorRange); + + // Scan the tokens that are at the beginning of the cursor, but are not + // capture by the child cursors. + for (unsigned I = BeforeChildren; I != AfterChildren; ++I) { + if (!clang_isInvalid(clang_getCursorKind(Cursors[I]))) + break; + + Cursors[I] = cursor; + } + + return CXChildVisit_Continue; +} + +static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + return static_cast<AnnotateTokensWorker*>(client_data)->Visit(cursor, parent); +} + +namespace { + +/// \brief Uses the macro expansions in the preprocessing record to find +/// and mark tokens that are macro arguments. This info is used by the +/// AnnotateTokensWorker. +class MarkMacroArgTokensVisitor { + SourceManager &SM; + CXToken *Tokens; + unsigned NumTokens; + unsigned CurIdx; + +public: + MarkMacroArgTokensVisitor(SourceManager &SM, + CXToken *tokens, unsigned numTokens) + : SM(SM), Tokens(tokens), NumTokens(numTokens), CurIdx(0) { } + + CXChildVisitResult visit(CXCursor cursor, CXCursor parent) { + if (cursor.kind != CXCursor_MacroExpansion) + return CXChildVisit_Continue; + + SourceRange macroRange = getCursorMacroExpansion(cursor)->getSourceRange(); + if (macroRange.getBegin() == macroRange.getEnd()) + return CXChildVisit_Continue; // it's not a function macro. + + for (; CurIdx < NumTokens; ++CurIdx) { + if (!SM.isBeforeInTranslationUnit(getTokenLoc(CurIdx), + macroRange.getBegin())) + break; + } + + if (CurIdx == NumTokens) + return CXChildVisit_Break; + + for (; CurIdx < NumTokens; ++CurIdx) { + SourceLocation tokLoc = getTokenLoc(CurIdx); + if (!SM.isBeforeInTranslationUnit(tokLoc, macroRange.getEnd())) + break; + + setFunctionMacroTokenLoc(CurIdx, SM.getMacroArgExpandedLocation(tokLoc)); + } + + if (CurIdx == NumTokens) + return CXChildVisit_Break; + + return CXChildVisit_Continue; + } + +private: + SourceLocation getTokenLoc(unsigned tokI) { + return SourceLocation::getFromRawEncoding(Tokens[tokI].int_data[1]); + } + + void setFunctionMacroTokenLoc(unsigned tokI, SourceLocation loc) { + // The third field is reserved and currently not used. Use it here + // to mark macro arg expanded tokens with their expanded locations. + Tokens[tokI].int_data[3] = loc.getRawEncoding(); + } +}; + +} // end anonymous namespace + +static CXChildVisitResult +MarkMacroArgTokensVisitorDelegate(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + return static_cast<MarkMacroArgTokensVisitor*>(client_data)->visit(cursor, + parent); +} + +namespace { + struct clang_annotateTokens_Data { + CXTranslationUnit TU; + ASTUnit *CXXUnit; + CXToken *Tokens; + unsigned NumTokens; + CXCursor *Cursors; + }; +} + +static void annotatePreprocessorTokens(CXTranslationUnit TU, + SourceRange RegionOfInterest, + AnnotateTokensData &Annotated) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + + SourceManager &SourceMgr = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> BeginLocInfo + = SourceMgr.getDecomposedLoc(RegionOfInterest.getBegin()); + std::pair<FileID, unsigned> EndLocInfo + = SourceMgr.getDecomposedLoc(RegionOfInterest.getEnd()); + + if (BeginLocInfo.first != EndLocInfo.first) + return; + + StringRef Buffer; + bool Invalid = false; + Buffer = SourceMgr.getBufferData(BeginLocInfo.first, &Invalid); + if (Buffer.empty() || Invalid) + return; + + Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first), + CXXUnit->getASTContext().getLangOpts(), + Buffer.begin(), Buffer.data() + BeginLocInfo.second, + Buffer.end()); + Lex.SetCommentRetentionState(true); + + // Lex tokens in raw mode until we hit the end of the range, to avoid + // entering #includes or expanding macros. + while (true) { + Token Tok; + Lex.LexFromRawLexer(Tok); + + reprocess: + if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) { + // We have found a preprocessing directive. Gobble it up so that we + // don't see it while preprocessing these tokens later, but keep track + // of all of the token locations inside this preprocessing directive so + // that we can annotate them appropriately. + // + // FIXME: Some simple tests here could identify macro definitions and + // #undefs, to provide specific cursor kinds for those. + SmallVector<SourceLocation, 32> Locations; + do { + Locations.push_back(Tok.getLocation()); + Lex.LexFromRawLexer(Tok); + } while (!Tok.isAtStartOfLine() && !Tok.is(tok::eof)); + + using namespace cxcursor; + CXCursor Cursor + = MakePreprocessingDirectiveCursor(SourceRange(Locations.front(), + Locations.back()), + TU); + for (unsigned I = 0, N = Locations.size(); I != N; ++I) { + Annotated[Locations[I].getRawEncoding()] = Cursor; + } + + if (Tok.isAtStartOfLine()) + goto reprocess; + + continue; + } + + if (Tok.is(tok::eof)) + break; + } +} + +// This gets run a separate thread to avoid stack blowout. +static void clang_annotateTokensImpl(void *UserData) { + CXTranslationUnit TU = ((clang_annotateTokens_Data*)UserData)->TU; + ASTUnit *CXXUnit = ((clang_annotateTokens_Data*)UserData)->CXXUnit; + CXToken *Tokens = ((clang_annotateTokens_Data*)UserData)->Tokens; + const unsigned NumTokens = ((clang_annotateTokens_Data*)UserData)->NumTokens; + CXCursor *Cursors = ((clang_annotateTokens_Data*)UserData)->Cursors; + + CIndexer *CXXIdx = (CIndexer*)TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) + setThreadBackgroundPriority(); + + // Determine the region of interest, which contains all of the tokens. + SourceRange RegionOfInterest; + RegionOfInterest.setBegin( + cxloc::translateSourceLocation(clang_getTokenLocation(TU, Tokens[0]))); + RegionOfInterest.setEnd( + cxloc::translateSourceLocation(clang_getTokenLocation(TU, + Tokens[NumTokens-1]))); + + // A mapping from the source locations found when re-lexing or traversing the + // region of interest to the corresponding cursors. + AnnotateTokensData Annotated; + + // Relex the tokens within the source range to look for preprocessing + // directives. + annotatePreprocessorTokens(TU, RegionOfInterest, Annotated); + + if (CXXUnit->getPreprocessor().getPreprocessingRecord()) { + // Search and mark tokens that are macro argument expansions. + MarkMacroArgTokensVisitor Visitor(CXXUnit->getSourceManager(), + Tokens, NumTokens); + CursorVisitor MacroArgMarker(TU, + MarkMacroArgTokensVisitorDelegate, &Visitor, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + RegionOfInterest); + MacroArgMarker.visitPreprocessedEntitiesInRegion(); + } + + // Annotate all of the source locations in the region of interest that map to + // a specific cursor. + AnnotateTokensWorker W(Annotated, Tokens, Cursors, NumTokens, + TU, RegionOfInterest); + + // FIXME: We use a ridiculous stack size here because the data-recursion + // algorithm uses a large stack frame than the non-data recursive version, + // and AnnotationTokensWorker currently transforms the data-recursion + // algorithm back into a traditional recursion by explicitly calling + // VisitChildren(). We will need to remove this explicit recursive call. + W.AnnotateTokens(); + + // If we ran into any entities that involve context-sensitive keywords, + // take another pass through the tokens to mark them as such. + if (W.hasContextSensitiveKeywords()) { + for (unsigned I = 0; I != NumTokens; ++I) { + if (clang_getTokenKind(Tokens[I]) != CXToken_Identifier) + continue; + + if (Cursors[I].kind == CXCursor_ObjCPropertyDecl) { + IdentifierInfo *II = static_cast<IdentifierInfo *>(Tokens[I].ptr_data); + if (ObjCPropertyDecl *Property + = dyn_cast_or_null<ObjCPropertyDecl>(getCursorDecl(Cursors[I]))) { + if (Property->getPropertyAttributesAsWritten() != 0 && + llvm::StringSwitch<bool>(II->getName()) + .Case("readonly", true) + .Case("assign", true) + .Case("unsafe_unretained", true) + .Case("readwrite", true) + .Case("retain", true) + .Case("copy", true) + .Case("nonatomic", true) + .Case("atomic", true) + .Case("getter", true) + .Case("setter", true) + .Case("strong", true) + .Case("weak", true) + .Default(false)) + Tokens[I].int_data[0] = CXToken_Keyword; + } + continue; + } + + if (Cursors[I].kind == CXCursor_ObjCInstanceMethodDecl || + Cursors[I].kind == CXCursor_ObjCClassMethodDecl) { + IdentifierInfo *II = static_cast<IdentifierInfo *>(Tokens[I].ptr_data); + if (llvm::StringSwitch<bool>(II->getName()) + .Case("in", true) + .Case("out", true) + .Case("inout", true) + .Case("oneway", true) + .Case("bycopy", true) + .Case("byref", true) + .Default(false)) + Tokens[I].int_data[0] = CXToken_Keyword; + continue; + } + + if (Cursors[I].kind == CXCursor_CXXFinalAttr || + Cursors[I].kind == CXCursor_CXXOverrideAttr) { + Tokens[I].int_data[0] = CXToken_Keyword; + continue; + } + } + } +} + +extern "C" { + +void clang_annotateTokens(CXTranslationUnit TU, + CXToken *Tokens, unsigned NumTokens, + CXCursor *Cursors) { + + if (NumTokens == 0 || !Tokens || !Cursors) + return; + + // Any token we don't specifically annotate will have a NULL cursor. + CXCursor C = clang_getNullCursor(); + for (unsigned I = 0; I != NumTokens; ++I) + Cursors[I] = C; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + if (!CXXUnit) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + clang_annotateTokens_Data data = { TU, CXXUnit, Tokens, NumTokens, Cursors }; + llvm::CrashRecoveryContext CRC; + if (!RunSafely(CRC, clang_annotateTokensImpl, &data, + GetSafetyThreadStackSize() * 2)) { + fprintf(stderr, "libclang: crash detected while annotating tokens\n"); + } +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Operations for querying linkage of a cursor. +//===----------------------------------------------------------------------===// + +extern "C" { +CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { + if (!clang_isDeclaration(cursor.kind)) + return CXLinkage_Invalid; + + Decl *D = cxcursor::getCursorDecl(cursor); + if (NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) + switch (ND->getLinkage()) { + case NoLinkage: return CXLinkage_NoLinkage; + case InternalLinkage: return CXLinkage_Internal; + case UniqueExternalLinkage: return CXLinkage_UniqueExternal; + case ExternalLinkage: return CXLinkage_External; + }; + + return CXLinkage_Invalid; +} +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Operations for querying language of a cursor. +//===----------------------------------------------------------------------===// + +static CXLanguageKind getDeclLanguage(const Decl *D) { + if (!D) + return CXLanguage_C; + + switch (D->getKind()) { + default: + break; + case Decl::ImplicitParam: + case Decl::ObjCAtDefsField: + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCCompatibleAlias: + case Decl::ObjCImplementation: + case Decl::ObjCInterface: + case Decl::ObjCIvar: + case Decl::ObjCMethod: + case Decl::ObjCProperty: + case Decl::ObjCPropertyImpl: + case Decl::ObjCProtocol: + return CXLanguage_ObjC; + case Decl::CXXConstructor: + case Decl::CXXConversion: + case Decl::CXXDestructor: + case Decl::CXXMethod: + case Decl::CXXRecord: + case Decl::ClassTemplate: + case Decl::ClassTemplatePartialSpecialization: + case Decl::ClassTemplateSpecialization: + case Decl::Friend: + case Decl::FriendTemplate: + case Decl::FunctionTemplate: + case Decl::LinkageSpec: + case Decl::Namespace: + case Decl::NamespaceAlias: + case Decl::NonTypeTemplateParm: + case Decl::StaticAssert: + case Decl::TemplateTemplateParm: + case Decl::TemplateTypeParm: + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + case Decl::Using: + case Decl::UsingDirective: + case Decl::UsingShadow: + return CXLanguage_CPlusPlus; + } + + return CXLanguage_C; +} + +extern "C" { + +enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + if (Decl *D = cxcursor::getCursorDecl(cursor)) { + if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDeleted()) + return CXAvailability_Available; + + switch (D->getAvailability()) { + case AR_Available: + case AR_NotYetIntroduced: + return CXAvailability_Available; + + case AR_Deprecated: + return CXAvailability_Deprecated; + + case AR_Unavailable: + return CXAvailability_NotAvailable; + } + } + + return CXAvailability_Available; +} + +CXLanguageKind clang_getCursorLanguage(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + return getDeclLanguage(cxcursor::getCursorDecl(cursor)); + + return CXLanguage_Invalid; +} + + /// \brief If the given cursor is the "templated" declaration + /// descibing a class or function template, return the class or + /// function template. +static Decl *maybeGetTemplateCursor(Decl *D) { + if (!D) + return 0; + + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FunctionTemplateDecl *FunTmpl = FD->getDescribedFunctionTemplate()) + return FunTmpl; + + if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) + if (ClassTemplateDecl *ClassTmpl = RD->getDescribedClassTemplate()) + return ClassTmpl; + + return D; +} + +CXCursor clang_getCursorSemanticParent(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) { + if (Decl *D = getCursorDecl(cursor)) { + DeclContext *DC = D->getDeclContext(); + if (!DC) + return clang_getNullCursor(); + + return MakeCXCursor(maybeGetTemplateCursor(cast<Decl>(DC)), + getCursorTU(cursor)); + } + } + + if (clang_isStatement(cursor.kind) || clang_isExpression(cursor.kind)) { + if (Decl *D = getCursorDecl(cursor)) + return MakeCXCursor(D, getCursorTU(cursor)); + } + + return clang_getNullCursor(); +} + +CXCursor clang_getCursorLexicalParent(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) { + if (Decl *D = getCursorDecl(cursor)) { + DeclContext *DC = D->getLexicalDeclContext(); + if (!DC) + return clang_getNullCursor(); + + return MakeCXCursor(maybeGetTemplateCursor(cast<Decl>(DC)), + getCursorTU(cursor)); + } + } + + // FIXME: Note that we can't easily compute the lexical context of a + // statement or expression, so we return nothing. + return clang_getNullCursor(); +} + +void clang_getOverriddenCursors(CXCursor cursor, + CXCursor **overridden, + unsigned *num_overridden) { + if (overridden) + *overridden = 0; + if (num_overridden) + *num_overridden = 0; + if (!overridden || !num_overridden) + return; + if (!clang_isDeclaration(cursor.kind)) + return; + + SmallVector<CXCursor, 8> Overridden; + cxcursor::getOverriddenCursors(cursor, Overridden); + + // Don't allocate memory if we have no overriden cursors. + if (Overridden.size() == 0) + return; + + *num_overridden = Overridden.size(); + *overridden = new CXCursor [Overridden.size()]; + std::copy(Overridden.begin(), Overridden.end(), *overridden); +} + +void clang_disposeOverriddenCursors(CXCursor *overridden) { + delete [] overridden; +} + +CXFile clang_getIncludedFile(CXCursor cursor) { + if (cursor.kind != CXCursor_InclusionDirective) + return 0; + + InclusionDirective *ID = getCursorInclusionDirective(cursor); + return (void *)ID->getFile(); +} + +} // end: extern "C" + + +//===----------------------------------------------------------------------===// +// C++ AST instrospection. +//===----------------------------------------------------------------------===// + +extern "C" { +unsigned clang_CXXMethod_isStatic(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + CXXMethodDecl *Method = 0; + Decl *D = cxcursor::getCursorDecl(C); + if (FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(D)) + Method = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl()); + else + Method = dyn_cast_or_null<CXXMethodDecl>(D); + return (Method && Method->isStatic()) ? 1 : 0; +} + +unsigned clang_CXXMethod_isVirtual(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + CXXMethodDecl *Method = 0; + Decl *D = cxcursor::getCursorDecl(C); + if (FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(D)) + Method = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl()); + else + Method = dyn_cast_or_null<CXXMethodDecl>(D); + return (Method && Method->isVirtual()) ? 1 : 0; +} +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Attribute introspection. +//===----------------------------------------------------------------------===// + +extern "C" { +CXType clang_getIBOutletCollectionType(CXCursor C) { + if (C.kind != CXCursor_IBOutletCollectionAttr) + return cxtype::MakeCXType(QualType(), cxcursor::getCursorTU(C)); + + IBOutletCollectionAttr *A = + cast<IBOutletCollectionAttr>(cxcursor::getCursorAttr(C)); + + return cxtype::MakeCXType(A->getInterface(), cxcursor::getCursorTU(C)); +} +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Inspecting memory usage. +//===----------------------------------------------------------------------===// + +typedef std::vector<CXTUResourceUsageEntry> MemUsageEntries; + +static inline void createCXTUResourceUsageEntry(MemUsageEntries &entries, + enum CXTUResourceUsageKind k, + unsigned long amount) { + CXTUResourceUsageEntry entry = { k, amount }; + entries.push_back(entry); +} + +extern "C" { + +const char *clang_getTUResourceUsageName(CXTUResourceUsageKind kind) { + const char *str = ""; + switch (kind) { + case CXTUResourceUsage_AST: + str = "ASTContext: expressions, declarations, and types"; + break; + case CXTUResourceUsage_Identifiers: + str = "ASTContext: identifiers"; + break; + case CXTUResourceUsage_Selectors: + str = "ASTContext: selectors"; + break; + case CXTUResourceUsage_GlobalCompletionResults: + str = "Code completion: cached global results"; + break; + case CXTUResourceUsage_SourceManagerContentCache: + str = "SourceManager: content cache allocator"; + break; + case CXTUResourceUsage_AST_SideTables: + str = "ASTContext: side tables"; + break; + case CXTUResourceUsage_SourceManager_Membuffer_Malloc: + str = "SourceManager: malloc'ed memory buffers"; + break; + case CXTUResourceUsage_SourceManager_Membuffer_MMap: + str = "SourceManager: mmap'ed memory buffers"; + break; + case CXTUResourceUsage_ExternalASTSource_Membuffer_Malloc: + str = "ExternalASTSource: malloc'ed memory buffers"; + break; + case CXTUResourceUsage_ExternalASTSource_Membuffer_MMap: + str = "ExternalASTSource: mmap'ed memory buffers"; + break; + case CXTUResourceUsage_Preprocessor: + str = "Preprocessor: malloc'ed memory"; + break; + case CXTUResourceUsage_PreprocessingRecord: + str = "Preprocessor: PreprocessingRecord"; + break; + case CXTUResourceUsage_SourceManager_DataStructures: + str = "SourceManager: data structures and tables"; + break; + case CXTUResourceUsage_Preprocessor_HeaderSearch: + str = "Preprocessor: header search tables"; + break; + } + return str; +} + +CXTUResourceUsage clang_getCXTUResourceUsage(CXTranslationUnit TU) { + if (!TU) { + CXTUResourceUsage usage = { (void*) 0, 0, 0 }; + return usage; + } + + ASTUnit *astUnit = static_cast<ASTUnit*>(TU->TUData); + OwningPtr<MemUsageEntries> entries(new MemUsageEntries()); + ASTContext &astContext = astUnit->getASTContext(); + + // How much memory is used by AST nodes and types? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_AST, + (unsigned long) astContext.getASTAllocatedMemory()); + + // How much memory is used by identifiers? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_Identifiers, + (unsigned long) astContext.Idents.getAllocator().getTotalMemory()); + + // How much memory is used for selectors? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_Selectors, + (unsigned long) astContext.Selectors.getTotalMemory()); + + // How much memory is used by ASTContext's side tables? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_AST_SideTables, + (unsigned long) astContext.getSideTableAllocatedMemory()); + + // How much memory is used for caching global code completion results? + unsigned long completionBytes = 0; + if (GlobalCodeCompletionAllocator *completionAllocator = + astUnit->getCachedCompletionAllocator().getPtr()) { + completionBytes = completionAllocator->getTotalMemory(); + } + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_GlobalCompletionResults, + completionBytes); + + // How much memory is being used by SourceManager's content cache? + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManagerContentCache, + (unsigned long) astContext.getSourceManager().getContentCacheSize()); + + // How much memory is being used by the MemoryBuffer's in SourceManager? + const SourceManager::MemoryBufferSizes &srcBufs = + astUnit->getSourceManager().getMemoryBufferSizes(); + + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManager_Membuffer_Malloc, + (unsigned long) srcBufs.malloc_bytes); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManager_Membuffer_MMap, + (unsigned long) srcBufs.mmap_bytes); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManager_DataStructures, + (unsigned long) astContext.getSourceManager() + .getDataStructureSizes()); + + // How much memory is being used by the ExternalASTSource? + if (ExternalASTSource *esrc = astContext.getExternalSource()) { + const ExternalASTSource::MemoryBufferSizes &sizes = + esrc->getMemoryBufferSizes(); + + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_ExternalASTSource_Membuffer_Malloc, + (unsigned long) sizes.malloc_bytes); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_ExternalASTSource_Membuffer_MMap, + (unsigned long) sizes.mmap_bytes); + } + + // How much memory is being used by the Preprocessor? + Preprocessor &pp = astUnit->getPreprocessor(); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_Preprocessor, + pp.getTotalMemory()); + + if (PreprocessingRecord *pRec = pp.getPreprocessingRecord()) { + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_PreprocessingRecord, + pRec->getTotalMemory()); + } + + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_Preprocessor_HeaderSearch, + pp.getHeaderSearchInfo().getTotalMemory()); + + CXTUResourceUsage usage = { (void*) entries.get(), + (unsigned) entries->size(), + entries->size() ? &(*entries)[0] : 0 }; + entries.take(); + return usage; +} + +void clang_disposeCXTUResourceUsage(CXTUResourceUsage usage) { + if (usage.data) + delete (MemUsageEntries*) usage.data; +} + +} // end extern "C" + +void clang::PrintLibclangResourceUsage(CXTranslationUnit TU) { + CXTUResourceUsage Usage = clang_getCXTUResourceUsage(TU); + for (unsigned I = 0; I != Usage.numEntries; ++I) + fprintf(stderr, " %s: %lu\n", + clang_getTUResourceUsageName(Usage.entries[I].kind), + Usage.entries[I].amount); + + clang_disposeCXTUResourceUsage(Usage); +} + +//===----------------------------------------------------------------------===// +// Misc. utility functions. +//===----------------------------------------------------------------------===// + +/// Default to using an 8 MB stack size on "safety" threads. +static unsigned SafetyStackThreadSize = 8 << 20; + +namespace clang { + +bool RunSafely(llvm::CrashRecoveryContext &CRC, + void (*Fn)(void*), void *UserData, + unsigned Size) { + if (!Size) + Size = GetSafetyThreadStackSize(); + if (Size) + return CRC.RunSafelyOnThread(Fn, UserData, Size); + return CRC.RunSafely(Fn, UserData); +} + +unsigned GetSafetyThreadStackSize() { + return SafetyStackThreadSize; +} + +void SetSafetyThreadStackSize(unsigned Value) { + SafetyStackThreadSize = Value; +} + +} + +void clang::setThreadBackgroundPriority() { + // FIXME: Move to llvm/Support and make it cross-platform. +#ifdef __APPLE__ + setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); +#endif +} + +void cxindex::printDiagsToStderr(ASTUnit *Unit) { + if (!Unit) + return; + + for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(), + DEnd = Unit->stored_diag_end(); + D != DEnd; ++D) { + CXStoredDiagnostic Diag(*D, Unit->getASTContext().getLangOpts()); + CXString Msg = clang_formatDiagnostic(&Diag, + clang_defaultDiagnosticDisplayOptions()); + fprintf(stderr, "%s\n", clang_getCString(Msg)); + clang_disposeString(Msg); + } +#ifdef LLVM_ON_WIN32 + // On Windows, force a flush, since there may be multiple copies of + // stderr and stdout in the file system, all with different buffers + // but writing to the same device. + fflush(stderr); +#endif +} + +extern "C" { + +CXString clang_getClangVersion() { + return createCXString(getClangFullVersion()); +} + +} // end: extern "C" + diff --git a/clang/tools/libclang/CIndexCXX.cpp b/clang/tools/libclang/CIndexCXX.cpp new file mode 100644 index 0000000..240b0f6 --- /dev/null +++ b/clang/tools/libclang/CIndexCXX.cpp @@ -0,0 +1,127 @@ +//===- CIndexCXX.cpp - Clang-C Source Indexing Library --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the libclang support for C++ cursors. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "CXType.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" + +using namespace clang; +using namespace clang::cxcursor; + +extern "C" { + +unsigned clang_isVirtualBase(CXCursor C) { + if (C.kind != CXCursor_CXXBaseSpecifier) + return 0; + + CXXBaseSpecifier *B = getCursorCXXBaseSpecifier(C); + return B->isVirtual(); +} + +enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor C) { + AccessSpecifier spec = AS_none; + + if (C.kind == CXCursor_CXXAccessSpecifier) + spec = getCursorDecl(C)->getAccess(); + else if (C.kind == CXCursor_CXXBaseSpecifier) + spec = getCursorCXXBaseSpecifier(C)->getAccessSpecifier(); + else + return CX_CXXInvalidAccessSpecifier; + + switch (spec) { + case AS_public: return CX_CXXPublic; + case AS_protected: return CX_CXXProtected; + case AS_private: return CX_CXXPrivate; + case AS_none: return CX_CXXInvalidAccessSpecifier; + } + + llvm_unreachable("Invalid AccessSpecifier!"); +} + +enum CXCursorKind clang_getTemplateCursorKind(CXCursor C) { + using namespace clang::cxcursor; + + switch (C.kind) { + case CXCursor_ClassTemplate: + case CXCursor_FunctionTemplate: + if (TemplateDecl *Template + = dyn_cast_or_null<TemplateDecl>(getCursorDecl(C))) + return MakeCXCursor(Template->getTemplatedDecl(), + static_cast<CXTranslationUnit>(C.data[2])).kind; + break; + + case CXCursor_ClassTemplatePartialSpecialization: + if (ClassTemplateSpecializationDecl *PartialSpec + = dyn_cast_or_null<ClassTemplatePartialSpecializationDecl>( + getCursorDecl(C))) { + switch (PartialSpec->getTagKind()) { + case TTK_Class: return CXCursor_ClassDecl; + case TTK_Struct: return CXCursor_StructDecl; + case TTK_Union: return CXCursor_UnionDecl; + case TTK_Enum: return CXCursor_NoDeclFound; + } + } + break; + + default: + break; + } + + return CXCursor_NoDeclFound; +} + +CXCursor clang_getSpecializedCursorTemplate(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return clang_getNullCursor(); + + Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + + Decl *Template = 0; + if (CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(D)) { + if (ClassTemplatePartialSpecializationDecl *PartialSpec + = dyn_cast<ClassTemplatePartialSpecializationDecl>(CXXRecord)) + Template = PartialSpec->getSpecializedTemplate(); + else if (ClassTemplateSpecializationDecl *ClassSpec + = dyn_cast<ClassTemplateSpecializationDecl>(CXXRecord)) { + llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> Result + = ClassSpec->getSpecializedTemplateOrPartial(); + if (Result.is<ClassTemplateDecl *>()) + Template = Result.get<ClassTemplateDecl *>(); + else + Template = Result.get<ClassTemplatePartialSpecializationDecl *>(); + + } else + Template = CXXRecord->getInstantiatedFromMemberClass(); + } else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) { + Template = Function->getPrimaryTemplate(); + if (!Template) + Template = Function->getInstantiatedFromMemberFunction(); + } else if (VarDecl *Var = dyn_cast<VarDecl>(D)) { + if (Var->isStaticDataMember()) + Template = Var->getInstantiatedFromStaticDataMember(); + } else if (RedeclarableTemplateDecl *Tmpl + = dyn_cast<RedeclarableTemplateDecl>(D)) + Template = Tmpl->getInstantiatedFromMemberTemplate(); + + if (!Template) + return clang_getNullCursor(); + + return MakeCXCursor(Template, static_cast<CXTranslationUnit>(C.data[2])); +} + +} // end extern "C" diff --git a/clang/tools/libclang/CIndexCodeCompletion.cpp b/clang/tools/libclang/CIndexCodeCompletion.cpp new file mode 100644 index 0000000..303fb1f --- /dev/null +++ b/clang/tools/libclang/CIndexCodeCompletion.cpp @@ -0,0 +1,975 @@ +//===- CIndexCodeCompletion.cpp - Code Completion API hooks ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Clang-C Source Indexing library hooks for +// code completion. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXString.h" +#include "CXCursor.h" +#include "CXString.h" +#include "CIndexDiagnostic.h" +#include "clang/AST/Type.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Atomic.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Program.h" +#include <cstdlib> +#include <cstdio> + + +#ifdef UDP_CODE_COMPLETION_LOGGER +#include "clang/Basic/Version.h" +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +using namespace clang; +using namespace clang::cxstring; + +extern "C" { + +enum CXCompletionChunkKind +clang_getCompletionChunkKind(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return CXCompletionChunk_Text; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + return CXCompletionChunk_TypedText; + case CodeCompletionString::CK_Text: + return CXCompletionChunk_Text; + case CodeCompletionString::CK_Optional: + return CXCompletionChunk_Optional; + case CodeCompletionString::CK_Placeholder: + return CXCompletionChunk_Placeholder; + case CodeCompletionString::CK_Informative: + return CXCompletionChunk_Informative; + case CodeCompletionString::CK_ResultType: + return CXCompletionChunk_ResultType; + case CodeCompletionString::CK_CurrentParameter: + return CXCompletionChunk_CurrentParameter; + case CodeCompletionString::CK_LeftParen: + return CXCompletionChunk_LeftParen; + case CodeCompletionString::CK_RightParen: + return CXCompletionChunk_RightParen; + case CodeCompletionString::CK_LeftBracket: + return CXCompletionChunk_LeftBracket; + case CodeCompletionString::CK_RightBracket: + return CXCompletionChunk_RightBracket; + case CodeCompletionString::CK_LeftBrace: + return CXCompletionChunk_LeftBrace; + case CodeCompletionString::CK_RightBrace: + return CXCompletionChunk_RightBrace; + case CodeCompletionString::CK_LeftAngle: + return CXCompletionChunk_LeftAngle; + case CodeCompletionString::CK_RightAngle: + return CXCompletionChunk_RightAngle; + case CodeCompletionString::CK_Comma: + return CXCompletionChunk_Comma; + case CodeCompletionString::CK_Colon: + return CXCompletionChunk_Colon; + case CodeCompletionString::CK_SemiColon: + return CXCompletionChunk_SemiColon; + case CodeCompletionString::CK_Equal: + return CXCompletionChunk_Equal; + case CodeCompletionString::CK_HorizontalSpace: + return CXCompletionChunk_HorizontalSpace; + case CodeCompletionString::CK_VerticalSpace: + return CXCompletionChunk_VerticalSpace; + } + + llvm_unreachable("Invalid CompletionKind!"); +} + +CXString clang_getCompletionChunkText(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return createCXString((const char*)0); + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + case CodeCompletionString::CK_ResultType: + case CodeCompletionString::CK_Colon: + case CodeCompletionString::CK_SemiColon: + case CodeCompletionString::CK_Equal: + case CodeCompletionString::CK_HorizontalSpace: + case CodeCompletionString::CK_VerticalSpace: + return createCXString((*CCStr)[chunk_number].Text, false); + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return createCXString(""); + } + + llvm_unreachable("Invalid CodeCompletionString Kind!"); +} + + +CXCompletionString +clang_getCompletionChunkCompletionString(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return 0; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + case CodeCompletionString::CK_ResultType: + case CodeCompletionString::CK_Colon: + case CodeCompletionString::CK_SemiColon: + case CodeCompletionString::CK_Equal: + case CodeCompletionString::CK_HorizontalSpace: + case CodeCompletionString::CK_VerticalSpace: + return 0; + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return (*CCStr)[chunk_number].Optional; + } + + llvm_unreachable("Invalid CompletionKind!"); +} + +unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? CCStr->size() : 0; +} + +unsigned clang_getCompletionPriority(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? CCStr->getPriority() : unsigned(CCP_Unlikely); +} + +enum CXAvailabilityKind +clang_getCompletionAvailability(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? static_cast<CXAvailabilityKind>(CCStr->getAvailability()) + : CXAvailability_Available; +} + +unsigned clang_getCompletionNumAnnotations(CXCompletionString completion_string) +{ + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr ? CCStr->getAnnotationCount() : 0; +} + +CXString clang_getCompletionAnnotation(CXCompletionString completion_string, + unsigned annotation_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr ? createCXString(CCStr->getAnnotation(annotation_number)) + : createCXString((const char *) 0); +} + +CXString +clang_getCompletionParent(CXCompletionString completion_string, + CXCursorKind *kind) { + if (kind) + *kind = CXCursor_NotImplemented; + + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr) + return createCXString((const char *)0); + + if (kind) + *kind = CCStr->getParentContextKind(); + return createCXString(CCStr->getParentContextName(), /*DupString=*/false); +} + +/// \brief The CXCodeCompleteResults structure we allocate internally; +/// the client only sees the initial CXCodeCompleteResults structure. +struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { + AllocatedCXCodeCompleteResults(const FileSystemOptions& FileSystemOpts); + ~AllocatedCXCodeCompleteResults(); + + /// \brief Diagnostics produced while performing code completion. + SmallVector<StoredDiagnostic, 8> Diagnostics; + + /// \brief Diag object + IntrusiveRefCntPtr<DiagnosticsEngine> Diag; + + /// \brief Language options used to adjust source locations. + LangOptions LangOpts; + + FileSystemOptions FileSystemOpts; + + /// \brief File manager, used for diagnostics. + IntrusiveRefCntPtr<FileManager> FileMgr; + + /// \brief Source manager, used for diagnostics. + IntrusiveRefCntPtr<SourceManager> SourceMgr; + + /// \brief Temporary files that should be removed once we have finished + /// with the code-completion results. + std::vector<llvm::sys::Path> TemporaryFiles; + + /// \brief Temporary buffers that will be deleted once we have finished with + /// the code-completion results. + SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers; + + /// \brief Allocator used to store globally cached code-completion results. + IntrusiveRefCntPtr<clang::GlobalCodeCompletionAllocator> + CachedCompletionAllocator; + + /// \brief Allocator used to store code completion results. + IntrusiveRefCntPtr<clang::GlobalCodeCompletionAllocator> + CodeCompletionAllocator; + + /// \brief Context under which completion occurred. + enum clang::CodeCompletionContext::Kind ContextKind; + + /// \brief A bitfield representing the acceptable completions for the + /// current context. + unsigned long long Contexts; + + /// \brief The kind of the container for the current context for completions. + enum CXCursorKind ContainerKind; + /// \brief The USR of the container for the current context for completions. + CXString ContainerUSR; + /// \brief a boolean value indicating whether there is complete information + /// about the container + unsigned ContainerIsIncomplete; + + /// \brief A string containing the Objective-C selector entered thus far for a + /// message send. + std::string Selector; +}; + +/// \brief Tracks the number of code-completion result objects that are +/// currently active. +/// +/// Used for debugging purposes only. +static llvm::sys::cas_flag CodeCompletionResultObjects; + +AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults( + const FileSystemOptions& FileSystemOpts) + : CXCodeCompleteResults(), + Diag(new DiagnosticsEngine( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs))), + FileSystemOpts(FileSystemOpts), + FileMgr(new FileManager(FileSystemOpts)), + SourceMgr(new SourceManager(*Diag, *FileMgr)), + CodeCompletionAllocator(new clang::GlobalCodeCompletionAllocator), + Contexts(CXCompletionContext_Unknown), + ContainerKind(CXCursor_InvalidCode), + ContainerUSR(createCXString("")), + ContainerIsIncomplete(1) +{ + if (getenv("LIBCLANG_OBJTRACKING")) { + llvm::sys::AtomicIncrement(&CodeCompletionResultObjects); + fprintf(stderr, "+++ %d completion results\n", CodeCompletionResultObjects); + } +} + +AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { + delete [] Results; + + clang_disposeString(ContainerUSR); + + for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) + TemporaryFiles[I].eraseFromDisk(); + for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I) + delete TemporaryBuffers[I]; + + if (getenv("LIBCLANG_OBJTRACKING")) { + llvm::sys::AtomicDecrement(&CodeCompletionResultObjects); + fprintf(stderr, "--- %d completion results\n", CodeCompletionResultObjects); + } +} + +} // end extern "C" + +static unsigned long long getContextsForContextKind( + enum CodeCompletionContext::Kind kind, + Sema &S) { + unsigned long long contexts = 0; + switch (kind) { + case CodeCompletionContext::CCC_OtherWithMacros: { + //We can allow macros here, but we don't know what else is permissible + //So we'll say the only thing permissible are macros + contexts = CXCompletionContext_MacroName; + break; + } + case CodeCompletionContext::CCC_TopLevel: + case CodeCompletionContext::CCC_ObjCIvarList: + case CodeCompletionContext::CCC_ClassStructUnion: + case CodeCompletionContext::CCC_Type: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_Statement: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_AnyValue; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_Expression: { + contexts = CXCompletionContext_AnyValue; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_ObjCMessageReceiver: { + contexts = CXCompletionContext_ObjCObjectValue | + CXCompletionContext_ObjCSelectorValue | + CXCompletionContext_ObjCInterface; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_CXXClassTypeValue | + CXCompletionContext_AnyType | + CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_DotMemberAccess: { + contexts = CXCompletionContext_DotMemberAccess; + break; + } + case CodeCompletionContext::CCC_ArrowMemberAccess: { + contexts = CXCompletionContext_ArrowMemberAccess; + break; + } + case CodeCompletionContext::CCC_ObjCPropertyAccess: { + contexts = CXCompletionContext_ObjCPropertyAccess; + break; + } + case CodeCompletionContext::CCC_EnumTag: { + contexts = CXCompletionContext_EnumTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_UnionTag: { + contexts = CXCompletionContext_UnionTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_ClassOrStructTag: { + contexts = CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_ObjCProtocolName: { + contexts = CXCompletionContext_ObjCProtocol; + break; + } + case CodeCompletionContext::CCC_Namespace: { + contexts = CXCompletionContext_Namespace; + break; + } + case CodeCompletionContext::CCC_PotentiallyQualifiedName: { + contexts = CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_MacroNameUse: { + contexts = CXCompletionContext_MacroName; + break; + } + case CodeCompletionContext::CCC_NaturalLanguage: { + contexts = CXCompletionContext_NaturalLanguage; + break; + } + case CodeCompletionContext::CCC_SelectorName: { + contexts = CXCompletionContext_ObjCSelectorName; + break; + } + case CodeCompletionContext::CCC_ParenthesizedExpression: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_AnyValue; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_ObjCInstanceMessage: { + contexts = CXCompletionContext_ObjCInstanceMessage; + break; + } + case CodeCompletionContext::CCC_ObjCClassMessage: { + contexts = CXCompletionContext_ObjCClassMessage; + break; + } + case CodeCompletionContext::CCC_ObjCInterfaceName: { + contexts = CXCompletionContext_ObjCInterface; + break; + } + case CodeCompletionContext::CCC_ObjCCategoryName: { + contexts = CXCompletionContext_ObjCCategory; + break; + } + case CodeCompletionContext::CCC_Other: + case CodeCompletionContext::CCC_ObjCInterface: + case CodeCompletionContext::CCC_ObjCImplementation: + case CodeCompletionContext::CCC_Name: + case CodeCompletionContext::CCC_MacroName: + case CodeCompletionContext::CCC_PreprocessorExpression: + case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_TypeQualifiers: { + //Only Clang results should be accepted, so we'll set all of the other + //context bits to 0 (i.e. the empty set) + contexts = CXCompletionContext_Unexposed; + break; + } + case CodeCompletionContext::CCC_Recovery: { + //We don't know what the current context is, so we'll return unknown + //This is the equivalent of setting all of the other context bits + contexts = CXCompletionContext_Unknown; + break; + } + } + return contexts; +} + +namespace { + class CaptureCompletionResults : public CodeCompleteConsumer { + AllocatedCXCodeCompleteResults &AllocatedResults; + CodeCompletionTUInfo CCTUInfo; + SmallVector<CXCompletionResult, 16> StoredResults; + CXTranslationUnit *TU; + public: + CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results, + CXTranslationUnit *TranslationUnit) + : CodeCompleteConsumer(true, false, true, false), + AllocatedResults(Results), CCTUInfo(Results.CodeCompletionAllocator), + TU(TranslationUnit) { } + ~CaptureCompletionResults() { Finish(); } + + virtual void ProcessCodeCompleteResults(Sema &S, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) { + StoredResults.reserve(StoredResults.size() + NumResults); + for (unsigned I = 0; I != NumResults; ++I) { + CodeCompletionString *StoredCompletion + = Results[I].CreateCodeCompletionString(S, getAllocator(), + getCodeCompletionTUInfo()); + + CXCompletionResult R; + R.CursorKind = Results[I].CursorKind; + R.CompletionString = StoredCompletion; + StoredResults.push_back(R); + } + + enum CodeCompletionContext::Kind contextKind = Context.getKind(); + + AllocatedResults.ContextKind = contextKind; + AllocatedResults.Contexts = getContextsForContextKind(contextKind, S); + + AllocatedResults.Selector = ""; + if (Context.getNumSelIdents() > 0) { + for (unsigned i = 0; i < Context.getNumSelIdents(); i++) { + IdentifierInfo *selIdent = Context.getSelIdents()[i]; + if (selIdent != NULL) { + StringRef selectorString = Context.getSelIdents()[i]->getName(); + AllocatedResults.Selector += selectorString; + } + AllocatedResults.Selector += ":"; + } + } + + QualType baseType = Context.getBaseType(); + NamedDecl *D = NULL; + + if (!baseType.isNull()) { + // Get the declaration for a class/struct/union/enum type + if (const TagType *Tag = baseType->getAs<TagType>()) + D = Tag->getDecl(); + // Get the @interface declaration for a (possibly-qualified) Objective-C + // object pointer type, e.g., NSString* + else if (const ObjCObjectPointerType *ObjPtr = + baseType->getAs<ObjCObjectPointerType>()) + D = ObjPtr->getInterfaceDecl(); + // Get the @interface declaration for an Objective-C object type + else if (const ObjCObjectType *Obj = baseType->getAs<ObjCObjectType>()) + D = Obj->getInterface(); + // Get the class for a C++ injected-class-name + else if (const InjectedClassNameType *Injected = + baseType->getAs<InjectedClassNameType>()) + D = Injected->getDecl(); + } + + if (D != NULL) { + CXCursor cursor = cxcursor::MakeCXCursor(D, *TU); + + CXCursorKind cursorKind = clang_getCursorKind(cursor); + CXString cursorUSR = clang_getCursorUSR(cursor); + + // Normally, clients of CXString shouldn't care whether or not + // a CXString is managed by a pool or by explicitly malloc'ed memory. + // However, there are cases when AllocatedResults outlives the + // CXTranslationUnit. This is a workaround that failure mode. + if (cxstring::isManagedByPool(cursorUSR)) { + CXString heapStr = + cxstring::createCXString(clang_getCString(cursorUSR), true); + clang_disposeString(cursorUSR); + cursorUSR = heapStr; + } + + AllocatedResults.ContainerKind = cursorKind; + AllocatedResults.ContainerUSR = cursorUSR; + + const Type *type = baseType.getTypePtrOrNull(); + if (type != NULL) { + AllocatedResults.ContainerIsIncomplete = type->isIncompleteType(); + } + else { + AllocatedResults.ContainerIsIncomplete = 1; + } + } + else { + AllocatedResults.ContainerKind = CXCursor_InvalidCode; + AllocatedResults.ContainerUSR = createCXString(""); + AllocatedResults.ContainerIsIncomplete = 1; + } + } + + virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates) { + StoredResults.reserve(StoredResults.size() + NumCandidates); + for (unsigned I = 0; I != NumCandidates; ++I) { + CodeCompletionString *StoredCompletion + = Candidates[I].CreateSignatureString(CurrentArg, S, getAllocator(), + getCodeCompletionTUInfo()); + + CXCompletionResult R; + R.CursorKind = CXCursor_NotImplemented; + R.CompletionString = StoredCompletion; + StoredResults.push_back(R); + } + } + + virtual CodeCompletionAllocator &getAllocator() { + return *AllocatedResults.CodeCompletionAllocator; + } + + virtual CodeCompletionTUInfo &getCodeCompletionTUInfo() { return CCTUInfo; } + + private: + void Finish() { + AllocatedResults.Results = new CXCompletionResult [StoredResults.size()]; + AllocatedResults.NumResults = StoredResults.size(); + std::memcpy(AllocatedResults.Results, StoredResults.data(), + StoredResults.size() * sizeof(CXCompletionResult)); + StoredResults.clear(); + } + }; +} + +extern "C" { +struct CodeCompleteAtInfo { + CXTranslationUnit TU; + const char *complete_filename; + unsigned complete_line; + unsigned complete_column; + struct CXUnsavedFile *unsaved_files; + unsigned num_unsaved_files; + unsigned options; + CXCodeCompleteResults *result; +}; +void clang_codeCompleteAt_Impl(void *UserData) { + CodeCompleteAtInfo *CCAI = static_cast<CodeCompleteAtInfo*>(UserData); + CXTranslationUnit TU = CCAI->TU; + const char *complete_filename = CCAI->complete_filename; + unsigned complete_line = CCAI->complete_line; + unsigned complete_column = CCAI->complete_column; + struct CXUnsavedFile *unsaved_files = CCAI->unsaved_files; + unsigned num_unsaved_files = CCAI->num_unsaved_files; + unsigned options = CCAI->options; + CCAI->result = 0; + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); +#endif +#endif + + bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0; + + ASTUnit *AST = static_cast<ASTUnit *>(TU->TUData); + if (!AST) + return; + + CIndexer *CXXIdx = (CIndexer*)TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) + setThreadBackgroundPriority(); + + ASTUnit::ConcurrencyCheck Check(*AST); + + // Perform the remapping of source files. + SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; + for (unsigned I = 0; I != num_unsaved_files; ++I) { + StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); + RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename, + Buffer)); + } + + if (EnableLogging) { + // FIXME: Add logging. + } + + // Parse the resulting source file to find code-completion results. + AllocatedCXCodeCompleteResults *Results = + new AllocatedCXCodeCompleteResults(AST->getFileSystemOpts()); + Results->Results = 0; + Results->NumResults = 0; + + // Create a code-completion consumer to capture the results. + CaptureCompletionResults Capture(*Results, &TU); + + // Perform completion. + AST->CodeComplete(complete_filename, complete_line, complete_column, + RemappedFiles.data(), RemappedFiles.size(), + (options & CXCodeComplete_IncludeMacros), + (options & CXCodeComplete_IncludeCodePatterns), + Capture, + *Results->Diag, Results->LangOpts, *Results->SourceMgr, + *Results->FileMgr, Results->Diagnostics, + Results->TemporaryBuffers); + + // Keep a reference to the allocator used for cached global completions, so + // that we can be sure that the memory used by our code completion strings + // doesn't get freed due to subsequent reparses (while the code completion + // results are still active). + Results->CachedCompletionAllocator = AST->getCachedCompletionAllocator(); + + + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); + SmallString<256> LogResult; + llvm::raw_svector_ostream os(LogResult); + + // Figure out the language and whether or not it uses PCH. + const char *lang = 0; + bool usesPCH = false; + + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + if (*I == 0) + continue; + if (strcmp(*I, "-x") == 0) { + if (I + 1 != E) { + lang = *(++I); + continue; + } + } + else if (strcmp(*I, "-include") == 0) { + if (I+1 != E) { + const char *arg = *(++I); + SmallString<512> pchName; + { + llvm::raw_svector_ostream os(pchName); + os << arg << ".pth"; + } + pchName.push_back('\0'); + struct stat stat_results; + if (stat(pchName.str().c_str(), &stat_results) == 0) + usesPCH = true; + continue; + } + } + } + + os << "{ "; + os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); + os << ", \"numRes\": " << Results->NumResults; + os << ", \"diags\": " << Results->Diagnostics.size(); + os << ", \"pch\": " << (usesPCH ? "true" : "false"); + os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"'; + const char *name = getlogin(); + os << ", \"user\": \"" << (name ? name : "unknown") << '"'; + os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; + os << " }"; + + StringRef res = os.str(); + if (res.size() > 0) { + do { + // Setup the UDP socket. + struct sockaddr_in servaddr; + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); + if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, + &servaddr.sin_addr) <= 0) + break; + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + break; + + sendto(sockfd, res.data(), res.size(), 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + close(sockfd); + } + while (false); + } +#endif +#endif + CCAI->result = Results; +} +CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + unsigned options) { + CodeCompleteAtInfo CCAI = { TU, complete_filename, complete_line, + complete_column, unsaved_files, num_unsaved_files, + options, 0 }; + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, clang_codeCompleteAt_Impl, &CCAI)) { + fprintf(stderr, "libclang: crash detected in code completion\n"); + static_cast<ASTUnit *>(TU->TUData)->setUnsafeToFree(true); + return 0; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) + PrintLibclangResourceUsage(TU); + + return CCAI.result; +} + +unsigned clang_defaultCodeCompleteOptions(void) { + return CXCodeComplete_IncludeMacros; +} + +void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { + if (!ResultsIn) + return; + + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + delete Results; +} + +unsigned +clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results) + return 0; + + return Results->Diagnostics.size(); +} + +CXDiagnostic +clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, + unsigned Index) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results || Index >= Results->Diagnostics.size()) + return 0; + + return new CXStoredDiagnostic(Results->Diagnostics[Index], Results->LangOpts); +} + +unsigned long long +clang_codeCompleteGetContexts(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results) + return 0; + + return Results->Contexts; +} + +enum CXCursorKind clang_codeCompleteGetContainerKind( + CXCodeCompleteResults *ResultsIn, + unsigned *IsIncomplete) { + AllocatedCXCodeCompleteResults *Results = + static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn); + if (!Results) + return CXCursor_InvalidCode; + + if (IsIncomplete != NULL) { + *IsIncomplete = Results->ContainerIsIncomplete; + } + + return Results->ContainerKind; +} + +CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results = + static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn); + if (!Results) + return createCXString(""); + + return createCXString(clang_getCString(Results->ContainerUSR)); +} + + +CXString clang_codeCompleteGetObjCSelector(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results = + static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn); + if (!Results) + return createCXString(""); + + return createCXString(Results->Selector); +} + +} // end extern "C" + +/// \brief Simple utility function that appends a \p New string to the given +/// \p Old string, using the \p Buffer for storage. +/// +/// \param Old The string to which we are appending. This parameter will be +/// updated to reflect the complete string. +/// +/// +/// \param New The string to append to \p Old. +/// +/// \param Buffer A buffer that stores the actual, concatenated string. It will +/// be used if the old string is already-non-empty. +static void AppendToString(StringRef &Old, StringRef New, + SmallString<256> &Buffer) { + if (Old.empty()) { + Old = New; + return; + } + + if (Buffer.empty()) + Buffer.append(Old.begin(), Old.end()); + Buffer.append(New.begin(), New.end()); + Old = Buffer.str(); +} + +/// \brief Get the typed-text blocks from the given code-completion string +/// and return them as a single string. +/// +/// \param String The code-completion string whose typed-text blocks will be +/// concatenated. +/// +/// \param Buffer A buffer used for storage of the completed name. +static StringRef GetTypedName(CodeCompletionString *String, + SmallString<256> &Buffer) { + StringRef Result; + for (CodeCompletionString::iterator C = String->begin(), CEnd = String->end(); + C != CEnd; ++C) { + if (C->Kind == CodeCompletionString::CK_TypedText) + AppendToString(Result, C->Text, Buffer); + } + + return Result; +} + +namespace { + struct OrderCompletionResults { + bool operator()(const CXCompletionResult &XR, + const CXCompletionResult &YR) const { + CodeCompletionString *X + = (CodeCompletionString *)XR.CompletionString; + CodeCompletionString *Y + = (CodeCompletionString *)YR.CompletionString; + + SmallString<256> XBuffer; + StringRef XText = GetTypedName(X, XBuffer); + SmallString<256> YBuffer; + StringRef YText = GetTypedName(Y, YBuffer); + + if (XText.empty() || YText.empty()) + return !XText.empty(); + + int result = XText.compare_lower(YText); + if (result < 0) + return true; + if (result > 0) + return false; + + result = XText.compare(YText); + return result < 0; + } + }; +} + +extern "C" { + void clang_sortCodeCompletionResults(CXCompletionResult *Results, + unsigned NumResults) { + std::stable_sort(Results, Results + NumResults, OrderCompletionResults()); + } +} diff --git a/clang/tools/libclang/CIndexDiagnostic.cpp b/clang/tools/libclang/CIndexDiagnostic.cpp new file mode 100644 index 0000000..8fbe3d8 --- /dev/null +++ b/clang/tools/libclang/CIndexDiagnostic.cpp @@ -0,0 +1,458 @@ +/*===-- CIndexDiagnostics.cpp - Diagnostics C Interface ---------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements the diagnostic functions of the Clang C interface. *| +|* *| +\*===----------------------------------------------------------------------===*/ +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXSourceLocation.h" +#include "CXString.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cxloc; +using namespace clang::cxstring; +using namespace clang::cxdiag; +using namespace llvm; + + +CXDiagnosticSetImpl::~CXDiagnosticSetImpl() { + for (std::vector<CXDiagnosticImpl *>::iterator it = Diagnostics.begin(), + et = Diagnostics.end(); + it != et; ++it) { + delete *it; + } +} + +CXDiagnosticImpl::~CXDiagnosticImpl() {} + +namespace { +class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl { + std::string Message; + CXSourceLocation Loc; +public: + CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L) + : CXDiagnosticImpl(CustomNoteDiagnosticKind), + Message(Msg), Loc(L) {} + + virtual ~CXDiagnosticCustomNoteImpl() {} + + CXDiagnosticSeverity getSeverity() const { + return CXDiagnostic_Note; + } + + CXSourceLocation getLocation() const { + return Loc; + } + + CXString getSpelling() const { + return createCXString(StringRef(Message), false); + } + + CXString getDiagnosticOption(CXString *Disable) const { + if (Disable) + *Disable = createCXString("", false); + return createCXString("", false); + } + + unsigned getCategory() const { return 0; } + CXString getCategoryText() const { return createCXString(""); } + + unsigned getNumRanges() const { return 0; } + CXSourceRange getRange(unsigned Range) const { return clang_getNullRange(); } + unsigned getNumFixIts() const { return 0; } + CXString getFixIt(unsigned FixIt, CXSourceRange *ReplacementRange) const { + if (ReplacementRange) + *ReplacementRange = clang_getNullRange(); + return createCXString("", false); + } +}; + +class CXDiagnosticRenderer : public DiagnosticNoteRenderer { +public: + CXDiagnosticRenderer(const SourceManager &SM, + const LangOptions &LangOpts, + const DiagnosticOptions &DiagOpts, + CXDiagnosticSetImpl *mainSet) + : DiagnosticNoteRenderer(SM, LangOpts, DiagOpts), + CurrentSet(mainSet), MainSet(mainSet) {} + + virtual ~CXDiagnosticRenderer() {} + + virtual void beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + + const StoredDiagnostic *SD = D.dyn_cast<const StoredDiagnostic*>(); + if (!SD) + return; + + if (Level != DiagnosticsEngine::Note) + CurrentSet = MainSet; + + CXStoredDiagnostic *CD = new CXStoredDiagnostic(*SD, LangOpts); + CurrentSet->appendDiagnostic(CD); + + if (Level != DiagnosticsEngine::Note) + CurrentSet = &CD->getChildDiagnostics(); + } + + virtual void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + DiagOrStoredDiag D) { + if (!D.isNull()) + return; + + CXSourceLocation L = translateSourceLocation(SM, LangOpts, Loc); + CXDiagnosticImpl *CD = new CXDiagnosticCustomNoteImpl(Message, L); + CurrentSet->appendDiagnostic(CD); + } + + virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) {} + + virtual void emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints) {} + + virtual void emitNote(SourceLocation Loc, StringRef Message) { + CXSourceLocation L = translateSourceLocation(SM, LangOpts, Loc); + CurrentSet->appendDiagnostic(new CXDiagnosticCustomNoteImpl(Message, + L)); + } + + CXDiagnosticSetImpl *CurrentSet; + CXDiagnosticSetImpl *MainSet; +}; +} + +CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU, + bool checkIfChanged) { + ASTUnit *AU = static_cast<ASTUnit *>(TU->TUData); + + if (TU->Diagnostics && checkIfChanged) { + // In normal use, ASTUnit's diagnostics should not change unless we reparse. + // Currently they can only change by using the internal testing flag + // '-error-on-deserialized-decl' which will error during deserialization of + // a declaration. What will happen is: + // + // -c-index-test gets a CXTranslationUnit + // -checks the diagnostics, the diagnostics set is lazily created, + // no errors are reported + // -later does an operation, like annotation of tokens, that triggers + // -error-on-deserialized-decl, that will emit a diagnostic error, + // that ASTUnit will catch and add to its stored diagnostics vector. + // -c-index-test wants to check whether an error occurred after performing + // the operation but can only query the lazily created set. + // + // We check here if a new diagnostic was appended since the last time the + // diagnostic set was created, in which case we reset it. + + CXDiagnosticSetImpl * + Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); + if (AU->stored_diag_size() != Set->getNumDiagnostics()) { + // Diagnostics in the ASTUnit were updated, reset the associated + // diagnostics. + delete Set; + TU->Diagnostics = 0; + } + } + + if (!TU->Diagnostics) { + CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl(); + TU->Diagnostics = Set; + DiagnosticOptions DOpts; + CXDiagnosticRenderer Renderer(AU->getSourceManager(), + AU->getASTContext().getLangOpts(), + DOpts, Set); + + for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(), + ei = AU->stored_diag_end(); it != ei; ++it) { + Renderer.emitStoredDiagnostic(*it); + } + } + return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); +} + +//----------------------------------------------------------------------------- +// C Interface Routines +//----------------------------------------------------------------------------- +extern "C" { + +unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) { + if (!Unit->TUData) + return 0; + return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics(); +} + +CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) { + CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit); + if (!D) + return 0; + + CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D); + if (Index >= Diags->getNumDiagnostics()) + return 0; + + return Diags->getDiagnostic(Index); +} + +CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) { + if (!Unit->TUData) + return 0; + return static_cast<CXDiagnostic>(lazyCreateDiags(Unit)); +} + +void clang_disposeDiagnostic(CXDiagnostic Diagnostic) { + // No-op. Kept as a legacy API. CXDiagnostics are now managed + // by the enclosing CXDiagnosticSet. +} + +CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { + if (!Diagnostic) + return createCXString(""); + + CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic); + + SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + + if (Options & CXDiagnostic_DisplaySourceLocation) { + // Print source location (file:line), along with optional column + // and source ranges. + CXFile File; + unsigned Line, Column; + clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic), + &File, &Line, &Column, 0); + if (File) { + CXString FName = clang_getFileName(File); + Out << clang_getCString(FName) << ":" << Line << ":"; + clang_disposeString(FName); + if (Options & CXDiagnostic_DisplayColumn) + Out << Column << ":"; + + if (Options & CXDiagnostic_DisplaySourceRanges) { + unsigned N = clang_getDiagnosticNumRanges(Diagnostic); + bool PrintedRange = false; + for (unsigned I = 0; I != N; ++I) { + CXFile StartFile, EndFile; + CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I); + + unsigned StartLine, StartColumn, EndLine, EndColumn; + clang_getSpellingLocation(clang_getRangeStart(Range), + &StartFile, &StartLine, &StartColumn, + 0); + clang_getSpellingLocation(clang_getRangeEnd(Range), + &EndFile, &EndLine, &EndColumn, 0); + + if (StartFile != EndFile || StartFile != File) + continue; + + Out << "{" << StartLine << ":" << StartColumn << "-" + << EndLine << ":" << EndColumn << "}"; + PrintedRange = true; + } + if (PrintedRange) + Out << ":"; + } + + Out << " "; + } + } + + /* Print warning/error/etc. */ + switch (Severity) { + case CXDiagnostic_Ignored: llvm_unreachable("impossible"); + case CXDiagnostic_Note: Out << "note: "; break; + case CXDiagnostic_Warning: Out << "warning: "; break; + case CXDiagnostic_Error: Out << "error: "; break; + case CXDiagnostic_Fatal: Out << "fatal error: "; break; + } + + CXString Text = clang_getDiagnosticSpelling(Diagnostic); + if (clang_getCString(Text)) + Out << clang_getCString(Text); + else + Out << "<no diagnostic text>"; + clang_disposeString(Text); + + if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId | + CXDiagnostic_DisplayCategoryName)) { + bool NeedBracket = true; + bool NeedComma = false; + + if (Options & CXDiagnostic_DisplayOption) { + CXString OptionName = clang_getDiagnosticOption(Diagnostic, 0); + if (const char *OptionText = clang_getCString(OptionName)) { + if (OptionText[0]) { + Out << " [" << OptionText; + NeedBracket = false; + NeedComma = true; + } + } + clang_disposeString(OptionName); + } + + if (Options & (CXDiagnostic_DisplayCategoryId | + CXDiagnostic_DisplayCategoryName)) { + if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) { + if (Options & CXDiagnostic_DisplayCategoryId) { + if (NeedBracket) + Out << " ["; + if (NeedComma) + Out << ", "; + Out << CategoryID; + NeedBracket = false; + NeedComma = true; + } + + if (Options & CXDiagnostic_DisplayCategoryName) { + CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic); + if (NeedBracket) + Out << " ["; + if (NeedComma) + Out << ", "; + Out << clang_getCString(CategoryName); + NeedBracket = false; + NeedComma = true; + clang_disposeString(CategoryName); + } + } + } + + (void) NeedComma; // Silence dead store warning. + if (!NeedBracket) + Out << "]"; + } + + return createCXString(Out.str(), true); +} + +unsigned clang_defaultDiagnosticDisplayOptions() { + return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn | + CXDiagnostic_DisplayOption; +} + +enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag)) + return D->getSeverity(); + return CXDiagnostic_Ignored; +} + +CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag)) + return D->getLocation(); + return clang_getNullLocation(); +} + +CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getSpelling(); + return createCXString(""); +} + +CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) { + if (Disable) + *Disable = createCXString(""); + + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getDiagnosticOption(Disable); + + return createCXString(""); +} + +unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getCategory(); + return 0; +} + +CXString clang_getDiagnosticCategoryName(unsigned Category) { + // Kept for backwards compatibility. + return createCXString(DiagnosticIDs::getCategoryNameFromID(Category)); +} + +CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getCategoryText(); + return createCXString(""); +} + +unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getNumRanges(); + return 0; +} + +CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) { + CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag); + if (!D || Range >= D->getNumRanges()) + return clang_getNullRange(); + return D->getRange(Range); +} + +unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getNumFixIts(); + return 0; +} + +CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt, + CXSourceRange *ReplacementRange) { + CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag); + if (!D || FixIt >= D->getNumFixIts()) { + if (ReplacementRange) + *ReplacementRange = clang_getNullRange(); + return createCXString(""); + } + return D->getFixIt(FixIt, ReplacementRange); +} + +void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) { + CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags); + if (D->isExternallyManaged()) + delete D; +} + +CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags, + unsigned Index) { + if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags)) + if (Index < D->getNumDiagnostics()) + return D->getDiagnostic(Index); + return 0; +} + +CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) { + CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics(); + return ChildDiags.empty() ? 0 : (CXDiagnosticSet) &ChildDiags; + } + return 0; +} + +unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) { + if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags)) + return D->getNumDiagnostics(); + return 0; +} + +} // end extern "C" diff --git a/clang/tools/libclang/CIndexDiagnostic.h b/clang/tools/libclang/CIndexDiagnostic.h new file mode 100644 index 0000000..b1c3978 --- /dev/null +++ b/clang/tools/libclang/CIndexDiagnostic.h @@ -0,0 +1,166 @@ +/*===-- CIndexDiagnostic.h - Diagnostics C Interface ------------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements the diagnostic functions of the Clang C interface. *| +|* *| +\*===----------------------------------------------------------------------===*/ +#ifndef LLVM_CLANG_CINDEX_DIAGNOSTIC_H +#define LLVM_CLANG_CINDEX_DIAGNOSTIC_H + +#include "clang-c/Index.h" +#include <vector> +#include <assert.h> + +namespace clang { + +class LangOptions; +class StoredDiagnostic; +class CXDiagnosticImpl; + +class CXDiagnosticSetImpl { + std::vector<CXDiagnosticImpl *> Diagnostics; + const bool IsExternallyManaged; +public: + CXDiagnosticSetImpl(bool isManaged = false) + : IsExternallyManaged(isManaged) {} + + virtual ~CXDiagnosticSetImpl(); + + size_t getNumDiagnostics() const { + return Diagnostics.size(); + } + + CXDiagnosticImpl *getDiagnostic(unsigned i) const { + assert(i < getNumDiagnostics()); + return Diagnostics[i]; + } + + void appendDiagnostic(CXDiagnosticImpl *D) { + Diagnostics.push_back(D); + } + + bool empty() const { + return Diagnostics.empty(); + } + + bool isExternallyManaged() const { return IsExternallyManaged; } +}; + +class CXDiagnosticImpl { +public: + enum Kind { StoredDiagnosticKind, LoadedDiagnosticKind, + CustomNoteDiagnosticKind }; + + virtual ~CXDiagnosticImpl(); + + /// \brief Return the severity of the diagnostic. + virtual CXDiagnosticSeverity getSeverity() const = 0; + + /// \brief Return the location of the diagnostic. + virtual CXSourceLocation getLocation() const = 0; + + /// \brief Return the spelling of the diagnostic. + virtual CXString getSpelling() const = 0; + + /// \brief Return the text for the diagnostic option. + virtual CXString getDiagnosticOption(CXString *Disable) const = 0; + + /// \brief Return the category of the diagnostic. + virtual unsigned getCategory() const = 0; + + /// \brief Return the category string of the diagnostic. + virtual CXString getCategoryText() const = 0; + + /// \brief Return the number of source ranges for the diagnostic. + virtual unsigned getNumRanges() const = 0; + + /// \brief Return the source ranges for the diagnostic. + virtual CXSourceRange getRange(unsigned Range) const = 0; + + /// \brief Return the number of FixIts. + virtual unsigned getNumFixIts() const = 0; + + /// \brief Return the FixIt information (source range and inserted text). + virtual CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const = 0; + + Kind getKind() const { return K; } + + CXDiagnosticSetImpl &getChildDiagnostics() { + return ChildDiags; + } + +protected: + CXDiagnosticImpl(Kind k) : K(k) {} + CXDiagnosticSetImpl ChildDiags; + + void append(CXDiagnosticImpl *D) { + ChildDiags.appendDiagnostic(D); + } + +private: + Kind K; +}; + +/// \brief The storage behind a CXDiagnostic +struct CXStoredDiagnostic : public CXDiagnosticImpl { + const StoredDiagnostic &Diag; + const LangOptions &LangOpts; + + CXStoredDiagnostic(const StoredDiagnostic &Diag, + const LangOptions &LangOpts) + : CXDiagnosticImpl(StoredDiagnosticKind), + Diag(Diag), LangOpts(LangOpts) { } + + virtual ~CXStoredDiagnostic() {} + + /// \brief Return the severity of the diagnostic. + virtual CXDiagnosticSeverity getSeverity() const; + + /// \brief Return the location of the diagnostic. + virtual CXSourceLocation getLocation() const; + + /// \brief Return the spelling of the diagnostic. + virtual CXString getSpelling() const; + + /// \brief Return the text for the diagnostic option. + virtual CXString getDiagnosticOption(CXString *Disable) const; + + /// \brief Return the category of the diagnostic. + virtual unsigned getCategory() const; + + /// \brief Return the category string of the diagnostic. + virtual CXString getCategoryText() const; + + /// \brief Return the number of source ranges for the diagnostic. + virtual unsigned getNumRanges() const; + + /// \brief Return the source ranges for the diagnostic. + virtual CXSourceRange getRange(unsigned Range) const; + + /// \brief Return the number of FixIts. + virtual unsigned getNumFixIts() const; + + /// \brief Return the FixIt information (source range and inserted text). + virtual CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const; + + static bool classof(const CXDiagnosticImpl *D) { + return D->getKind() == StoredDiagnosticKind; + } +}; + +namespace cxdiag { +CXDiagnosticSetImpl *lazyCreateDiags(CXTranslationUnit TU, + bool checkIfChanged = false); +} // end namespace cxdiag + +} // end namespace clang + +#endif // LLVM_CLANG_CINDEX_DIAGNOSTIC_H diff --git a/clang/tools/libclang/CIndexHigh.cpp b/clang/tools/libclang/CIndexHigh.cpp new file mode 100644 index 0000000..ec76898 --- /dev/null +++ b/clang/tools/libclang/CIndexHigh.cpp @@ -0,0 +1,423 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CursorVisitor.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXTranslationUnit.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/DeclObjC.h" + +using namespace clang; +using namespace cxcursor; + +static void getTopOverriddenMethods(CXTranslationUnit TU, + Decl *D, + SmallVectorImpl<Decl *> &Methods) { + if (!D) + return; + if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D)) + return; + + SmallVector<CXCursor, 8> Overridden; + cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden); + + if (Overridden.empty()) { + Methods.push_back(D->getCanonicalDecl()); + return; + } + + for (SmallVector<CXCursor, 8>::iterator + I = Overridden.begin(), E = Overridden.end(); I != E; ++I) + getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); +} + +namespace { + +struct FindFileIdRefVisitData { + CXTranslationUnit TU; + FileID FID; + Decl *Dcl; + int SelectorIdIdx; + CXCursorAndRangeVisitor visitor; + + typedef SmallVector<Decl *, 8> TopMethodsTy; + TopMethodsTy TopMethods; + + FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, + Decl *D, int selectorIdIdx, + CXCursorAndRangeVisitor visitor) + : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) { + Dcl = getCanonical(D); + getTopOverriddenMethods(TU, Dcl, TopMethods); + } + + ASTContext &getASTContext() const { + return static_cast<ASTUnit *>(TU->TUData)->getASTContext(); + } + + /// \brief We are looking to find all semantically relevant identifiers, + /// so the definition of "canonical" here is different than in the AST, e.g. + /// + /// \code + /// class C { + /// C() {} + /// }; + /// \endcode + /// + /// we consider the canonical decl of the constructor decl to be the class + /// itself, so both 'C' can be highlighted. + Decl *getCanonical(Decl *D) const { + if (!D) + return 0; + + D = D->getCanonicalDecl(); + + if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) { + if (ImplD->getClassInterface()) + return getCanonical(ImplD->getClassInterface()); + + } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) { + return getCanonical(CXXCtorD->getParent()); + } + + return D; + } + + bool isHit(Decl *D) const { + if (!D) + return false; + + D = getCanonical(D); + if (D == Dcl) + return true; + + if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) + return isOverriddingMethod(D); + + return false; + } + +private: + bool isOverriddingMethod(Decl *D) const { + if (std::find(TopMethods.begin(), TopMethods.end(), D) != + TopMethods.end()) + return true; + + TopMethodsTy methods; + getTopOverriddenMethods(TU, D, methods); + for (TopMethodsTy::iterator + I = methods.begin(), E = methods.end(); I != E; ++I) { + if (std::find(TopMethods.begin(), TopMethods.end(), *I) != + TopMethods.end()) + return true; + } + + return false; + } +}; + +} // end anonymous namespace. + +/// \brief For a macro \arg Loc, returns the file spelling location and sets +/// to \arg isMacroArg whether the spelling resides inside a macro definition or +/// a macro argument. +static SourceLocation getFileSpellingLoc(SourceManager &SM, + SourceLocation Loc, + bool &isMacroArg) { + assert(Loc.isMacroID()); + SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc); + if (SpellLoc.isMacroID()) + return getFileSpellingLoc(SM, SpellLoc, isMacroArg); + + isMacroArg = SM.isMacroArgExpansion(Loc); + return SpellLoc; +} + +static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + CXCursor declCursor = clang_getCursorReferenced(cursor); + if (!clang_isDeclaration(declCursor.kind)) + return CXChildVisit_Recurse; + + Decl *D = cxcursor::getCursorDecl(declCursor); + if (!D) + return CXChildVisit_Continue; + + FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data; + if (data->isHit(D)) { + cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor); + + // We are looking for identifiers to highlight so for objc methods (and + // not a parameter) we can only highlight the selector identifiers. + if ((cursor.kind == CXCursor_ObjCClassMethodDecl || + cursor.kind == CXCursor_ObjCInstanceMethodDecl) && + cxcursor::getSelectorIdentifierIndex(cursor) == -1) + return CXChildVisit_Recurse; + + if (clang_isExpression(cursor.kind)) { + if (cursor.kind == CXCursor_DeclRefExpr || + cursor.kind == CXCursor_MemberRefExpr) { + // continue.. + + } else if (cursor.kind == CXCursor_ObjCMessageExpr && + cxcursor::getSelectorIdentifierIndex(cursor) != -1) { + // continue.. + + } else + return CXChildVisit_Recurse; + } + + SourceLocation + Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); + SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor); + if (SelIdLoc.isValid()) + Loc = SelIdLoc; + + ASTContext &Ctx = data->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + bool isInMacroDef = false; + if (Loc.isMacroID()) { + bool isMacroArg; + Loc = getFileSpellingLoc(SM, Loc, isMacroArg); + isInMacroDef = !isMacroArg; + } + + // We are looking for identifiers in a specific file. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + if (LocInfo.first != data->FID) + return CXChildVisit_Recurse; + + if (isInMacroDef) { + // FIXME: For a macro definition make sure that all expansions + // of it expand to the same reference before allowing to point to it. + return CXChildVisit_Recurse; + } + + data->visitor.visit(data->visitor.context, cursor, + cxloc::translateSourceRange(Ctx, Loc)); + } + return CXChildVisit_Recurse; +} + +static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, + const FileEntry *File, + CXCursorAndRangeVisitor Visitor) { + assert(clang_isDeclaration(declCursor.kind)); + ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); + SourceManager &SM = Unit->getSourceManager(); + + FileID FID = SM.translateFile(File); + Decl *Dcl = cxcursor::getCursorDecl(declCursor); + if (!Dcl) + return; + + FindFileIdRefVisitData data(TU, FID, Dcl, + cxcursor::getSelectorIdentifierIndex(declCursor), + Visitor); + + if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) { + clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), + findFileIdRefVisit, &data); + return; + } + + SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); + CursorVisitor FindIdRefsVisitor(TU, + findFileIdRefVisit, &data, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + Range, + /*VisitDeclsOnly=*/true); + FindIdRefsVisitor.visitFileRegion(); +} + +namespace { + +struct FindFileMacroRefVisitData { + ASTUnit &Unit; + const FileEntry *File; + const IdentifierInfo *Macro; + CXCursorAndRangeVisitor visitor; + + FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File, + const IdentifierInfo *Macro, + CXCursorAndRangeVisitor visitor) + : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { } + + ASTContext &getASTContext() const { + return Unit.getASTContext(); + } +}; + +} // anonymous namespace + +static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + const IdentifierInfo *Macro = 0; + if (cursor.kind == CXCursor_MacroDefinition) + Macro = getCursorMacroDefinition(cursor)->getName(); + else if (cursor.kind == CXCursor_MacroExpansion) + Macro = getCursorMacroExpansion(cursor)->getName(); + if (!Macro) + return CXChildVisit_Continue; + + FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data; + if (data->Macro != Macro) + return CXChildVisit_Continue; + + SourceLocation + Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); + + ASTContext &Ctx = data->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + bool isInMacroDef = false; + if (Loc.isMacroID()) { + bool isMacroArg; + Loc = getFileSpellingLoc(SM, Loc, isMacroArg); + isInMacroDef = !isMacroArg; + } + + // We are looking for identifiers in a specific file. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + if (SM.getFileEntryForID(LocInfo.first) != data->File) + return CXChildVisit_Continue; + + if (isInMacroDef) { + // FIXME: For a macro definition make sure that all expansions + // of it expand to the same reference before allowing to point to it. + return CXChildVisit_Continue; + } + + data->visitor.visit(data->visitor.context, cursor, + cxloc::translateSourceRange(Ctx, Loc)); + return CXChildVisit_Continue; +} + +static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor, + const FileEntry *File, + CXCursorAndRangeVisitor Visitor) { + if (Cursor.kind != CXCursor_MacroDefinition && + Cursor.kind != CXCursor_MacroExpansion) + return; + + ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); + SourceManager &SM = Unit->getSourceManager(); + + FileID FID = SM.translateFile(File); + const IdentifierInfo *Macro = 0; + if (Cursor.kind == CXCursor_MacroDefinition) + Macro = getCursorMacroDefinition(Cursor)->getName(); + else + Macro = getCursorMacroExpansion(Cursor)->getName(); + if (!Macro) + return; + + FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor); + + SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); + CursorVisitor FindMacroRefsVisitor(TU, + findFileMacroRefVisit, &data, + /*VisitPreprocessorLast=*/false, + /*VisitIncludedEntities=*/false, + Range); + FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); +} + + +//===----------------------------------------------------------------------===// +// libclang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { + +void clang_findReferencesInFile(CXCursor cursor, CXFile file, + CXCursorAndRangeVisitor visitor) { + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + if (clang_Cursor_isNull(cursor)) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Null cursor\n"; + return; + } + if (cursor.kind == CXCursor_NoDeclFound) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Got CXCursor_NoDeclFound\n"; + return; + } + if (!file) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Null file\n"; + return; + } + if (!visitor.visit) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Null visitor\n"; + return; + } + + ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); + if (!CXXUnit) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + if (cursor.kind == CXCursor_MacroDefinition || + cursor.kind == CXCursor_MacroExpansion) { + findMacroRefsInFile(cxcursor::getCursorTU(cursor), + cursor, + static_cast<const FileEntry *>(file), + visitor); + return; + } + + // We are interested in semantics of identifiers so for C++ constructor exprs + // prefer type references, e.g.: + // + // return MyStruct(); + // + // for 'MyStruct' we'll have a cursor pointing at the constructor decl but + // we are actually interested in the type declaration. + cursor = cxcursor::getTypeRefCursor(cursor); + + CXCursor refCursor = clang_getCursorReferenced(cursor); + + if (!clang_isDeclaration(refCursor.kind)) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a " + "declaration\n"; + return; + } + + findIdRefsInFile(cxcursor::getCursorTU(cursor), + refCursor, + static_cast<const FileEntry *>(file), + visitor); +} + +static enum CXVisitorResult _visitCursorAndRange(void *context, + CXCursor cursor, + CXSourceRange range) { + CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; + return INVOKE_BLOCK2(block, cursor, range); +} + +void clang_findReferencesInFileWithBlock(CXCursor cursor, + CXFile file, + CXCursorAndRangeVisitorBlock block) { + CXCursorAndRangeVisitor visitor = { block, + block ? _visitCursorAndRange : 0 }; + return clang_findReferencesInFile(cursor, file, visitor); +} + +} // end: extern "C" + diff --git a/clang/tools/libclang/CIndexInclusionStack.cpp b/clang/tools/libclang/CIndexInclusionStack.cpp new file mode 100644 index 0000000..848ca31 --- /dev/null +++ b/clang/tools/libclang/CIndexInclusionStack.cpp @@ -0,0 +1,71 @@ +//===- CIndexInclusionStack.cpp - Clang-C Source Indexing Library ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a callback mechanism for clients to get the inclusion +// stack from a translation unit. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXSourceLocation.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +extern "C" { +void clang_getInclusions(CXTranslationUnit TU, CXInclusionVisitor CB, + CXClientData clientData) { + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData); + SourceManager &SM = CXXUnit->getSourceManager(); + ASTContext &Ctx = CXXUnit->getASTContext(); + + SmallVector<CXSourceLocation, 10> InclusionStack; + unsigned n = SM.local_sloc_entry_size(); + + // In the case where all the SLocEntries are in an external source, traverse + // those SLocEntries as well. This is the case where we are looking + // at the inclusion stack of an AST/PCH file. + const SrcMgr::SLocEntry &(SourceManager::*Getter)(unsigned, bool*) const; + if (n == 1) { + Getter = &SourceManager::getLoadedSLocEntry; + n = SM.loaded_sloc_entry_size(); + } else + Getter = &SourceManager::getLocalSLocEntry; + + for (unsigned i = 0 ; i < n ; ++i) { + bool Invalid = false; + const SrcMgr::SLocEntry &SL = (SM.*Getter)(i, &Invalid); + + if (!SL.isFile() || Invalid) + continue; + + const SrcMgr::FileInfo &FI = SL.getFile(); + if (!FI.getContentCache()->OrigEntry) + continue; + + // Build the inclusion stack. + SourceLocation L = FI.getIncludeLoc(); + InclusionStack.clear(); + while (L.isValid()) { + PresumedLoc PLoc = SM.getPresumedLoc(L); + InclusionStack.push_back(cxloc::translateSourceLocation(Ctx, L)); + L = PLoc.isValid()? PLoc.getIncludeLoc() : SourceLocation(); + } + + // Callback to the client. + // FIXME: We should have a function to construct CXFiles. + CB((CXFile) FI.getContentCache()->OrigEntry, + InclusionStack.data(), InclusionStack.size(), clientData); + } +} +} // end extern C diff --git a/clang/tools/libclang/CIndexUSRs.cpp b/clang/tools/libclang/CIndexUSRs.cpp new file mode 100644 index 0000000..7c79b69 --- /dev/null +++ b/clang/tools/libclang/CIndexUSRs.cpp @@ -0,0 +1,932 @@ +//===- CIndexUSR.cpp - Clang-C Source Indexing Library --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the generation and use of USRs from CXEntities. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "CXString.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cxstring; + +//===----------------------------------------------------------------------===// +// USR generation. +//===----------------------------------------------------------------------===// + +namespace { +class USRGenerator : public DeclVisitor<USRGenerator> { + OwningPtr<SmallString<128> > OwnedBuf; + SmallVectorImpl<char> &Buf; + llvm::raw_svector_ostream Out; + bool IgnoreResults; + ASTContext *Context; + bool generatedLoc; + + llvm::DenseMap<const Type *, unsigned> TypeSubstitutions; + +public: + explicit USRGenerator(ASTContext *Ctx = 0, SmallVectorImpl<char> *extBuf = 0) + : OwnedBuf(extBuf ? 0 : new SmallString<128>()), + Buf(extBuf ? *extBuf : *OwnedBuf.get()), + Out(Buf), + IgnoreResults(false), + Context(Ctx), + generatedLoc(false) + { + // Add the USR space prefix. + Out << "c:"; + } + + StringRef str() { + return Out.str(); + } + + USRGenerator* operator->() { return this; } + + template <typename T> + llvm::raw_svector_ostream &operator<<(const T &x) { + Out << x; + return Out; + } + + bool ignoreResults() const { return IgnoreResults; } + + // Visitation methods from generating USRs from AST elements. + void VisitDeclContext(DeclContext *D); + void VisitFieldDecl(FieldDecl *D); + void VisitFunctionDecl(FunctionDecl *D); + void VisitNamedDecl(NamedDecl *D); + void VisitNamespaceDecl(NamespaceDecl *D); + void VisitNamespaceAliasDecl(NamespaceAliasDecl *D); + void VisitFunctionTemplateDecl(FunctionTemplateDecl *D); + void VisitClassTemplateDecl(ClassTemplateDecl *D); + void VisitObjCContainerDecl(ObjCContainerDecl *CD); + void VisitObjCMethodDecl(ObjCMethodDecl *MD); + void VisitObjCPropertyDecl(ObjCPropertyDecl *D); + void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); + void VisitTagDecl(TagDecl *D); + void VisitTypedefDecl(TypedefDecl *D); + void VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D); + void VisitVarDecl(VarDecl *D); + void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); + void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D); + void VisitLinkageSpecDecl(LinkageSpecDecl *D) { + IgnoreResults = true; + } + void VisitUsingDirectiveDecl(UsingDirectiveDecl *D) { + IgnoreResults = true; + } + void VisitUsingDecl(UsingDecl *D) { + IgnoreResults = true; + } + void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D) { + IgnoreResults = true; + } + void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D) { + IgnoreResults = true; + } + + /// Generate the string component containing the location of the + /// declaration. + bool GenLoc(const Decl *D); + + /// String generation methods used both by the visitation methods + /// and from other clients that want to directly generate USRs. These + /// methods do not construct complete USRs (which incorporate the parents + /// of an AST element), but only the fragments concerning the AST element + /// itself. + + /// Generate a USR for an Objective-C class. + void GenObjCClass(StringRef cls); + /// Generate a USR for an Objective-C class category. + void GenObjCCategory(StringRef cls, StringRef cat); + /// Generate a USR fragment for an Objective-C instance variable. The + /// complete USR can be created by concatenating the USR for the + /// encompassing class with this USR fragment. + void GenObjCIvar(StringRef ivar); + /// Generate a USR fragment for an Objective-C method. + void GenObjCMethod(StringRef sel, bool isInstanceMethod); + /// Generate a USR fragment for an Objective-C property. + void GenObjCProperty(StringRef prop); + /// Generate a USR for an Objective-C protocol. + void GenObjCProtocol(StringRef prot); + + void VisitType(QualType T); + void VisitTemplateParameterList(const TemplateParameterList *Params); + void VisitTemplateName(TemplateName Name); + void VisitTemplateArgument(const TemplateArgument &Arg); + + /// Emit a Decl's name using NamedDecl::printName() and return true if + /// the decl had no name. + bool EmitDeclName(const NamedDecl *D); +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Generating USRs from ASTS. +//===----------------------------------------------------------------------===// + +bool USRGenerator::EmitDeclName(const NamedDecl *D) { + Out.flush(); + const unsigned startSize = Buf.size(); + D->printName(Out); + Out.flush(); + const unsigned endSize = Buf.size(); + return startSize == endSize; +} + +static bool InAnonymousNamespace(const Decl *D) { + if (const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext())) + return ND->isAnonymousNamespace(); + return false; +} + +static inline bool ShouldGenerateLocation(const NamedDecl *D) { + return D->getLinkage() != ExternalLinkage && !InAnonymousNamespace(D); +} + +void USRGenerator::VisitDeclContext(DeclContext *DC) { + if (NamedDecl *D = dyn_cast<NamedDecl>(DC)) + Visit(D); +} + +void USRGenerator::VisitFieldDecl(FieldDecl *D) { + // The USR for an ivar declared in a class extension is based on the + // ObjCInterfaceDecl, not the ObjCCategoryDecl. + if (ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + VisitDeclContext(D->getDeclContext()); + Out << (isa<ObjCIvarDecl>(D) ? "@" : "@FI@"); + if (EmitDeclName(D)) { + // Bit fields can be anonymous. + IgnoreResults = true; + return; + } +} + +void USRGenerator::VisitFunctionDecl(FunctionDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + + VisitDeclContext(D->getDeclContext()); + if (FunctionTemplateDecl *FunTmpl = D->getDescribedFunctionTemplate()) { + Out << "@FT@"; + VisitTemplateParameterList(FunTmpl->getTemplateParameters()); + } else + Out << "@F@"; + D->printName(Out); + + ASTContext &Ctx = *Context; + if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) + return; + + if (const TemplateArgumentList * + SpecArgs = D->getTemplateSpecializationArgs()) { + Out << '<'; + for (unsigned I = 0, N = SpecArgs->size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(SpecArgs->get(I)); + } + Out << '>'; + } + + // Mangle in type information for the arguments. + for (FunctionDecl::param_iterator I = D->param_begin(), E = D->param_end(); + I != E; ++I) { + Out << '#'; + if (ParmVarDecl *PD = *I) + VisitType(PD->getType()); + } + if (D->isVariadic()) + Out << '.'; + Out << '#'; + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (MD->isStatic()) + Out << 'S'; + if (unsigned quals = MD->getTypeQualifiers()) + Out << (char)('0' + quals); + } +} + +void USRGenerator::VisitNamedDecl(NamedDecl *D) { + VisitDeclContext(D->getDeclContext()); + Out << "@"; + + if (EmitDeclName(D)) { + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, + // e.g.: void (*f)(void *); + // In this case, don't generate a USR. + IgnoreResults = true; + } +} + +void USRGenerator::VisitVarDecl(VarDecl *D) { + // VarDecls can be declared 'extern' within a function or method body, + // but their enclosing DeclContext is the function, not the TU. We need + // to check the storage class to correctly generate the USR. + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + + VisitDeclContext(D->getDeclContext()); + + // Variables always have simple names. + StringRef s = D->getName(); + + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if (s.empty()) + IgnoreResults = true; + else + Out << '@' << s; +} + +void USRGenerator::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + GenLoc(D); + return; +} + +void USRGenerator::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + GenLoc(D); + return; +} + +void USRGenerator::VisitNamespaceDecl(NamespaceDecl *D) { + if (D->isAnonymousNamespace()) { + Out << "@aN"; + return; + } + + VisitDeclContext(D->getDeclContext()); + if (!IgnoreResults) + Out << "@N@" << D->getName(); +} + +void USRGenerator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { + VisitFunctionDecl(D->getTemplatedDecl()); +} + +void USRGenerator::VisitClassTemplateDecl(ClassTemplateDecl *D) { + VisitTagDecl(D->getTemplatedDecl()); +} + +void USRGenerator::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + VisitDeclContext(D->getDeclContext()); + if (!IgnoreResults) + Out << "@NA@" << D->getName(); +} + +void USRGenerator::VisitObjCMethodDecl(ObjCMethodDecl *D) { + DeclContext *container = D->getDeclContext(); + if (ObjCProtocolDecl *pd = dyn_cast<ObjCProtocolDecl>(container)) { + Visit(pd); + } + else { + // The USR for a method declared in a class extension or category is based on + // the ObjCInterfaceDecl, not the ObjCCategoryDecl. + ObjCInterfaceDecl *ID = D->getClassInterface(); + if (!ID) { + IgnoreResults = true; + return; + } + Visit(ID); + } + // Ideally we would use 'GenObjCMethod', but this is such a hot path + // for Objective-C code that we don't want to use + // DeclarationName::getAsString(). + Out << (D->isInstanceMethod() ? "(im)" : "(cm)"); + DeclarationName N(D->getSelector()); + N.printName(Out); +} + +void USRGenerator::VisitObjCContainerDecl(ObjCContainerDecl *D) { + switch (D->getKind()) { + default: + llvm_unreachable("Invalid ObjC container."); + case Decl::ObjCInterface: + case Decl::ObjCImplementation: + GenObjCClass(D->getName()); + break; + case Decl::ObjCCategory: { + ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(D); + ObjCInterfaceDecl *ID = CD->getClassInterface(); + if (!ID) { + // Handle invalid code where the @interface might not + // have been specified. + // FIXME: We should be able to generate this USR even if the + // @interface isn't available. + IgnoreResults = true; + return; + } + // Specially handle class extensions, which are anonymous categories. + // We want to mangle in the location to uniquely distinguish them. + if (CD->IsClassExtension()) { + Out << "objc(ext)" << ID->getName() << '@'; + GenLoc(CD); + } + else + GenObjCCategory(ID->getName(), CD->getName()); + + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CD = cast<ObjCCategoryImplDecl>(D); + ObjCInterfaceDecl *ID = CD->getClassInterface(); + if (!ID) { + // Handle invalid code where the @interface might not + // have been specified. + // FIXME: We should be able to generate this USR even if the + // @interface isn't available. + IgnoreResults = true; + return; + } + GenObjCCategory(ID->getName(), CD->getName()); + break; + } + case Decl::ObjCProtocol: + GenObjCProtocol(cast<ObjCProtocolDecl>(D)->getName()); + break; + } +} + +void USRGenerator::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { + // The USR for a property declared in a class extension or category is based + // on the ObjCInterfaceDecl, not the ObjCCategoryDecl. + if (ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + Visit(cast<Decl>(D->getDeclContext())); + GenObjCProperty(D->getName()); +} + +void USRGenerator::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { + if (ObjCPropertyDecl *PD = D->getPropertyDecl()) { + VisitObjCPropertyDecl(PD); + return; + } + + IgnoreResults = true; +} + +void USRGenerator::VisitTagDecl(TagDecl *D) { + // Add the location of the tag decl to handle resolution across + // translation units. + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + + D = D->getCanonicalDecl(); + VisitDeclContext(D->getDeclContext()); + + bool AlreadyStarted = false; + if (CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(D)) { + if (ClassTemplateDecl *ClassTmpl = CXXRecord->getDescribedClassTemplate()) { + AlreadyStarted = true; + + switch (D->getTagKind()) { + case TTK_Struct: Out << "@ST"; break; + case TTK_Class: Out << "@CT"; break; + case TTK_Union: Out << "@UT"; break; + case TTK_Enum: llvm_unreachable("enum template"); + } + VisitTemplateParameterList(ClassTmpl->getTemplateParameters()); + } else if (ClassTemplatePartialSpecializationDecl *PartialSpec + = dyn_cast<ClassTemplatePartialSpecializationDecl>(CXXRecord)) { + AlreadyStarted = true; + + switch (D->getTagKind()) { + case TTK_Struct: Out << "@SP"; break; + case TTK_Class: Out << "@CP"; break; + case TTK_Union: Out << "@UP"; break; + case TTK_Enum: llvm_unreachable("enum partial specialization"); + } + VisitTemplateParameterList(PartialSpec->getTemplateParameters()); + } + } + + if (!AlreadyStarted) { + switch (D->getTagKind()) { + case TTK_Struct: Out << "@S"; break; + case TTK_Class: Out << "@C"; break; + case TTK_Union: Out << "@U"; break; + case TTK_Enum: Out << "@E"; break; + } + } + + Out << '@'; + Out.flush(); + assert(Buf.size() > 0); + const unsigned off = Buf.size() - 1; + + if (EmitDeclName(D)) { + if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl()) { + Buf[off] = 'A'; + Out << '@' << *TD; + } + else + Buf[off] = 'a'; + } + + // For a class template specialization, mangle the template arguments. + if (ClassTemplateSpecializationDecl *Spec + = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + const TemplateArgumentList &Args = Spec->getTemplateInstantiationArgs(); + Out << '>'; + for (unsigned I = 0, N = Args.size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(Args.get(I)); + } + } +} + +void USRGenerator::VisitTypedefDecl(TypedefDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + DeclContext *DC = D->getDeclContext(); + if (NamedDecl *DCN = dyn_cast<NamedDecl>(DC)) + Visit(DCN); + Out << "@T@"; + Out << D->getName(); +} + +void USRGenerator::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + GenLoc(D); + return; +} + +bool USRGenerator::GenLoc(const Decl *D) { + if (generatedLoc) + return IgnoreResults; + generatedLoc = true; + + // Guard against null declarations in invalid code. + if (!D) { + IgnoreResults = true; + return true; + } + + // Use the location of canonical decl. + D = D->getCanonicalDecl(); + + const SourceManager &SM = Context->getSourceManager(); + SourceLocation L = D->getLocStart(); + if (L.isInvalid()) { + IgnoreResults = true; + return true; + } + L = SM.getExpansionLoc(L); + const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(L); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + Out << llvm::sys::path::filename(FE->getName()); + } + else { + // This case really isn't interesting. + IgnoreResults = true; + return true; + } + // Use the offest into the FileID to represent the location. Using + // a line/column can cause us to look back at the original source file, + // which is expensive. + Out << '@' << Decomposed.second; + return IgnoreResults; +} + +void USRGenerator::VisitType(QualType T) { + // This method mangles in USR information for types. It can possibly + // just reuse the naming-mangling logic used by codegen, although the + // requirements for USRs might not be the same. + ASTContext &Ctx = *Context; + + do { + T = Ctx.getCanonicalType(T); + Qualifiers Q = T.getQualifiers(); + unsigned qVal = 0; + if (Q.hasConst()) + qVal |= 0x1; + if (Q.hasVolatile()) + qVal |= 0x2; + if (Q.hasRestrict()) + qVal |= 0x4; + if(qVal) + Out << ((char) ('0' + qVal)); + + // Mangle in ObjC GC qualifiers? + + if (const PackExpansionType *Expansion = T->getAs<PackExpansionType>()) { + Out << 'P'; + T = Expansion->getPattern(); + } + + if (const BuiltinType *BT = T->getAs<BuiltinType>()) { + unsigned char c = '\0'; + switch (BT->getKind()) { + case BuiltinType::Void: + c = 'v'; break; + case BuiltinType::Bool: + c = 'b'; break; + case BuiltinType::Char_U: + case BuiltinType::UChar: + c = 'c'; break; + case BuiltinType::Char16: + c = 'q'; break; + case BuiltinType::Char32: + c = 'w'; break; + case BuiltinType::UShort: + c = 's'; break; + case BuiltinType::UInt: + c = 'i'; break; + case BuiltinType::ULong: + c = 'l'; break; + case BuiltinType::ULongLong: + c = 'k'; break; + case BuiltinType::UInt128: + c = 'j'; break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + c = 'C'; break; + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + c = 'W'; break; + case BuiltinType::Short: + c = 'S'; break; + case BuiltinType::Int: + c = 'I'; break; + case BuiltinType::Long: + c = 'L'; break; + case BuiltinType::LongLong: + c = 'K'; break; + case BuiltinType::Int128: + c = 'J'; break; + case BuiltinType::Half: + c = 'h'; break; + case BuiltinType::Float: + c = 'f'; break; + case BuiltinType::Double: + c = 'd'; break; + case BuiltinType::LongDouble: + c = 'D'; break; + case BuiltinType::NullPtr: + c = 'n'; break; +#define BUILTIN_TYPE(Id, SingletonId) +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#include "clang/AST/BuiltinTypes.def" + case BuiltinType::Dependent: + IgnoreResults = true; + return; + case BuiltinType::ObjCId: + c = 'o'; break; + case BuiltinType::ObjCClass: + c = 'O'; break; + case BuiltinType::ObjCSel: + c = 'e'; break; + } + Out << c; + return; + } + + // If we have already seen this (non-built-in) type, use a substitution + // encoding. + llvm::DenseMap<const Type *, unsigned>::iterator Substitution + = TypeSubstitutions.find(T.getTypePtr()); + if (Substitution != TypeSubstitutions.end()) { + Out << 'S' << Substitution->second << '_'; + return; + } else { + // Record this as a substitution. + unsigned Number = TypeSubstitutions.size(); + TypeSubstitutions[T.getTypePtr()] = Number; + } + + if (const PointerType *PT = T->getAs<PointerType>()) { + Out << '*'; + T = PT->getPointeeType(); + continue; + } + if (const ReferenceType *RT = T->getAs<ReferenceType>()) { + Out << '&'; + T = RT->getPointeeType(); + continue; + } + if (const FunctionProtoType *FT = T->getAs<FunctionProtoType>()) { + Out << 'F'; + VisitType(FT->getResultType()); + for (FunctionProtoType::arg_type_iterator + I = FT->arg_type_begin(), E = FT->arg_type_end(); I!=E; ++I) { + VisitType(*I); + } + if (FT->isVariadic()) + Out << '.'; + return; + } + if (const BlockPointerType *BT = T->getAs<BlockPointerType>()) { + Out << 'B'; + T = BT->getPointeeType(); + continue; + } + if (const ComplexType *CT = T->getAs<ComplexType>()) { + Out << '<'; + T = CT->getElementType(); + continue; + } + if (const TagType *TT = T->getAs<TagType>()) { + Out << '$'; + VisitTagDecl(TT->getDecl()); + return; + } + if (const TemplateTypeParmType *TTP = T->getAs<TemplateTypeParmType>()) { + Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); + return; + } + if (const TemplateSpecializationType *Spec + = T->getAs<TemplateSpecializationType>()) { + Out << '>'; + VisitTemplateName(Spec->getTemplateName()); + Out << Spec->getNumArgs(); + for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) + VisitTemplateArgument(Spec->getArg(I)); + return; + } + + // Unhandled type. + Out << ' '; + break; + } while (true); +} + +void USRGenerator::VisitTemplateParameterList( + const TemplateParameterList *Params) { + if (!Params) + return; + Out << '>' << Params->size(); + for (TemplateParameterList::const_iterator P = Params->begin(), + PEnd = Params->end(); + P != PEnd; ++P) { + Out << '#'; + if (isa<TemplateTypeParmDecl>(*P)) { + if (cast<TemplateTypeParmDecl>(*P)->isParameterPack()) + Out<< 'p'; + Out << 'T'; + continue; + } + + if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { + if (NTTP->isParameterPack()) + Out << 'p'; + Out << 'N'; + VisitType(NTTP->getType()); + continue; + } + + TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(*P); + if (TTP->isParameterPack()) + Out << 'p'; + Out << 't'; + VisitTemplateParameterList(TTP->getTemplateParameters()); + } +} + +void USRGenerator::VisitTemplateName(TemplateName Name) { + if (TemplateDecl *Template = Name.getAsTemplateDecl()) { + if (TemplateTemplateParmDecl *TTP + = dyn_cast<TemplateTemplateParmDecl>(Template)) { + Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); + return; + } + + Visit(Template); + return; + } + + // FIXME: Visit dependent template names. +} + +void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) { + switch (Arg.getKind()) { + case TemplateArgument::Null: + break; + + case TemplateArgument::Declaration: + if (Decl *D = Arg.getAsDecl()) + Visit(D); + break; + + case TemplateArgument::TemplateExpansion: + Out << 'P'; // pack expansion of... + // Fall through + case TemplateArgument::Template: + VisitTemplateName(Arg.getAsTemplateOrTemplatePattern()); + break; + + case TemplateArgument::Expression: + // FIXME: Visit expressions. + break; + + case TemplateArgument::Pack: + Out << 'p' << Arg.pack_size(); + for (TemplateArgument::pack_iterator P = Arg.pack_begin(), PEnd = Arg.pack_end(); + P != PEnd; ++P) + VisitTemplateArgument(*P); + break; + + case TemplateArgument::Type: + VisitType(Arg.getAsType()); + break; + + case TemplateArgument::Integral: + Out << 'V'; + VisitType(Arg.getIntegralType()); + Out << *Arg.getAsIntegral(); + break; + } +} + +//===----------------------------------------------------------------------===// +// General purpose USR generation methods. +//===----------------------------------------------------------------------===// + +void USRGenerator::GenObjCClass(StringRef cls) { + Out << "objc(cs)" << cls; +} + +void USRGenerator::GenObjCCategory(StringRef cls, StringRef cat) { + Out << "objc(cy)" << cls << '@' << cat; +} + +void USRGenerator::GenObjCIvar(StringRef ivar) { + Out << '@' << ivar; +} + +void USRGenerator::GenObjCMethod(StringRef meth, bool isInstanceMethod) { + Out << (isInstanceMethod ? "(im)" : "(cm)") << meth; +} + +void USRGenerator::GenObjCProperty(StringRef prop) { + Out << "(py)" << prop; +} + +void USRGenerator::GenObjCProtocol(StringRef prot) { + Out << "objc(pl)" << prot; +} + +//===----------------------------------------------------------------------===// +// API hooks. +//===----------------------------------------------------------------------===// + +static inline StringRef extractUSRSuffix(StringRef s) { + return s.startswith("c:") ? s.substr(2) : ""; +} + +bool cxcursor::getDeclCursorUSR(const Decl *D, SmallVectorImpl<char> &Buf) { + // Don't generate USRs for things with invalid locations. + if (!D || D->getLocStart().isInvalid()) + return true; + + // Check if the cursor has 'NoLinkage'. + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + switch (ND->getLinkage()) { + case ExternalLinkage: + // Generate USRs for all entities with external linkage. + break; + case NoLinkage: + case UniqueExternalLinkage: + // We allow enums, typedefs, and structs that have no linkage to + // have USRs that are anchored to the file they were defined in + // (e.g., the header). This is a little gross, but in principal + // enums/anonymous structs/etc. defined in a common header file + // are referred to across multiple translation units. + if (isa<TagDecl>(ND) || isa<TypedefDecl>(ND) || + isa<EnumConstantDecl>(ND) || isa<FieldDecl>(ND) || + isa<VarDecl>(ND) || isa<NamespaceDecl>(ND)) + break; + // Fall-through. + case InternalLinkage: + if (isa<FunctionDecl>(ND)) + break; + } + + { + USRGenerator UG(&D->getASTContext(), &Buf); + UG->Visit(const_cast<Decl*>(D)); + + if (UG->ignoreResults()) + return true; + } + + return false; +} + +extern "C" { + +CXString clang_getCursorUSR(CXCursor C) { + const CXCursorKind &K = clang_getCursorKind(C); + + if (clang_isDeclaration(K)) { + Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return createCXString(""); + + CXTranslationUnit TU = cxcursor::getCursorTU(C); + if (!TU) + return createCXString(""); + + CXStringBuf *buf = cxstring::getCXStringBuf(TU); + if (!buf) + return createCXString(""); + + bool Ignore = cxcursor::getDeclCursorUSR(D, buf->Data); + if (Ignore) { + disposeCXStringBuf(buf); + return createCXString(""); + } + + // Return the C-string, but don't make a copy since it is already in + // the string buffer. + buf->Data.push_back('\0'); + return createCXString(buf); + } + + if (K == CXCursor_MacroDefinition) { + CXTranslationUnit TU = cxcursor::getCursorTU(C); + if (!TU) + return createCXString(""); + + CXStringBuf *buf = cxstring::getCXStringBuf(TU); + if (!buf) + return createCXString(""); + + { + USRGenerator UG(&cxcursor::getCursorASTUnit(C)->getASTContext(), + &buf->Data); + UG << "macro@" + << cxcursor::getCursorMacroDefinition(C)->getName()->getNameStart(); + } + buf->Data.push_back('\0'); + return createCXString(buf); + } + + return createCXString(""); +} + +CXString clang_constructUSR_ObjCIvar(const char *name, CXString classUSR) { + USRGenerator UG; + UG << extractUSRSuffix(clang_getCString(classUSR)); + UG->GenObjCIvar(name); + return createCXString(UG.str(), true); +} + +CXString clang_constructUSR_ObjCMethod(const char *name, + unsigned isInstanceMethod, + CXString classUSR) { + USRGenerator UG; + UG << extractUSRSuffix(clang_getCString(classUSR)); + UG->GenObjCMethod(name, isInstanceMethod); + return createCXString(UG.str(), true); +} + +CXString clang_constructUSR_ObjCClass(const char *name) { + USRGenerator UG; + UG->GenObjCClass(name); + return createCXString(UG.str(), true); +} + +CXString clang_constructUSR_ObjCProtocol(const char *name) { + USRGenerator UG; + UG->GenObjCProtocol(name); + return createCXString(UG.str(), true); +} + +CXString clang_constructUSR_ObjCCategory(const char *class_name, + const char *category_name) { + USRGenerator UG; + UG->GenObjCCategory(class_name, category_name); + return createCXString(UG.str(), true); +} + +CXString clang_constructUSR_ObjCProperty(const char *property, + CXString classUSR) { + USRGenerator UG; + UG << extractUSRSuffix(clang_getCString(classUSR)); + UG->GenObjCProperty(property); + return createCXString(UG.str(), true); +} + +} // end extern "C" diff --git a/clang/tools/libclang/CIndexer.cpp b/clang/tools/libclang/CIndexer.cpp new file mode 100644 index 0000000..d458789 --- /dev/null +++ b/clang/tools/libclang/CIndexer.cpp @@ -0,0 +1,152 @@ +//===- CIndex.cpp - Clang-C Source Indexing Library -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Clang-C Source Indexing library. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Program.h" + +#include <cstdio> +#include <vector> +#include <sstream> + +#ifdef __CYGWIN__ +#include <cygwin/version.h> +#include <sys/cygwin.h> +#define LLVM_ON_WIN32 1 +#endif + +#ifdef LLVM_ON_WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#endif + +using namespace clang; + +std::string CIndexer::getClangResourcesPath() { + // Did we already compute the path? + if (!ResourcesPath.empty()) + return ResourcesPath.str(); + + // Find the location where this library lives (libclang.dylib). +#ifdef LLVM_ON_WIN32 + MEMORY_BASIC_INFORMATION mbi; + char path[MAX_PATH]; + VirtualQuery((void *)(uintptr_t)clang_createTranslationUnit, &mbi, + sizeof(mbi)); + GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, path, MAX_PATH); + +#ifdef __CYGWIN__ + char w32path[MAX_PATH]; + strcpy(w32path, path); +#if CYGWIN_VERSION_API_MAJOR > 0 || CYGWIN_VERSION_API_MINOR >= 181 + cygwin_conv_path(CCP_WIN_A_TO_POSIX, w32path, path, MAX_PATH); +#else + cygwin_conv_to_full_posix_path(w32path, path); +#endif +#endif + + llvm::sys::Path LibClangPath(path); + LibClangPath.eraseComponent(); +#else + // This silly cast below avoids a C++ warning. + Dl_info info; + if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) == 0) + llvm_unreachable("Call to dladdr() failed"); + + llvm::sys::Path LibClangPath(info.dli_fname); + + // We now have the CIndex directory, locate clang relative to it. + LibClangPath.eraseComponent(); +#endif + + LibClangPath.appendComponent("clang"); + LibClangPath.appendComponent(CLANG_VERSION_STRING); + + // Cache our result. + ResourcesPath = LibClangPath; + return LibClangPath.str(); +} + +static llvm::sys::Path GetTemporaryPath() { + // FIXME: This is lame; sys::Path should provide this function (in particular, + // it should know how to find the temporary files dir). + std::string Error; + const char *TmpDir = ::getenv("TMPDIR"); + if (!TmpDir) + TmpDir = ::getenv("TEMP"); + if (!TmpDir) + TmpDir = ::getenv("TMP"); + if (!TmpDir) + TmpDir = "/tmp"; + llvm::sys::Path P(TmpDir); + P.appendComponent("remap"); + if (P.makeUnique(false, &Error)) + return llvm::sys::Path(""); + + // FIXME: Grumble, makeUnique sometimes leaves the file around!? PR3837. + P.eraseFromDisk(false, 0); + + return P; +} + +bool clang::RemapFiles(unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + std::vector<std::string> &RemapArgs, + std::vector<llvm::sys::Path> &TemporaryFiles) { + for (unsigned i = 0; i != num_unsaved_files; ++i) { + // Write the contents of this unsaved file into the temporary file. + llvm::sys::Path SavedFile(GetTemporaryPath()); + if (SavedFile.empty()) + return true; + + std::string ErrorInfo; + llvm::raw_fd_ostream OS(SavedFile.c_str(), ErrorInfo, + llvm::raw_fd_ostream::F_Binary); + if (!ErrorInfo.empty()) + return true; + + OS.write(unsaved_files[i].Contents, unsaved_files[i].Length); + OS.close(); + if (OS.has_error()) { + SavedFile.eraseFromDisk(); + OS.clear_error(); + return true; + } + + // Remap the file. + std::string RemapArg = unsaved_files[i].Filename; + RemapArg += ';'; + RemapArg += SavedFile.str(); + RemapArgs.push_back("-Xclang"); + RemapArgs.push_back("-remap-file"); + RemapArgs.push_back("-Xclang"); + RemapArgs.push_back(RemapArg); + TemporaryFiles.push_back(SavedFile); + } + + return false; +} + diff --git a/clang/tools/libclang/CIndexer.h b/clang/tools/libclang/CIndexer.h new file mode 100644 index 0000000..1e5fb82 --- /dev/null +++ b/clang/tools/libclang/CIndexer.h @@ -0,0 +1,104 @@ +//===- CIndexer.h - Clang-C Source Indexing Library -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines CIndexer, a subclass of Indexer that provides extra +// functionality needed by the CIndex library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CINDEXER_H +#define LLVM_CLANG_CINDEXER_H + +#include "clang-c/Index.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Path.h" +#include <vector> + +namespace llvm { + class CrashRecoveryContext; +} + +namespace clang { + class ASTUnit; + +class CIndexer { + bool OnlyLocalDecls; + bool DisplayDiagnostics; + unsigned Options; // CXGlobalOptFlags. + + llvm::sys::Path ResourcesPath; + std::string WorkingDir; + +public: + CIndexer() : OnlyLocalDecls(false), DisplayDiagnostics(false), + Options(CXGlobalOpt_None) { } + + /// \brief Whether we only want to see "local" declarations (that did not + /// come from a previous precompiled header). If false, we want to see all + /// declarations. + bool getOnlyLocalDecls() const { return OnlyLocalDecls; } + void setOnlyLocalDecls(bool Local = true) { OnlyLocalDecls = Local; } + + bool getDisplayDiagnostics() const { return DisplayDiagnostics; } + void setDisplayDiagnostics(bool Display = true) { + DisplayDiagnostics = Display; + } + + unsigned getCXGlobalOptFlags() const { return Options; } + void setCXGlobalOptFlags(unsigned options) { Options = options; } + + bool isOptEnabled(CXGlobalOptFlags opt) const { + return Options & opt; + } + + /// \brief Get the path of the clang resource files. + std::string getClangResourcesPath(); + + const std::string &getWorkingDirectory() const { return WorkingDir; } + void setWorkingDirectory(const std::string &Dir) { WorkingDir = Dir; } +}; + + /** + * \brief Given a set of "unsaved" files, create temporary files and + * construct the clang -cc1 argument list needed to perform the remapping. + * + * \returns true if an error occurred. + */ + bool RemapFiles(unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + std::vector<std::string> &RemapArgs, + std::vector<llvm::sys::Path> &TemporaryFiles); + + /// \brief Return the current size to request for "safety". + unsigned GetSafetyThreadStackSize(); + + /// \brief Set the current size to request for "safety" (or 0, if safety + /// threads should not be used). + void SetSafetyThreadStackSize(unsigned Value); + + /// \brief Execution the given code "safely", using crash recovery or safety + /// threads when possible. + /// + /// \return False if a crash was detected. + bool RunSafely(llvm::CrashRecoveryContext &CRC, + void (*Fn)(void*), void *UserData, unsigned Size = 0); + + /// \brief Set the thread priority to background. + /// FIXME: Move to llvm/Support. + void setThreadBackgroundPriority(); + + /// \brief Print libclang's resource usage to standard error. + void PrintLibclangResourceUsage(CXTranslationUnit TU); + + namespace cxindex { + void printDiagsToStderr(ASTUnit *Unit); + } +} + +#endif diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt new file mode 100644 index 0000000..fb0b91f --- /dev/null +++ b/clang/tools/libclang/CMakeLists.txt @@ -0,0 +1,90 @@ +set(LLVM_USED_LIBS + clangARCMigrate + clangRewrite + clangFrontend + clangDriver + clangSerialization + clangSema + clangEdit + clangAST + clangLex + clangBasic) + +set( LLVM_LINK_COMPONENTS + support + mc + ) + +set(SOURCES + ARCMigrate.cpp + CIndex.cpp + CIndexCXX.cpp + CIndexCodeCompletion.cpp + CIndexDiagnostic.cpp + CIndexDiagnostic.h + CIndexHigh.cpp + CIndexInclusionStack.cpp + CIndexUSRs.cpp + CIndexer.cpp + CIndexer.h + CXCursor.cpp + CXCursor.h + CXLoadedDiagnostic.cpp + CXLoadedDiagnostic.h + CXSourceLocation.cpp + CXSourceLocation.h + CXStoredDiagnostic.cpp + CXString.cpp + CXString.h + CXTranslationUnit.h + CXType.cpp + CXType.h + IndexBody.cpp + IndexDecl.cpp + IndexTypeSourceInfo.cpp + Index_Internal.h + Indexing.cpp + IndexingContext.cpp + IndexingContext.h + ../../include/clang-c/Index.h + ) + +if( LLVM_ENABLE_PIC ) + set(SHARED_LIBRARY TRUE) + add_clang_library(libclang ${SOURCES}) + + set_target_properties(libclang + PROPERTIES + OUTPUT_NAME "libclang" + VERSION ${LIBCLANG_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(LIBCLANG_LINK_FLAGS + "-Wl,-compatibility_version -Wl,1 -Wl,-dead_strip -Wl,-seg1addr -Wl,0xE0000000") + set_target_properties(libclang + PROPERTIES + LINK_FLAGS "${LIBCLANG_LINK_FLAGS}" + INSTALL_NAME_DIR "@executable_path/../lib") + endif() + + if(MSVC) + # windows.h doesn't compile with /Za + get_target_property(NON_ANSI_COMPILE_FLAGS libclang COMPILE_FLAGS) + string(REPLACE "/Za" "" NON_ANSI_COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) + set_target_properties(libclang PROPERTIES + COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) + endif() + + set(LIBCLANG_STATIC_TARGET_NAME libclang_static) +else() + set(LIBCLANG_STATIC_TARGET_NAME libclang) +endif() + +if( NOT BUILD_SHARED_LIBS AND NOT WIN32 ) + add_clang_library(${LIBCLANG_STATIC_TARGET_NAME} STATIC ${SOURCES}) + + set_target_properties(${LIBCLANG_STATIC_TARGET_NAME} + PROPERTIES + OUTPUT_NAME "libclang") +endif() diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp new file mode 100644 index 0000000..a298759 --- /dev/null +++ b/clang/tools/libclang/CXCursor.cpp @@ -0,0 +1,1156 @@ +//===- CXCursor.cpp - Routines for manipulating CXCursors -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. It should be the +// only file that has internal knowledge of the encoding of the data in +// CXCursor. +// +//===----------------------------------------------------------------------===// + +#include "CXTranslationUnit.h" +#include "CXCursor.h" +#include "CXString.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang-c/Index.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace cxcursor; + +CXCursor cxcursor::MakeCXCursorInvalid(CXCursorKind K) { + assert(K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid); + CXCursor C = { K, 0, { 0, 0, 0 } }; + return C; +} + +static CXCursorKind GetCursorKind(const Attr *A) { + assert(A && "Invalid arguments!"); + switch (A->getKind()) { + default: break; + case attr::IBAction: return CXCursor_IBActionAttr; + case attr::IBOutlet: return CXCursor_IBOutletAttr; + case attr::IBOutletCollection: return CXCursor_IBOutletCollectionAttr; + case attr::Final: return CXCursor_CXXFinalAttr; + case attr::Override: return CXCursor_CXXOverrideAttr; + case attr::Annotate: return CXCursor_AnnotateAttr; + case attr::AsmLabel: return CXCursor_AsmLabelAttr; + } + + return CXCursor_UnexposedAttr; +} + +CXCursor cxcursor::MakeCXCursor(const Attr *A, Decl *Parent, + CXTranslationUnit TU) { + assert(A && Parent && TU && "Invalid arguments!"); + CXCursor C = { GetCursorKind(A), 0, { Parent, (void*)A, TU } }; + return C; +} + +CXCursor cxcursor::MakeCXCursor(Decl *D, CXTranslationUnit TU, + SourceRange RegionOfInterest, + bool FirstInDeclGroup) { + assert(D && TU && "Invalid arguments!"); + + CXCursorKind K = getCursorKindForDecl(D); + + if (K == CXCursor_ObjCClassMethodDecl || + K == CXCursor_ObjCInstanceMethodDecl) { + int SelectorIdIndex = -1; + // Check if cursor points to a selector id. + if (RegionOfInterest.isValid() && + RegionOfInterest.getBegin() == RegionOfInterest.getEnd()) { + SmallVector<SourceLocation, 16> SelLocs; + cast<ObjCMethodDecl>(D)->getSelectorLocs(SelLocs); + SmallVector<SourceLocation, 16>::iterator + I=std::find(SelLocs.begin(), SelLocs.end(),RegionOfInterest.getBegin()); + if (I != SelLocs.end()) + SelectorIdIndex = I - SelLocs.begin(); + } + CXCursor C = { K, SelectorIdIndex, + { D, (void*)(intptr_t) (FirstInDeclGroup ? 1 : 0), TU }}; + return C; + } + + CXCursor C = { K, 0, { D, (void*)(intptr_t) (FirstInDeclGroup ? 1 : 0), TU }}; + return C; +} + +CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, + SourceRange RegionOfInterest) { + assert(S && TU && "Invalid arguments!"); + CXCursorKind K = CXCursor_NotImplemented; + + switch (S->getStmtClass()) { + case Stmt::NoStmtClass: + break; + + case Stmt::CaseStmtClass: + K = CXCursor_CaseStmt; + break; + + case Stmt::DefaultStmtClass: + K = CXCursor_DefaultStmt; + break; + + case Stmt::IfStmtClass: + K = CXCursor_IfStmt; + break; + + case Stmt::SwitchStmtClass: + K = CXCursor_SwitchStmt; + break; + + case Stmt::WhileStmtClass: + K = CXCursor_WhileStmt; + break; + + case Stmt::DoStmtClass: + K = CXCursor_DoStmt; + break; + + case Stmt::ForStmtClass: + K = CXCursor_ForStmt; + break; + + case Stmt::GotoStmtClass: + K = CXCursor_GotoStmt; + break; + + case Stmt::IndirectGotoStmtClass: + K = CXCursor_IndirectGotoStmt; + break; + + case Stmt::ContinueStmtClass: + K = CXCursor_ContinueStmt; + break; + + case Stmt::BreakStmtClass: + K = CXCursor_BreakStmt; + break; + + case Stmt::ReturnStmtClass: + K = CXCursor_ReturnStmt; + break; + + case Stmt::AsmStmtClass: + K = CXCursor_AsmStmt; + break; + + case Stmt::ObjCAtTryStmtClass: + K = CXCursor_ObjCAtTryStmt; + break; + + case Stmt::ObjCAtCatchStmtClass: + K = CXCursor_ObjCAtCatchStmt; + break; + + case Stmt::ObjCAtFinallyStmtClass: + K = CXCursor_ObjCAtFinallyStmt; + break; + + case Stmt::ObjCAtThrowStmtClass: + K = CXCursor_ObjCAtThrowStmt; + break; + + case Stmt::ObjCAtSynchronizedStmtClass: + K = CXCursor_ObjCAtSynchronizedStmt; + break; + + case Stmt::ObjCAutoreleasePoolStmtClass: + K = CXCursor_ObjCAutoreleasePoolStmt; + break; + + case Stmt::ObjCForCollectionStmtClass: + K = CXCursor_ObjCForCollectionStmt; + break; + + case Stmt::CXXCatchStmtClass: + K = CXCursor_CXXCatchStmt; + break; + + case Stmt::CXXTryStmtClass: + K = CXCursor_CXXTryStmt; + break; + + case Stmt::CXXForRangeStmtClass: + K = CXCursor_CXXForRangeStmt; + break; + + case Stmt::SEHTryStmtClass: + K = CXCursor_SEHTryStmt; + break; + + case Stmt::SEHExceptStmtClass: + K = CXCursor_SEHExceptStmt; + break; + + case Stmt::SEHFinallyStmtClass: + K = CXCursor_SEHFinallyStmt; + break; + + case Stmt::ArrayTypeTraitExprClass: + case Stmt::AsTypeExprClass: + case Stmt::AtomicExprClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::BinaryTypeTraitExprClass: + case Stmt::TypeTraitExprClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::CXXUuidofExprClass: + case Stmt::ChooseExprClass: + case Stmt::DesignatedInitExprClass: + case Stmt::ExprWithCleanupsClass: + case Stmt::ExpressionTraitExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::ImplicitCastExprClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::MaterializeTemporaryExprClass: + case Stmt::ObjCIndirectCopyRestoreExprClass: + case Stmt::OffsetOfExprClass: + case Stmt::ParenListExprClass: + case Stmt::PredefinedExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::UnaryExprOrTypeTraitExprClass: + case Stmt::UnaryTypeTraitExprClass: + case Stmt::VAArgExprClass: + case Stmt::ObjCArrayLiteralClass: + case Stmt::ObjCDictionaryLiteralClass: + case Stmt::ObjCNumericLiteralClass: + case Stmt::ObjCSubscriptRefExprClass: + K = CXCursor_UnexposedExpr; + break; + + case Stmt::OpaqueValueExprClass: + if (Expr *Src = cast<OpaqueValueExpr>(S)->getSourceExpr()) + return MakeCXCursor(Src, Parent, TU, RegionOfInterest); + K = CXCursor_UnexposedExpr; + break; + + case Stmt::PseudoObjectExprClass: + return MakeCXCursor(cast<PseudoObjectExpr>(S)->getSyntacticForm(), + Parent, TU, RegionOfInterest); + + case Stmt::CompoundStmtClass: + K = CXCursor_CompoundStmt; + break; + + case Stmt::NullStmtClass: + K = CXCursor_NullStmt; + break; + + case Stmt::LabelStmtClass: + K = CXCursor_LabelStmt; + break; + + case Stmt::AttributedStmtClass: + K = CXCursor_UnexposedStmt; + break; + + case Stmt::DeclStmtClass: + K = CXCursor_DeclStmt; + break; + + case Stmt::IntegerLiteralClass: + K = CXCursor_IntegerLiteral; + break; + + case Stmt::FloatingLiteralClass: + K = CXCursor_FloatingLiteral; + break; + + case Stmt::ImaginaryLiteralClass: + K = CXCursor_ImaginaryLiteral; + break; + + case Stmt::StringLiteralClass: + K = CXCursor_StringLiteral; + break; + + case Stmt::CharacterLiteralClass: + K = CXCursor_CharacterLiteral; + break; + + case Stmt::ParenExprClass: + K = CXCursor_ParenExpr; + break; + + case Stmt::UnaryOperatorClass: + K = CXCursor_UnaryOperator; + break; + + case Stmt::CXXNoexceptExprClass: + K = CXCursor_UnaryExpr; + break; + + case Stmt::ArraySubscriptExprClass: + K = CXCursor_ArraySubscriptExpr; + break; + + case Stmt::BinaryOperatorClass: + K = CXCursor_BinaryOperator; + break; + + case Stmt::CompoundAssignOperatorClass: + K = CXCursor_CompoundAssignOperator; + break; + + case Stmt::ConditionalOperatorClass: + K = CXCursor_ConditionalOperator; + break; + + case Stmt::CStyleCastExprClass: + K = CXCursor_CStyleCastExpr; + break; + + case Stmt::CompoundLiteralExprClass: + K = CXCursor_CompoundLiteralExpr; + break; + + case Stmt::InitListExprClass: + K = CXCursor_InitListExpr; + break; + + case Stmt::AddrLabelExprClass: + K = CXCursor_AddrLabelExpr; + break; + + case Stmt::StmtExprClass: + K = CXCursor_StmtExpr; + break; + + case Stmt::GenericSelectionExprClass: + K = CXCursor_GenericSelectionExpr; + break; + + case Stmt::GNUNullExprClass: + K = CXCursor_GNUNullExpr; + break; + + case Stmt::CXXStaticCastExprClass: + K = CXCursor_CXXStaticCastExpr; + break; + + case Stmt::CXXDynamicCastExprClass: + K = CXCursor_CXXDynamicCastExpr; + break; + + case Stmt::CXXReinterpretCastExprClass: + K = CXCursor_CXXReinterpretCastExpr; + break; + + case Stmt::CXXConstCastExprClass: + K = CXCursor_CXXConstCastExpr; + break; + + case Stmt::CXXFunctionalCastExprClass: + K = CXCursor_CXXFunctionalCastExpr; + break; + + case Stmt::CXXTypeidExprClass: + K = CXCursor_CXXTypeidExpr; + break; + + case Stmt::CXXBoolLiteralExprClass: + K = CXCursor_CXXBoolLiteralExpr; + break; + + case Stmt::CXXNullPtrLiteralExprClass: + K = CXCursor_CXXNullPtrLiteralExpr; + break; + + case Stmt::CXXThisExprClass: + K = CXCursor_CXXThisExpr; + break; + + case Stmt::CXXThrowExprClass: + K = CXCursor_CXXThrowExpr; + break; + + case Stmt::CXXNewExprClass: + K = CXCursor_CXXNewExpr; + break; + + case Stmt::CXXDeleteExprClass: + K = CXCursor_CXXDeleteExpr; + break; + + case Stmt::ObjCStringLiteralClass: + K = CXCursor_ObjCStringLiteral; + break; + + case Stmt::ObjCEncodeExprClass: + K = CXCursor_ObjCEncodeExpr; + break; + + case Stmt::ObjCSelectorExprClass: + K = CXCursor_ObjCSelectorExpr; + break; + + case Stmt::ObjCProtocolExprClass: + K = CXCursor_ObjCProtocolExpr; + break; + + case Stmt::ObjCBoolLiteralExprClass: + K = CXCursor_ObjCBoolLiteralExpr; + break; + + case Stmt::ObjCBridgedCastExprClass: + K = CXCursor_ObjCBridgedCastExpr; + break; + + case Stmt::BlockExprClass: + K = CXCursor_BlockExpr; + break; + + case Stmt::PackExpansionExprClass: + K = CXCursor_PackExpansionExpr; + break; + + case Stmt::SizeOfPackExprClass: + K = CXCursor_SizeOfPackExpr; + break; + + case Stmt::DeclRefExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: + case Stmt::SubstNonTypeTemplateParmPackExprClass: + case Stmt::UnresolvedLookupExprClass: + K = CXCursor_DeclRefExpr; + break; + + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::MemberExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCIvarRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + case Stmt::UnresolvedMemberExprClass: + K = CXCursor_MemberRefExpr; + break; + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::CUDAKernelCallExprClass: + case Stmt::CXXConstructExprClass: + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::UserDefinedLiteralClass: + K = CXCursor_CallExpr; + break; + + case Stmt::LambdaExprClass: + K = CXCursor_LambdaExpr; + break; + + case Stmt::ObjCMessageExprClass: { + K = CXCursor_ObjCMessageExpr; + int SelectorIdIndex = -1; + // Check if cursor points to a selector id. + if (RegionOfInterest.isValid() && + RegionOfInterest.getBegin() == RegionOfInterest.getEnd()) { + SmallVector<SourceLocation, 16> SelLocs; + cast<ObjCMessageExpr>(S)->getSelectorLocs(SelLocs); + SmallVector<SourceLocation, 16>::iterator + I=std::find(SelLocs.begin(), SelLocs.end(),RegionOfInterest.getBegin()); + if (I != SelLocs.end()) + SelectorIdIndex = I - SelLocs.begin(); + } + CXCursor C = { K, 0, { Parent, S, TU } }; + return getSelectorIdentifierCursor(SelectorIdIndex, C); + } + + case Stmt::MSDependentExistsStmtClass: + K = CXCursor_UnexposedStmt; + break; + } + + CXCursor C = { K, 0, { Parent, S, TU } }; + return C; +} + +CXCursor cxcursor::MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Super && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_ObjCSuperClassRef, 0, { Super, RawLoc, TU } }; + return C; +} + +std::pair<ObjCInterfaceDecl *, SourceLocation> +cxcursor::getCursorObjCSuperClassRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCSuperClassRef); + return std::make_pair(static_cast<ObjCInterfaceDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorObjCProtocolRef(const ObjCProtocolDecl *Proto, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Proto && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_ObjCProtocolRef, 0, { (void*)Proto, RawLoc, TU } }; + return C; +} + +std::pair<ObjCProtocolDecl *, SourceLocation> +cxcursor::getCursorObjCProtocolRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCProtocolRef); + return std::make_pair(static_cast<ObjCProtocolDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorObjCClassRef(const ObjCInterfaceDecl *Class, + SourceLocation Loc, + CXTranslationUnit TU) { + // 'Class' can be null for invalid code. + if (!Class) + return MakeCXCursorInvalid(CXCursor_InvalidCode); + assert(TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_ObjCClassRef, 0, { (void*)Class, RawLoc, TU } }; + return C; +} + +std::pair<ObjCInterfaceDecl *, SourceLocation> +cxcursor::getCursorObjCClassRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCClassRef); + return std::make_pair(static_cast<ObjCInterfaceDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorTypeRef(const TypeDecl *Type, SourceLocation Loc, + CXTranslationUnit TU) { + assert(Type && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_TypeRef, 0, { (void*)Type, RawLoc, TU } }; + return C; +} + +std::pair<TypeDecl *, SourceLocation> +cxcursor::getCursorTypeRef(CXCursor C) { + assert(C.kind == CXCursor_TypeRef); + return std::make_pair(static_cast<TypeDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorTemplateRef(const TemplateDecl *Template, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Template && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_TemplateRef, 0, { (void*)Template, RawLoc, TU } }; + return C; +} + +std::pair<TemplateDecl *, SourceLocation> +cxcursor::getCursorTemplateRef(CXCursor C) { + assert(C.kind == CXCursor_TemplateRef); + return std::make_pair(static_cast<TemplateDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorNamespaceRef(const NamedDecl *NS, + SourceLocation Loc, + CXTranslationUnit TU) { + + assert(NS && (isa<NamespaceDecl>(NS) || isa<NamespaceAliasDecl>(NS)) && TU && + "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_NamespaceRef, 0, { (void*)NS, RawLoc, TU } }; + return C; +} + +std::pair<NamedDecl *, SourceLocation> +cxcursor::getCursorNamespaceRef(CXCursor C) { + assert(C.kind == CXCursor_NamespaceRef); + return std::make_pair(static_cast<NamedDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorVariableRef(const VarDecl *Var, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Var && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_VariableRef, 0, { (void*)Var, RawLoc, TU } }; + return C; +} + +std::pair<VarDecl *, SourceLocation> +cxcursor::getCursorVariableRef(CXCursor C) { + assert(C.kind == CXCursor_VariableRef); + return std::make_pair(static_cast<VarDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorMemberRef(const FieldDecl *Field, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Field && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_MemberRef, 0, { (void*)Field, RawLoc, TU } }; + return C; +} + +std::pair<FieldDecl *, SourceLocation> +cxcursor::getCursorMemberRef(CXCursor C) { + assert(C.kind == CXCursor_MemberRef); + return std::make_pair(static_cast<FieldDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorCXXBaseSpecifier(const CXXBaseSpecifier *B, + CXTranslationUnit TU){ + CXCursor C = { CXCursor_CXXBaseSpecifier, 0, { (void*)B, 0, TU } }; + return C; +} + +CXXBaseSpecifier *cxcursor::getCursorCXXBaseSpecifier(CXCursor C) { + assert(C.kind == CXCursor_CXXBaseSpecifier); + return static_cast<CXXBaseSpecifier*>(C.data[0]); +} + +CXCursor cxcursor::MakePreprocessingDirectiveCursor(SourceRange Range, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_PreprocessingDirective, 0, + { reinterpret_cast<void *>(Range.getBegin().getRawEncoding()), + reinterpret_cast<void *>(Range.getEnd().getRawEncoding()), + TU } + }; + return C; +} + +SourceRange cxcursor::getCursorPreprocessingDirective(CXCursor C) { + assert(C.kind == CXCursor_PreprocessingDirective); + SourceRange Range = SourceRange(SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t> (C.data[0])), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t> (C.data[1]))); + ASTUnit *TU = getCursorASTUnit(C); + return TU->mapRangeFromPreamble(Range); +} + +CXCursor cxcursor::MakeMacroDefinitionCursor(MacroDefinition *MI, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_MacroDefinition, 0, { MI, 0, TU } }; + return C; +} + +MacroDefinition *cxcursor::getCursorMacroDefinition(CXCursor C) { + assert(C.kind == CXCursor_MacroDefinition); + return static_cast<MacroDefinition *>(C.data[0]); +} + +CXCursor cxcursor::MakeMacroExpansionCursor(MacroExpansion *MI, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_MacroExpansion, 0, { MI, 0, TU } }; + return C; +} + +MacroExpansion *cxcursor::getCursorMacroExpansion(CXCursor C) { + assert(C.kind == CXCursor_MacroExpansion); + return static_cast<MacroExpansion *>(C.data[0]); +} + +CXCursor cxcursor::MakeInclusionDirectiveCursor(InclusionDirective *ID, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_InclusionDirective, 0, { ID, 0, TU } }; + return C; +} + +InclusionDirective *cxcursor::getCursorInclusionDirective(CXCursor C) { + assert(C.kind == CXCursor_InclusionDirective); + return static_cast<InclusionDirective *>(C.data[0]); +} + +CXCursor cxcursor::MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Label && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_LabelRef, 0, { Label, RawLoc, TU } }; + return C; +} + +std::pair<LabelStmt*, SourceLocation> +cxcursor::getCursorLabelRef(CXCursor C) { + assert(C.kind == CXCursor_LabelRef); + return std::make_pair(static_cast<LabelStmt *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorOverloadedDeclRef(OverloadExpr *E, + CXTranslationUnit TU) { + assert(E && TU && "Invalid arguments!"); + OverloadedDeclRefStorage Storage(E); + void *RawLoc = reinterpret_cast<void *>(E->getNameLoc().getRawEncoding()); + CXCursor C = { + CXCursor_OverloadedDeclRef, 0, + { Storage.getOpaqueValue(), RawLoc, TU } + }; + return C; +} + +CXCursor cxcursor::MakeCursorOverloadedDeclRef(Decl *D, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(D && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + OverloadedDeclRefStorage Storage(D); + CXCursor C = { + CXCursor_OverloadedDeclRef, 0, + { Storage.getOpaqueValue(), RawLoc, TU } + }; + return C; +} + +CXCursor cxcursor::MakeCursorOverloadedDeclRef(TemplateName Name, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Name.getAsOverloadedTemplate() && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + OverloadedDeclRefStorage Storage(Name.getAsOverloadedTemplate()); + CXCursor C = { + CXCursor_OverloadedDeclRef, 0, + { Storage.getOpaqueValue(), RawLoc, TU } + }; + return C; +} + +std::pair<cxcursor::OverloadedDeclRefStorage, SourceLocation> +cxcursor::getCursorOverloadedDeclRef(CXCursor C) { + assert(C.kind == CXCursor_OverloadedDeclRef); + return std::make_pair(OverloadedDeclRefStorage::getFromOpaqueValue(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +Decl *cxcursor::getCursorDecl(CXCursor Cursor) { + return (Decl *)Cursor.data[0]; +} + +Expr *cxcursor::getCursorExpr(CXCursor Cursor) { + return dyn_cast_or_null<Expr>(getCursorStmt(Cursor)); +} + +Stmt *cxcursor::getCursorStmt(CXCursor Cursor) { + if (Cursor.kind == CXCursor_ObjCSuperClassRef || + Cursor.kind == CXCursor_ObjCProtocolRef || + Cursor.kind == CXCursor_ObjCClassRef) + return 0; + + return (Stmt *)Cursor.data[1]; +} + +Attr *cxcursor::getCursorAttr(CXCursor Cursor) { + return (Attr *)Cursor.data[1]; +} + +Decl *cxcursor::getCursorParentDecl(CXCursor Cursor) { + return (Decl *)Cursor.data[0]; +} + +ASTContext &cxcursor::getCursorContext(CXCursor Cursor) { + return getCursorASTUnit(Cursor)->getASTContext(); +} + +ASTUnit *cxcursor::getCursorASTUnit(CXCursor Cursor) { + CXTranslationUnit TU = static_cast<CXTranslationUnit>(Cursor.data[2]); + if (!TU) + return 0; + return static_cast<ASTUnit *>(TU->TUData); +} + +CXTranslationUnit cxcursor::getCursorTU(CXCursor Cursor) { + return static_cast<CXTranslationUnit>(Cursor.data[2]); +} + +static void CollectOverriddenMethodsRecurse(CXTranslationUnit TU, + ObjCContainerDecl *Container, + ObjCMethodDecl *Method, + SmallVectorImpl<CXCursor> &Methods, + bool MovedToSuper) { + if (!Container) + return; + + // In categories look for overriden methods from protocols. A method from + // category is not "overriden" since it is considered as the "same" method + // (same USR) as the one from the interface. + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(Container)) { + // Check whether we have a matching method at this category but only if we + // are at the super class level. + if (MovedToSuper) + if (ObjCMethodDecl * + Overridden = Container->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + if (Method != Overridden) { + // We found an override at this category; there is no need to look + // into its protocols. + Methods.push_back(MakeCXCursor(Overridden, TU)); + return; + } + + for (ObjCCategoryDecl::protocol_iterator P = Category->protocol_begin(), + PEnd = Category->protocol_end(); + P != PEnd; ++P) + CollectOverriddenMethodsRecurse(TU, *P, Method, Methods, MovedToSuper); + return; + } + + // Check whether we have a matching method at this level. + if (ObjCMethodDecl *Overridden = Container->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + if (Method != Overridden) { + // We found an override at this level; there is no need to look + // into other protocols or categories. + Methods.push_back(MakeCXCursor(Overridden, TU)); + return; + } + + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) { + for (ObjCProtocolDecl::protocol_iterator P = Protocol->protocol_begin(), + PEnd = Protocol->protocol_end(); + P != PEnd; ++P) + CollectOverriddenMethodsRecurse(TU, *P, Method, Methods, MovedToSuper); + } + + if (ObjCInterfaceDecl *Interface = dyn_cast<ObjCInterfaceDecl>(Container)) { + for (ObjCInterfaceDecl::protocol_iterator P = Interface->protocol_begin(), + PEnd = Interface->protocol_end(); + P != PEnd; ++P) + CollectOverriddenMethodsRecurse(TU, *P, Method, Methods, MovedToSuper); + + for (ObjCCategoryDecl *Category = Interface->getCategoryList(); + Category; Category = Category->getNextClassCategory()) + CollectOverriddenMethodsRecurse(TU, Category, Method, Methods, + MovedToSuper); + + if (ObjCInterfaceDecl *Super = Interface->getSuperClass()) + return CollectOverriddenMethodsRecurse(TU, Super, Method, Methods, + /*MovedToSuper=*/true); + } +} + +static inline void CollectOverriddenMethods(CXTranslationUnit TU, + ObjCContainerDecl *Container, + ObjCMethodDecl *Method, + SmallVectorImpl<CXCursor> &Methods) { + CollectOverriddenMethodsRecurse(TU, Container, Method, Methods, + /*MovedToSuper=*/false); +} + +void cxcursor::getOverriddenCursors(CXCursor cursor, + SmallVectorImpl<CXCursor> &overridden) { + assert(clang_isDeclaration(cursor.kind)); + Decl *D = getCursorDecl(cursor); + if (!D) + return; + + // Handle C++ member functions. + CXTranslationUnit TU = getCursorTU(cursor); + if (CXXMethodDecl *CXXMethod = dyn_cast<CXXMethodDecl>(D)) { + for (CXXMethodDecl::method_iterator + M = CXXMethod->begin_overridden_methods(), + MEnd = CXXMethod->end_overridden_methods(); + M != MEnd; ++M) + overridden.push_back(MakeCXCursor(const_cast<CXXMethodDecl*>(*M), TU)); + return; + } + + ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(D); + if (!Method) + return; + + if (ObjCProtocolDecl * + ProtD = dyn_cast<ObjCProtocolDecl>(Method->getDeclContext())) { + CollectOverriddenMethods(TU, ProtD, Method, overridden); + + } else if (ObjCImplDecl * + IMD = dyn_cast<ObjCImplDecl>(Method->getDeclContext())) { + ObjCInterfaceDecl *ID = IMD->getClassInterface(); + if (!ID) + return; + // Start searching for overridden methods using the method from the + // interface as starting point. + if (ObjCMethodDecl *IFaceMeth = ID->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + Method = IFaceMeth; + CollectOverriddenMethods(TU, ID, Method, overridden); + + } else if (ObjCCategoryDecl * + CatD = dyn_cast<ObjCCategoryDecl>(Method->getDeclContext())) { + ObjCInterfaceDecl *ID = CatD->getClassInterface(); + if (!ID) + return; + // Start searching for overridden methods using the method from the + // interface as starting point. + if (ObjCMethodDecl *IFaceMeth = ID->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + Method = IFaceMeth; + CollectOverriddenMethods(TU, ID, Method, overridden); + + } else { + CollectOverriddenMethods(TU, + dyn_cast_or_null<ObjCContainerDecl>(Method->getDeclContext()), + Method, overridden); + } +} + +std::pair<int, SourceLocation> +cxcursor::getSelectorIdentifierIndexAndLoc(CXCursor cursor) { + if (cursor.kind == CXCursor_ObjCMessageExpr) { + if (cursor.xdata != -1) + return std::make_pair(cursor.xdata, + cast<ObjCMessageExpr>(getCursorExpr(cursor)) + ->getSelectorLoc(cursor.xdata)); + } else if (cursor.kind == CXCursor_ObjCClassMethodDecl || + cursor.kind == CXCursor_ObjCInstanceMethodDecl) { + if (cursor.xdata != -1) + return std::make_pair(cursor.xdata, + cast<ObjCMethodDecl>(getCursorDecl(cursor)) + ->getSelectorLoc(cursor.xdata)); + } + + return std::make_pair(-1, SourceLocation()); +} + +CXCursor cxcursor::getSelectorIdentifierCursor(int SelIdx, CXCursor cursor) { + CXCursor newCursor = cursor; + + if (cursor.kind == CXCursor_ObjCMessageExpr) { + if (SelIdx == -1 || + unsigned(SelIdx) >= cast<ObjCMessageExpr>(getCursorExpr(cursor)) + ->getNumSelectorLocs()) + newCursor.xdata = -1; + else + newCursor.xdata = SelIdx; + } else if (cursor.kind == CXCursor_ObjCClassMethodDecl || + cursor.kind == CXCursor_ObjCInstanceMethodDecl) { + if (SelIdx == -1 || + unsigned(SelIdx) >= cast<ObjCMethodDecl>(getCursorDecl(cursor)) + ->getNumSelectorLocs()) + newCursor.xdata = -1; + else + newCursor.xdata = SelIdx; + } + + return newCursor; +} + +CXCursor cxcursor::getTypeRefCursor(CXCursor cursor) { + if (cursor.kind != CXCursor_CallExpr) + return cursor; + + if (cursor.xdata == 0) + return cursor; + + Expr *E = getCursorExpr(cursor); + TypeSourceInfo *Type = 0; + if (CXXUnresolvedConstructExpr * + UnCtor = dyn_cast<CXXUnresolvedConstructExpr>(E)) { + Type = UnCtor->getTypeSourceInfo(); + } else if (CXXTemporaryObjectExpr *Tmp = dyn_cast<CXXTemporaryObjectExpr>(E)){ + Type = Tmp->getTypeSourceInfo(); + } + + if (!Type) + return cursor; + + CXTranslationUnit TU = getCursorTU(cursor); + QualType Ty = Type->getType(); + TypeLoc TL = Type->getTypeLoc(); + SourceLocation Loc = TL.getBeginLoc(); + + if (const ElaboratedType *ElabT = Ty->getAs<ElaboratedType>()) { + Ty = ElabT->getNamedType(); + ElaboratedTypeLoc ElabTL = cast<ElaboratedTypeLoc>(TL); + Loc = ElabTL.getNamedTypeLoc().getBeginLoc(); + } + + if (const TypedefType *Typedef = Ty->getAs<TypedefType>()) + return MakeCursorTypeRef(Typedef->getDecl(), Loc, TU); + if (const TagType *Tag = Ty->getAs<TagType>()) + return MakeCursorTypeRef(Tag->getDecl(), Loc, TU); + if (const TemplateTypeParmType *TemplP = Ty->getAs<TemplateTypeParmType>()) + return MakeCursorTypeRef(TemplP->getDecl(), Loc, TU); + + return cursor; +} + +bool cxcursor::operator==(CXCursor X, CXCursor Y) { + return X.kind == Y.kind && X.data[0] == Y.data[0] && X.data[1] == Y.data[1] && + X.data[2] == Y.data[2]; +} + +// FIXME: Remove once we can model DeclGroups and their appropriate ranges +// properly in the ASTs. +bool cxcursor::isFirstInDeclGroup(CXCursor C) { + assert(clang_isDeclaration(C.kind)); + return ((uintptr_t) (C.data[1])) != 0; +} + +//===----------------------------------------------------------------------===// +// libclang CXCursor APIs +//===----------------------------------------------------------------------===// + +extern "C" { + +int clang_Cursor_isNull(CXCursor cursor) { + return clang_equalCursors(cursor, clang_getNullCursor()); +} + +CXTranslationUnit clang_Cursor_getTranslationUnit(CXCursor cursor) { + return getCursorTU(cursor); +} + +int clang_Cursor_getNumArguments(CXCursor C) { + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D)) + return MD->param_size(); + if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) + return FD->param_size(); + } + + return -1; +} + +CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i) { + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D)) { + if (i < MD->param_size()) + return cxcursor::MakeCXCursor(MD->param_begin()[i], + cxcursor::getCursorTU(C)); + } else if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { + if (i < FD->param_size()) + return cxcursor::MakeCXCursor(FD->param_begin()[i], + cxcursor::getCursorTU(C)); + } + } + + return clang_getNullCursor(); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// CXCursorSet. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap<CXCursor, unsigned> CXCursorSet_Impl; + +static inline CXCursorSet packCXCursorSet(CXCursorSet_Impl *setImpl) { + return (CXCursorSet) setImpl; +} +static inline CXCursorSet_Impl *unpackCXCursorSet(CXCursorSet set) { + return (CXCursorSet_Impl*) set; +} +namespace llvm { +template<> struct DenseMapInfo<CXCursor> { +public: + static inline CXCursor getEmptyKey() { + return MakeCXCursorInvalid(CXCursor_InvalidFile); + } + static inline CXCursor getTombstoneKey() { + return MakeCXCursorInvalid(CXCursor_NoDeclFound); + } + static inline unsigned getHashValue(const CXCursor &cursor) { + return llvm::DenseMapInfo<std::pair<void*,void*> > + ::getHashValue(std::make_pair(cursor.data[0], cursor.data[1])); + } + static inline bool isEqual(const CXCursor &x, const CXCursor &y) { + return x.kind == y.kind && + x.data[0] == y.data[0] && + x.data[1] == y.data[1]; + } +}; +} + +extern "C" { +CXCursorSet clang_createCXCursorSet() { + return packCXCursorSet(new CXCursorSet_Impl()); +} + +void clang_disposeCXCursorSet(CXCursorSet set) { + delete unpackCXCursorSet(set); +} + +unsigned clang_CXCursorSet_contains(CXCursorSet set, CXCursor cursor) { + CXCursorSet_Impl *setImpl = unpackCXCursorSet(set); + if (!setImpl) + return 0; + return setImpl->find(cursor) == setImpl->end(); +} + +unsigned clang_CXCursorSet_insert(CXCursorSet set, CXCursor cursor) { + // Do not insert invalid cursors into the set. + if (cursor.kind >= CXCursor_FirstInvalid && + cursor.kind <= CXCursor_LastInvalid) + return 1; + + CXCursorSet_Impl *setImpl = unpackCXCursorSet(set); + if (!setImpl) + return 1; + unsigned &entry = (*setImpl)[cursor]; + unsigned flag = entry == 0 ? 1 : 0; + entry = 1; + return flag; +} + +CXCompletionString clang_getCursorCompletionString(CXCursor cursor) { + enum CXCursorKind kind = clang_getCursorKind(cursor); + if (clang_isDeclaration(kind)) { + Decl *decl = getCursorDecl(cursor); + if (NamedDecl *namedDecl = dyn_cast_or_null<NamedDecl>(decl)) { + ASTUnit *unit = getCursorASTUnit(cursor); + CodeCompletionResult Result(namedDecl); + CodeCompletionString *String + = Result.CreateCodeCompletionString(unit->getASTContext(), + unit->getPreprocessor(), + unit->getCodeCompletionTUInfo().getAllocator(), + unit->getCodeCompletionTUInfo()); + return String; + } + } + else if (kind == CXCursor_MacroDefinition) { + MacroDefinition *definition = getCursorMacroDefinition(cursor); + const IdentifierInfo *MacroInfo = definition->getName(); + ASTUnit *unit = getCursorASTUnit(cursor); + CodeCompletionResult Result(const_cast<IdentifierInfo *>(MacroInfo)); + CodeCompletionString *String + = Result.CreateCodeCompletionString(unit->getASTContext(), + unit->getPreprocessor(), + unit->getCodeCompletionTUInfo().getAllocator(), + unit->getCodeCompletionTUInfo()); + return String; + } + return NULL; +} + +} // end: extern "C" diff --git a/clang/tools/libclang/CXCursor.h b/clang/tools/libclang/CXCursor.h new file mode 100644 index 0000000..947b0a3 --- /dev/null +++ b/clang/tools/libclang/CXCursor.h @@ -0,0 +1,249 @@ +//===- CXCursor.h - Routines for manipulating CXCursors -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXCURSOR_H +#define LLVM_CLANG_CXCURSOR_H + +#include "clang-c/Index.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerUnion.h" +#include <utility> + +namespace clang { + +class ASTContext; +class ASTUnit; +class Attr; +class CXXBaseSpecifier; +class Decl; +class Expr; +class FieldDecl; +class InclusionDirective; +class LabelStmt; +class MacroDefinition; +class MacroExpansion; +class NamedDecl; +class ObjCInterfaceDecl; +class ObjCProtocolDecl; +class OverloadedTemplateStorage; +class OverloadExpr; +class Stmt; +class TemplateDecl; +class TemplateName; +class TypeDecl; +class VarDecl; + +namespace cxcursor { + +CXCursor getCursor(CXTranslationUnit, SourceLocation); + +CXCursor MakeCXCursor(const clang::Attr *A, clang::Decl *Parent, + CXTranslationUnit TU); +CXCursor MakeCXCursor(clang::Decl *D, CXTranslationUnit TU, + SourceRange RegionOfInterest = SourceRange(), + bool FirstInDeclGroup = true); +CXCursor MakeCXCursor(clang::Stmt *S, clang::Decl *Parent, + CXTranslationUnit TU, + SourceRange RegionOfInterest = SourceRange()); +CXCursor MakeCXCursorInvalid(CXCursorKind K); + +/// \brief Create an Objective-C superclass reference at the given location. +CXCursor MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, + SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack an ObjCSuperClassRef cursor into the interface it references +/// and optionally the location where the reference occurred. +std::pair<ObjCInterfaceDecl *, SourceLocation> + getCursorObjCSuperClassRef(CXCursor C); + +/// \brief Create an Objective-C protocol reference at the given location. +CXCursor MakeCursorObjCProtocolRef(const ObjCProtocolDecl *Proto, + SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack an ObjCProtocolRef cursor into the protocol it references +/// and optionally the location where the reference occurred. +std::pair<ObjCProtocolDecl *, SourceLocation> + getCursorObjCProtocolRef(CXCursor C); + +/// \brief Create an Objective-C class reference at the given location. +CXCursor MakeCursorObjCClassRef(const ObjCInterfaceDecl *Class, + SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack an ObjCClassRef cursor into the class it references +/// and optionally the location where the reference occurred. +std::pair<ObjCInterfaceDecl *, SourceLocation> + getCursorObjCClassRef(CXCursor C); + +/// \brief Create a type reference at the given location. +CXCursor MakeCursorTypeRef(const TypeDecl *Type, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a TypeRef cursor into the class it references +/// and optionally the location where the reference occurred. +std::pair<TypeDecl *, SourceLocation> getCursorTypeRef(CXCursor C); + +/// \brief Create a reference to a template at the given location. +CXCursor MakeCursorTemplateRef(const TemplateDecl *Template, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a TemplateRef cursor into the template it references and +/// the location where the reference occurred. +std::pair<TemplateDecl *, SourceLocation> getCursorTemplateRef(CXCursor C); + +/// \brief Create a reference to a namespace or namespace alias at the given +/// location. +CXCursor MakeCursorNamespaceRef(const NamedDecl *NS, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a NamespaceRef cursor into the namespace or namespace alias +/// it references and the location where the reference occurred. +std::pair<NamedDecl *, SourceLocation> getCursorNamespaceRef(CXCursor C); + +/// \brief Create a reference to a variable at the given location. +CXCursor MakeCursorVariableRef(const VarDecl *Var, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a VariableRef cursor into the variable it references and the +/// location where the where the reference occurred. +std::pair<VarDecl *, SourceLocation> getCursorVariableRef(CXCursor C); + +/// \brief Create a reference to a field at the given location. +CXCursor MakeCursorMemberRef(const FieldDecl *Field, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a MemberRef cursor into the field it references and the +/// location where the reference occurred. +std::pair<FieldDecl *, SourceLocation> getCursorMemberRef(CXCursor C); + +/// \brief Create a CXX base specifier cursor. +CXCursor MakeCursorCXXBaseSpecifier(const CXXBaseSpecifier *B, + CXTranslationUnit TU); + +/// \brief Unpack a CXXBaseSpecifier cursor into a CXXBaseSpecifier. +CXXBaseSpecifier *getCursorCXXBaseSpecifier(CXCursor C); + +/// \brief Create a preprocessing directive cursor. +CXCursor MakePreprocessingDirectiveCursor(SourceRange Range, + CXTranslationUnit TU); + +/// \brief Unpack a given preprocessing directive to retrieve its source range. +SourceRange getCursorPreprocessingDirective(CXCursor C); + +/// \brief Create a macro definition cursor. +CXCursor MakeMacroDefinitionCursor(MacroDefinition *, CXTranslationUnit TU); + +/// \brief Unpack a given macro definition cursor to retrieve its +/// source range. +MacroDefinition *getCursorMacroDefinition(CXCursor C); + +/// \brief Create a macro expansion cursor. +CXCursor MakeMacroExpansionCursor(MacroExpansion *, + CXTranslationUnit TU); + +/// \brief Unpack a given macro expansion cursor to retrieve its +/// source range. +MacroExpansion *getCursorMacroExpansion(CXCursor C); + +/// \brief Create an inclusion directive cursor. +CXCursor MakeInclusionDirectiveCursor(InclusionDirective *, + CXTranslationUnit TU); + +/// \brief Unpack a given inclusion directive cursor to retrieve its +/// source range. +InclusionDirective *getCursorInclusionDirective(CXCursor C); + +/// \brief Create a label reference at the given location. +CXCursor MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, + CXTranslationUnit TU); + +/// \brief Unpack a label reference into the label statement it refers to and +/// the location of the reference. +std::pair<LabelStmt *, SourceLocation> getCursorLabelRef(CXCursor C); + +/// \brief Create a overloaded declaration reference cursor for an expression. +CXCursor MakeCursorOverloadedDeclRef(OverloadExpr *E, CXTranslationUnit TU); + +/// \brief Create a overloaded declaration reference cursor for a declaration. +CXCursor MakeCursorOverloadedDeclRef(Decl *D, SourceLocation Location, + CXTranslationUnit TU); + +/// \brief Create a overloaded declaration reference cursor for a template name. +CXCursor MakeCursorOverloadedDeclRef(TemplateName Template, + SourceLocation Location, + CXTranslationUnit TU); + +/// \brief Internal storage for an overloaded declaration reference cursor; +typedef llvm::PointerUnion3<OverloadExpr *, Decl *, + OverloadedTemplateStorage *> + OverloadedDeclRefStorage; + +/// \brief Unpack an overloaded declaration reference into an expression, +/// declaration, or template name along with the source location. +std::pair<OverloadedDeclRefStorage, SourceLocation> + getCursorOverloadedDeclRef(CXCursor C); + +Decl *getCursorDecl(CXCursor Cursor); +Expr *getCursorExpr(CXCursor Cursor); +Stmt *getCursorStmt(CXCursor Cursor); +Attr *getCursorAttr(CXCursor Cursor); +Decl *getCursorParentDecl(CXCursor Cursor); + +ASTContext &getCursorContext(CXCursor Cursor); +ASTUnit *getCursorASTUnit(CXCursor Cursor); +CXTranslationUnit getCursorTU(CXCursor Cursor); + +void getOverriddenCursors(CXCursor cursor, + SmallVectorImpl<CXCursor> &overridden); + +/// \brief Returns a index/location pair for a selector identifier if the cursor +/// points to one. +std::pair<int, SourceLocation> getSelectorIdentifierIndexAndLoc(CXCursor); +static inline int getSelectorIdentifierIndex(CXCursor cursor) { + return getSelectorIdentifierIndexAndLoc(cursor).first; +} +static inline SourceLocation getSelectorIdentifierLoc(CXCursor cursor) { + return getSelectorIdentifierIndexAndLoc(cursor).second; +} + +CXCursor getSelectorIdentifierCursor(int SelIdx, CXCursor cursor); + +static inline CXCursor getTypeRefedCallExprCursor(CXCursor cursor) { + CXCursor newCursor = cursor; + if (cursor.kind == CXCursor_CallExpr) + newCursor.xdata = 1; + return newCursor; +} + +CXCursor getTypeRefCursor(CXCursor cursor); + +/// \brief Generate a USR for \arg D and put it in \arg Buf. +/// \returns true if no USR was computed or the result should be ignored, +/// false otherwise. +bool getDeclCursorUSR(const Decl *D, SmallVectorImpl<char> &Buf); + +bool operator==(CXCursor X, CXCursor Y); + +inline bool operator!=(CXCursor X, CXCursor Y) { + return !(X == Y); +} + +/// \brief Return true if the cursor represents a declaration that is the +/// first in a declaration group. +bool isFirstInDeclGroup(CXCursor C); + +}} // end namespace: clang::cxcursor + +#endif diff --git a/clang/tools/libclang/CXLoadedDiagnostic.cpp b/clang/tools/libclang/CXLoadedDiagnostic.cpp new file mode 100644 index 0000000..e5b6ccc --- /dev/null +++ b/clang/tools/libclang/CXLoadedDiagnostic.cpp @@ -0,0 +1,672 @@ +/*===-- CXLoadedDiagnostic.cpp - Handling of persisent diags -*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements handling of persisent diagnostics. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#include "CXLoadedDiagnostic.h" +#include "CXString.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/Optional.h" +#include "clang/Basic/LLVM.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/MemoryBuffer.h" +#include <assert.h> + +using namespace clang; +using namespace clang::cxstring; + +//===----------------------------------------------------------------------===// +// Extend CXDiagnosticSetImpl which contains strings for diagnostics. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap<unsigned, llvm::StringRef> Strings; + +namespace { +class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl { +public: + CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {} + virtual ~CXLoadedDiagnosticSetImpl() {} + + llvm::StringRef makeString(const char *blob, unsigned blobLen); + + llvm::BumpPtrAllocator Alloc; + Strings Categories; + Strings WarningFlags; + Strings FileNames; + + FileSystemOptions FO; + FileManager FakeFiles; + llvm::DenseMap<unsigned, const FileEntry *> Files; +}; +} + +llvm::StringRef CXLoadedDiagnosticSetImpl::makeString(const char *blob, + unsigned bloblen) { + char *mem = Alloc.Allocate<char>(bloblen + 1); + memcpy(mem, blob, bloblen); + // Add a null terminator for those clients accessing the buffer + // like a c-string. + mem[bloblen] = '\0'; + return llvm::StringRef(mem, bloblen); +} + +//===----------------------------------------------------------------------===// +// Cleanup. +//===----------------------------------------------------------------------===// + +CXLoadedDiagnostic::~CXLoadedDiagnostic() {} + +//===----------------------------------------------------------------------===// +// Public CXLoadedDiagnostic methods. +//===----------------------------------------------------------------------===// + +CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const { + // FIXME: possibly refactor with logic in CXStoredDiagnostic. + switch (severity) { + case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored; + case DiagnosticsEngine::Note: return CXDiagnostic_Note; + case DiagnosticsEngine::Warning: return CXDiagnostic_Warning; + case DiagnosticsEngine::Error: return CXDiagnostic_Error; + case DiagnosticsEngine::Fatal: return CXDiagnostic_Fatal; + } + + llvm_unreachable("Invalid diagnostic level"); +} + +static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) { + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + uintptr_t V = (uintptr_t) DLoc; + V |= 0x1; + CXSourceLocation Loc = { { (void*) V, 0 }, 0 }; + return Loc; +} + +CXSourceLocation CXLoadedDiagnostic::getLocation() const { + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + return makeLocation(&DiagLoc); +} + +CXString CXLoadedDiagnostic::getSpelling() const { + return cxstring::createCXString(Spelling, false); +} + +CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const { + if (DiagOption.empty()) + return createCXString(""); + + // FIXME: possibly refactor with logic in CXStoredDiagnostic. + if (Disable) + *Disable = createCXString((Twine("-Wno-") + DiagOption).str()); + return createCXString((Twine("-W") + DiagOption).str()); +} + +unsigned CXLoadedDiagnostic::getCategory() const { + return category; +} + +CXString CXLoadedDiagnostic::getCategoryText() const { + return cxstring::createCXString(CategoryText); +} + +unsigned CXLoadedDiagnostic::getNumRanges() const { + return Ranges.size(); +} + +CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const { + assert(Range < Ranges.size()); + return Ranges[Range]; +} + +unsigned CXLoadedDiagnostic::getNumFixIts() const { + return FixIts.size(); +} + +CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const { + assert(FixIt < FixIts.size()); + if (ReplacementRange) + *ReplacementRange = FixIts[FixIt].first; + return FixIts[FixIt].second; +} + +void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location, + CXFile *file, + unsigned int *line, + unsigned int *column, + unsigned int *offset) { + + + // CXSourceLocation consists of the following fields: + // + // void *ptr_data[2]; + // unsigned int_data; + // + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + // + // For now, do the unoptimized approach and store the data in a side + // data structure. We can optimize this case later. + + uintptr_t V = (uintptr_t) location.ptr_data[0]; + assert((V & 0x1) == 1); + V &= ~(uintptr_t)1; + + const Location &Loc = *((Location*)V); + + if (file) + *file = Loc.file; + if (line) + *line = Loc.line; + if (column) + *column = Loc.column; + if (offset) + *offset = Loc.offset; +} + +//===----------------------------------------------------------------------===// +// Deserialize diagnostics. +//===----------------------------------------------------------------------===// + +enum { MaxSupportedVersion = 1 }; +typedef SmallVector<uint64_t, 64> RecordData; +enum LoadResult { Failure = 1, Success = 0 }; +enum StreamResult { Read_EndOfStream, + Read_BlockBegin, + Read_Failure, + Read_Record, + Read_BlockEnd }; + +namespace { +class DiagLoader { + enum CXLoadDiag_Error *error; + CXString *errorString; + + void reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { + if (error) + *error = code; + if (errorString) + *errorString = createCXString(err); + } + + void reportInvalidFile(llvm::StringRef err) { + return reportBad(CXLoadDiag_InvalidFile, err); + } + + LoadResult readMetaBlock(llvm::BitstreamCursor &Stream); + + LoadResult readDiagnosticBlock(llvm::BitstreamCursor &Stream, + CXDiagnosticSetImpl &Diags, + CXLoadedDiagnosticSetImpl &TopDiags); + + StreamResult readToNextRecordOrBlock(llvm::BitstreamCursor &Stream, + llvm::StringRef errorContext, + unsigned &BlockOrRecordID, + const bool atTopLevel = false); + + + LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags, + Strings &strings, llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen, + bool allowEmptyString = false); + + LoadResult readString(CXLoadedDiagnosticSetImpl &TopDiags, + llvm::StringRef &RetStr, + llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen, + bool allowEmptyString = false); + + LoadResult readRange(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, unsigned RecStartIdx, + CXSourceRange &SR); + + LoadResult readLocation(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, unsigned &offset, + CXLoadedDiagnostic::Location &Loc); + +public: + DiagLoader(enum CXLoadDiag_Error *e, CXString *es) + : error(e), errorString(es) { + if (error) + *error = CXLoadDiag_None; + if (errorString) + *errorString = createCXString(""); + } + + CXDiagnosticSet load(const char *file); +}; +} + +CXDiagnosticSet DiagLoader::load(const char *file) { + // Open the diagnostics file. + std::string ErrStr; + FileSystemOptions FO; + FileManager FileMgr(FO); + + OwningPtr<llvm::MemoryBuffer> Buffer; + Buffer.reset(FileMgr.getBufferForFile(file)); + + if (!Buffer) { + reportBad(CXLoadDiag_CannotLoad, ErrStr); + return 0; + } + + llvm::BitstreamReader StreamFile; + StreamFile.init((const unsigned char *)Buffer->getBufferStart(), + (const unsigned char *)Buffer->getBufferEnd()); + + llvm::BitstreamCursor Stream; + Stream.init(StreamFile); + + // Sniff for the signature. + if (Stream.Read(8) != 'D' || + Stream.Read(8) != 'I' || + Stream.Read(8) != 'A' || + Stream.Read(8) != 'G') { + reportBad(CXLoadDiag_InvalidFile, + "Bad header in diagnostics file"); + return 0; + } + + OwningPtr<CXLoadedDiagnosticSetImpl> + Diags(new CXLoadedDiagnosticSetImpl()); + + while (true) { + unsigned BlockID = 0; + StreamResult Res = readToNextRecordOrBlock(Stream, "Top-level", + BlockID, true); + switch (Res) { + case Read_EndOfStream: + return (CXDiagnosticSet) Diags.take(); + case Read_Failure: + return 0; + case Read_Record: + llvm_unreachable("Top-level does not have records"); + case Read_BlockEnd: + continue; + case Read_BlockBegin: + break; + } + + switch (BlockID) { + case serialized_diags::BLOCK_META: + if (readMetaBlock(Stream)) + return 0; + break; + case serialized_diags::BLOCK_DIAG: + if (readDiagnosticBlock(Stream, *Diags.get(), *Diags.get())) + return 0; + break; + default: + if (!Stream.SkipBlock()) { + reportInvalidFile("Malformed block at top-level of diagnostics file"); + return 0; + } + break; + } + } +} + +StreamResult DiagLoader::readToNextRecordOrBlock(llvm::BitstreamCursor &Stream, + llvm::StringRef errorContext, + unsigned &blockOrRecordID, + const bool atTopLevel) { + + blockOrRecordID = 0; + + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + // Handle the top-level specially. + if (atTopLevel) { + if (Code == llvm::bitc::ENTER_SUBBLOCK) { + unsigned BlockID = Stream.ReadSubBlockID(); + if (BlockID == llvm::bitc::BLOCKINFO_BLOCK_ID) { + if (Stream.ReadBlockInfoBlock()) { + reportInvalidFile("Malformed BlockInfoBlock in diagnostics file"); + return Read_Failure; + } + continue; + } + blockOrRecordID = BlockID; + return Read_BlockBegin; + } + reportInvalidFile("Only blocks can appear at the top of a " + "diagnostic file"); + return Read_Failure; + } + + switch ((llvm::bitc::FixedAbbrevIDs)Code) { + case llvm::bitc::ENTER_SUBBLOCK: + blockOrRecordID = Stream.ReadSubBlockID(); + return Read_BlockBegin; + + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) { + reportInvalidFile("Cannot read end of block"); + return Read_Failure; + } + return Read_BlockEnd; + + case llvm::bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + continue; + + case llvm::bitc::UNABBREV_RECORD: + reportInvalidFile("Diagnostics file should have no unabbreviated " + "records"); + return Read_Failure; + + default: + // We found a record. + blockOrRecordID = Code; + return Read_Record; + } + } + + if (atTopLevel) + return Read_EndOfStream; + + reportInvalidFile(Twine("Premature end of diagnostics file within ").str() + + errorContext.str()); + return Read_Failure; +} + +LoadResult DiagLoader::readMetaBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) { + reportInvalidFile("Malformed metadata block"); + return Failure; + } + + bool versionChecked = false; + + while (true) { + unsigned blockOrCode = 0; + StreamResult Res = readToNextRecordOrBlock(Stream, "Metadata Block", + blockOrCode); + + switch(Res) { + case Read_EndOfStream: + llvm_unreachable("EndOfStream handled by readToNextRecordOrBlock"); + case Read_Failure: + return Failure; + case Read_Record: + break; + case Read_BlockBegin: + if (Stream.SkipBlock()) { + reportInvalidFile("Malformed metadata block"); + return Failure; + } + case Read_BlockEnd: + if (!versionChecked) { + reportInvalidFile("Diagnostics file does not contain version" + " information"); + return Failure; + } + return Success; + } + + RecordData Record; + const char *Blob; + unsigned BlobLen; + unsigned recordID = Stream.ReadRecord(blockOrCode, Record, &Blob, &BlobLen); + + if (recordID == serialized_diags::RECORD_VERSION) { + if (Record.size() < 1) { + reportInvalidFile("malformed VERSION identifier in diagnostics file"); + return Failure; + } + if (Record[0] > MaxSupportedVersion) { + reportInvalidFile("diagnosics file is a newer version than the one " + "supported"); + return Failure; + } + versionChecked = true; + } + } +} + +LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, + llvm::StringRef &RetStr, + llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen, + bool allowEmptyString) { + + // Basic buffer overflow check. + if (BlobLen > 65536) { + reportInvalidFile(std::string("Out-of-bounds string in ") + + std::string(errorContext)); + return Failure; + } + + if (allowEmptyString && Record.size() >= 1 && BlobLen == 0) { + RetStr = ""; + return Success; + } + + if (Record.size() < 1 || BlobLen == 0) { + reportInvalidFile(std::string("Corrupted ") + std::string(errorContext) + + std::string(" entry")); + return Failure; + } + + RetStr = TopDiags.makeString(BlobStart, BlobLen); + return Success; +} + +LoadResult DiagLoader::readString(CXLoadedDiagnosticSetImpl &TopDiags, + Strings &strings, + llvm::StringRef errorContext, + RecordData &Record, + const char *BlobStart, + unsigned BlobLen, + bool allowEmptyString) { + llvm::StringRef RetStr; + if (readString(TopDiags, RetStr, errorContext, Record, BlobStart, BlobLen, + allowEmptyString)) + return Failure; + strings[Record[0]] = RetStr; + return Success; +} + +LoadResult DiagLoader::readLocation(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, unsigned &offset, + CXLoadedDiagnostic::Location &Loc) { + if (Record.size() < offset + 3) { + reportInvalidFile("Corrupted source location"); + return Failure; + } + + unsigned fileID = Record[offset++]; + if (fileID == 0) { + // Sentinel value. + Loc.file = 0; + Loc.line = 0; + Loc.column = 0; + Loc.offset = 0; + return Success; + } + + const FileEntry *FE = TopDiags.Files[fileID]; + if (!FE) { + reportInvalidFile("Corrupted file entry in source location"); + return Failure; + } + Loc.file = (void*) FE; + Loc.line = Record[offset++]; + Loc.column = Record[offset++]; + Loc.offset = Record[offset++]; + return Success; +} + +LoadResult DiagLoader::readRange(CXLoadedDiagnosticSetImpl &TopDiags, + RecordData &Record, + unsigned int RecStartIdx, + CXSourceRange &SR) { + CXLoadedDiagnostic::Location *Start, *End; + Start = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>(); + End = TopDiags.Alloc.Allocate<CXLoadedDiagnostic::Location>(); + + if (readLocation(TopDiags, Record, RecStartIdx, *Start)) + return Failure; + if (readLocation(TopDiags, Record, RecStartIdx, *End)) + return Failure; + + CXSourceLocation startLoc = makeLocation(Start); + CXSourceLocation endLoc = makeLocation(End); + SR = clang_getRange(startLoc, endLoc); + return Success; +} + +LoadResult DiagLoader::readDiagnosticBlock(llvm::BitstreamCursor &Stream, + CXDiagnosticSetImpl &Diags, + CXLoadedDiagnosticSetImpl &TopDiags){ + + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { + reportInvalidFile("malformed diagnostic block"); + return Failure; + } + + OwningPtr<CXLoadedDiagnostic> D(new CXLoadedDiagnostic()); + RecordData Record; + + while (true) { + unsigned blockOrCode = 0; + StreamResult Res = readToNextRecordOrBlock(Stream, "Diagnostic Block", + blockOrCode); + switch (Res) { + case Read_EndOfStream: + llvm_unreachable("EndOfStream handled in readToNextRecordOrBlock"); + case Read_Failure: + return Failure; + case Read_BlockBegin: { + // The only blocks we care about are subdiagnostics. + if (blockOrCode != serialized_diags::BLOCK_DIAG) { + if (!Stream.SkipBlock()) { + reportInvalidFile("Invalid subblock in Diagnostics block"); + return Failure; + } + } else if (readDiagnosticBlock(Stream, D->getChildDiagnostics(), + TopDiags)) { + return Failure; + } + + continue; + } + case Read_BlockEnd: + Diags.appendDiagnostic(D.take()); + return Success; + case Read_Record: + break; + } + + // Read the record. + Record.clear(); + const char *BlobStart = 0; + unsigned BlobLen = 0; + unsigned recID = Stream.ReadRecord(blockOrCode, Record, + BlobStart, BlobLen); + + if (recID < serialized_diags::RECORD_FIRST || + recID > serialized_diags::RECORD_LAST) + continue; + + switch ((serialized_diags::RecordIDs)recID) { + case serialized_diags::RECORD_VERSION: + continue; + case serialized_diags::RECORD_CATEGORY: + if (readString(TopDiags, TopDiags.Categories, "category", Record, + BlobStart, BlobLen, + /* allowEmptyString */ true)) + return Failure; + continue; + + case serialized_diags::RECORD_DIAG_FLAG: + if (readString(TopDiags, TopDiags.WarningFlags, "warning flag", Record, + BlobStart, BlobLen)) + return Failure; + continue; + + case serialized_diags::RECORD_FILENAME: { + if (readString(TopDiags, TopDiags.FileNames, "filename", Record, + BlobStart, BlobLen)) + return Failure; + + if (Record.size() < 3) { + reportInvalidFile("Invalid file entry"); + return Failure; + } + + const FileEntry *FE = + TopDiags.FakeFiles.getVirtualFile(TopDiags.FileNames[Record[0]], + /* size */ Record[1], + /* time */ Record[2]); + + TopDiags.Files[Record[0]] = FE; + continue; + } + + case serialized_diags::RECORD_SOURCE_RANGE: { + CXSourceRange SR; + if (readRange(TopDiags, Record, 0, SR)) + return Failure; + D->Ranges.push_back(SR); + continue; + } + + case serialized_diags::RECORD_FIXIT: { + CXSourceRange SR; + if (readRange(TopDiags, Record, 0, SR)) + return Failure; + llvm::StringRef RetStr; + if (readString(TopDiags, RetStr, "FIXIT", Record, BlobStart, BlobLen, + /* allowEmptyString */ true)) + return Failure; + D->FixIts.push_back(std::make_pair(SR, createCXString(RetStr, false))); + continue; + } + + case serialized_diags::RECORD_DIAG: { + D->severity = Record[0]; + unsigned offset = 1; + if (readLocation(TopDiags, Record, offset, D->DiagLoc)) + return Failure; + D->category = Record[offset++]; + unsigned diagFlag = Record[offset++]; + D->DiagOption = diagFlag ? TopDiags.WarningFlags[diagFlag] : ""; + D->CategoryText = D->category ? TopDiags.Categories[D->category] : ""; + D->Spelling = TopDiags.makeString(BlobStart, BlobLen); + continue; + } + } + } +} + +extern "C" { +CXDiagnosticSet clang_loadDiagnostics(const char *file, + enum CXLoadDiag_Error *error, + CXString *errorString) { + DiagLoader L(error, errorString); + return L.load(file); +} +} // end extern 'C'. diff --git a/clang/tools/libclang/CXLoadedDiagnostic.h b/clang/tools/libclang/CXLoadedDiagnostic.h new file mode 100644 index 0000000..d4a321e --- /dev/null +++ b/clang/tools/libclang/CXLoadedDiagnostic.h @@ -0,0 +1,94 @@ +/*===-- CXLoadedDiagnostic.h - Handling of persisent diags ------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements handling of persisent diagnostics. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_CINDEX_LOADED_DIAGNOSTIC_H +#define LLVM_CLANG_CINDEX_LOADED_DIAGNOSTIC_H + +#include "CIndexDiagnostic.h" +#include "llvm/ADT/StringRef.h" +#include "clang/Basic/LLVM.h" +#include <string> +#include <vector> + +namespace clang { +class CXLoadedDiagnostic : public CXDiagnosticImpl { +public: + CXLoadedDiagnostic() : CXDiagnosticImpl(LoadedDiagnosticKind), + severity(0), category(0) {} + + virtual ~CXLoadedDiagnostic(); + + /// \brief Return the severity of the diagnostic. + virtual CXDiagnosticSeverity getSeverity() const; + + /// \brief Return the location of the diagnostic. + virtual CXSourceLocation getLocation() const; + + /// \brief Return the spelling of the diagnostic. + virtual CXString getSpelling() const; + + /// \brief Return the text for the diagnostic option. + virtual CXString getDiagnosticOption(CXString *Disable) const; + + /// \brief Return the category of the diagnostic. + virtual unsigned getCategory() const; + + /// \brief Return the category string of the diagnostic. + virtual CXString getCategoryText() const; + + /// \brief Return the number of source ranges for the diagnostic. + virtual unsigned getNumRanges() const; + + /// \brief Return the source ranges for the diagnostic. + virtual CXSourceRange getRange(unsigned Range) const; + + /// \brief Return the number of FixIts. + virtual unsigned getNumFixIts() const; + + /// \brief Return the FixIt information (source range and inserted text). + virtual CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const; + + static bool classof(const CXDiagnosticImpl *D) { + return D->getKind() == LoadedDiagnosticKind; + } + + /// \brief Decode the CXSourceLocation into file, line, column, and offset. + static void decodeLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset); + + struct Location { + CXFile file; + unsigned line; + unsigned column; + unsigned offset; + + Location() : line(0), column(0), offset(0) {} + }; + + Location DiagLoc; + + std::vector<CXSourceRange> Ranges; + std::vector<std::pair<CXSourceRange, CXString> > FixIts; + llvm::StringRef Spelling; + llvm::StringRef DiagOption; + llvm::StringRef CategoryText; + unsigned severity; + unsigned category; +}; +} + +#endif diff --git a/clang/tools/libclang/CXSourceLocation.cpp b/clang/tools/libclang/CXSourceLocation.cpp new file mode 100644 index 0000000..a6bf8fc --- /dev/null +++ b/clang/tools/libclang/CXSourceLocation.cpp @@ -0,0 +1,326 @@ +//===- CXSourceLocation.cpp - CXSourceLocations APIs ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXSourceLocations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" + +#include "CIndexer.h" +#include "CXString.h" +#include "CXSourceLocation.h" +#include "CXTranslationUnit.h" +#include "CXLoadedDiagnostic.h" + +using namespace clang; +using namespace clang::cxstring; + +//===----------------------------------------------------------------------===// +// Internal predicates on CXSourceLocations. +//===----------------------------------------------------------------------===// + +static bool isASTUnitSourceLocation(const CXSourceLocation &L) { + // If the lowest bit is clear then the first ptr_data entry is a SourceManager + // pointer, or the CXSourceLocation is a null location. + return ((uintptr_t)L.ptr_data[0] & 0x1) == 0; +} + +//===----------------------------------------------------------------------===// +// Basic construction and comparison of CXSourceLocations and CXSourceRanges. +//===----------------------------------------------------------------------===// + +extern "C" { + +CXSourceLocation clang_getNullLocation() { + CXSourceLocation Result = { { 0, 0 }, 0 }; + return Result; +} + +unsigned clang_equalLocations(CXSourceLocation loc1, CXSourceLocation loc2) { + return (loc1.ptr_data[0] == loc2.ptr_data[0] && + loc1.ptr_data[1] == loc2.ptr_data[1] && + loc1.int_data == loc2.int_data); +} + +CXSourceRange clang_getNullRange() { + CXSourceRange Result = { { 0, 0 }, 0, 0 }; + return Result; +} + +CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { + if (!isASTUnitSourceLocation(begin)) { + if (isASTUnitSourceLocation(end)) + return clang_getNullRange(); + CXSourceRange Result = { { begin.ptr_data[0], end.ptr_data[0] }, 0, 0 }; + return Result; + } + + if (begin.ptr_data[0] != end.ptr_data[0] || + begin.ptr_data[1] != end.ptr_data[1]) + return clang_getNullRange(); + + CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] }, + begin.int_data, end.int_data }; + + return Result; +} + +unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) { + return range1.ptr_data[0] == range2.ptr_data[0] + && range1.ptr_data[1] == range2.ptr_data[1] + && range1.begin_int_data == range2.begin_int_data + && range1.end_int_data == range2.end_int_data; +} + +int clang_Range_isNull(CXSourceRange range) { + return clang_equalRanges(range, clang_getNullRange()); +} + + +CXSourceLocation clang_getRangeStart(CXSourceRange range) { + // Special decoding for CXSourceLocations for CXLoadedDiagnostics. + if ((uintptr_t)range.ptr_data[0] & 0x1) { + CXSourceLocation Result = { { range.ptr_data[0], 0 }, 0 }; + return Result; + } + + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, + range.begin_int_data }; + return Result; +} + +CXSourceLocation clang_getRangeEnd(CXSourceRange range) { + // Special decoding for CXSourceLocations for CXLoadedDiagnostics. + if ((uintptr_t)range.ptr_data[0] & 0x1) { + CXSourceLocation Result = { { range.ptr_data[1], 0 }, 0 }; + return Result; + } + + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, + range.end_int_data }; + return Result; +} + +} // end extern "C" + +//===----------------------------------------------------------------------===// +// Getting CXSourceLocations and CXSourceRanges from a translation unit. +//===----------------------------------------------------------------------===// + +extern "C" { + +CXSourceLocation clang_getLocation(CXTranslationUnit tu, + CXFile file, + unsigned line, + unsigned column) { + if (!tu || !file) + return clang_getNullLocation(); + + bool Logging = ::getenv("LIBCLANG_LOGGING"); + ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + const FileEntry *File = static_cast<const FileEntry *>(file); + SourceLocation SLoc = CXXUnit->getLocation(File, line, column); + if (SLoc.isInvalid()) { + if (Logging) + llvm::errs() << "clang_getLocation(\"" << File->getName() + << "\", " << line << ", " << column << ") = invalid\n"; + return clang_getNullLocation(); + } + + if (Logging) + llvm::errs() << "clang_getLocation(\"" << File->getName() + << "\", " << line << ", " << column << ") = " + << SLoc.getRawEncoding() << "\n"; + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); +} + +CXSourceLocation clang_getLocationForOffset(CXTranslationUnit tu, + CXFile file, + unsigned offset) { + if (!tu || !file) + return clang_getNullLocation(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu->TUData); + + SourceLocation SLoc + = CXXUnit->getLocation(static_cast<const FileEntry *>(file), offset); + + if (SLoc.isInvalid()) + return clang_getNullLocation(); + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); +} + +} // end extern "C" + +//===----------------------------------------------------------------------===// +// Routines for expanding and manipulating CXSourceLocations, regardless +// of their origin. +//===----------------------------------------------------------------------===// + +static void createNullLocation(CXFile *file, unsigned *line, + unsigned *column, unsigned *offset) { + if (file) + *file = 0; + if (line) + *line = 0; + if (column) + *column = 0; + if (offset) + *offset = 0; + return; +} + +static void createNullLocation(CXString *filename, unsigned *line, + unsigned *column, unsigned *offset = 0) { + if (filename) + *filename = createCXString(""); + if (line) + *line = 0; + if (column) + *column = 0; + if (offset) + *offset = 0; + return; +} + +extern "C" { + +void clang_getExpansionLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) { + createNullLocation(file, line, column, offset); + return; + } + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + + // Check that the FileID is invalid on the expansion location. + // This can manifest in invalid code. + FileID fileID = SM.getFileID(ExpansionLoc); + bool Invalid = false; + const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid); + if (Invalid || !sloc.isFile()) { + createNullLocation(file, line, column, offset); + return; + } + + if (file) + *file = (void *)SM.getFileEntryForSLocEntry(sloc); + if (line) + *line = SM.getExpansionLineNumber(ExpansionLoc); + if (column) + *column = SM.getExpansionColumnNumber(ExpansionLoc); + if (offset) + *offset = SM.getDecomposedLoc(ExpansionLoc).second; +} + +void clang_getPresumedLocation(CXSourceLocation location, + CXString *filename, + unsigned *line, + unsigned *column) { + + if (!isASTUnitSourceLocation(location)) { + // Other SourceLocation implementations do not support presumed locations + // at this time. + createNullLocation(filename, line, column); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) + createNullLocation(filename, line, column); + else { + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + PresumedLoc PreLoc = SM.getPresumedLoc(Loc); + + if (filename) + *filename = createCXString(PreLoc.getFilename()); + if (line) + *line = PreLoc.getLine(); + if (column) + *column = PreLoc.getColumn(); + } +} + +void clang_getInstantiationLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + // Redirect to new API. + clang_getExpansionLocation(location, file, line, column, offset); +} + +void clang_getSpellingLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, + column, offset); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) + return createNullLocation(file, line, column, offset); + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + SourceLocation SpellLoc = Loc; + if (SpellLoc.isMacroID()) { + SourceLocation SimpleSpellingLoc = SM.getImmediateSpellingLoc(SpellLoc); + if (SimpleSpellingLoc.isFileID() && + SM.getFileEntryForID(SM.getDecomposedLoc(SimpleSpellingLoc).first)) + SpellLoc = SimpleSpellingLoc; + else + SpellLoc = SM.getExpansionLoc(SpellLoc); + } + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isInvalid()) + return createNullLocation(file, line, column, offset); + + if (file) + *file = (void *)SM.getFileEntryForID(FID); + if (line) + *line = SM.getLineNumber(FID, FileOffset); + if (column) + *column = SM.getColumnNumber(FID, FileOffset); + if (offset) + *offset = FileOffset; +} + +} // end extern "C" + diff --git a/clang/tools/libclang/CXSourceLocation.h b/clang/tools/libclang/CXSourceLocation.h new file mode 100644 index 0000000..6c5e858 --- /dev/null +++ b/clang/tools/libclang/CXSourceLocation.h @@ -0,0 +1,78 @@ +//===- CXSourceLocation.h - CXSourceLocations Utilities ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXSourceLocations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXSOURCELOCATION_H +#define LLVM_CLANG_CXSOURCELOCATION_H + +#include "clang-c/Index.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/LangOptions.h" +#include "clang/AST/ASTContext.h" + +namespace clang { + +class SourceManager; + +namespace cxloc { + +/// \brief Translate a Clang source location into a CIndex source location. +static inline CXSourceLocation +translateSourceLocation(const SourceManager &SM, const LangOptions &LangOpts, + SourceLocation Loc) { + if (Loc.isInvalid()) + clang_getNullLocation(); + + CXSourceLocation Result = { { (void*) &SM, (void*) &LangOpts, }, + Loc.getRawEncoding() }; + return Result; +} + +/// \brief Translate a Clang source location into a CIndex source location. +static inline CXSourceLocation translateSourceLocation(ASTContext &Context, + SourceLocation Loc) { + return translateSourceLocation(Context.getSourceManager(), + Context.getLangOpts(), + Loc); +} + +/// \brief Translate a Clang source range into a CIndex source range. +/// +/// Clang internally represents ranges where the end location points to the +/// start of the token at the end. However, for external clients it is more +/// useful to have a CXSourceRange be a proper half-open interval. This routine +/// does the appropriate translation. +CXSourceRange translateSourceRange(const SourceManager &SM, + const LangOptions &LangOpts, + const CharSourceRange &R); + +/// \brief Translate a Clang source range into a CIndex source range. +static inline CXSourceRange translateSourceRange(ASTContext &Context, + SourceRange R) { + return translateSourceRange(Context.getSourceManager(), + Context.getLangOpts(), + CharSourceRange::getTokenRange(R)); +} + +static inline SourceLocation translateSourceLocation(CXSourceLocation L) { + return SourceLocation::getFromRawEncoding(L.int_data); +} + +static inline SourceRange translateCXSourceRange(CXSourceRange R) { + return SourceRange(SourceLocation::getFromRawEncoding(R.begin_int_data), + SourceLocation::getFromRawEncoding(R.end_int_data)); +} + + +}} // end namespace: clang::cxloc + +#endif diff --git a/clang/tools/libclang/CXStoredDiagnostic.cpp b/clang/tools/libclang/CXStoredDiagnostic.cpp new file mode 100644 index 0000000..8284dc9 --- /dev/null +++ b/clang/tools/libclang/CXStoredDiagnostic.cpp @@ -0,0 +1,119 @@ +/*===-- CXStoreDiagnostic.cpp - Diagnostics C Interface ----------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements part of the diagnostic functions of the Clang C interface. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXSourceLocation.h" +#include "CXString.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cxloc; +using namespace clang::cxstring; + +CXDiagnosticSeverity CXStoredDiagnostic::getSeverity() const { + switch (Diag.getLevel()) { + case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored; + case DiagnosticsEngine::Note: return CXDiagnostic_Note; + case DiagnosticsEngine::Warning: return CXDiagnostic_Warning; + case DiagnosticsEngine::Error: return CXDiagnostic_Error; + case DiagnosticsEngine::Fatal: return CXDiagnostic_Fatal; + } + + llvm_unreachable("Invalid diagnostic level"); +} + +CXSourceLocation CXStoredDiagnostic::getLocation() const { + if (Diag.getLocation().isInvalid()) + return clang_getNullLocation(); + + return translateSourceLocation(Diag.getLocation().getManager(), + LangOpts, Diag.getLocation()); +} + +CXString CXStoredDiagnostic::getSpelling() const { + return createCXString(Diag.getMessage(), false); +} + +CXString CXStoredDiagnostic::getDiagnosticOption(CXString *Disable) const { + unsigned ID = Diag.getID(); + StringRef Option = DiagnosticIDs::getWarningOptionForDiag(ID); + if (!Option.empty()) { + if (Disable) + *Disable = createCXString((Twine("-Wno-") + Option).str()); + return createCXString((Twine("-W") + Option).str()); + } + + if (ID == diag::fatal_too_many_errors) { + if (Disable) + *Disable = createCXString("-ferror-limit=0"); + return createCXString("-ferror-limit="); + } + + bool EnabledByDefault; + if (DiagnosticIDs::isBuiltinExtensionDiag(ID, EnabledByDefault) && + !EnabledByDefault) + return createCXString("-pedantic"); + + return createCXString(""); +} + +unsigned CXStoredDiagnostic::getCategory() const { + return DiagnosticIDs::getCategoryNumberForDiag(Diag.getID()); +} + +CXString CXStoredDiagnostic::getCategoryText() const { + unsigned catID = DiagnosticIDs::getCategoryNumberForDiag(Diag.getID()); + return createCXString(DiagnosticIDs::getCategoryNameFromID(catID)); +} + +unsigned CXStoredDiagnostic::getNumRanges() const { + if (Diag.getLocation().isInvalid()) + return 0; + + return Diag.range_size(); +} + +CXSourceRange CXStoredDiagnostic::getRange(unsigned int Range) const { + assert(Diag.getLocation().isValid()); + return translateSourceRange(Diag.getLocation().getManager(), + LangOpts, + Diag.range_begin()[Range]); +} + +unsigned CXStoredDiagnostic::getNumFixIts() const { + if (Diag.getLocation().isInvalid()) + return 0; + return Diag.fixit_size(); +} + +CXString CXStoredDiagnostic::getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const { + const FixItHint &Hint = Diag.fixit_begin()[FixIt]; + if (ReplacementRange) { + // Create a range that covers the entire replacement (or + // removal) range, adjusting the end of the range to point to + // the end of the token. + *ReplacementRange = translateSourceRange(Diag.getLocation().getManager(), + LangOpts, Hint.RemoveRange); + } + return createCXString(Hint.CodeToInsert); +} + diff --git a/clang/tools/libclang/CXString.cpp b/clang/tools/libclang/CXString.cpp new file mode 100644 index 0000000..bb09cd5 --- /dev/null +++ b/clang/tools/libclang/CXString.cpp @@ -0,0 +1,134 @@ +//===- CXString.cpp - Routines for manipulating CXStrings -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXStrings. It should be the +// only file that has internal knowledge of the encoding of the data in +// CXStrings. +// +//===----------------------------------------------------------------------===// + +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang-c/Index.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace clang::cxstring; + +enum CXStringFlag { CXS_Unmanaged, CXS_Malloc, CXS_StringBuf }; + +//===----------------------------------------------------------------------===// +// Basic generation of CXStrings. +//===----------------------------------------------------------------------===// + +CXString cxstring::createCXString(const char *String, bool DupString){ + CXString Str; + if (DupString) { + Str.data = strdup(String); + Str.private_flags = (unsigned) CXS_Malloc; + } else { + Str.data = (void*)String; + Str.private_flags = (unsigned) CXS_Unmanaged; + } + return Str; +} + +CXString cxstring::createCXString(StringRef String, bool DupString) { + CXString Result; + if (DupString || (!String.empty() && String.data()[String.size()] != 0)) { + char *Spelling = (char *)malloc(String.size() + 1); + memmove(Spelling, String.data(), String.size()); + Spelling[String.size()] = 0; + Result.data = Spelling; + Result.private_flags = (unsigned) CXS_Malloc; + } else { + Result.data = (void*) String.data(); + Result.private_flags = (unsigned) CXS_Unmanaged; + } + return Result; +} + +CXString cxstring::createCXString(CXStringBuf *buf) { + CXString Str; + Str.data = buf; + Str.private_flags = (unsigned) CXS_StringBuf; + return Str; +} + + +//===----------------------------------------------------------------------===// +// String pools. +//===----------------------------------------------------------------------===// + + +typedef std::vector<CXStringBuf *> CXStringPool; + +void *cxstring::createCXStringPool() { + return new CXStringPool(); +} + +void cxstring::disposeCXStringPool(void *p) { + CXStringPool *pool = static_cast<CXStringPool*>(p); + if (pool) { + for (CXStringPool::iterator I = pool->begin(), E = pool->end(); + I != E; ++I) { + delete *I; + } + delete pool; + } +} + +CXStringBuf *cxstring::getCXStringBuf(CXTranslationUnit TU) { + CXStringPool *pool = static_cast<CXStringPool*>(TU->StringPool); + if (pool->empty()) + return new CXStringBuf(TU); + CXStringBuf *buf = pool->back(); + buf->Data.clear(); + pool->pop_back(); + return buf; +} + +void cxstring::disposeCXStringBuf(CXStringBuf *buf) { + if (buf) + static_cast<CXStringPool*>(buf->TU->StringPool)->push_back(buf); +} + +bool cxstring::isManagedByPool(CXString str) { + return ((CXStringFlag) str.private_flags) == CXS_StringBuf; +} + +//===----------------------------------------------------------------------===// +// libClang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { +const char *clang_getCString(CXString string) { + if (string.private_flags == (unsigned) CXS_StringBuf) { + return ((CXStringBuf*)string.data)->Data.data(); + } + return (const char*) string.data; +} + +void clang_disposeString(CXString string) { + switch ((CXStringFlag) string.private_flags) { + case CXS_Unmanaged: + break; + case CXS_Malloc: + if (string.data) + free((void*)string.data); + break; + case CXS_StringBuf: + disposeCXStringBuf((CXStringBuf *) string.data); + break; + } +} +} // end: extern "C" + diff --git a/clang/tools/libclang/CXString.h b/clang/tools/libclang/CXString.h new file mode 100644 index 0000000..c354bd2 --- /dev/null +++ b/clang/tools/libclang/CXString.h @@ -0,0 +1,57 @@ +//===- CXString.h - Routines for manipulating CXStrings -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXStrings. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXSTRING_H +#define LLVM_CLANG_CXSTRING_H + +#include "clang-c/Index.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { +namespace cxstring { + +struct CXStringBuf { + SmallString<128> Data; + CXTranslationUnit TU; + CXStringBuf(CXTranslationUnit tu) : TU(tu) {} +}; + +/// \brief Create a CXString object from a C string. +CXString createCXString(const char *String, bool DupString = false); + +/// \brief Create a CXString object from a StringRef. +CXString createCXString(StringRef String, bool DupString = true); + +/// \brief Create a CXString object that is backed by a string buffer. +CXString createCXString(CXStringBuf *buf); + +/// \brief Create an opaque string pool used for fast geneneration of strings. +void *createCXStringPool(); + +/// \brief Dispose of a string pool. +void disposeCXStringPool(void *pool); + +CXStringBuf *getCXStringBuf(CXTranslationUnit TU); + +void disposeCXStringBuf(CXStringBuf *buf); + +/// \brief Returns true if the CXString data is managed by a pool. +bool isManagedByPool(CXString str); + +} +} + +#endif + diff --git a/clang/tools/libclang/CXTranslationUnit.h b/clang/tools/libclang/CXTranslationUnit.h new file mode 100644 index 0000000..3ad867c --- /dev/null +++ b/clang/tools/libclang/CXTranslationUnit.h @@ -0,0 +1,53 @@ +//===- CXTranslationUnit.h - Routines for manipulating CXTranslationUnits -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXTranslationUnits. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXTRANSLATIONUNIT_H +#define LLVM_CLANG_CXTRANSLATIONUNIT_H + +extern "C" { +struct CXTranslationUnitImpl { + void *CIdx; + void *TUData; + void *StringPool; + void *Diagnostics; +}; +} + +namespace clang { + class ASTUnit; + class CIndexer; + +namespace cxtu { + +CXTranslationUnitImpl *MakeCXTranslationUnit(CIndexer *CIdx, ASTUnit *TU); + +class CXTUOwner { + CXTranslationUnitImpl *TU; + +public: + CXTUOwner(CXTranslationUnitImpl *tu) : TU(tu) { } + ~CXTUOwner(); + + CXTranslationUnitImpl *getTU() const { return TU; } + + CXTranslationUnitImpl *takeTU() { + CXTranslationUnitImpl *retTU = TU; + TU = 0; + return retTU; + } +}; + + +}} // end namespace clang::cxtu + +#endif diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp new file mode 100644 index 0000000..850fac1 --- /dev/null +++ b/clang/tools/libclang/CXType.cpp @@ -0,0 +1,631 @@ +//===- CXTypes.cpp - Implements 'CXTypes' aspect of libclang ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +// +// This file implements the 'CXTypes' API hooks in the Clang-C library. +// +//===--------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXCursor.h" +#include "CXString.h" +#include "CXType.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Frontend/ASTUnit.h" + +using namespace clang; + +static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) { +#define BTCASE(K) case BuiltinType::K: return CXType_##K + switch (BT->getKind()) { + BTCASE(Void); + BTCASE(Bool); + BTCASE(Char_U); + BTCASE(UChar); + BTCASE(Char16); + BTCASE(Char32); + BTCASE(UShort); + BTCASE(UInt); + BTCASE(ULong); + BTCASE(ULongLong); + BTCASE(UInt128); + BTCASE(Char_S); + BTCASE(SChar); + case BuiltinType::WChar_S: return CXType_WChar; + case BuiltinType::WChar_U: return CXType_WChar; + BTCASE(Short); + BTCASE(Int); + BTCASE(Long); + BTCASE(LongLong); + BTCASE(Int128); + BTCASE(Float); + BTCASE(Double); + BTCASE(LongDouble); + BTCASE(NullPtr); + BTCASE(Overload); + BTCASE(Dependent); + BTCASE(ObjCId); + BTCASE(ObjCClass); + BTCASE(ObjCSel); + default: + return CXType_Unexposed; + } +#undef BTCASE +} + +static CXTypeKind GetTypeKind(QualType T) { + const Type *TP = T.getTypePtrOrNull(); + if (!TP) + return CXType_Invalid; + +#define TKCASE(K) case Type::K: return CXType_##K + switch (TP->getTypeClass()) { + case Type::Builtin: + return GetBuiltinTypeKind(cast<BuiltinType>(TP)); + TKCASE(Complex); + TKCASE(Pointer); + TKCASE(BlockPointer); + TKCASE(LValueReference); + TKCASE(RValueReference); + TKCASE(Record); + TKCASE(Enum); + TKCASE(Typedef); + TKCASE(ObjCInterface); + TKCASE(ObjCObjectPointer); + TKCASE(FunctionNoProto); + TKCASE(FunctionProto); + TKCASE(ConstantArray); + TKCASE(Vector); + default: + return CXType_Unexposed; + } +#undef TKCASE +} + + +CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) { + CXTypeKind TK = GetTypeKind(T); + CXType CT = { TK, { TK == CXType_Invalid ? 0 : T.getAsOpaquePtr(), TU }}; + return CT; +} + +using cxtype::MakeCXType; + +static inline QualType GetQualType(CXType CT) { + return QualType::getFromOpaquePtr(CT.data[0]); +} + +static inline CXTranslationUnit GetTU(CXType CT) { + return static_cast<CXTranslationUnit>(CT.data[1]); +} + +extern "C" { + +CXType clang_getCursorType(CXCursor C) { + using namespace cxcursor; + + CXTranslationUnit TU = cxcursor::getCursorTU(C); + ASTContext &Context = static_cast<ASTUnit *>(TU->TUData)->getASTContext(); + if (clang_isExpression(C.kind)) { + QualType T = cxcursor::getCursorExpr(C)->getType(); + return MakeCXType(T, TU); + } + + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return MakeCXType(QualType(), TU); + + if (TypeDecl *TD = dyn_cast<TypeDecl>(D)) + return MakeCXType(Context.getTypeDeclType(TD), TU); + if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) + return MakeCXType(Context.getObjCInterfaceType(ID), TU); + if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) + return MakeCXType(VD->getType(), TU); + if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) + return MakeCXType(PD->getType(), TU); + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + return MakeCXType(FD->getType(), TU); + return MakeCXType(QualType(), TU); + } + + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + QualType T + = Context.getObjCInterfaceType(getCursorObjCSuperClassRef(C).first); + return MakeCXType(T, TU); + } + + case CXCursor_ObjCClassRef: { + QualType T = Context.getObjCInterfaceType(getCursorObjCClassRef(C).first); + return MakeCXType(T, TU); + } + + case CXCursor_TypeRef: { + QualType T = Context.getTypeDeclType(getCursorTypeRef(C).first); + return MakeCXType(T, TU); + + } + + case CXCursor_CXXBaseSpecifier: + return cxtype::MakeCXType(getCursorCXXBaseSpecifier(C)->getType(), TU); + + case CXCursor_MemberRef: + return cxtype::MakeCXType(getCursorMemberRef(C).first->getType(), TU); + + case CXCursor_VariableRef: + return cxtype::MakeCXType(getCursorVariableRef(C).first->getType(), TU); + + case CXCursor_ObjCProtocolRef: + case CXCursor_TemplateRef: + case CXCursor_NamespaceRef: + case CXCursor_OverloadedDeclRef: + default: + break; + } + + return MakeCXType(QualType(), TU); + } + + return MakeCXType(QualType(), TU); +} + +CXType clang_getTypedefDeclUnderlyingType(CXCursor C) { + using namespace cxcursor; + CXTranslationUnit TU = cxcursor::getCursorTU(C); + + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + + if (TypedefNameDecl *TD = dyn_cast_or_null<TypedefNameDecl>(D)) { + QualType T = TD->getUnderlyingType(); + return MakeCXType(T, TU); + } + + return MakeCXType(QualType(), TU); + } + + return MakeCXType(QualType(), TU); +} + +CXType clang_getEnumDeclIntegerType(CXCursor C) { + using namespace cxcursor; + CXTranslationUnit TU = cxcursor::getCursorTU(C); + + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + + if (EnumDecl *TD = dyn_cast_or_null<EnumDecl>(D)) { + QualType T = TD->getIntegerType(); + return MakeCXType(T, TU); + } + + return MakeCXType(QualType(), TU); + } + + return MakeCXType(QualType(), TU); +} + +long long clang_getEnumConstantDeclValue(CXCursor C) { + using namespace cxcursor; + + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + + if (EnumConstantDecl *TD = dyn_cast_or_null<EnumConstantDecl>(D)) { + return TD->getInitVal().getSExtValue(); + } + + return LLONG_MIN; + } + + return LLONG_MIN; +} + +unsigned long long clang_getEnumConstantDeclUnsignedValue(CXCursor C) { + using namespace cxcursor; + + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + + if (EnumConstantDecl *TD = dyn_cast_or_null<EnumConstantDecl>(D)) { + return TD->getInitVal().getZExtValue(); + } + + return ULLONG_MAX; + } + + return ULLONG_MAX; +} + +CXType clang_getCanonicalType(CXType CT) { + if (CT.kind == CXType_Invalid) + return CT; + + QualType T = GetQualType(CT); + CXTranslationUnit TU = GetTU(CT); + + if (T.isNull()) + return MakeCXType(QualType(), GetTU(CT)); + + ASTUnit *AU = static_cast<ASTUnit*>(TU->TUData); + return MakeCXType(AU->getASTContext().getCanonicalType(T), TU); +} + +unsigned clang_isConstQualifiedType(CXType CT) { + QualType T = GetQualType(CT); + return T.isLocalConstQualified(); +} + +unsigned clang_isVolatileQualifiedType(CXType CT) { + QualType T = GetQualType(CT); + return T.isLocalVolatileQualified(); +} + +unsigned clang_isRestrictQualifiedType(CXType CT) { + QualType T = GetQualType(CT); + return T.isLocalRestrictQualified(); +} + +CXType clang_getPointeeType(CXType CT) { + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (!TP) + return MakeCXType(QualType(), GetTU(CT)); + + switch (TP->getTypeClass()) { + case Type::Pointer: + T = cast<PointerType>(TP)->getPointeeType(); + break; + case Type::BlockPointer: + T = cast<BlockPointerType>(TP)->getPointeeType(); + break; + case Type::LValueReference: + case Type::RValueReference: + T = cast<ReferenceType>(TP)->getPointeeType(); + break; + case Type::ObjCObjectPointer: + T = cast<ObjCObjectPointerType>(TP)->getPointeeType(); + break; + default: + T = QualType(); + break; + } + return MakeCXType(T, GetTU(CT)); +} + +CXCursor clang_getTypeDeclaration(CXType CT) { + if (CT.kind == CXType_Invalid) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (!TP) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + Decl *D = 0; + +try_again: + switch (TP->getTypeClass()) { + case Type::Typedef: + D = cast<TypedefType>(TP)->getDecl(); + break; + case Type::ObjCObject: + D = cast<ObjCObjectType>(TP)->getInterface(); + break; + case Type::ObjCInterface: + D = cast<ObjCInterfaceType>(TP)->getDecl(); + break; + case Type::Record: + case Type::Enum: + D = cast<TagType>(TP)->getDecl(); + break; + case Type::TemplateSpecialization: + if (const RecordType *Record = TP->getAs<RecordType>()) + D = Record->getDecl(); + else + D = cast<TemplateSpecializationType>(TP)->getTemplateName() + .getAsTemplateDecl(); + break; + + case Type::InjectedClassName: + D = cast<InjectedClassNameType>(TP)->getDecl(); + break; + + // FIXME: Template type parameters! + + case Type::Elaborated: + TP = cast<ElaboratedType>(TP)->getNamedType().getTypePtrOrNull(); + goto try_again; + + default: + break; + } + + if (!D) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + return cxcursor::MakeCXCursor(D, GetTU(CT)); +} + +CXString clang_getTypeKindSpelling(enum CXTypeKind K) { + const char *s = 0; +#define TKIND(X) case CXType_##X: s = "" #X ""; break + switch (K) { + TKIND(Invalid); + TKIND(Unexposed); + TKIND(Void); + TKIND(Bool); + TKIND(Char_U); + TKIND(UChar); + TKIND(Char16); + TKIND(Char32); + TKIND(UShort); + TKIND(UInt); + TKIND(ULong); + TKIND(ULongLong); + TKIND(UInt128); + TKIND(Char_S); + TKIND(SChar); + case CXType_WChar: s = "WChar"; break; + TKIND(Short); + TKIND(Int); + TKIND(Long); + TKIND(LongLong); + TKIND(Int128); + TKIND(Float); + TKIND(Double); + TKIND(LongDouble); + TKIND(NullPtr); + TKIND(Overload); + TKIND(Dependent); + TKIND(ObjCId); + TKIND(ObjCClass); + TKIND(ObjCSel); + TKIND(Complex); + TKIND(Pointer); + TKIND(BlockPointer); + TKIND(LValueReference); + TKIND(RValueReference); + TKIND(Record); + TKIND(Enum); + TKIND(Typedef); + TKIND(ObjCInterface); + TKIND(ObjCObjectPointer); + TKIND(FunctionNoProto); + TKIND(FunctionProto); + TKIND(ConstantArray); + TKIND(Vector); + } +#undef TKIND + return cxstring::createCXString(s); +} + +unsigned clang_equalTypes(CXType A, CXType B) { + return A.data[0] == B.data[0] && A.data[1] == B.data[1];; +} + +unsigned clang_isFunctionTypeVariadic(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return 0; + + if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) + return (unsigned)FD->isVariadic(); + + if (T->getAs<FunctionNoProtoType>()) + return 1; + + return 0; +} + +CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return CXCallingConv_Invalid; + + if (const FunctionType *FD = T->getAs<FunctionType>()) { +#define TCALLINGCONV(X) case CC_##X: return CXCallingConv_##X + switch (FD->getCallConv()) { + TCALLINGCONV(Default); + TCALLINGCONV(C); + TCALLINGCONV(X86StdCall); + TCALLINGCONV(X86FastCall); + TCALLINGCONV(X86ThisCall); + TCALLINGCONV(X86Pascal); + TCALLINGCONV(AAPCS); + TCALLINGCONV(AAPCS_VFP); + } +#undef TCALLINGCONV + } + + return CXCallingConv_Invalid; +} + +int clang_getNumArgTypes(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return -1; + + if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) { + return FD->getNumArgs(); + } + + if (T->getAs<FunctionNoProtoType>()) { + return 0; + } + + return -1; +} + +CXType clang_getArgType(CXType X, unsigned i) { + QualType T = GetQualType(X); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(X)); + + if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) { + unsigned numArgs = FD->getNumArgs(); + if (i >= numArgs) + return MakeCXType(QualType(), GetTU(X)); + + return MakeCXType(FD->getArgType(i), GetTU(X)); + } + + return MakeCXType(QualType(), GetTU(X)); +} + +CXType clang_getResultType(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(X)); + + if (const FunctionType *FD = T->getAs<FunctionType>()) + return MakeCXType(FD->getResultType(), GetTU(X)); + + return MakeCXType(QualType(), GetTU(X)); +} + +CXType clang_getCursorResultType(CXCursor C) { + if (clang_isDeclaration(C.kind)) { + Decl *D = cxcursor::getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D)) + return MakeCXType(MD->getResultType(), cxcursor::getCursorTU(C)); + + return clang_getResultType(clang_getCursorType(C)); + } + + return MakeCXType(QualType(), cxcursor::getCursorTU(C)); +} + +unsigned clang_isPODType(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return 0; + + CXTranslationUnit TU = GetTU(X); + ASTUnit *AU = static_cast<ASTUnit*>(TU->TUData); + + return T.isPODType(AU->getASTContext()) ? 1 : 0; +} + +CXType clang_getElementType(CXType CT) { + QualType ET = QualType(); + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + ET = cast<ConstantArrayType> (TP)->getElementType(); + break; + case Type::Vector: + ET = cast<VectorType> (TP)->getElementType(); + break; + case Type::Complex: + ET = cast<ComplexType> (TP)->getElementType(); + break; + default: + break; + } + } + return MakeCXType(ET, GetTU(CT)); +} + +long long clang_getNumElements(CXType CT) { + long long result = -1; + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + result = cast<ConstantArrayType> (TP)->getSize().getSExtValue(); + break; + case Type::Vector: + result = cast<VectorType> (TP)->getNumElements(); + break; + default: + break; + } + } + return result; +} + +CXType clang_getArrayElementType(CXType CT) { + QualType ET = QualType(); + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + ET = cast<ConstantArrayType> (TP)->getElementType(); + break; + default: + break; + } + } + return MakeCXType(ET, GetTU(CT)); +} + +long long clang_getArraySize(CXType CT) { + long long result = -1; + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + result = cast<ConstantArrayType> (TP)->getSize().getSExtValue(); + break; + default: + break; + } + } + return result; +} + +CXString clang_getDeclObjCTypeEncoding(CXCursor C) { + if ((C.kind < CXCursor_FirstDecl) || (C.kind > CXCursor_LastDecl)) + return cxstring::createCXString(""); + + Decl *D = static_cast<Decl*>(C.data[0]); + CXTranslationUnit TU = static_cast<CXTranslationUnit>(C.data[2]); + ASTUnit *AU = static_cast<ASTUnit*>(TU->TUData); + ASTContext &Ctx = AU->getASTContext(); + std::string encoding; + + if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) { + if (Ctx.getObjCEncodingForMethodDecl(OMD, encoding)) + return cxstring::createCXString("?"); + } else if (ObjCPropertyDecl *OPD = dyn_cast<ObjCPropertyDecl>(D)) + Ctx.getObjCEncodingForPropertyDecl(OPD, NULL, encoding); + else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + Ctx.getObjCEncodingForFunctionDecl(FD, encoding); + else { + QualType Ty; + if (TypeDecl *TD = dyn_cast<TypeDecl>(D)) + Ty = Ctx.getTypeDeclType(TD); + if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) + Ty = VD->getType(); + else return cxstring::createCXString("?"); + Ctx.getObjCEncodingForType(Ty, encoding); + } + + return cxstring::createCXString(encoding); +} + +} // end: extern "C" diff --git a/clang/tools/libclang/CXType.h b/clang/tools/libclang/CXType.h new file mode 100644 index 0000000..7660beb --- /dev/null +++ b/clang/tools/libclang/CXType.h @@ -0,0 +1,29 @@ +//===- CXTypes.h - Routines for manipulating CXTypes ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXTYPES_H +#define LLVM_CLANG_CXTYPES_H + +#include "clang-c/Index.h" +#include "clang/AST/Type.h" + +namespace clang { + +class ASTUnit; + +namespace cxtype { + +CXType MakeCXType(QualType T, CXTranslationUnit TU); + +}} // end namespace clang::cxtype +#endif diff --git a/clang/tools/libclang/CursorVisitor.h b/clang/tools/libclang/CursorVisitor.h new file mode 100644 index 0000000..88b70a4 --- /dev/null +++ b/clang/tools/libclang/CursorVisitor.h @@ -0,0 +1,259 @@ +//===- CursorVisitor.h - CursorVisitor interface --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIBCLANG_CURSORVISITOR_H +#define LLVM_CLANG_LIBCLANG_CURSORVISITOR_H + +#include "Index_Internal.h" +#include "CXCursor.h" +#include "CXTranslationUnit.h" + +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/TypeLocVisitor.h" + +namespace clang { + class PreprocessingRecord; + class ASTUnit; + +namespace cxcursor { + +class VisitorJob { +public: + enum Kind { DeclVisitKind, StmtVisitKind, MemberExprPartsKind, + TypeLocVisitKind, OverloadExprPartsKind, + DeclRefExprPartsKind, LabelRefVisitKind, + ExplicitTemplateArgsVisitKind, + NestedNameSpecifierLocVisitKind, + DeclarationNameInfoVisitKind, + MemberRefVisitKind, SizeOfPackExprPartsKind, + LambdaExprPartsKind }; +protected: + void *data[3]; + CXCursor parent; + Kind K; + VisitorJob(CXCursor C, Kind k, void *d1, void *d2 = 0, void *d3 = 0) + : parent(C), K(k) { + data[0] = d1; + data[1] = d2; + data[2] = d3; + } +public: + Kind getKind() const { return K; } + const CXCursor &getParent() const { return parent; } + static bool classof(VisitorJob *VJ) { return true; } +}; + +typedef SmallVector<VisitorJob, 10> VisitorWorkList; + +// Cursor visitor. +class CursorVisitor : public DeclVisitor<CursorVisitor, bool>, + public TypeLocVisitor<CursorVisitor, bool> +{ + /// \brief The translation unit we are traversing. + CXTranslationUnit TU; + ASTUnit *AU; + + /// \brief The parent cursor whose children we are traversing. + CXCursor Parent; + + /// \brief The declaration that serves at the parent of any statement or + /// expression nodes. + Decl *StmtParent; + + /// \brief The visitor function. + CXCursorVisitor Visitor; + + /// \brief The opaque client data, to be passed along to the visitor. + CXClientData ClientData; + + /// \brief Whether we should visit the preprocessing record entries last, + /// after visiting other declarations. + bool VisitPreprocessorLast; + + /// \brief Whether we should visit declarations or preprocessing record + /// entries that are #included inside the \arg RegionOfInterest. + bool VisitIncludedEntities; + + /// \brief When valid, a source range to which the cursor should restrict + /// its search. + SourceRange RegionOfInterest; + + /// \brief Whether we should only visit declarations and not preprocessing + /// record entries. + bool VisitDeclsOnly; + + // FIXME: Eventually remove. This part of a hack to support proper + // iteration over all Decls contained lexically within an ObjC container. + DeclContext::decl_iterator *DI_current; + DeclContext::decl_iterator DE_current; + SmallVectorImpl<Decl *>::iterator *FileDI_current; + SmallVectorImpl<Decl *>::iterator FileDE_current; + + // Cache of pre-allocated worklists for data-recursion walk of Stmts. + SmallVector<VisitorWorkList*, 5> WorkListFreeList; + SmallVector<VisitorWorkList*, 5> WorkListCache; + + using DeclVisitor<CursorVisitor, bool>::Visit; + using TypeLocVisitor<CursorVisitor, bool>::Visit; + + /// \brief Determine whether this particular source range comes before, comes + /// after, or overlaps the region of interest. + /// + /// \param R a half-open source range retrieved from the abstract syntax tree. + RangeComparisonResult CompareRegionOfInterest(SourceRange R); + + void visitDeclsFromFileRegion(FileID File, unsigned Offset, unsigned Length); + + class SetParentRAII { + CXCursor &Parent; + Decl *&StmtParent; + CXCursor OldParent; + + public: + SetParentRAII(CXCursor &Parent, Decl *&StmtParent, CXCursor NewParent) + : Parent(Parent), StmtParent(StmtParent), OldParent(Parent) + { + Parent = NewParent; + if (clang_isDeclaration(Parent.kind)) + StmtParent = getCursorDecl(Parent); + } + + ~SetParentRAII() { + Parent = OldParent; + if (clang_isDeclaration(Parent.kind)) + StmtParent = getCursorDecl(Parent); + } + }; + +public: + CursorVisitor(CXTranslationUnit TU, CXCursorVisitor Visitor, + CXClientData ClientData, + bool VisitPreprocessorLast, + bool VisitIncludedPreprocessingEntries = false, + SourceRange RegionOfInterest = SourceRange(), + bool VisitDeclsOnly = false) + : TU(TU), AU(static_cast<ASTUnit*>(TU->TUData)), + Visitor(Visitor), ClientData(ClientData), + VisitPreprocessorLast(VisitPreprocessorLast), + VisitIncludedEntities(VisitIncludedPreprocessingEntries), + RegionOfInterest(RegionOfInterest), + VisitDeclsOnly(VisitDeclsOnly), + DI_current(0), FileDI_current(0) + { + Parent.kind = CXCursor_NoDeclFound; + Parent.data[0] = 0; + Parent.data[1] = 0; + Parent.data[2] = 0; + StmtParent = 0; + } + + ~CursorVisitor() { + // Free the pre-allocated worklists for data-recursion. + for (SmallVectorImpl<VisitorWorkList*>::iterator + I = WorkListCache.begin(), E = WorkListCache.end(); I != E; ++I) { + delete *I; + } + } + + ASTUnit *getASTUnit() const { return static_cast<ASTUnit*>(TU->TUData); } + CXTranslationUnit getTU() const { return TU; } + + bool Visit(CXCursor Cursor, bool CheckedRegionOfInterest = false); + + /// \brief Visit declarations and preprocessed entities for the file region + /// designated by \see RegionOfInterest. + void visitFileRegion(); + + bool visitPreprocessedEntitiesInRegion(); + + bool shouldVisitIncludedEntities() const { + return VisitIncludedEntities; + } + + template<typename InputIterator> + bool visitPreprocessedEntities(InputIterator First, InputIterator Last, + PreprocessingRecord &PPRec, + FileID FID = FileID()); + + bool VisitChildren(CXCursor Parent); + + // Declaration visitors + bool VisitTypeAliasDecl(TypeAliasDecl *D); + bool VisitAttributes(Decl *D); + bool VisitBlockDecl(BlockDecl *B); + bool VisitCXXRecordDecl(CXXRecordDecl *D); + llvm::Optional<bool> shouldVisitCursor(CXCursor C); + bool VisitDeclContext(DeclContext *DC); + bool VisitTranslationUnitDecl(TranslationUnitDecl *D); + bool VisitTypedefDecl(TypedefDecl *D); + bool VisitTagDecl(TagDecl *D); + bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D); + bool VisitClassTemplatePartialSpecializationDecl( + ClassTemplatePartialSpecializationDecl *D); + bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D); + bool VisitEnumConstantDecl(EnumConstantDecl *D); + bool VisitDeclaratorDecl(DeclaratorDecl *DD); + bool VisitFunctionDecl(FunctionDecl *ND); + bool VisitFieldDecl(FieldDecl *D); + bool VisitVarDecl(VarDecl *); + bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); + bool VisitFunctionTemplateDecl(FunctionTemplateDecl *D); + bool VisitClassTemplateDecl(ClassTemplateDecl *D); + bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D); + bool VisitObjCMethodDecl(ObjCMethodDecl *ND); + bool VisitObjCContainerDecl(ObjCContainerDecl *D); + bool VisitObjCCategoryDecl(ObjCCategoryDecl *ND); + bool VisitObjCProtocolDecl(ObjCProtocolDecl *PID); + bool VisitObjCPropertyDecl(ObjCPropertyDecl *PD); + bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); + bool VisitObjCImplDecl(ObjCImplDecl *D); + bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D); + bool VisitObjCImplementationDecl(ObjCImplementationDecl *D); + // FIXME: ObjCCompatibleAliasDecl requires aliased-class locations. + bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *PD); + bool VisitLinkageSpecDecl(LinkageSpecDecl *D); + bool VisitNamespaceDecl(NamespaceDecl *D); + bool VisitNamespaceAliasDecl(NamespaceAliasDecl *D); + bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D); + bool VisitUsingDecl(UsingDecl *D); + bool VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D); + bool VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D); + + // Name visitor + bool VisitDeclarationNameInfo(DeclarationNameInfo Name); + bool VisitNestedNameSpecifier(NestedNameSpecifier *NNS, SourceRange Range); + bool VisitNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); + + // Template visitors + bool VisitTemplateParameters(const TemplateParameterList *Params); + bool VisitTemplateName(TemplateName Name, SourceLocation Loc); + bool VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL); + + // Type visitors +#define ABSTRACT_TYPELOC(CLASS, PARENT) +#define TYPELOC(CLASS, PARENT) \ + bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TyLoc); +#include "clang/AST/TypeLocNodes.def" + + bool VisitTagTypeLoc(TagTypeLoc TL); + bool VisitArrayTypeLoc(ArrayTypeLoc TL); + bool VisitFunctionTypeLoc(FunctionTypeLoc TL, bool SkipResultType = false); + + // Data-recursive visitor functions. + bool IsInRegionOfInterest(CXCursor C); + bool RunVisitorWorkList(VisitorWorkList &WL); + void EnqueueWorkList(VisitorWorkList &WL, Stmt *S); + LLVM_ATTRIBUTE_NOINLINE bool Visit(Stmt *S); +}; + +} +} + +#endif + diff --git a/clang/tools/libclang/IndexBody.cpp b/clang/tools/libclang/IndexBody.cpp new file mode 100644 index 0000000..74a8d37 --- /dev/null +++ b/clang/tools/libclang/IndexBody.cpp @@ -0,0 +1,154 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" + +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace cxindex; + +namespace { + +class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + + typedef RecursiveASTVisitor<BodyIndexer> base; +public: + BodyIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseTypeLoc(TypeLoc TL) { + IndexCtx.indexTypeLoc(TL, Parent, ParentDC); + return true; + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); + return true; + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, E); + return true; + } + + bool VisitMemberExpr(MemberExpr *E) { + IndexCtx.handleReference(E->getMemberDecl(), E->getMemberLoc(), + Parent, ParentDC, E); + return true; + } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, E); + } + return true; + } + + bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { + IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, E); + return true; + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (TypeSourceInfo *Cls = E->getClassReceiverTypeInfo()) + IndexCtx.indexTypeSourceInfo(Cls, Parent, ParentDC); + + if (ObjCMethodDecl *MD = E->getMethodDecl()) + IndexCtx.handleReference(MD, E->getSelectorStartLoc(), + Parent, ParentDC, E, + E->isImplicit() ? CXIdxEntityRef_Implicit + : CXIdxEntityRef_Direct); + return true; + } + + bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + if (E->isExplicitProperty()) + IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), + Parent, ParentDC, E); + + // No need to do a handleReference for the objc method, because there will + // be a message expr as part of PseudoObjectExpr. + return true; + } + + bool VisitObjCNumericLiteral(ObjCNumericLiteral *E) { + if (ObjCMethodDecl *MD = E->getObjCNumericLiteralMethod()) + IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, E, CXIdxEntityRef_Implicit); + return true; + } + + bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) + IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, E, CXIdxEntityRef_Implicit); + return true; + } + + bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) + IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, E, CXIdxEntityRef_Implicit); + return true; + } + + bool VisitCXXConstructExpr(CXXConstructExpr *E) { + IndexCtx.handleReference(E->getConstructor(), E->getLocation(), + Parent, ParentDC, E); + return true; + } + + bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E) { + if (E->getOperatorLoc().isInvalid()) + return true; // implicit. + return base::TraverseCXXOperatorCallExpr(E); + } + + bool VisitDeclStmt(DeclStmt *S) { + if (IndexCtx.shouldIndexFunctionLocalSymbols()) + IndexCtx.indexDeclGroupRef(S->getDeclGroup()); + return true; + } + + bool TraverseLambdaCapture(LambdaExpr::Capture C) { + if (C.capturesThis()) + return true; + + if (IndexCtx.shouldIndexFunctionLocalSymbols()) + IndexCtx.handleReference(C.getCapturedVar(), C.getLocation(), + Parent, ParentDC); + return true; + } + +}; + +} // anonymous namespace + +void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC) { + if (!S) + return; + + if (DC == 0) + DC = Parent->getLexicalDeclContext(); + BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast<Stmt*>(S)); +} diff --git a/clang/tools/libclang/IndexDecl.cpp b/clang/tools/libclang/IndexDecl.cpp new file mode 100644 index 0000000..c257c34 --- /dev/null +++ b/clang/tools/libclang/IndexDecl.cpp @@ -0,0 +1,336 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" + +#include "clang/AST/DeclVisitor.h" + +using namespace clang; +using namespace cxindex; + +namespace { + +class IndexingDeclVisitor : public DeclVisitor<IndexingDeclVisitor, bool> { + IndexingContext &IndexCtx; + +public: + explicit IndexingDeclVisitor(IndexingContext &indexCtx) + : IndexCtx(indexCtx) { } + + void handleDeclarator(DeclaratorDecl *D, const NamedDecl *Parent = 0) { + if (!Parent) Parent = D; + + if (!IndexCtx.shouldIndexFunctionLocalSymbols()) { + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); + } else { + if (ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { + IndexCtx.handleVar(Parm); + } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + for (FunctionDecl::param_iterator + PI = FD->param_begin(), PE = FD->param_end(); PI != PE; ++PI) { + IndexCtx.handleVar(*PI); + } + } + } + } + + void handleObjCMethod(ObjCMethodDecl *D) { + IndexCtx.handleObjCMethod(D); + if (D->isImplicit()) + return; + + IndexCtx.indexTypeSourceInfo(D->getResultTypeSourceInfo(), D); + for (ObjCMethodDecl::param_iterator + I = D->param_begin(), E = D->param_end(); I != E; ++I) + handleDeclarator(*I, D); + + if (D->isThisDeclarationADefinition()) { + const Stmt *Body = D->getBody(); + if (Body) { + IndexCtx.indexBody(Body, D, D); + } + } + } + + bool VisitFunctionDecl(FunctionDecl *D) { + IndexCtx.handleFunction(D); + handleDeclarator(D); + + if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) { + // Constructor initializers. + for (CXXConstructorDecl::init_iterator I = Ctor->init_begin(), + E = Ctor->init_end(); + I != E; ++I) { + CXXCtorInitializer *Init = *I; + if (Init->isWritten()) { + IndexCtx.indexTypeSourceInfo(Init->getTypeSourceInfo(), D); + if (const FieldDecl *Member = Init->getAnyMember()) + IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D); + IndexCtx.indexBody(Init->getInit(), D, D); + } + } + } + + if (D->isThisDeclarationADefinition()) { + const Stmt *Body = D->getBody(); + if (Body) { + IndexCtx.indexBody(Body, D, D); + } + } + return true; + } + + bool VisitVarDecl(VarDecl *D) { + IndexCtx.handleVar(D); + handleDeclarator(D); + IndexCtx.indexBody(D->getInit(), D); + return true; + } + + bool VisitFieldDecl(FieldDecl *D) { + IndexCtx.handleField(D); + handleDeclarator(D); + if (D->isBitField()) + IndexCtx.indexBody(D->getBitWidth(), D); + else if (D->hasInClassInitializer()) + IndexCtx.indexBody(D->getInClassInitializer(), D); + return true; + } + + bool VisitEnumConstantDecl(EnumConstantDecl *D) { + IndexCtx.handleEnumerator(D); + IndexCtx.indexBody(D->getInitExpr(), D); + return true; + } + + bool VisitTypedefDecl(TypedefNameDecl *D) { + IndexCtx.handleTypedefName(D); + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); + return true; + } + + bool VisitTagDecl(TagDecl *D) { + // Non-free standing tags are handled in indexTypeSourceInfo. + if (D->isFreeStanding()) + IndexCtx.indexTagDecl(D); + return true; + } + + bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { + IndexCtx.handleObjCInterface(D); + + if (D->isThisDeclarationADefinition()) { + IndexCtx.indexTUDeclsInObjCContainer(); + IndexCtx.indexDeclContext(D); + } + return true; + } + + bool VisitObjCProtocolDecl(ObjCProtocolDecl *D) { + IndexCtx.handleObjCProtocol(D); + + if (D->isThisDeclarationADefinition()) { + IndexCtx.indexTUDeclsInObjCContainer(); + IndexCtx.indexDeclContext(D); + } + return true; + } + + bool VisitObjCImplementationDecl(ObjCImplementationDecl *D) { + const ObjCInterfaceDecl *Class = D->getClassInterface(); + if (!Class) + return true; + + if (Class->isImplicitInterfaceDecl()) + IndexCtx.handleObjCInterface(Class); + + IndexCtx.handleObjCImplementation(D); + + IndexCtx.indexTUDeclsInObjCContainer(); + IndexCtx.indexDeclContext(D); + return true; + } + + bool VisitObjCCategoryDecl(ObjCCategoryDecl *D) { + IndexCtx.handleObjCCategory(D); + + IndexCtx.indexTUDeclsInObjCContainer(); + IndexCtx.indexDeclContext(D); + return true; + } + + bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { + const ObjCCategoryDecl *Cat = D->getCategoryDecl(); + if (!Cat) + return true; + + IndexCtx.handleObjCCategoryImpl(D); + + IndexCtx.indexTUDeclsInObjCContainer(); + IndexCtx.indexDeclContext(D); + return true; + } + + bool VisitObjCMethodDecl(ObjCMethodDecl *D) { + // Methods associated with a property, even user-declared ones, are + // handled when we handle the property. + if (D->isSynthesized()) + return true; + + handleObjCMethod(D); + return true; + } + + bool VisitObjCPropertyDecl(ObjCPropertyDecl *D) { + if (ObjCMethodDecl *MD = D->getGetterMethodDecl()) + if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) + handleObjCMethod(MD); + if (ObjCMethodDecl *MD = D->getSetterMethodDecl()) + if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) + handleObjCMethod(MD); + IndexCtx.handleObjCProperty(D); + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); + return true; + } + + bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { + ObjCPropertyDecl *PD = D->getPropertyDecl(); + IndexCtx.handleSynthesizedObjCProperty(D); + + if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return true; + assert(D->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize); + + if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { + if (!IvarD->getSynthesize()) + IndexCtx.handleReference(IvarD, D->getPropertyIvarDeclLoc(), 0, + D->getDeclContext()); + } + + if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { + if (MD->isSynthesized()) + IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), + D->getLexicalDeclContext()); + } + if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { + if (MD->isSynthesized()) + IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), + D->getLexicalDeclContext()); + } + return true; + } + + bool VisitNamespaceDecl(NamespaceDecl *D) { + IndexCtx.handleNamespace(D); + IndexCtx.indexDeclContext(D); + return true; + } + + bool VisitUsingDecl(UsingDecl *D) { + // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, + // we should do better. + + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + for (UsingDecl::shadow_iterator + I = D->shadow_begin(), E = D->shadow_end(); I != E; ++I) { + IndexCtx.handleReference((*I)->getUnderlyingDecl(), D->getLocation(), + D, D->getLexicalDeclContext()); + } + return true; + } + + bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D) { + // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, + // we should do better. + + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), + D->getLocation(), D, D->getLexicalDeclContext()); + return true; + } + + bool VisitClassTemplateDecl(ClassTemplateDecl *D) { + IndexCtx.handleClassTemplate(D); + if (D->isThisDeclarationADefinition()) + IndexCtx.indexDeclContext(D->getTemplatedDecl()); + return true; + } + + bool VisitClassTemplateSpecializationDecl( + ClassTemplateSpecializationDecl *D) { + // FIXME: Notify subsequent callbacks if info comes from implicit + // instantiation. + if (D->isThisDeclarationADefinition() && + (IndexCtx.shouldIndexImplicitTemplateInsts() || + !IndexCtx.isTemplateImplicitInstantiation(D))) + IndexCtx.indexTagDecl(D); + return true; + } + + bool VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { + IndexCtx.handleFunctionTemplate(D); + FunctionDecl *FD = D->getTemplatedDecl(); + handleDeclarator(FD, D); + if (FD->isThisDeclarationADefinition()) { + const Stmt *Body = FD->getBody(); + if (Body) { + IndexCtx.indexBody(Body, D, FD); + } + } + return true; + } + + bool VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { + IndexCtx.handleTypeAliasTemplate(D); + IndexCtx.indexTypeSourceInfo(D->getTemplatedDecl()->getTypeSourceInfo(), D); + return true; + } +}; + +} // anonymous namespace + +void IndexingContext::indexDecl(const Decl *D) { + if (D->isImplicit() && shouldIgnoreIfImplicit(D)) + return; + + bool Handled = IndexingDeclVisitor(*this).Visit(const_cast<Decl*>(D)); + if (!Handled && isa<DeclContext>(D)) + indexDeclContext(cast<DeclContext>(D)); +} + +void IndexingContext::indexDeclContext(const DeclContext *DC) { + for (DeclContext::decl_iterator + I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { + indexDecl(*I); + } +} + +void IndexingContext::indexTopLevelDecl(Decl *D) { + if (isNotFromSourceFile(D->getLocation())) + return; + + if (isa<ObjCMethodDecl>(D)) + return; // Wait for the objc container. + + indexDecl(D); +} + +void IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) + indexTopLevelDecl(*I); +} + +void IndexingContext::indexTUDeclsInObjCContainer() { + while (!TUDeclsInObjCContainer.empty()) { + DeclGroupRef DG = TUDeclsInObjCContainer.front(); + TUDeclsInObjCContainer.pop_front(); + indexDeclGroupRef(DG); + } +} diff --git a/clang/tools/libclang/IndexTypeSourceInfo.cpp b/clang/tools/libclang/IndexTypeSourceInfo.cpp new file mode 100644 index 0000000..b62d521 --- /dev/null +++ b/clang/tools/libclang/IndexTypeSourceInfo.cpp @@ -0,0 +1,156 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" + +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace cxindex; + +namespace { + +class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + +public: + TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent, + const DeclContext *DC) + : IndexCtx(indexCtx), Parent(parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), + Parent, ParentDC); + return true; + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); + return true; + } + + bool VisitTagTypeLoc(TagTypeLoc TL) { + TagDecl *D = TL.getDecl(); + if (D->getParentFunctionOrMethod()) + return true; + + if (TL.isDefinition()) { + IndexCtx.indexTagDecl(D); + return true; + } + + if (D->getLocation() == TL.getNameLoc()) + IndexCtx.handleTagDecl(D); + else + IndexCtx.handleReference(D, TL.getNameLoc(), + Parent, ParentDC); + return true; + } + + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { + IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), + Parent, ParentDC); + return true; + } + + bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { + for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) { + IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i), + Parent, ParentDC); + } + return true; + } + + bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { + if (const TemplateSpecializationType *T = TL.getTypePtr()) { + if (IndexCtx.shouldIndexImplicitTemplateInsts()) { + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + IndexCtx.handleReference(RD, TL.getTemplateNameLoc(), + Parent, ParentDC); + } else { + if (const TemplateDecl *D = T->getTemplateName().getAsTemplateDecl()) + IndexCtx.handleReference(D, TL.getTemplateNameLoc(), + Parent, ParentDC); + } + } + return true; + } + + bool TraverseStmt(Stmt *S) { + IndexCtx.indexBody(S, Parent, ParentDC); + return true; + } +}; + +} // anonymous namespace + +void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo, + const NamedDecl *Parent, + const DeclContext *DC) { + if (!TInfo || TInfo->getTypeLoc().isNull()) + return; + + indexTypeLoc(TInfo->getTypeLoc(), Parent, DC); +} + +void IndexingContext::indexTypeLoc(TypeLoc TL, + const NamedDecl *Parent, + const DeclContext *DC) { + if (TL.isNull()) + return; + + if (DC == 0) + DC = Parent->getLexicalDeclContext(); + TypeIndexer(*this, Parent, DC).TraverseTypeLoc(TL); +} + +void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + const NamedDecl *Parent, + const DeclContext *DC) { + if (!NNS) + return; + + if (NestedNameSpecifierLoc Prefix = NNS.getPrefix()) + indexNestedNameSpecifierLoc(Prefix, Parent, DC); + + if (DC == 0) + DC = Parent->getLexicalDeclContext(); + SourceLocation Loc = NNS.getSourceRange().getBegin(); + + switch (NNS.getNestedNameSpecifier()->getKind()) { + case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Global: + break; + + case NestedNameSpecifier::Namespace: + handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(), + Loc, Parent, DC); + break; + case NestedNameSpecifier::NamespaceAlias: + handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(), + Loc, Parent, DC); + break; + + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + indexTypeLoc(NNS.getTypeLoc(), Parent, DC); + break; + } +} + +void IndexingContext::indexTagDecl(const TagDecl *D) { + if (handleTagDecl(D)) { + if (D->isThisDeclarationADefinition()) + indexDeclContext(D); + } +} diff --git a/clang/tools/libclang/Index_Internal.h b/clang/tools/libclang/Index_Internal.h new file mode 100644 index 0000000..2d42cb8 --- /dev/null +++ b/clang/tools/libclang/Index_Internal.h @@ -0,0 +1,55 @@ +//===- CXString.h - Routines for manipulating CXStrings -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXStrings. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBCLANG_INDEX_INTERNAL_H +#define LLVM_LIBCLANG_INDEX_INTERNAL_H + +#include "clang-c/Index.h" + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(blocks) + +#define INVOKE_BLOCK2(block, arg1, arg2) block(arg1, arg2) + +#else +// If we are compiled with a compiler that doesn't have native blocks support, +// define and call the block manually. + +#define INVOKE_BLOCK2(block, arg1, arg2) block->invoke(block, arg1, arg2) + +typedef struct _CXCursorAndRangeVisitorBlock { + void *isa; + int flags; + int reserved; + enum CXVisitorResult (*invoke)(_CXCursorAndRangeVisitorBlock *, + CXCursor, CXSourceRange); +} *CXCursorAndRangeVisitorBlock; + +#endif // !__has_feature(blocks) + +/// \brief The result of comparing two source ranges. +enum RangeComparisonResult { + /// \brief Either the ranges overlap or one of the ranges is invalid. + RangeOverlap, + + /// \brief The first range ends before the second range starts. + RangeBefore, + + /// \brief The first range starts after the second range ends. + RangeAfter +}; + +#endif diff --git a/clang/tools/libclang/Indexing.cpp b/clang/tools/libclang/Indexing.cpp new file mode 100644 index 0000000..e660c4d --- /dev/null +++ b/clang/tools/libclang/Indexing.cpp @@ -0,0 +1,818 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXTranslationUnit.h" +#include "CXString.h" +#include "CIndexDiagnostic.h" +#include "CIndexer.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/Utils.h" +#include "clang/Sema/SemaConsumer.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PPCallbacks.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/CrashRecoveryContext.h" + +using namespace clang; +using namespace cxstring; +using namespace cxtu; +using namespace cxindex; + +static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx); + +namespace { + +//===----------------------------------------------------------------------===// +// IndexPPCallbacks +//===----------------------------------------------------------------------===// + +class IndexPPCallbacks : public PPCallbacks { + Preprocessor &PP; + IndexingContext &IndexCtx; + bool IsMainFileEntered; + +public: + IndexPPCallbacks(Preprocessor &PP, IndexingContext &indexCtx) + : PP(PP), IndexCtx(indexCtx), IsMainFileEntered(false) { } + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID) { + if (IsMainFileEntered) + return; + + SourceManager &SM = PP.getSourceManager(); + SourceLocation MainFileLoc = SM.getLocForStartOfFile(SM.getMainFileID()); + + if (Loc == MainFileLoc && Reason == PPCallbacks::EnterFile) { + IsMainFileEntered = true; + IndexCtx.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); + } + } + + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + StringRef SearchPath, + StringRef RelativePath) { + bool isImport = (IncludeTok.is(tok::identifier) && + IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); + IndexCtx.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled); + } + + /// MacroDefined - This hook is called whenever a macro definition is seen. + virtual void MacroDefined(const Token &Id, const MacroInfo *MI) { + } + + /// MacroUndefined - This hook is called whenever a macro #undef is seen. + /// MI is released immediately following this callback. + virtual void MacroUndefined(const Token &MacroNameTok, const MacroInfo *MI) { + } + + /// MacroExpands - This is called by when a macro invocation is found. + virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo* MI, + SourceRange Range) { + } + + /// SourceRangeSkipped - This hook is called when a source range is skipped. + /// \param Range The SourceRange that was skipped. The range begins at the + /// #if/#else directive and ends after the #endif/#else directive. + virtual void SourceRangeSkipped(SourceRange Range) { + } +}; + +//===----------------------------------------------------------------------===// +// IndexingConsumer +//===----------------------------------------------------------------------===// + +class IndexingConsumer : public ASTConsumer { + IndexingContext &IndexCtx; + +public: + explicit IndexingConsumer(IndexingContext &indexCtx) + : IndexCtx(indexCtx) { } + + // ASTConsumer Implementation + + virtual void Initialize(ASTContext &Context) { + IndexCtx.setASTContext(Context); + IndexCtx.startedTranslationUnit(); + } + + virtual void HandleTranslationUnit(ASTContext &Ctx) { + } + + virtual bool HandleTopLevelDecl(DeclGroupRef DG) { + IndexCtx.indexDeclGroupRef(DG); + return !IndexCtx.shouldAbort(); + } + + /// \brief Handle the specified top-level declaration that occurred inside + /// and ObjC container. + virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + // They will be handled after the interface is seen first. + IndexCtx.addTUDeclInObjCContainer(D); + } + + /// \brief This is called by the AST reader when deserializing things. + /// The default implementation forwards to HandleTopLevelDecl but we don't + /// care about them when indexing, so have an empty definition. + virtual void HandleInterestingDecl(DeclGroupRef D) {} + + virtual void HandleTagDeclDefinition(TagDecl *D) { + if (!IndexCtx.shouldIndexImplicitTemplateInsts()) + return; + + if (IndexCtx.isTemplateImplicitInstantiation(D)) + IndexCtx.indexDecl(D); + } + + virtual void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) { + if (!IndexCtx.shouldIndexImplicitTemplateInsts()) + return; + + IndexCtx.indexDecl(D); + } +}; + +//===----------------------------------------------------------------------===// +// CaptureDiagnosticConsumer +//===----------------------------------------------------------------------===// + +class CaptureDiagnosticConsumer : public DiagnosticConsumer { + SmallVector<StoredDiagnostic, 4> Errors; +public: + + virtual void HandleDiagnostic(DiagnosticsEngine::Level level, + const Diagnostic &Info) { + if (level >= DiagnosticsEngine::Error) + Errors.push_back(StoredDiagnostic(level, Info)); + } + + DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { + return new IgnoringDiagConsumer(); + } +}; + +//===----------------------------------------------------------------------===// +// IndexingFrontendAction +//===----------------------------------------------------------------------===// + +class IndexingFrontendAction : public ASTFrontendAction { + IndexingContext IndexCtx; + CXTranslationUnit CXTU; + +public: + IndexingFrontendAction(CXClientData clientData, + IndexerCallbacks &indexCallbacks, + unsigned indexOptions, + CXTranslationUnit cxTU) + : IndexCtx(clientData, indexCallbacks, indexOptions, cxTU), + CXTU(cxTU) { } + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + IndexCtx.setASTContext(CI.getASTContext()); + Preprocessor &PP = CI.getPreprocessor(); + PP.addPPCallbacks(new IndexPPCallbacks(PP, IndexCtx)); + IndexCtx.setPreprocessor(PP); + return new IndexingConsumer(IndexCtx); + } + + virtual void EndSourceFileAction() { + indexDiagnostics(CXTU, IndexCtx); + } + + virtual TranslationUnitKind getTranslationUnitKind() { + if (IndexCtx.shouldIndexImplicitTemplateInsts()) + return TU_Complete; + else + return TU_Prefix; + } + virtual bool hasCodeCompletionSupport() const { return false; } +}; + +//===----------------------------------------------------------------------===// +// clang_indexSourceFileUnit Implementation +//===----------------------------------------------------------------------===// + +struct IndexSourceFileInfo { + CXIndexAction idxAction; + CXClientData client_data; + IndexerCallbacks *index_callbacks; + unsigned index_callbacks_size; + unsigned index_options; + const char *source_filename; + const char *const *command_line_args; + int num_command_line_args; + struct CXUnsavedFile *unsaved_files; + unsigned num_unsaved_files; + CXTranslationUnit *out_TU; + unsigned TU_options; + int result; +}; + +struct MemBufferOwner { + SmallVector<const llvm::MemoryBuffer *, 8> Buffers; + + ~MemBufferOwner() { + for (SmallVectorImpl<const llvm::MemoryBuffer *>::iterator + I = Buffers.begin(), E = Buffers.end(); I != E; ++I) + delete *I; + } +}; + +} // anonymous namespace + +static void clang_indexSourceFile_Impl(void *UserData) { + IndexSourceFileInfo *ITUI = + static_cast<IndexSourceFileInfo*>(UserData); + CXIndex CIdx = (CXIndex)ITUI->idxAction; + CXClientData client_data = ITUI->client_data; + IndexerCallbacks *client_index_callbacks = ITUI->index_callbacks; + unsigned index_callbacks_size = ITUI->index_callbacks_size; + unsigned index_options = ITUI->index_options; + const char *source_filename = ITUI->source_filename; + const char * const *command_line_args = ITUI->command_line_args; + int num_command_line_args = ITUI->num_command_line_args; + struct CXUnsavedFile *unsaved_files = ITUI->unsaved_files; + unsigned num_unsaved_files = ITUI->num_unsaved_files; + CXTranslationUnit *out_TU = ITUI->out_TU; + unsigned TU_options = ITUI->TU_options; + ITUI->result = 1; // init as error. + + if (out_TU) + *out_TU = 0; + bool requestedToGetTU = (out_TU != 0); + + if (!CIdx) + return; + if (!client_index_callbacks || index_callbacks_size == 0) + return; + + IndexerCallbacks CB; + memset(&CB, 0, sizeof(CB)); + unsigned ClientCBSize = index_callbacks_size < sizeof(CB) + ? index_callbacks_size : sizeof(CB); + memcpy(&CB, client_index_callbacks, ClientCBSize); + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + CaptureDiagnosticConsumer *CaptureDiag = new CaptureDiagnosticConsumer(); + + // Configure the diagnostics. + DiagnosticOptions DiagOpts; + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(DiagOpts, num_command_line_args, + command_line_args, + CaptureDiag, + /*ShouldOwnClient=*/true, + /*ShouldCloneClient=*/false)); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.getPtr()); + + OwningPtr<std::vector<const char *> > + Args(new std::vector<const char*>()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<std::vector<const char*> > + ArgsCleanup(Args.get()); + + Args->insert(Args->end(), command_line_args, + command_line_args + num_command_line_args); + + // The 'source_filename' argument is optional. If the caller does not + // specify it then it is assumed that the source file is specified + // in the actual argument list. + // Put the source file after command_line_args otherwise if '-x' flag is + // present it will be unused. + if (source_filename) + Args->push_back(source_filename); + + IntrusiveRefCntPtr<CompilerInvocation> + CInvok(createInvocationFromCommandLine(*Args, Diags)); + + if (!CInvok) + return; + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInvocation, + llvm::CrashRecoveryContextReleaseRefCleanup<CompilerInvocation> > + CInvokCleanup(CInvok.getPtr()); + + if (CInvok->getFrontendOpts().Inputs.empty()) + return; + + OwningPtr<MemBufferOwner> BufOwner(new MemBufferOwner()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<MemBufferOwner> + BufOwnerCleanup(BufOwner.get()); + + for (unsigned I = 0; I != num_unsaved_files; ++I) { + StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); + CInvok->getPreprocessorOpts().addRemappedFile(unsaved_files[I].Filename, Buffer); + BufOwner->Buffers.push_back(Buffer); + } + + // Since libclang is primarily used by batch tools dealing with + // (often very broken) source code, where spell-checking can have a + // significant negative impact on performance (particularly when + // precompiled headers are involved), we disable it. + CInvok->getLangOpts()->SpellChecking = false; + + if (!requestedToGetTU) + CInvok->getPreprocessorOpts().DetailedRecord = false; + + if (index_options & CXIndexOpt_SuppressWarnings) + CInvok->getDiagnosticOpts().IgnoreWarnings = true; + + ASTUnit *Unit = ASTUnit::create(CInvok.getPtr(), Diags, + /*CaptureDiagnostics=*/true); + OwningPtr<CXTUOwner> CXTU(new CXTUOwner(MakeCXTranslationUnit(CXXIdx, Unit))); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CXTUOwner> + CXTUCleanup(CXTU.get()); + + OwningPtr<IndexingFrontendAction> IndexAction; + IndexAction.reset(new IndexingFrontendAction(client_data, CB, + index_options, CXTU->getTU())); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<IndexingFrontendAction> + IndexActionCleanup(IndexAction.get()); + + bool Persistent = requestedToGetTU; + StringRef ResourceFilesPath = CXXIdx->getClangResourcesPath(); + bool OnlyLocalDecls = false; + bool PrecompilePreamble = false; + bool CacheCodeCompletionResults = false; + PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts(); + PPOpts.DetailedRecord = false; + PPOpts.AllowPCHWithCompilerErrors = true; + + if (requestedToGetTU) { + OnlyLocalDecls = CXXIdx->getOnlyLocalDecls(); + PrecompilePreamble = TU_options & CXTranslationUnit_PrecompiledPreamble; + // FIXME: Add a flag for modules. + CacheCodeCompletionResults + = TU_options & CXTranslationUnit_CacheCompletionResults; + if (TU_options & CXTranslationUnit_DetailedPreprocessingRecord) { + PPOpts.DetailedRecord = true; + } + } + + DiagnosticErrorTrap DiagTrap(*Diags); + bool Success = ASTUnit::LoadFromCompilerInvocationAction(CInvok.getPtr(), Diags, + IndexAction.get(), + Unit, + Persistent, + ResourceFilesPath, + OnlyLocalDecls, + /*CaptureDiagnostics=*/true, + PrecompilePreamble, + CacheCodeCompletionResults); + if (DiagTrap.hasErrorOccurred() && CXXIdx->getDisplayDiagnostics()) + printDiagsToStderr(Unit); + + if (!Success) + return; + + if (out_TU) + *out_TU = CXTU->takeTU(); + + ITUI->result = 0; // success. +} + +//===----------------------------------------------------------------------===// +// clang_indexTranslationUnit Implementation +//===----------------------------------------------------------------------===// + +namespace { + +struct IndexTranslationUnitInfo { + CXIndexAction idxAction; + CXClientData client_data; + IndexerCallbacks *index_callbacks; + unsigned index_callbacks_size; + unsigned index_options; + CXTranslationUnit TU; + int result; +}; + +} // anonymous namespace + +static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { + Preprocessor &PP = Unit.getPreprocessor(); + if (!PP.getPreprocessingRecord()) + return; + + PreprocessingRecord &PPRec = *PP.getPreprocessingRecord(); + + // FIXME: Only deserialize inclusion directives. + // FIXME: Only deserialize stuff from the last chained PCH, not the PCH/Module + // that it depends on. + + bool OnlyLocal = !Unit.isMainFileAST() && Unit.getOnlyLocalDecls(); + PreprocessingRecord::iterator I, E; + if (OnlyLocal) { + I = PPRec.local_begin(); + E = PPRec.local_end(); + } else { + I = PPRec.begin(); + E = PPRec.end(); + } + + for (; I != E; ++I) { + PreprocessedEntity *PPE = *I; + + if (InclusionDirective *ID = dyn_cast<InclusionDirective>(PPE)) { + IdxCtx.ppIncludedFile(ID->getSourceRange().getBegin(), ID->getFileName(), + ID->getFile(), ID->getKind() == InclusionDirective::Import, + !ID->wasInQuotes()); + } + } +} + +static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IdxCtx) { + // FIXME: Only deserialize stuff from the last chained PCH, not the PCH/Module + // that it depends on. + + bool OnlyLocal = !Unit.isMainFileAST() && Unit.getOnlyLocalDecls(); + + if (OnlyLocal) { + for (ASTUnit::top_level_iterator TL = Unit.top_level_begin(), + TLEnd = Unit.top_level_end(); + TL != TLEnd; ++TL) { + IdxCtx.indexTopLevelDecl(*TL); + if (IdxCtx.shouldAbort()) + return; + } + + } else { + TranslationUnitDecl *TUDecl = Unit.getASTContext().getTranslationUnitDecl(); + for (TranslationUnitDecl::decl_iterator + I = TUDecl->decls_begin(), E = TUDecl->decls_end(); I != E; ++I) { + IdxCtx.indexTopLevelDecl(*I); + if (IdxCtx.shouldAbort()) + return; + } + } +} + +static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx) { + if (!IdxCtx.hasDiagnosticCallback()) + return; + + CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(TU); + IdxCtx.handleDiagnosticSet(DiagSet); +} + +static void clang_indexTranslationUnit_Impl(void *UserData) { + IndexTranslationUnitInfo *ITUI = + static_cast<IndexTranslationUnitInfo*>(UserData); + CXTranslationUnit TU = ITUI->TU; + CXClientData client_data = ITUI->client_data; + IndexerCallbacks *client_index_callbacks = ITUI->index_callbacks; + unsigned index_callbacks_size = ITUI->index_callbacks_size; + unsigned index_options = ITUI->index_options; + ITUI->result = 1; // init as error. + + if (!TU) + return; + if (!client_index_callbacks || index_callbacks_size == 0) + return; + + CIndexer *CXXIdx = (CIndexer*)TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + IndexerCallbacks CB; + memset(&CB, 0, sizeof(CB)); + unsigned ClientCBSize = index_callbacks_size < sizeof(CB) + ? index_callbacks_size : sizeof(CB); + memcpy(&CB, client_index_callbacks, ClientCBSize); + + OwningPtr<IndexingContext> IndexCtx; + IndexCtx.reset(new IndexingContext(client_data, CB, index_options, TU)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<IndexingContext> + IndexCtxCleanup(IndexCtx.get()); + + OwningPtr<IndexingConsumer> IndexConsumer; + IndexConsumer.reset(new IndexingConsumer(*IndexCtx)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<IndexingConsumer> + IndexConsumerCleanup(IndexConsumer.get()); + + ASTUnit *Unit = static_cast<ASTUnit *>(TU->TUData); + if (!Unit) + return; + + FileManager &FileMgr = Unit->getFileManager(); + + if (Unit->getOriginalSourceFileName().empty()) + IndexCtx->enteredMainFile(0); + else + IndexCtx->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); + + IndexConsumer->Initialize(Unit->getASTContext()); + + indexPreprocessingRecord(*Unit, *IndexCtx); + indexTranslationUnit(*Unit, *IndexCtx); + indexDiagnostics(TU, *IndexCtx); + + ITUI->result = 0; +} + +//===----------------------------------------------------------------------===// +// libclang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { + +int clang_index_isEntityObjCContainerKind(CXIdxEntityKind K) { + return CXIdxEntity_ObjCClass <= K && K <= CXIdxEntity_ObjCCategory; +} + +const CXIdxObjCContainerDeclInfo * +clang_index_getObjCContainerDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return 0; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCContainerDeclInfo * + ContInfo = dyn_cast<ObjCContainerDeclInfo>(DI)) + return &ContInfo->ObjCContDeclInfo; + + return 0; +} + +const CXIdxObjCInterfaceDeclInfo * +clang_index_getObjCInterfaceDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return 0; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCInterfaceDeclInfo * + InterInfo = dyn_cast<ObjCInterfaceDeclInfo>(DI)) + return &InterInfo->ObjCInterDeclInfo; + + return 0; +} + +const CXIdxObjCCategoryDeclInfo * +clang_index_getObjCCategoryDeclInfo(const CXIdxDeclInfo *DInfo){ + if (!DInfo) + return 0; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCCategoryDeclInfo * + CatInfo = dyn_cast<ObjCCategoryDeclInfo>(DI)) + return &CatInfo->ObjCCatDeclInfo; + + return 0; +} + +const CXIdxObjCProtocolRefListInfo * +clang_index_getObjCProtocolRefListInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return 0; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + + if (const ObjCInterfaceDeclInfo * + InterInfo = dyn_cast<ObjCInterfaceDeclInfo>(DI)) + return InterInfo->ObjCInterDeclInfo.protocols; + + if (const ObjCProtocolDeclInfo * + ProtInfo = dyn_cast<ObjCProtocolDeclInfo>(DI)) + return &ProtInfo->ObjCProtoRefListInfo; + + if (const ObjCCategoryDeclInfo *CatInfo = dyn_cast<ObjCCategoryDeclInfo>(DI)) + return CatInfo->ObjCCatDeclInfo.protocols; + + return 0; +} + +const CXIdxObjCPropertyDeclInfo * +clang_index_getObjCPropertyDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return 0; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCPropertyDeclInfo *PropInfo = dyn_cast<ObjCPropertyDeclInfo>(DI)) + return &PropInfo->ObjCPropDeclInfo; + + return 0; +} + +const CXIdxIBOutletCollectionAttrInfo * +clang_index_getIBOutletCollectionAttrInfo(const CXIdxAttrInfo *AInfo) { + if (!AInfo) + return 0; + + const AttrInfo *DI = static_cast<const AttrInfo *>(AInfo); + if (const IBOutletCollectionInfo * + IBInfo = dyn_cast<IBOutletCollectionInfo>(DI)) + return &IBInfo->IBCollInfo; + + return 0; +} + +const CXIdxCXXClassDeclInfo * +clang_index_getCXXClassDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return 0; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const CXXClassDeclInfo *ClassInfo = dyn_cast<CXXClassDeclInfo>(DI)) + return &ClassInfo->CXXClassInfo; + + return 0; +} + +CXIdxClientContainer +clang_index_getClientContainer(const CXIdxContainerInfo *info) { + if (!info) + return 0; + const ContainerInfo *Container = static_cast<const ContainerInfo *>(info); + return Container->IndexCtx->getClientContainerForDC(Container->DC); +} + +void clang_index_setClientContainer(const CXIdxContainerInfo *info, + CXIdxClientContainer client) { + if (!info) + return; + const ContainerInfo *Container = static_cast<const ContainerInfo *>(info); + Container->IndexCtx->addContainerInMap(Container->DC, client); +} + +CXIdxClientEntity clang_index_getClientEntity(const CXIdxEntityInfo *info) { + if (!info) + return 0; + const EntityInfo *Entity = static_cast<const EntityInfo *>(info); + return Entity->IndexCtx->getClientEntity(Entity->Dcl); +} + +void clang_index_setClientEntity(const CXIdxEntityInfo *info, + CXIdxClientEntity client) { + if (!info) + return; + const EntityInfo *Entity = static_cast<const EntityInfo *>(info); + Entity->IndexCtx->setClientEntity(Entity->Dcl, client); +} + +CXIndexAction clang_IndexAction_create(CXIndex CIdx) { + // For now, CXIndexAction is featureless. + return CIdx; +} + +void clang_IndexAction_dispose(CXIndexAction idxAction) { + // For now, CXIndexAction is featureless. +} + +int clang_indexSourceFile(CXIndexAction idxAction, + CXClientData client_data, + IndexerCallbacks *index_callbacks, + unsigned index_callbacks_size, + unsigned index_options, + const char *source_filename, + const char * const *command_line_args, + int num_command_line_args, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + CXTranslationUnit *out_TU, + unsigned TU_options) { + + IndexSourceFileInfo ITUI = { idxAction, client_data, index_callbacks, + index_callbacks_size, index_options, + source_filename, command_line_args, + num_command_line_args, unsaved_files, + num_unsaved_files, out_TU, TU_options, 0 }; + + if (getenv("LIBCLANG_NOTHREADS")) { + clang_indexSourceFile_Impl(&ITUI); + return ITUI.result; + } + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, clang_indexSourceFile_Impl, &ITUI)) { + fprintf(stderr, "libclang: crash detected during indexing source file: {\n"); + fprintf(stderr, " 'source_filename' : '%s'\n", source_filename); + fprintf(stderr, " 'command_line_args' : ["); + for (int i = 0; i != num_command_line_args; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "'%s'", command_line_args[i]); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'unsaved_files' : ["); + for (unsigned i = 0; i != num_unsaved_files; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "('%s', '...', %ld)", unsaved_files[i].Filename, + unsaved_files[i].Length); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'options' : %d,\n", TU_options); + fprintf(stderr, "}\n"); + + return 1; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) { + if (out_TU) + PrintLibclangResourceUsage(*out_TU); + } + + return ITUI.result; +} + +int clang_indexTranslationUnit(CXIndexAction idxAction, + CXClientData client_data, + IndexerCallbacks *index_callbacks, + unsigned index_callbacks_size, + unsigned index_options, + CXTranslationUnit TU) { + + IndexTranslationUnitInfo ITUI = { idxAction, client_data, index_callbacks, + index_callbacks_size, index_options, TU, + 0 }; + + if (getenv("LIBCLANG_NOTHREADS")) { + clang_indexTranslationUnit_Impl(&ITUI); + return ITUI.result; + } + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, clang_indexTranslationUnit_Impl, &ITUI)) { + fprintf(stderr, "libclang: crash detected during indexing TU\n"); + + return 1; + } + + return ITUI.result; +} + +void clang_indexLoc_getFileLocation(CXIdxLoc location, + CXIdxClientFile *indexFile, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + if (indexFile) *indexFile = 0; + if (file) *file = 0; + if (line) *line = 0; + if (column) *column = 0; + if (offset) *offset = 0; + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + if (!location.ptr_data[0] || Loc.isInvalid()) + return; + + IndexingContext &IndexCtx = + *static_cast<IndexingContext*>(location.ptr_data[0]); + IndexCtx.translateLoc(Loc, indexFile, file, line, column, offset); +} + +CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + if (!location.ptr_data[0] || Loc.isInvalid()) + return clang_getNullLocation(); + + IndexingContext &IndexCtx = + *static_cast<IndexingContext*>(location.ptr_data[0]); + return cxloc::translateSourceLocation(IndexCtx.getASTContext(), Loc); +} + +} // end: extern "C" + diff --git a/clang/tools/libclang/IndexingContext.cpp b/clang/tools/libclang/IndexingContext.cpp new file mode 100644 index 0000000..ace5c75 --- /dev/null +++ b/clang/tools/libclang/IndexingContext.cpp @@ -0,0 +1,1080 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "CXTranslationUnit.h" +#include "CIndexDiagnostic.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" + +using namespace clang; +using namespace cxindex; +using namespace cxcursor; + +IndexingContext::ObjCProtocolListInfo::ObjCProtocolListInfo( + const ObjCProtocolList &ProtList, + IndexingContext &IdxCtx, + ScratchAlloc &SA) { + ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator + I = ProtList.begin(), E = ProtList.end(); I != E; ++I, ++LI) { + SourceLocation Loc = *LI; + ObjCProtocolDecl *PD = *I; + ProtEntities.push_back(EntityInfo()); + IdxCtx.getEntityInfo(PD, ProtEntities.back(), SA); + CXIdxObjCProtocolRefInfo ProtInfo = { 0, + MakeCursorObjCProtocolRef(PD, Loc, IdxCtx.CXTU), + IdxCtx.getIndexLoc(Loc) }; + ProtInfos.push_back(ProtInfo); + + if (IdxCtx.shouldSuppressRefs()) + IdxCtx.markEntityOccurrenceInFile(PD, Loc); + } + + for (unsigned i = 0, e = ProtInfos.size(); i != e; ++i) + ProtInfos[i].protocol = &ProtEntities[i]; + + for (unsigned i = 0, e = ProtInfos.size(); i != e; ++i) + Prots.push_back(&ProtInfos[i]); +} + + +IBOutletCollectionInfo::IBOutletCollectionInfo( + const IBOutletCollectionInfo &other) + : AttrInfo(CXIdxAttr_IBOutletCollection, other.cursor, other.loc, other.A) { + + IBCollInfo.attrInfo = this; + IBCollInfo.classCursor = other.IBCollInfo.classCursor; + IBCollInfo.classLoc = other.IBCollInfo.classLoc; + if (other.IBCollInfo.objcClass) { + ClassInfo = other.ClassInfo; + IBCollInfo.objcClass = &ClassInfo; + } else + IBCollInfo.objcClass = 0; +} + +AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) + : SA(IdxCtx), ref_cnt(0) { + + if (!D->hasAttrs()) + return; + + for (AttrVec::const_iterator AttrI = D->attr_begin(), AttrE = D->attr_end(); + AttrI != AttrE; ++AttrI) { + const Attr *A = *AttrI; + CXCursor C = MakeCXCursor(A, const_cast<Decl *>(D), IdxCtx.CXTU); + CXIdxLoc Loc = IdxCtx.getIndexLoc(A->getLocation()); + switch (C.kind) { + default: + Attrs.push_back(AttrInfo(CXIdxAttr_Unexposed, C, Loc, A)); + break; + case CXCursor_IBActionAttr: + Attrs.push_back(AttrInfo(CXIdxAttr_IBAction, C, Loc, A)); + break; + case CXCursor_IBOutletAttr: + Attrs.push_back(AttrInfo(CXIdxAttr_IBOutlet, C, Loc, A)); + break; + case CXCursor_IBOutletCollectionAttr: + IBCollAttrs.push_back(IBOutletCollectionInfo(C, Loc, A)); + break; + } + } + + for (unsigned i = 0, e = IBCollAttrs.size(); i != e; ++i) { + IBOutletCollectionInfo &IBInfo = IBCollAttrs[i]; + CXAttrs.push_back(&IBInfo); + + const IBOutletCollectionAttr * + IBAttr = cast<IBOutletCollectionAttr>(IBInfo.A); + IBInfo.IBCollInfo.attrInfo = &IBInfo; + IBInfo.IBCollInfo.classLoc = IdxCtx.getIndexLoc(IBAttr->getInterfaceLoc()); + IBInfo.IBCollInfo.objcClass = 0; + IBInfo.IBCollInfo.classCursor = clang_getNullCursor(); + QualType Ty = IBAttr->getInterface(); + if (const ObjCInterfaceType *InterTy = Ty->getAs<ObjCInterfaceType>()) { + if (const ObjCInterfaceDecl *InterD = InterTy->getInterface()) { + IdxCtx.getEntityInfo(InterD, IBInfo.ClassInfo, SA); + IBInfo.IBCollInfo.objcClass = &IBInfo.ClassInfo; + IBInfo.IBCollInfo.classCursor = MakeCursorObjCClassRef(InterD, + IBAttr->getInterfaceLoc(), IdxCtx.CXTU); + } + } + } + + for (unsigned i = 0, e = Attrs.size(); i != e; ++i) + CXAttrs.push_back(&Attrs[i]); +} + +IntrusiveRefCntPtr<AttrListInfo> +AttrListInfo::create(const Decl *D, IndexingContext &IdxCtx) { + ScratchAlloc SA(IdxCtx); + AttrListInfo *attrs = SA.allocate<AttrListInfo>(); + return new (attrs) AttrListInfo(D, IdxCtx); +} + +IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, + IndexingContext &IdxCtx, + ScratchAlloc &SA) { + for (CXXRecordDecl::base_class_const_iterator + I = D->bases_begin(), E = D->bases_end(); I != E; ++I) { + const CXXBaseSpecifier &Base = *I; + BaseEntities.push_back(EntityInfo()); + const NamedDecl *BaseD = 0; + QualType T = Base.getType(); + SourceLocation Loc = getBaseLoc(Base); + + if (const TypedefType *TDT = T->getAs<TypedefType>()) { + BaseD = TDT->getDecl(); + } else if (const TemplateSpecializationType * + TST = T->getAs<TemplateSpecializationType>()) { + BaseD = TST->getTemplateName().getAsTemplateDecl(); + } else if (const RecordType *RT = T->getAs<RecordType>()) { + BaseD = RT->getDecl(); + } + + if (BaseD) + IdxCtx.getEntityInfo(BaseD, BaseEntities.back(), SA); + CXIdxBaseClassInfo BaseInfo = { 0, + MakeCursorCXXBaseSpecifier(&Base, IdxCtx.CXTU), + IdxCtx.getIndexLoc(Loc) }; + BaseInfos.push_back(BaseInfo); + } + + for (unsigned i = 0, e = BaseInfos.size(); i != e; ++i) { + if (BaseEntities[i].name && BaseEntities[i].USR) + BaseInfos[i].base = &BaseEntities[i]; + } + + for (unsigned i = 0, e = BaseInfos.size(); i != e; ++i) + CXBases.push_back(&BaseInfos[i]); +} + +SourceLocation IndexingContext::CXXBasesListInfo::getBaseLoc( + const CXXBaseSpecifier &Base) const { + SourceLocation Loc = Base.getSourceRange().getBegin(); + TypeLoc TL; + if (Base.getTypeSourceInfo()) + TL = Base.getTypeSourceInfo()->getTypeLoc(); + if (TL.isNull()) + return Loc; + + if (const QualifiedTypeLoc *QL = dyn_cast<QualifiedTypeLoc>(&TL)) + TL = QL->getUnqualifiedLoc(); + + if (const ElaboratedTypeLoc *EL = dyn_cast<ElaboratedTypeLoc>(&TL)) + return EL->getNamedTypeLoc().getBeginLoc(); + if (const DependentNameTypeLoc *DL = dyn_cast<DependentNameTypeLoc>(&TL)) + return DL->getNameLoc(); + if (const DependentTemplateSpecializationTypeLoc * + DTL = dyn_cast<DependentTemplateSpecializationTypeLoc>(&TL)) + return DTL->getTemplateNameLoc(); + + return Loc; +} + +const char *ScratchAlloc::toCStr(StringRef Str) { + if (Str.empty()) + return ""; + if (Str.data()[Str.size()] == '\0') + return Str.data(); + return copyCStr(Str); +} + +const char *ScratchAlloc::copyCStr(StringRef Str) { + char *buf = IdxCtx.StrScratch.Allocate<char>(Str.size() + 1); + std::uninitialized_copy(Str.begin(), Str.end(), buf); + buf[Str.size()] = '\0'; + return buf; +} + +void IndexingContext::setASTContext(ASTContext &ctx) { + Ctx = &ctx; + static_cast<ASTUnit*>(CXTU->TUData)->setASTContext(&ctx); +} + +void IndexingContext::setPreprocessor(Preprocessor &PP) { + static_cast<ASTUnit*>(CXTU->TUData)->setPreprocessor(&PP); +} + +bool IndexingContext::shouldAbort() { + if (!CB.abortQuery) + return false; + return CB.abortQuery(ClientData, 0); +} + +void IndexingContext::enteredMainFile(const FileEntry *File) { + if (File && CB.enteredMainFile) { + CXIdxClientFile idxFile = CB.enteredMainFile(ClientData, (CXFile)File, 0); + FileMap[File] = idxFile; + } +} + +void IndexingContext::ppIncludedFile(SourceLocation hashLoc, + StringRef filename, + const FileEntry *File, + bool isImport, bool isAngled) { + if (!CB.ppIncludedFile) + return; + + ScratchAlloc SA(*this); + CXIdxIncludedFileInfo Info = { getIndexLoc(hashLoc), + SA.toCStr(filename), + (CXFile)File, + isImport, isAngled }; + CXIdxClientFile idxFile = CB.ppIncludedFile(ClientData, &Info); + FileMap[File] = idxFile; +} + +void IndexingContext::startedTranslationUnit() { + CXIdxClientContainer idxCont = 0; + if (CB.startedTranslationUnit) + idxCont = CB.startedTranslationUnit(ClientData, 0); + addContainerInMap(Ctx->getTranslationUnitDecl(), idxCont); +} + +void IndexingContext::handleDiagnosticSet(CXDiagnostic CXDiagSet) { + if (!CB.diagnostic) + return; + + CB.diagnostic(ClientData, CXDiagSet, 0); +} + +bool IndexingContext::handleDecl(const NamedDecl *D, + SourceLocation Loc, CXCursor Cursor, + DeclInfo &DInfo, + const DeclContext *LexicalDC) { + if (!CB.indexDeclaration || !D) + return false; + if (D->isImplicit() && shouldIgnoreIfImplicit(D)) + return false; + + ScratchAlloc SA(*this); + getEntityInfo(D, DInfo.EntInfo, SA); + if ((!shouldIndexFunctionLocalSymbols() && !DInfo.EntInfo.USR) + || Loc.isInvalid()) + return false; + + if (!LexicalDC) + LexicalDC = D->getLexicalDeclContext(); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(D, Loc); + + DInfo.entityInfo = &DInfo.EntInfo; + DInfo.cursor = Cursor; + DInfo.loc = getIndexLoc(Loc); + DInfo.isImplicit = D->isImplicit(); + + DInfo.attributes = DInfo.EntInfo.attributes; + DInfo.numAttributes = DInfo.EntInfo.numAttributes; + + getContainerInfo(D->getDeclContext(), DInfo.SemanticContainer); + DInfo.semanticContainer = &DInfo.SemanticContainer; + + if (LexicalDC == D->getDeclContext()) { + DInfo.lexicalContainer = &DInfo.SemanticContainer; + } else if (isTemplateImplicitInstantiation(D)) { + // Implicit instantiations have the lexical context of where they were + // instantiated first. We choose instead the semantic context because: + // 1) at the time that we see the instantiation we have not seen the + // function where it occurred yet. + // 2) the lexical context of the first instantiation is not useful + // information anyway. + DInfo.lexicalContainer = &DInfo.SemanticContainer; + } else { + getContainerInfo(LexicalDC, DInfo.LexicalContainer); + DInfo.lexicalContainer = &DInfo.LexicalContainer; + } + + if (DInfo.isContainer) { + getContainerInfo(getEntityContainer(D), DInfo.DeclAsContainer); + DInfo.declAsContainer = &DInfo.DeclAsContainer; + } + + CB.indexDeclaration(ClientData, &DInfo); + return true; +} + +bool IndexingContext::handleObjCContainer(const ObjCContainerDecl *D, + SourceLocation Loc, CXCursor Cursor, + ObjCContainerDeclInfo &ContDInfo) { + ContDInfo.ObjCContDeclInfo.declInfo = &ContDInfo; + return handleDecl(D, Loc, Cursor, ContDInfo); +} + +bool IndexingContext::handleFunction(const FunctionDecl *D) { + DeclInfo DInfo(!D->isFirstDeclaration(), D->isThisDeclarationADefinition(), + D->isThisDeclarationADefinition()); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleVar(const VarDecl *D) { + DeclInfo DInfo(!D->isFirstDeclaration(), D->isThisDeclarationADefinition(), + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleField(const FieldDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleEnumerator(const EnumConstantDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleTagDecl(const TagDecl *D) { + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(D)) + return handleCXXRecordDecl(CXXRD, D); + + DeclInfo DInfo(!D->isFirstDeclaration(), D->isThisDeclarationADefinition(), + D->isThisDeclarationADefinition()); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleTypedefName(const TypedefNameDecl *D) { + DeclInfo DInfo(!D->isFirstDeclaration(), /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { + // For @class forward declarations, suppress them the same way as references. + if (!D->isThisDeclarationADefinition()) { + if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) + return false; // already occurred. + + // FIXME: This seems like the wrong definition for redeclaration. + bool isRedeclaration = D->hasDefinition() || D->getPreviousDecl(); + ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/true, isRedeclaration, + /*isImplementation=*/false); + return handleObjCContainer(D, D->getLocation(), + MakeCursorObjCClassRef(D, D->getLocation(), + CXTU), + ContDInfo); + } + + ScratchAlloc SA(*this); + + CXIdxBaseClassInfo BaseClass; + EntityInfo BaseEntity; + BaseClass.cursor = clang_getNullCursor(); + if (ObjCInterfaceDecl *SuperD = D->getSuperClass()) { + getEntityInfo(SuperD, BaseEntity, SA); + SourceLocation SuperLoc = D->getSuperClassLoc(); + BaseClass.base = &BaseEntity; + BaseClass.cursor = MakeCursorObjCSuperClassRef(SuperD, SuperLoc, CXTU); + BaseClass.loc = getIndexLoc(SuperLoc); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(SuperD, SuperLoc); + } + + ObjCProtocolList EmptyProtoList; + ObjCProtocolListInfo ProtInfo(D->isThisDeclarationADefinition() + ? D->getReferencedProtocols() + : EmptyProtoList, + *this, SA); + + ObjCInterfaceDeclInfo InterInfo(D); + InterInfo.ObjCProtoListInfo = ProtInfo.getListInfo(); + InterInfo.ObjCInterDeclInfo.containerInfo = &InterInfo.ObjCContDeclInfo; + InterInfo.ObjCInterDeclInfo.superInfo = D->getSuperClass() ? &BaseClass : 0; + InterInfo.ObjCInterDeclInfo.protocols = &InterInfo.ObjCProtoListInfo; + + return handleObjCContainer(D, D->getLocation(), getCursor(D), InterInfo); +} + +bool IndexingContext::handleObjCImplementation( + const ObjCImplementationDecl *D) { + ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/false, + /*isRedeclaration=*/true, + /*isImplementation=*/true); + return handleObjCContainer(D, D->getLocation(), getCursor(D), ContDInfo); +} + +bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { + if (!D->isThisDeclarationADefinition()) { + if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) + return false; // already occurred. + + // FIXME: This seems like the wrong definition for redeclaration. + bool isRedeclaration = D->hasDefinition() || D->getPreviousDecl(); + ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/true, + isRedeclaration, + /*isImplementation=*/false); + return handleObjCContainer(D, D->getLocation(), + MakeCursorObjCProtocolRef(D, D->getLocation(), + CXTU), + ContDInfo); + } + + ScratchAlloc SA(*this); + ObjCProtocolList EmptyProtoList; + ObjCProtocolListInfo ProtListInfo(D->isThisDeclarationADefinition() + ? D->getReferencedProtocols() + : EmptyProtoList, + *this, SA); + + ObjCProtocolDeclInfo ProtInfo(D); + ProtInfo.ObjCProtoRefListInfo = ProtListInfo.getListInfo(); + + return handleObjCContainer(D, D->getLocation(), getCursor(D), ProtInfo); +} + +bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { + ScratchAlloc SA(*this); + + ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/false); + EntityInfo ClassEntity; + const ObjCInterfaceDecl *IFaceD = D->getClassInterface(); + SourceLocation ClassLoc = D->getLocation(); + SourceLocation CategoryLoc = D->IsClassExtension() ? ClassLoc + : D->getCategoryNameLoc(); + getEntityInfo(IFaceD, ClassEntity, SA); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(IFaceD, ClassLoc); + + ObjCProtocolListInfo ProtInfo(D->getReferencedProtocols(), *this, SA); + + CatDInfo.ObjCCatDeclInfo.containerInfo = &CatDInfo.ObjCContDeclInfo; + if (IFaceD) { + CatDInfo.ObjCCatDeclInfo.objcClass = &ClassEntity; + CatDInfo.ObjCCatDeclInfo.classCursor = + MakeCursorObjCClassRef(IFaceD, ClassLoc, CXTU); + } else { + CatDInfo.ObjCCatDeclInfo.objcClass = 0; + CatDInfo.ObjCCatDeclInfo.classCursor = clang_getNullCursor(); + } + CatDInfo.ObjCCatDeclInfo.classLoc = getIndexLoc(ClassLoc); + CatDInfo.ObjCProtoListInfo = ProtInfo.getListInfo(); + CatDInfo.ObjCCatDeclInfo.protocols = &CatDInfo.ObjCProtoListInfo; + + return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); +} + +bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { + ScratchAlloc SA(*this); + + const ObjCCategoryDecl *CatD = D->getCategoryDecl(); + ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/true); + EntityInfo ClassEntity; + const ObjCInterfaceDecl *IFaceD = CatD->getClassInterface(); + SourceLocation ClassLoc = D->getLocation(); + SourceLocation CategoryLoc = D->getCategoryNameLoc(); + getEntityInfo(IFaceD, ClassEntity, SA); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(IFaceD, ClassLoc); + + CatDInfo.ObjCCatDeclInfo.containerInfo = &CatDInfo.ObjCContDeclInfo; + if (IFaceD) { + CatDInfo.ObjCCatDeclInfo.objcClass = &ClassEntity; + CatDInfo.ObjCCatDeclInfo.classCursor = + MakeCursorObjCClassRef(IFaceD, ClassLoc, CXTU); + } else { + CatDInfo.ObjCCatDeclInfo.objcClass = 0; + CatDInfo.ObjCCatDeclInfo.classCursor = clang_getNullCursor(); + } + CatDInfo.ObjCCatDeclInfo.classLoc = getIndexLoc(ClassLoc); + CatDInfo.ObjCCatDeclInfo.protocols = 0; + + return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); +} + +bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { + DeclInfo DInfo(!D->isCanonicalDecl(), D->isThisDeclarationADefinition(), + D->isThisDeclarationADefinition()); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleSynthesizedObjCProperty( + const ObjCPropertyImplDecl *D) { + ObjCPropertyDecl *PD = D->getPropertyDecl(); + return handleReference(PD, D->getLocation(), getCursor(D), 0, D->getDeclContext()); +} + +bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, + SourceLocation Loc, + const DeclContext *LexicalDC) { + DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC); +} + +bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { + ScratchAlloc SA(*this); + + ObjCPropertyDeclInfo DInfo; + EntityInfo GetterEntity; + EntityInfo SetterEntity; + + DInfo.ObjCPropDeclInfo.declInfo = &DInfo; + + if (ObjCMethodDecl *Getter = D->getGetterMethodDecl()) { + getEntityInfo(Getter, GetterEntity, SA); + DInfo.ObjCPropDeclInfo.getter = &GetterEntity; + } else { + DInfo.ObjCPropDeclInfo.getter = 0; + } + if (ObjCMethodDecl *Setter = D->getSetterMethodDecl()) { + getEntityInfo(Setter, SetterEntity, SA); + DInfo.ObjCPropDeclInfo.setter = &SetterEntity; + } else { + DInfo.ObjCPropDeclInfo.setter = 0; + } + + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleNamespace(const NamespaceDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/!D->isOriginalNamespace(), + /*isDefinition=*/true, + /*isContainer=*/true); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleClassTemplate(const ClassTemplateDecl *D) { + return handleCXXRecordDecl(D->getTemplatedDecl(), D); +} + +bool IndexingContext::handleFunctionTemplate(const FunctionTemplateDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), + /*isDefinition=*/D->isThisDeclarationADefinition(), + /*isContainer=*/D->isThisDeclarationADefinition()); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), + /*isDefinition=*/true, /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + const Expr *E, + CXIdxEntityRefKind Kind) { + if (!D) + return false; + + CXCursor Cursor = E ? MakeCXCursor(const_cast<Expr*>(E), + const_cast<Decl*>(cast<Decl>(DC)), CXTU) + : getRefCursor(D, Loc); + return handleReference(D, Loc, Cursor, Parent, DC, E, Kind); +} + +bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, + CXCursor Cursor, + const NamedDecl *Parent, + const DeclContext *DC, + const Expr *E, + CXIdxEntityRefKind Kind) { + if (!CB.indexEntityReference) + return false; + + if (!D) + return false; + if (Loc.isInvalid()) + return false; + if (!shouldIndexFunctionLocalSymbols() && D->getParentFunctionOrMethod()) + return false; + if (isNotFromSourceFile(D->getLocation())) + return false; + if (D->isImplicit() && shouldIgnoreIfImplicit(D)) + return false; + + if (shouldSuppressRefs()) { + if (markEntityOccurrenceInFile(D, Loc)) + return false; // already occurred. + } + + ScratchAlloc SA(*this); + EntityInfo RefEntity, ParentEntity; + getEntityInfo(D, RefEntity, SA); + if (!RefEntity.USR) + return false; + + getEntityInfo(Parent, ParentEntity, SA); + + ContainerInfo Container; + getContainerInfo(DC, Container); + + CXIdxEntityRefInfo Info = { Kind, + Cursor, + getIndexLoc(Loc), + &RefEntity, + Parent ? &ParentEntity : 0, + &Container }; + CB.indexEntityReference(ClientData, &Info); + return true; +} + +bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { + if (Loc.isInvalid()) + return true; + SourceManager &SM = Ctx->getSourceManager(); + SourceLocation FileLoc = SM.getFileLoc(Loc); + FileID FID = SM.getFileID(FileLoc); + return SM.getFileEntryForID(FID) == 0; +} + +void IndexingContext::addContainerInMap(const DeclContext *DC, + CXIdxClientContainer container) { + if (!DC) + return; + + ContainerMapTy::iterator I = ContainerMap.find(DC); + if (I == ContainerMap.end()) { + if (container) + ContainerMap[DC] = container; + return; + } + // Allow changing the container of a previously seen DeclContext so we + // can handle invalid user code, like a function re-definition. + if (container) + I->second = container; + else + ContainerMap.erase(I); +} + +CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { + if (!D) + return 0; + EntityMapTy::const_iterator I = EntityMap.find(D); + if (I == EntityMap.end()) + return 0; + return I->second; +} + +void IndexingContext::setClientEntity(const Decl *D, CXIdxClientEntity client) { + if (!D) + return; + EntityMap[D] = client; +} + +bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, + const NamedDecl *OrigD) { + if (RD->isThisDeclarationADefinition()) { + ScratchAlloc SA(*this); + CXXClassDeclInfo CXXDInfo(/*isRedeclaration=*/!OrigD->isCanonicalDecl(), + /*isDefinition=*/RD->isThisDeclarationADefinition()); + CXXBasesListInfo BaseList(RD, *this, SA); + CXXDInfo.CXXClassInfo.declInfo = &CXXDInfo; + CXXDInfo.CXXClassInfo.bases = BaseList.getBases(); + CXXDInfo.CXXClassInfo.numBases = BaseList.getNumBases(); + + if (shouldSuppressRefs()) { + // Go through bases and mark them as referenced. + for (unsigned i = 0, e = BaseList.getNumBases(); i != e; ++i) { + const CXIdxBaseClassInfo *baseInfo = BaseList.getBases()[i]; + if (baseInfo->base) { + const NamedDecl *BaseD = BaseList.BaseEntities[i].Dcl; + SourceLocation + Loc = SourceLocation::getFromRawEncoding(baseInfo->loc.int_data); + markEntityOccurrenceInFile(BaseD, Loc); + } + } + } + + return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), CXXDInfo); + } + + DeclInfo DInfo(/*isRedeclaration=*/!OrigD->isCanonicalDecl(), + /*isDefinition=*/RD->isThisDeclarationADefinition(), + /*isContainer=*/RD->isThisDeclarationADefinition()); + return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), DInfo); +} + +bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, + SourceLocation Loc) { + if (!D || Loc.isInvalid()) + return true; + + SourceManager &SM = Ctx->getSourceManager(); + D = getEntityDecl(D); + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SM.getFileLoc(Loc)); + FileID FID = LocInfo.first; + if (FID.isInvalid()) + return true; + + const FileEntry *FE = SM.getFileEntryForID(FID); + if (!FE) + return true; + RefFileOccurence RefOccur(FE, D); + std::pair<llvm::DenseSet<RefFileOccurence>::iterator, bool> + res = RefFileOccurences.insert(RefOccur); + if (!res.second) + return true; // already in map. + + return false; +} + +const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { + assert(D); + D = cast<NamedDecl>(D->getCanonicalDecl()); + + if (const ObjCImplementationDecl * + ImplD = dyn_cast<ObjCImplementationDecl>(D)) { + return getEntityDecl(ImplD->getClassInterface()); + + } else if (const ObjCCategoryImplDecl * + CatImplD = dyn_cast<ObjCCategoryImplDecl>(D)) { + return getEntityDecl(CatImplD->getCategoryDecl()); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FunctionTemplateDecl *TemplD = FD->getDescribedFunctionTemplate()) + return getEntityDecl(TemplD); + } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { + if (ClassTemplateDecl *TemplD = RD->getDescribedClassTemplate()) + return getEntityDecl(TemplD); + } + + return D; +} + +const DeclContext * +IndexingContext::getEntityContainer(const Decl *D) const { + const DeclContext *DC = dyn_cast<DeclContext>(D); + if (DC) + return DC; + + if (const ClassTemplateDecl *ClassTempl = dyn_cast<ClassTemplateDecl>(D)) { + DC = ClassTempl->getTemplatedDecl(); + } if (const FunctionTemplateDecl * + FuncTempl = dyn_cast<FunctionTemplateDecl>(D)) { + DC = FuncTempl->getTemplatedDecl(); + } + + return DC; +} + +CXIdxClientContainer +IndexingContext::getClientContainerForDC(const DeclContext *DC) const { + if (!DC) + return 0; + + ContainerMapTy::const_iterator I = ContainerMap.find(DC); + if (I == ContainerMap.end()) + return 0; + + return I->second; +} + +CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { + if (!File) + return 0; + + FileMapTy::iterator FI = FileMap.find(File); + if (FI != FileMap.end()) + return FI->second; + + return 0; +} + +CXIdxLoc IndexingContext::getIndexLoc(SourceLocation Loc) const { + CXIdxLoc idxLoc = { {0, 0}, 0 }; + if (Loc.isInvalid()) + return idxLoc; + + idxLoc.ptr_data[0] = (void*)this; + idxLoc.int_data = Loc.getRawEncoding(); + return idxLoc; +} + +void IndexingContext::translateLoc(SourceLocation Loc, + CXIdxClientFile *indexFile, CXFile *file, + unsigned *line, unsigned *column, + unsigned *offset) { + if (Loc.isInvalid()) + return; + + SourceManager &SM = Ctx->getSourceManager(); + Loc = SM.getFileLoc(Loc); + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isInvalid()) + return; + + const FileEntry *FE = SM.getFileEntryForID(FID); + if (indexFile) + *indexFile = getIndexFile(FE); + if (file) + *file = (void *)FE; + if (line) + *line = SM.getLineNumber(FID, FileOffset); + if (column) + *column = SM.getColumnNumber(FID, FileOffset); + if (offset) + *offset = FileOffset; +} + +void IndexingContext::getEntityInfo(const NamedDecl *D, + EntityInfo &EntityInfo, + ScratchAlloc &SA) { + if (!D) + return; + + D = getEntityDecl(D); + EntityInfo.cursor = getCursor(D); + EntityInfo.Dcl = D; + EntityInfo.IndexCtx = this; + EntityInfo.kind = CXIdxEntity_Unexposed; + EntityInfo.templateKind = CXIdxEntity_NonTemplate; + EntityInfo.lang = CXIdxEntityLang_C; + + if (D->hasAttrs()) { + EntityInfo.AttrList = AttrListInfo::create(D, *this); + EntityInfo.attributes = EntityInfo.AttrList->getAttrs(); + EntityInfo.numAttributes = EntityInfo.AttrList->getNumAttrs(); + } + + if (const TagDecl *TD = dyn_cast<TagDecl>(D)) { + switch (TD->getTagKind()) { + case TTK_Struct: + EntityInfo.kind = CXIdxEntity_Struct; break; + case TTK_Union: + EntityInfo.kind = CXIdxEntity_Union; break; + case TTK_Class: + EntityInfo.kind = CXIdxEntity_CXXClass; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + case TTK_Enum: + EntityInfo.kind = CXIdxEntity_Enum; break; + } + + if (const CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(D)) + if (!CXXRec->isCLike()) + EntityInfo.lang = CXIdxEntityLang_CXX; + + if (isa<ClassTemplatePartialSpecializationDecl>(D)) { + EntityInfo.templateKind = CXIdxEntity_TemplatePartialSpecialization; + } else if (isa<ClassTemplateSpecializationDecl>(D)) { + EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; + } + + } else { + switch (D->getKind()) { + case Decl::Typedef: + EntityInfo.kind = CXIdxEntity_Typedef; break; + case Decl::Function: + EntityInfo.kind = CXIdxEntity_Function; + break; + case Decl::ParmVar: + EntityInfo.kind = CXIdxEntity_Variable; + break; + case Decl::Var: + EntityInfo.kind = CXIdxEntity_Variable; + if (isa<CXXRecordDecl>(D->getDeclContext())) { + EntityInfo.kind = CXIdxEntity_CXXStaticVariable; + EntityInfo.lang = CXIdxEntityLang_CXX; + } + break; + case Decl::Field: + EntityInfo.kind = CXIdxEntity_Field; + if (const CXXRecordDecl * + CXXRec = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { + // FIXME: isPOD check is not sufficient, a POD can contain methods, + // we want a isCStructLike check. + if (!CXXRec->isPOD()) + EntityInfo.lang = CXIdxEntityLang_CXX; + } + break; + case Decl::EnumConstant: + EntityInfo.kind = CXIdxEntity_EnumConstant; break; + case Decl::ObjCInterface: + EntityInfo.kind = CXIdxEntity_ObjCClass; + EntityInfo.lang = CXIdxEntityLang_ObjC; + break; + case Decl::ObjCProtocol: + EntityInfo.kind = CXIdxEntity_ObjCProtocol; + EntityInfo.lang = CXIdxEntityLang_ObjC; + break; + case Decl::ObjCCategory: + EntityInfo.kind = CXIdxEntity_ObjCCategory; + EntityInfo.lang = CXIdxEntityLang_ObjC; + break; + case Decl::ObjCMethod: + if (cast<ObjCMethodDecl>(D)->isInstanceMethod()) + EntityInfo.kind = CXIdxEntity_ObjCInstanceMethod; + else + EntityInfo.kind = CXIdxEntity_ObjCClassMethod; + EntityInfo.lang = CXIdxEntityLang_ObjC; + break; + case Decl::ObjCProperty: + EntityInfo.kind = CXIdxEntity_ObjCProperty; + EntityInfo.lang = CXIdxEntityLang_ObjC; + break; + case Decl::ObjCIvar: + EntityInfo.kind = CXIdxEntity_ObjCIvar; + EntityInfo.lang = CXIdxEntityLang_ObjC; + break; + case Decl::Namespace: + EntityInfo.kind = CXIdxEntity_CXXNamespace; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + case Decl::NamespaceAlias: + EntityInfo.kind = CXIdxEntity_CXXNamespaceAlias; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + case Decl::CXXConstructor: + EntityInfo.kind = CXIdxEntity_CXXConstructor; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + case Decl::CXXDestructor: + EntityInfo.kind = CXIdxEntity_CXXDestructor; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + case Decl::CXXConversion: + EntityInfo.kind = CXIdxEntity_CXXConversionFunction; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + case Decl::CXXMethod: { + const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); + if (MD->isStatic()) + EntityInfo.kind = CXIdxEntity_CXXStaticMethod; + else + EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + } + case Decl::ClassTemplate: + EntityInfo.kind = CXIdxEntity_CXXClass; + EntityInfo.templateKind = CXIdxEntity_Template; + break; + case Decl::FunctionTemplate: + EntityInfo.kind = CXIdxEntity_Function; + EntityInfo.templateKind = CXIdxEntity_Template; + if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>( + cast<FunctionTemplateDecl>(D)->getTemplatedDecl())) { + if (isa<CXXConstructorDecl>(MD)) + EntityInfo.kind = CXIdxEntity_CXXConstructor; + else if (isa<CXXDestructorDecl>(MD)) + EntityInfo.kind = CXIdxEntity_CXXDestructor; + else if (isa<CXXConversionDecl>(MD)) + EntityInfo.kind = CXIdxEntity_CXXConversionFunction; + else { + if (MD->isStatic()) + EntityInfo.kind = CXIdxEntity_CXXStaticMethod; + else + EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; + } + } + break; + case Decl::TypeAliasTemplate: + EntityInfo.kind = CXIdxEntity_CXXTypeAlias; + EntityInfo.templateKind = CXIdxEntity_Template; + break; + case Decl::TypeAlias: + EntityInfo.kind = CXIdxEntity_CXXTypeAlias; + EntityInfo.lang = CXIdxEntityLang_CXX; + break; + default: + break; + } + } + + if (EntityInfo.kind == CXIdxEntity_Unexposed) + return; + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) + EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; + } + + if (EntityInfo.templateKind != CXIdxEntity_NonTemplate) + EntityInfo.lang = CXIdxEntityLang_CXX; + + if (IdentifierInfo *II = D->getIdentifier()) { + EntityInfo.name = SA.toCStr(II->getName()); + + } else if (isa<TagDecl>(D) || isa<FieldDecl>(D) || isa<NamespaceDecl>(D)) { + EntityInfo.name = 0; // anonymous tag/field/namespace. + + } else { + SmallString<256> StrBuf; + { + llvm::raw_svector_ostream OS(StrBuf); + D->printName(OS); + } + EntityInfo.name = SA.copyCStr(StrBuf.str()); + } + + { + SmallString<512> StrBuf; + bool Ignore = getDeclCursorUSR(D, StrBuf); + if (Ignore) { + EntityInfo.USR = 0; + } else { + EntityInfo.USR = SA.copyCStr(StrBuf.str()); + } + } +} + +void IndexingContext::getContainerInfo(const DeclContext *DC, + ContainerInfo &ContInfo) { + ContInfo.cursor = getCursor(cast<Decl>(DC)); + ContInfo.DC = DC; + ContInfo.IndexCtx = this; +} + +CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { + if (const TypeDecl *TD = dyn_cast<TypeDecl>(D)) + return MakeCursorTypeRef(TD, Loc, CXTU); + if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) + return MakeCursorObjCClassRef(ID, Loc, CXTU); + if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) + return MakeCursorObjCProtocolRef(PD, Loc, CXTU); + if (const TemplateDecl *Template = dyn_cast<TemplateDecl>(D)) + return MakeCursorTemplateRef(Template, Loc, CXTU); + if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(D)) + return MakeCursorNamespaceRef(Namespace, Loc, CXTU); + if (const NamespaceAliasDecl *Namespace = dyn_cast<NamespaceAliasDecl>(D)) + return MakeCursorNamespaceRef(Namespace, Loc, CXTU); + if (const FieldDecl *Field = dyn_cast<FieldDecl>(D)) + return MakeCursorMemberRef(Field, Loc, CXTU); + if (const VarDecl *Var = dyn_cast<VarDecl>(D)) + return MakeCursorVariableRef(Var, Loc, CXTU); + + return clang_getNullCursor(); +} + +bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { + if (isa<ObjCInterfaceDecl>(D)) + return false; + if (isa<ObjCCategoryDecl>(D)) + return false; + if (isa<ObjCIvarDecl>(D)) + return false; + if (isa<ObjCMethodDecl>(D)) + return false; + return true; +} + +bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + return SD->getSpecializationKind() == TSK_ImplicitInstantiation; + } + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + return FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation; + } + return false; +} diff --git a/clang/tools/libclang/IndexingContext.h b/clang/tools/libclang/IndexingContext.h new file mode 100644 index 0000000..6271660 --- /dev/null +++ b/clang/tools/libclang/IndexingContext.h @@ -0,0 +1,556 @@ +//===- IndexingContext.h - Higher level API functions ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Index_Internal.h" +#include "CXCursor.h" + +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclGroup.h" +#include "llvm/ADT/DenseSet.h" +#include <deque> + +namespace clang { + class FileEntry; + class ObjCPropertyDecl; + class ClassTemplateDecl; + class FunctionTemplateDecl; + class TypeAliasTemplateDecl; + class ClassTemplateSpecializationDecl; + +namespace cxindex { + class IndexingContext; + class AttrListInfo; + +class ScratchAlloc { + IndexingContext &IdxCtx; + +public: + explicit ScratchAlloc(IndexingContext &indexCtx); + ScratchAlloc(const ScratchAlloc &SA); + + ~ScratchAlloc(); + + const char *toCStr(StringRef Str); + const char *copyCStr(StringRef Str); + + template <typename T> + T *allocate(); +}; + +struct EntityInfo : public CXIdxEntityInfo { + const NamedDecl *Dcl; + IndexingContext *IndexCtx; + IntrusiveRefCntPtr<AttrListInfo> AttrList; + + EntityInfo() { + name = USR = 0; + attributes = 0; + numAttributes = 0; + } +}; + +struct ContainerInfo : public CXIdxContainerInfo { + const DeclContext *DC; + IndexingContext *IndexCtx; +}; + +struct DeclInfo : public CXIdxDeclInfo { + enum DInfoKind { + Info_Decl, + + Info_ObjCContainer, + Info_ObjCInterface, + Info_ObjCProtocol, + Info_ObjCCategory, + + Info_ObjCProperty, + + Info_CXXClass + }; + + DInfoKind Kind; + + EntityInfo EntInfo; + ContainerInfo SemanticContainer; + ContainerInfo LexicalContainer; + ContainerInfo DeclAsContainer; + + DeclInfo(bool isRedeclaration, bool isDefinition, bool isContainer) + : Kind(Info_Decl) { + this->isRedeclaration = isRedeclaration; + this->isDefinition = isDefinition; + this->isContainer = isContainer; + attributes = 0; + numAttributes = 0; + declAsContainer = semanticContainer = lexicalContainer = 0; + } + DeclInfo(DInfoKind K, + bool isRedeclaration, bool isDefinition, bool isContainer) + : Kind(K) { + this->isRedeclaration = isRedeclaration; + this->isDefinition = isDefinition; + this->isContainer = isContainer; + attributes = 0; + numAttributes = 0; + declAsContainer = semanticContainer = lexicalContainer = 0; + } + + static bool classof(const DeclInfo *) { return true; } +}; + +struct ObjCContainerDeclInfo : public DeclInfo { + CXIdxObjCContainerDeclInfo ObjCContDeclInfo; + + ObjCContainerDeclInfo(bool isForwardRef, + bool isRedeclaration, + bool isImplementation) + : DeclInfo(Info_ObjCContainer, isRedeclaration, + /*isDefinition=*/!isForwardRef, /*isContainer=*/!isForwardRef) { + init(isForwardRef, isImplementation); + } + ObjCContainerDeclInfo(DInfoKind K, + bool isForwardRef, + bool isRedeclaration, + bool isImplementation) + : DeclInfo(K, isRedeclaration, /*isDefinition=*/!isForwardRef, + /*isContainer=*/!isForwardRef) { + init(isForwardRef, isImplementation); + } + + static bool classof(const DeclInfo *D) { + return Info_ObjCContainer <= D->Kind && D->Kind <= Info_ObjCCategory; + } + static bool classof(const ObjCContainerDeclInfo *D) { return true; } + +private: + void init(bool isForwardRef, bool isImplementation) { + if (isForwardRef) + ObjCContDeclInfo.kind = CXIdxObjCContainer_ForwardRef; + else if (isImplementation) + ObjCContDeclInfo.kind = CXIdxObjCContainer_Implementation; + else + ObjCContDeclInfo.kind = CXIdxObjCContainer_Interface; + } +}; + +struct ObjCInterfaceDeclInfo : public ObjCContainerDeclInfo { + CXIdxObjCInterfaceDeclInfo ObjCInterDeclInfo; + CXIdxObjCProtocolRefListInfo ObjCProtoListInfo; + + ObjCInterfaceDeclInfo(const ObjCInterfaceDecl *D) + : ObjCContainerDeclInfo(Info_ObjCInterface, + /*isForwardRef=*/false, + /*isRedeclaration=*/D->getPreviousDecl() != 0, + /*isImplementation=*/false) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCInterface; + } + static bool classof(const ObjCInterfaceDeclInfo *D) { return true; } +}; + +struct ObjCProtocolDeclInfo : public ObjCContainerDeclInfo { + CXIdxObjCProtocolRefListInfo ObjCProtoRefListInfo; + + ObjCProtocolDeclInfo(const ObjCProtocolDecl *D) + : ObjCContainerDeclInfo(Info_ObjCProtocol, + /*isForwardRef=*/false, + /*isRedeclaration=*/D->getPreviousDecl(), + /*isImplementation=*/false) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCProtocol; + } + static bool classof(const ObjCProtocolDeclInfo *D) { return true; } +}; + +struct ObjCCategoryDeclInfo : public ObjCContainerDeclInfo { + CXIdxObjCCategoryDeclInfo ObjCCatDeclInfo; + CXIdxObjCProtocolRefListInfo ObjCProtoListInfo; + + explicit ObjCCategoryDeclInfo(bool isImplementation) + : ObjCContainerDeclInfo(Info_ObjCCategory, + /*isForwardRef=*/false, + /*isRedeclaration=*/isImplementation, + /*isImplementation=*/isImplementation) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCCategory; + } + static bool classof(const ObjCCategoryDeclInfo *D) { return true; } +}; + +struct ObjCPropertyDeclInfo : public DeclInfo { + CXIdxObjCPropertyDeclInfo ObjCPropDeclInfo; + + ObjCPropertyDeclInfo() + : DeclInfo(Info_ObjCProperty, + /*isRedeclaration=*/false, /*isDefinition=*/false, + /*isContainer=*/false) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCProperty; + } + static bool classof(const ObjCPropertyDeclInfo *D) { return true; } +}; + +struct CXXClassDeclInfo : public DeclInfo { + CXIdxCXXClassDeclInfo CXXClassInfo; + + CXXClassDeclInfo(bool isRedeclaration, bool isDefinition) + : DeclInfo(Info_CXXClass, isRedeclaration, isDefinition, isDefinition) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_CXXClass; + } + static bool classof(const CXXClassDeclInfo *D) { return true; } +}; + +struct AttrInfo : public CXIdxAttrInfo { + const Attr *A; + + AttrInfo(CXIdxAttrKind Kind, CXCursor C, CXIdxLoc Loc, const Attr *A) { + kind = Kind; + cursor = C; + loc = Loc; + this->A = A; + } + + static bool classof(const AttrInfo *) { return true; } +}; + +struct IBOutletCollectionInfo : public AttrInfo { + EntityInfo ClassInfo; + CXIdxIBOutletCollectionAttrInfo IBCollInfo; + + IBOutletCollectionInfo(CXCursor C, CXIdxLoc Loc, const Attr *A) : + AttrInfo(CXIdxAttr_IBOutletCollection, C, Loc, A) { + assert(C.kind == CXCursor_IBOutletCollectionAttr); + IBCollInfo.objcClass = 0; + } + + IBOutletCollectionInfo(const IBOutletCollectionInfo &other); + + static bool classof(const AttrInfo *A) { + return A->kind == CXIdxAttr_IBOutletCollection; + } + static bool classof(const IBOutletCollectionInfo *D) { return true; } +}; + +class AttrListInfo { + ScratchAlloc SA; + + SmallVector<AttrInfo, 2> Attrs; + SmallVector<IBOutletCollectionInfo, 2> IBCollAttrs; + SmallVector<CXIdxAttrInfo *, 2> CXAttrs; + unsigned ref_cnt; + + AttrListInfo(const AttrListInfo&); // DO NOT IMPLEMENT + void operator=(const AttrListInfo&); // DO NOT IMPLEMENT +public: + AttrListInfo(const Decl *D, IndexingContext &IdxCtx); + + static IntrusiveRefCntPtr<AttrListInfo> create(const Decl *D, + IndexingContext &IdxCtx); + + const CXIdxAttrInfo *const *getAttrs() const { + if (CXAttrs.empty()) + return 0; + return CXAttrs.data(); + } + unsigned getNumAttrs() const { return (unsigned)CXAttrs.size(); } + + /// \brief Retain/Release only useful when we allocate a AttrListInfo from the + /// BumpPtrAllocator, and not from the stack; so that we keep a pointer + // in the EntityInfo + void Retain() { ++ref_cnt; } + void Release() { + assert (ref_cnt > 0 && "Reference count is already zero."); + if (--ref_cnt == 0) { + // Memory is allocated from a BumpPtrAllocator, no need to delete it. + this->~AttrListInfo(); + } + } +}; + +struct RefFileOccurence { + const FileEntry *File; + const Decl *Dcl; + + RefFileOccurence(const FileEntry *File, const Decl *Dcl) + : File(File), Dcl(Dcl) { } +}; + +class IndexingContext { + ASTContext *Ctx; + CXClientData ClientData; + IndexerCallbacks &CB; + unsigned IndexOptions; + CXTranslationUnit CXTU; + + typedef llvm::DenseMap<const FileEntry *, CXIdxClientFile> FileMapTy; + typedef llvm::DenseMap<const DeclContext *, CXIdxClientContainer> + ContainerMapTy; + typedef llvm::DenseMap<const Decl *, CXIdxClientEntity> EntityMapTy; + + FileMapTy FileMap; + ContainerMapTy ContainerMap; + EntityMapTy EntityMap; + + llvm::DenseSet<RefFileOccurence> RefFileOccurences; + + std::deque<DeclGroupRef> TUDeclsInObjCContainer; + + llvm::BumpPtrAllocator StrScratch; + unsigned StrAdapterCount; + friend class ScratchAlloc; + + struct ObjCProtocolListInfo { + SmallVector<CXIdxObjCProtocolRefInfo, 4> ProtInfos; + SmallVector<EntityInfo, 4> ProtEntities; + SmallVector<CXIdxObjCProtocolRefInfo *, 4> Prots; + + CXIdxObjCProtocolRefListInfo getListInfo() const { + CXIdxObjCProtocolRefListInfo Info = { Prots.data(), + (unsigned)Prots.size() }; + return Info; + } + + ObjCProtocolListInfo(const ObjCProtocolList &ProtList, + IndexingContext &IdxCtx, + ScratchAlloc &SA); + }; + + struct CXXBasesListInfo { + SmallVector<CXIdxBaseClassInfo, 4> BaseInfos; + SmallVector<EntityInfo, 4> BaseEntities; + SmallVector<CXIdxBaseClassInfo *, 4> CXBases; + + const CXIdxBaseClassInfo *const *getBases() const { + return CXBases.data(); + } + unsigned getNumBases() const { return (unsigned)CXBases.size(); } + + CXXBasesListInfo(const CXXRecordDecl *D, + IndexingContext &IdxCtx, ScratchAlloc &SA); + + private: + SourceLocation getBaseLoc(const CXXBaseSpecifier &Base) const; + }; + + friend class AttrListInfo; + +public: + IndexingContext(CXClientData clientData, IndexerCallbacks &indexCallbacks, + unsigned indexOptions, CXTranslationUnit cxTU) + : Ctx(0), ClientData(clientData), CB(indexCallbacks), + IndexOptions(indexOptions), CXTU(cxTU), + StrScratch(/*size=*/1024), StrAdapterCount(0) { } + + ASTContext &getASTContext() const { return *Ctx; } + + void setASTContext(ASTContext &ctx); + void setPreprocessor(Preprocessor &PP); + + bool shouldSuppressRefs() const { + return IndexOptions & CXIndexOpt_SuppressRedundantRefs; + } + + bool shouldIndexFunctionLocalSymbols() const { + return IndexOptions & CXIndexOpt_IndexFunctionLocalSymbols; + } + + bool shouldIndexImplicitTemplateInsts() const { + return IndexOptions & CXIndexOpt_IndexImplicitTemplateInstantiations; + } + + bool shouldAbort(); + + bool hasDiagnosticCallback() const { return CB.diagnostic; } + + void enteredMainFile(const FileEntry *File); + + void ppIncludedFile(SourceLocation hashLoc, + StringRef filename, const FileEntry *File, + bool isImport, bool isAngled); + + void startedTranslationUnit(); + + void indexDecl(const Decl *D); + + void indexTagDecl(const TagDecl *D); + + void indexTypeSourceInfo(TypeSourceInfo *TInfo, const NamedDecl *Parent, + const DeclContext *DC = 0); + + void indexTypeLoc(TypeLoc TL, const NamedDecl *Parent, + const DeclContext *DC = 0); + + void indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + const NamedDecl *Parent, + const DeclContext *DC = 0); + + void indexDeclContext(const DeclContext *DC); + + void indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC = 0); + + void handleDiagnosticSet(CXDiagnosticSet CXDiagSet); + + bool handleFunction(const FunctionDecl *FD); + + bool handleVar(const VarDecl *D); + + bool handleField(const FieldDecl *D); + + bool handleEnumerator(const EnumConstantDecl *D); + + bool handleTagDecl(const TagDecl *D); + + bool handleTypedefName(const TypedefNameDecl *D); + + bool handleObjCInterface(const ObjCInterfaceDecl *D); + bool handleObjCImplementation(const ObjCImplementationDecl *D); + + bool handleObjCProtocol(const ObjCProtocolDecl *D); + + bool handleObjCCategory(const ObjCCategoryDecl *D); + bool handleObjCCategoryImpl(const ObjCCategoryImplDecl *D); + + bool handleObjCMethod(const ObjCMethodDecl *D); + + bool handleSynthesizedObjCProperty(const ObjCPropertyImplDecl *D); + bool handleSynthesizedObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc, + const DeclContext *LexicalDC); + + bool handleObjCProperty(const ObjCPropertyDecl *D); + + bool handleNamespace(const NamespaceDecl *D); + + bool handleClassTemplate(const ClassTemplateDecl *D); + bool handleFunctionTemplate(const FunctionTemplateDecl *D); + bool handleTypeAliasTemplate(const TypeAliasTemplateDecl *D); + + bool handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, + const NamedDecl *Parent, + const DeclContext *DC, + const Expr *E = 0, + CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct); + + bool handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + const Expr *E = 0, + CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct); + + bool isNotFromSourceFile(SourceLocation Loc) const; + + void indexTopLevelDecl(Decl *D); + void indexTUDeclsInObjCContainer(); + void indexDeclGroupRef(DeclGroupRef DG); + + void addTUDeclInObjCContainer(DeclGroupRef DG) { + TUDeclsInObjCContainer.push_back(DG); + } + + void translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, + unsigned *line, unsigned *column, unsigned *offset); + + CXIdxClientContainer getClientContainerForDC(const DeclContext *DC) const; + void addContainerInMap(const DeclContext *DC, CXIdxClientContainer container); + + CXIdxClientEntity getClientEntity(const Decl *D) const; + void setClientEntity(const Decl *D, CXIdxClientEntity client); + + static bool isTemplateImplicitInstantiation(const Decl *D); + +private: + bool handleDecl(const NamedDecl *D, + SourceLocation Loc, CXCursor Cursor, + DeclInfo &DInfo, + const DeclContext *LexicalDC = 0); + + bool handleObjCContainer(const ObjCContainerDecl *D, + SourceLocation Loc, CXCursor Cursor, + ObjCContainerDeclInfo &ContDInfo); + + bool handleCXXRecordDecl(const CXXRecordDecl *RD, const NamedDecl *OrigD); + + bool markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc); + + const NamedDecl *getEntityDecl(const NamedDecl *D) const; + + const DeclContext *getEntityContainer(const Decl *D) const; + + CXIdxClientFile getIndexFile(const FileEntry *File); + + CXIdxLoc getIndexLoc(SourceLocation Loc) const; + + void getEntityInfo(const NamedDecl *D, + EntityInfo &EntityInfo, + ScratchAlloc &SA); + + void getContainerInfo(const DeclContext *DC, ContainerInfo &ContInfo); + + CXCursor getCursor(const Decl *D) { + return cxcursor::MakeCXCursor(const_cast<Decl*>(D), CXTU); + } + + CXCursor getRefCursor(const NamedDecl *D, SourceLocation Loc); + + static bool shouldIgnoreIfImplicit(const Decl *D); +}; + +inline ScratchAlloc::ScratchAlloc(IndexingContext &idxCtx) : IdxCtx(idxCtx) { + ++IdxCtx.StrAdapterCount; +} +inline ScratchAlloc::ScratchAlloc(const ScratchAlloc &SA) : IdxCtx(SA.IdxCtx) { + ++IdxCtx.StrAdapterCount; +} + +inline ScratchAlloc::~ScratchAlloc() { + --IdxCtx.StrAdapterCount; + if (IdxCtx.StrAdapterCount == 0) + IdxCtx.StrScratch.Reset(); +} + +template <typename T> +inline T *ScratchAlloc::allocate() { + return IdxCtx.StrScratch.Allocate<T>(); +} + +}} // end clang::cxindex + +namespace llvm { + /// Define DenseMapInfo so that FileID's can be used as keys in DenseMap and + /// DenseSets. + template <> + struct DenseMapInfo<clang::cxindex::RefFileOccurence> { + static inline clang::cxindex::RefFileOccurence getEmptyKey() { + return clang::cxindex::RefFileOccurence(0, 0); + } + + static inline clang::cxindex::RefFileOccurence getTombstoneKey() { + return clang::cxindex::RefFileOccurence((const clang::FileEntry *)~0, + (const clang::Decl *)~0); + } + + static unsigned getHashValue(clang::cxindex::RefFileOccurence S) { + llvm::FoldingSetNodeID ID; + ID.AddPointer(S.File); + ID.AddPointer(S.Dcl); + return ID.ComputeHash(); + } + + static bool isEqual(clang::cxindex::RefFileOccurence LHS, + clang::cxindex::RefFileOccurence RHS) { + return LHS.File == RHS.File && LHS.Dcl == RHS.Dcl; + } + }; +} diff --git a/clang/tools/libclang/Makefile b/clang/tools/libclang/Makefile new file mode 100644 index 0000000..08bf3c6 --- /dev/null +++ b/clang/tools/libclang/Makefile @@ -0,0 +1,53 @@ +##===- tools/libclang/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME = clang + +EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/libclang.exports + +LINK_LIBS_IN_SHARED = 1 +SHARED_LIBRARY = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clangARCMigrate.a clangRewrite.a clangFrontend.a clangDriver.a \ + clangSerialization.a \ + clangParse.a clangSema.a clangEdit.a clangAnalysis.a \ + clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile + +# Add soname to the library. +ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux FreeBSD GNU)) + LDFLAGS += -Wl,-soname,lib$(LIBRARYNAME)$(SHLIBEXT) +endif + +##===----------------------------------------------------------------------===## +# FIXME: This is copied from the 'lto' makefile. Should we share this? +##===----------------------------------------------------------------------===## + +ifeq ($(HOST_OS),Darwin) + LLVMLibsOptions += -Wl,-compatibility_version,1 + + # Set dylib internal version number to submission number. + ifdef LLVM_SUBMIT_VERSION + LLVMLibsOptions += -Wl,-current_version \ + -Wl,$(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) + endif + + # Extra options to override libtool defaults. + LLVMLibsOptions += -Wl,-dead_strip -Wl,-seg1addr,0xE0000000 + + # Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line + DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/') + ifneq ($(DARWIN_VERS),8) + LLVMLibsOptions += -Wl,-install_name \ + -Wl,"@rpath/lib$(LIBRARYNAME)$(SHLIBEXT)" + endif +endif diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports new file mode 100644 index 0000000..d3b64db --- /dev/null +++ b/clang/tools/libclang/libclang.exports @@ -0,0 +1,203 @@ +clang_CXCursorSet_contains +clang_CXCursorSet_insert +clang_CXIndex_getGlobalOptions +clang_CXIndex_setGlobalOptions +clang_CXXMethod_isStatic +clang_CXXMethod_isVirtual +clang_Cursor_getArgument +clang_Cursor_getNumArguments +clang_Cursor_getObjCSelectorIndex +clang_Cursor_getSpellingNameRange +clang_Cursor_getTranslationUnit +clang_Cursor_isNull +clang_IndexAction_create +clang_IndexAction_dispose +clang_Range_isNull +clang_annotateTokens +clang_codeCompleteAt +clang_codeCompleteGetContainerKind +clang_codeCompleteGetContainerUSR +clang_codeCompleteGetContexts +clang_codeCompleteGetDiagnostic +clang_codeCompleteGetNumDiagnostics +clang_codeCompleteGetObjCSelector +clang_constructUSR_ObjCCategory +clang_constructUSR_ObjCClass +clang_constructUSR_ObjCIvar +clang_constructUSR_ObjCMethod +clang_constructUSR_ObjCProperty +clang_constructUSR_ObjCProtocol +clang_createCXCursorSet +clang_createIndex +clang_createTranslationUnit +clang_createTranslationUnitFromSourceFile +clang_defaultCodeCompleteOptions +clang_defaultDiagnosticDisplayOptions +clang_defaultEditingTranslationUnitOptions +clang_defaultReparseOptions +clang_defaultSaveOptions +clang_disposeCXCursorSet +clang_disposeCXTUResourceUsage +clang_disposeCodeCompleteResults +clang_disposeDiagnostic +clang_disposeDiagnosticSet +clang_disposeIndex +clang_disposeOverriddenCursors +clang_disposeString +clang_disposeTokens +clang_disposeTranslationUnit +clang_enableStackTraces +clang_equalCursors +clang_equalLocations +clang_equalRanges +clang_equalTypes +clang_executeOnThread +clang_findReferencesInFile +clang_findReferencesInFileWithBlock +clang_formatDiagnostic +clang_getArgType +clang_getArrayElementType +clang_getArraySize +clang_getCString +clang_getCXTUResourceUsage +clang_getCXXAccessSpecifier +clang_getCanonicalCursor +clang_getCanonicalType +clang_getChildDiagnostics +clang_getClangVersion +clang_getCompletionAnnotation +clang_getCompletionAvailability +clang_getCompletionChunkCompletionString +clang_getCompletionChunkKind +clang_getCompletionChunkText +clang_getCompletionNumAnnotations +clang_getCompletionParent +clang_getCompletionPriority +clang_getCursor +clang_getCursorAvailability +clang_getCursorCompletionString +clang_getCursorDefinition +clang_getCursorDisplayName +clang_getCursorExtent +clang_getCursorKind +clang_getCursorKindSpelling +clang_getCursorLanguage +clang_getCursorLexicalParent +clang_getCursorLinkage +clang_getCursorLocation +clang_getCursorReferenceNameRange +clang_getCursorReferenced +clang_getCursorResultType +clang_getCursorSemanticParent +clang_getCursorSpelling +clang_getCursorType +clang_getCursorUSR +clang_getDeclObjCTypeEncoding +clang_getDefinitionSpellingAndExtent +clang_getDiagnostic +clang_getDiagnosticCategory +clang_getDiagnosticCategoryName +clang_getDiagnosticCategoryText +clang_getDiagnosticFixIt +clang_getDiagnosticInSet +clang_getDiagnosticLocation +clang_getDiagnosticNumFixIts +clang_getDiagnosticNumRanges +clang_getDiagnosticOption +clang_getDiagnosticRange +clang_getDiagnosticSetFromTU +clang_getDiagnosticSeverity +clang_getDiagnosticSpelling +clang_getElementType +clang_getEnumConstantDeclUnsignedValue +clang_getEnumConstantDeclValue +clang_getEnumDeclIntegerType +clang_getExpansionLocation +clang_getFile +clang_getFileName +clang_getFileTime +clang_getFunctionTypeCallingConv +clang_getIBOutletCollectionType +clang_getIncludedFile +clang_getInclusions +clang_getInstantiationLocation +clang_getLocation +clang_getLocationForOffset +clang_getNullCursor +clang_getNullLocation +clang_getNullRange +clang_getNumArgTypes +clang_getNumCompletionChunks +clang_getNumDiagnostics +clang_getNumDiagnosticsInSet +clang_getNumElements +clang_getNumOverloadedDecls +clang_getOverloadedDecl +clang_getOverriddenCursors +clang_getPointeeType +clang_getPresumedLocation +clang_getRange +clang_getRangeEnd +clang_getRangeStart +clang_getRemappings +clang_getRemappingsFromFileList +clang_getResultType +clang_getSpecializedCursorTemplate +clang_getSpellingLocation +clang_getTUResourceUsageName +clang_getTemplateCursorKind +clang_getTokenExtent +clang_getTokenKind +clang_getTokenLocation +clang_getTokenSpelling +clang_getTranslationUnitCursor +clang_getTranslationUnitSpelling +clang_getTypeDeclaration +clang_getTypeKindSpelling +clang_getTypedefDeclUnderlyingType +clang_hashCursor +clang_indexLoc_getCXSourceLocation +clang_indexLoc_getFileLocation +clang_indexSourceFile +clang_indexTranslationUnit +clang_index_getCXXClassDeclInfo +clang_index_getClientContainer +clang_index_getClientEntity +clang_index_getIBOutletCollectionAttrInfo +clang_index_getObjCCategoryDeclInfo +clang_index_getObjCContainerDeclInfo +clang_index_getObjCInterfaceDeclInfo +clang_index_getObjCPropertyDeclInfo +clang_index_getObjCProtocolRefListInfo +clang_index_isEntityObjCContainerKind +clang_index_setClientContainer +clang_index_setClientEntity +clang_isAttribute +clang_isConstQualifiedType +clang_isCursorDefinition +clang_isDeclaration +clang_isExpression +clang_isFileMultipleIncludeGuarded +clang_isFunctionTypeVariadic +clang_isInvalid +clang_isPODType +clang_isPreprocessing +clang_isReference +clang_isRestrictQualifiedType +clang_isStatement +clang_isTranslationUnit +clang_isUnexposed +clang_isVirtualBase +clang_isVolatileQualifiedType +clang_loadDiagnostics +clang_parseTranslationUnit +clang_remap_dispose +clang_remap_getFilenames +clang_remap_getNumFiles +clang_reparseTranslationUnit +clang_saveTranslationUnit +clang_sortCodeCompletionResults +clang_toggleCrashRecovery +clang_tokenize +clang_visitChildren +clang_visitChildrenWithBlock diff --git a/clang/tools/scan-build/c++-analyzer b/clang/tools/scan-build/c++-analyzer new file mode 120000 index 0000000..ca10bf5 --- /dev/null +++ b/clang/tools/scan-build/c++-analyzer @@ -0,0 +1 @@ +ccc-analyzer
\ No newline at end of file diff --git a/clang/tools/scan-build/ccc-analyzer b/clang/tools/scan-build/ccc-analyzer new file mode 100755 index 0000000..d01bd0f --- /dev/null +++ b/clang/tools/scan-build/ccc-analyzer @@ -0,0 +1,686 @@ +#!/usr/bin/env perl +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to interpose between the build system and gcc. It invokes +# both gcc and the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin; +use Cwd qw/ getcwd abs_path /; +use File::Temp qw/ tempfile /; +use File::Path qw / mkpath /; +use File::Basename; +use Text::ParseWords; + +##===----------------------------------------------------------------------===## +# Compiler command setup. +##===----------------------------------------------------------------------===## + +my $Compiler; +my $Clang; +my $DefaultCCompiler; +my $DefaultCXXCompiler; + +if (`uname -a` =~ m/Darwin/) { + $DefaultCCompiler = 'clang'; + $DefaultCXXCompiler = 'clang++'; +} else { + $DefaultCCompiler = 'gcc'; + $DefaultCXXCompiler = 'g++'; +} + +if ($FindBin::Script =~ /c\+\+-analyzer/) { + $Compiler = $ENV{'CCC_CXX'}; + if (!defined $Compiler) { $Compiler = $DefaultCXXCompiler; } + + $Clang = $ENV{'CLANG_CXX'}; + if (!defined $Clang) { $Clang = 'clang++'; } +} +else { + $Compiler = $ENV{'CCC_CC'}; + if (!defined $Compiler) { $Compiler = $DefaultCCompiler; } + + $Clang = $ENV{'CLANG'}; + if (!defined $Clang) { $Clang = 'clang'; } +} + +##===----------------------------------------------------------------------===## +# Cleanup. +##===----------------------------------------------------------------------===## + +my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'}; +if (!defined $ReportFailures) { $ReportFailures = 1; } + +my $CleanupFile; +my $ResultFile; + +# Remove any stale files at exit. +END { + if (defined $ResultFile && -z $ResultFile) { + `rm -f $ResultFile`; + } + if (defined $CleanupFile) { + `rm -f $CleanupFile`; + } +} + +##----------------------------------------------------------------------------## +# Process Clang Crashes. +##----------------------------------------------------------------------------## + +sub GetPPExt { + my $Lang = shift; + if ($Lang =~ /objective-c\+\+/) { return ".mii" }; + if ($Lang =~ /objective-c/) { return ".mi"; } + if ($Lang =~ /c\+\+/) { return ".ii"; } + return ".i"; +} + +# Set this to 1 if we want to include 'parser rejects' files. +my $IncludeParserRejects = 0; +my $ParserRejects = "Parser Rejects"; +my $AttributeIgnored = "Attribute Ignored"; +my $OtherError = "Other Error"; + +sub ProcessClangFailure { + my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_; + my $Dir = "$HtmlDir/failures"; + mkpath $Dir; + + my $prefix = "clang_crash"; + if ($ErrorType eq $ParserRejects) { + $prefix = "clang_parser_rejects"; + } + elsif ($ErrorType eq $AttributeIgnored) { + $prefix = "clang_attribute_ignored"; + } + elsif ($ErrorType eq $OtherError) { + $prefix = "clang_other_error"; + } + + # Generate the preprocessed file with Clang. + my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX", + SUFFIX => GetPPExt($Lang), + DIR => $Dir); + system $Clang, @$Args, "-E", "-o", $PPFile; + close ($PPH); + + # Create the info file. + open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n"; + print OUT abs_path($file), "\n"; + print OUT "$ErrorType\n"; + print OUT "@$Args\n"; + close OUT; + `uname -a >> $PPFile.info.txt 2>&1`; + `$Compiler -v >> $PPFile.info.txt 2>&1`; + system 'mv',$ofile,"$PPFile.stderr.txt"; + return (basename $PPFile); +} + +##----------------------------------------------------------------------------## +# Running the analyzer. +##----------------------------------------------------------------------------## + +sub GetCCArgs { + my $mode = shift; + my $Args = shift; + + pipe (FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Clang, "-###", $mode, @$Args; + } + close(TO_PARENT); + my $line; + while (<FROM_CHILD>) { + next if (!/-cc1/); + $line = $_; + } + + waitpid($pid,0); + close(FROM_CHILD); + + die "could not find clang line\n" if (!defined $line); + # Strip the newline and initial whitspace + chomp $line; + $line =~ s/^\s+//; + my @items = quotewords('\s+', 0, $line); + my $cmd = shift @items; + die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/)); + return \@items; +} + +sub Analyze { + my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir, + $file) = @_; + + my @Args = @$OriginalArgs; + my $Cmd; + my @CmdArgs; + my @CmdArgsSansAnalyses; + + if ($Lang =~ /header/) { + exit 0 if (!defined ($Output)); + $Cmd = 'cp'; + push @CmdArgs, $file; + # Remove the PCH extension. + $Output =~ s/[.]gch$//; + push @CmdArgs, $Output; + @CmdArgsSansAnalyses = @CmdArgs; + } + else { + $Cmd = $Clang; + if ($Lang eq "objective-c" || $Lang eq "objective-c++") { + push @Args,'-DIBOutlet=__attribute__((iboutlet))'; + push @Args,'-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection)))'; + push @Args,'-DIBAction=void)__attribute__((ibaction)'; + } + + # Create arguments for doing regular parsing. + my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args); + @CmdArgsSansAnalyses = @$SyntaxArgs; + + # Create arguments for doing static analysis. + if (defined $ResultFile) { + push @Args, '-o', $ResultFile; + } + elsif (defined $HtmlDir) { + push @Args, '-o', $HtmlDir; + } + if ($Verbose) { + push @Args, "-Xclang", "-analyzer-display-progress"; + } + + foreach my $arg (@$AnalyzeArgs) { + push @Args, "-Xclang", $arg; + } + + # Display Ubiviz graph? + if (defined $ENV{'CCC_UBI'}) { + push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph"; + } + + my $AnalysisArgs = GetCCArgs("--analyze", \@Args); + @CmdArgs = @$AnalysisArgs; + } + + my @PrintArgs; + my $dir; + + if ($Verbose) { + $dir = getcwd(); + print STDERR "\n[LOCATION]: $dir\n"; + push @PrintArgs,"'$Cmd'"; + foreach my $arg (@CmdArgs) { + push @PrintArgs,"\'$arg\'"; + } + } + if ($Verbose == 1) { + # We MUST print to stderr. Some clients use the stdout output of + # gcc for various purposes. + print STDERR join(' ', @PrintArgs); + print STDERR "\n"; + } + elsif ($Verbose == 2) { + print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n"; + } + + # Capture the STDERR of clang and send it to a temporary file. + # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR. + # We save the output file in the 'crashes' directory if clang encounters + # any problems with the file. + pipe (FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Cmd, @CmdArgs; + } + + close TO_PARENT; + my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir); + + while (<FROM_CHILD>) { + print $ofh $_; + print STDERR $_; + } + + waitpid($pid,0); + close(FROM_CHILD); + my $Result = $?; + + # Did the command die because of a signal? + if ($ReportFailures) { + if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) { + ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, "Crash", $ofile); + } + elsif ($Result) { + if ($IncludeParserRejects && !($file =~/conftest/)) { + ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, $ParserRejects, $ofile); + } else { + ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, $OtherError, $ofile); + } + } + else { + # Check if there were any unhandled attributes. + if (open(CHILD, $ofile)) { + my %attributes_not_handled; + + # Don't flag warnings about the following attributes that we + # know are currently not supported by Clang. + $attributes_not_handled{"cdecl"} = 1; + + my $ppfile; + while (<CHILD>) { + next if (! /warning: '([^\']+)' attribute ignored/); + + # Have we already spotted this unhandled attribute? + next if (defined $attributes_not_handled{$1}); + $attributes_not_handled{$1} = 1; + + # Get the name of the attribute file. + my $dir = "$HtmlDir/failures"; + my $afile = "$dir/attribute_ignored_$1.txt"; + + # Only create another preprocessed file if the attribute file + # doesn't exist yet. + next if (-e $afile); + + # Add this file to the list of files that contained this attribute. + # Generate a preprocessed file if we haven't already. + if (!(defined $ppfile)) { + $ppfile = ProcessClangFailure($Clang, $Lang, $file, + \@CmdArgsSansAnalyses, + $HtmlDir, $AttributeIgnored, $ofile); + } + + mkpath $dir; + open(AFILE, ">$afile"); + print AFILE "$ppfile\n"; + close(AFILE); + } + close CHILD; + } + } + } + + unlink($ofile); +} + +##----------------------------------------------------------------------------## +# Lookup tables. +##----------------------------------------------------------------------------## + +my %CompileOptionMap = ( + '-nostdinc' => 0, + '-fblocks' => 0, + '-fno-builtin' => 0, + '-fobjc-gc-only' => 0, + '-fobjc-gc' => 0, + '-ffreestanding' => 0, + '-include' => 1, + '-idirafter' => 1, + '-imacros' => 1, + '-iprefix' => 1, + '-iquote' => 1, + '-isystem' => 1, + '-iwithprefix' => 1, + '-iwithprefixbefore' => 1 +); + +my %LinkerOptionMap = ( + '-framework' => 1, + '-fobjc-link-runtime' => 0 +); + +my %CompilerLinkerOptionMap = ( + '-fobjc-arc' => 0, + '-fobjc-abi-version' => 0, # This is really a 1 argument, but always has '=' + '-isysroot' => 1, + '-arch' => 1, + '-m32' => 0, + '-m64' => 0, + '-v' => 0, + '-fpascal-strings' => 0, + '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '=' + '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '=' +); + +my %IgnoredOptionMap = ( + '-MT' => 1, # Ignore these preprocessor options. + '-MF' => 1, + + '-fsyntax-only' => 0, + '-save-temps' => 0, + '-install_name' => 1, + '-exported_symbols_list' => 1, + '-current_version' => 1, + '-compatibility_version' => 1, + '-init' => 1, + '-e' => 1, + '-seg1addr' => 1, + '-bundle_loader' => 1, + '-multiply_defined' => 1, + '-sectorder' => 3, + '--param' => 1, + '-u' => 1, + '--serialize-diagnostics' => 1 +); + +my %LangMap = ( + 'c' => 'c', + 'cp' => 'c++', + 'cpp' => 'c++', + 'cxx' => 'c++', + 'txx' => 'c++', + 'cc' => 'c++', + 'ii' => 'c++', + 'i' => 'c-cpp-output', + 'm' => 'objective-c', + 'mi' => 'objective-c-cpp-output', + 'mm' => 'objective-c++' +); + +my %UniqueOptions = ( + '-isysroot' => 0 +); + +##----------------------------------------------------------------------------## +# Languages accepted. +##----------------------------------------------------------------------------## + +my %LangsAccepted = ( + "objective-c" => 1, + "c" => 1, + "c++" => 1, + "objective-c++" => 1 +); + +##----------------------------------------------------------------------------## +# Main Logic. +##----------------------------------------------------------------------------## + +my $Action = 'link'; +my @CompileOpts; +my @LinkOpts; +my @Files; +my $Lang; +my $Output; +my %Uniqued; + +# Forward arguments to gcc. +my $Status = system($Compiler,@ARGV); +if (defined $ENV{'CCC_ANALYZER_LOG'}) { + print "$Compiler @ARGV\n"; +} +if ($Status) { exit($Status >> 8); } + +# Get the analysis options. +my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; + +# Get the store model. +my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'}; + +# Get the constraints engine. +my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'}; + +# Get the output format. +my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'}; +if (!defined $OutputFormat) { $OutputFormat = "html"; } + +# Determine the level of verbosity. +my $Verbose = 0; +if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; } +if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; } + +# Get the HTML output directory. +my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; + +my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); +my %ArchsSeen; +my $HadArch = 0; + +# Process the arguments. +foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { + my $Arg = $ARGV[$i]; + my ($ArgKey) = split /=/,$Arg,2; + + # Modes ccc-analyzer supports + if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; } + elsif ($Arg eq '-c') { $Action = 'compile'; } + elsif ($Arg =~ /^-print-prog-name/) { exit 0; } + + # Specially handle duplicate cases of -arch + if ($Arg eq "-arch") { + my $arch = $ARGV[$i+1]; + # We don't want to process 'ppc' because of Clang's lack of support + # for Altivec (also some #defines won't likely be defined correctly, etc.) + if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; } + $HadArch = 1; + ++$i; + next; + } + + # Options with possible arguments that should pass through to compiler. + if (defined $CompileOptionMap{$ArgKey}) { + my $Cnt = $CompileOptionMap{$ArgKey}; + push @CompileOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to linker. + if (defined $LinkerOptionMap{$ArgKey}) { + my $Cnt = $LinkerOptionMap{$ArgKey}; + push @LinkOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to both compiler + # and the linker. + if (defined $CompilerLinkerOptionMap{$ArgKey}) { + my $Cnt = $CompilerLinkerOptionMap{$ArgKey}; + + # Check if this is an option that should have a unique value, and if so + # determine if the value was checked before. + if ($UniqueOptions{$Arg}) { + if (defined $Uniqued{$Arg}) { + $i += $Cnt; + next; + } + $Uniqued{$Arg} = 1; + } + + push @CompileOpts,$Arg; + push @LinkOpts,$Arg; + + while ($Cnt > 0) { + ++$i; --$Cnt; + push @CompileOpts, $ARGV[$i]; + push @LinkOpts, $ARGV[$i]; + } + next; + } + + # Ignored options. + if (defined $IgnoredOptionMap{$ArgKey}) { + my $Cnt = $IgnoredOptionMap{$ArgKey}; + while ($Cnt > 0) { + ++$i; --$Cnt; + } + next; + } + + # Compile mode flags. + if ($Arg =~ /^-[D,I,U](.*)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + next; + } + + # Language. + if ($Arg eq '-x') { + $Lang = $ARGV[$i+1]; + ++$i; next; + } + + # Output file. + if ($Arg eq '-o') { + ++$i; + $Output = $ARGV[$i]; + next; + } + + # Get the link mode. + if ($Arg =~ /^-[l,L,O]/) { + if ($Arg eq '-O') { push @LinkOpts,'-O1'; } + elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } + else { push @LinkOpts,$Arg; } + next; + } + + if ($Arg =~ /^-std=/) { + push @CompileOpts,$Arg; + next; + } + +# if ($Arg =~ /^-f/) { +# # FIXME: Not sure if the remaining -fxxxx options have no arguments. +# push @CompileOpts,$Arg; +# push @LinkOpts,$Arg; # FIXME: Not sure if these are link opts. +# } + + # Get the compiler/link mode. + if ($Arg =~ /^-F(.+)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + push @LinkOpts,$Tmp; + next; + } + + # Input files. + if ($Arg eq '-filelist') { + # FIXME: Make sure we aren't walking off the end. + open(IN, $ARGV[$i+1]); + while (<IN>) { s/\015?\012//; push @Files,$_; } + close(IN); + ++$i; + next; + } + + # Handle -Wno-. We don't care about extra warnings, but + # we should suppress ones that we don't want to see. + if ($Arg =~ /^-Wno-/) { + push @CompileOpts, $Arg; + next; + } + + if (!($Arg =~ /^-/)) { + push @Files, $Arg; + next; + } +} + +if ($Action eq 'compile' or $Action eq 'link') { + my @Archs = keys %ArchsSeen; + # Skip the file if we don't support the architectures specified. + exit 0 if ($HadArch && scalar(@Archs) == 0); + + foreach my $file (@Files) { + # Determine the language for the file. + my $FileLang = $Lang; + + if (!defined($FileLang)) { + # Infer the language from the extension. + if ($file =~ /[.]([^.]+)$/) { + $FileLang = $LangMap{$1}; + } + } + + # FileLang still not defined? Skip the file. + next if (!defined $FileLang); + + # Language not accepted? + next if (!defined $LangsAccepted{$FileLang}); + + my @CmdArgs; + my @AnalyzeArgs; + + if ($FileLang ne 'unknown') { + push @CmdArgs, '-x', $FileLang; + } + + if (defined $StoreModel) { + push @AnalyzeArgs, "-analyzer-store=$StoreModel"; + } + + if (defined $ConstraintsModel) { + push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; + } + + if (defined $Analyses) { + push @AnalyzeArgs, split '\s+', $Analyses; + } + + if (defined $OutputFormat) { + push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat; + if ($OutputFormat =~ /plist/) { + # Change "Output" to be a file. + my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist", + DIR => $HtmlDir); + $ResultFile = $f; + # If the HtmlDir is not set, we sould clean up the plist files. + if (!defined $HtmlDir || -z $HtmlDir) { + $CleanupFile = $f; + } + } + } + + push @CmdArgs, @CompileOpts; + push @CmdArgs, $file; + + if (scalar @Archs) { + foreach my $arch (@Archs) { + my @NewArgs; + push @NewArgs, '-arch', $arch; + push @NewArgs, @CmdArgs; + Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file); + } + } + else { + Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file); + } + } +} + +exit($Status >> 8); + diff --git a/clang/tools/scan-build/scan-build b/clang/tools/scan-build/scan-build new file mode 100755 index 0000000..59b0baf --- /dev/null +++ b/clang/tools/scan-build/scan-build @@ -0,0 +1,1436 @@ +#!/usr/bin/env perl +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to wrap a build so that all calls to gcc are intercepted +# and piped to the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin qw($RealBin); +use Digest::MD5; +use File::Basename; +use Term::ANSIColor; +use Term::ANSIColor qw(:constants); +use Cwd qw/ getcwd abs_path /; +use Sys::Hostname; + +my $Verbose = 0; # Verbose output from this script. +my $Prog = "scan-build"; +my $BuildName; +my $BuildDate; + +my $TERM = $ENV{'TERM'}; +my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT + and defined $ENV{'SCAN_BUILD_COLOR'}); + +my $UserName = HtmlEscape(getpwuid($<) || 'unknown'); +my $HostName = HtmlEscape(hostname() || 'unknown'); +my $CurrentDir = HtmlEscape(getcwd()); +my $CurrentDirSuffix = basename($CurrentDir); + +my $CmdArgs; + +my $HtmlTitle; + +my $Date = localtime(); + +##----------------------------------------------------------------------------## +# Diagnostics +##----------------------------------------------------------------------------## + +sub Diag { + if ($UseColor) { + print BOLD, MAGENTA "$Prog: @_"; + print RESET; + } + else { + print "$Prog: @_"; + } +} + +sub DiagCrashes { + my $Dir = shift; + Diag ("The analyzer encountered problems on some source files.\n"); + Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n"); + Diag ("Please consider submitting a bug report using these files:\n"); + Diag (" http://clang-analyzer.llvm.org/filing_bugs.html\n") +} + +sub DieDiag { + if ($UseColor) { + print BOLD, RED "$Prog: "; + print RESET, RED @_; + print RESET; + } + else { + print "$Prog: ", @_; + } + exit(0); +} + +##----------------------------------------------------------------------------## +# Some initial preprocessing of Clang options. +##----------------------------------------------------------------------------## + +# Find 'clang' +my $ClangSB = Cwd::realpath("$RealBin/bin/clang"); +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = Cwd::realpath("$RealBin/clang"); +} +my $Clang; +if (!defined $ClangSB || ! -x $ClangSB) { + # Default to looking for 'clang' in the path. + $Clang = `which clang`; + chomp $Clang; + if ($Clang eq "") { + DieDiag("No 'clang' executable found in path."); + } +} +else { + $Clang = $ClangSB; +} +my $ClangCXX = $Clang . "++"; + +##----------------------------------------------------------------------------## +# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. +##----------------------------------------------------------------------------## + +sub GetHTMLRunDir { + die "Not enough arguments." if (@_ == 0); + my $Dir = shift @_; + my $TmpMode = 0; + if (!defined $Dir) { + if (`uname` =~ /Darwin/) { + $Dir = $ENV{'TMPDIR'}; + if (!defined $Dir) { $Dir = "/tmp"; } + } + else { + $Dir = "/tmp"; + } + $TmpMode = 1; + } + + # Chop off any trailing '/' characters. + while ($Dir =~ /\/$/) { chop $Dir; } + + # Get current date and time. + my @CurrentTime = localtime(); + my $year = $CurrentTime[5] + 1900; + my $day = $CurrentTime[3]; + my $month = $CurrentTime[4] + 1; + my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day); + + # Determine the run number. + my $RunNumber; + + if (-d $Dir) { + if (! -r $Dir) { + DieDiag("directory '$Dir' exists but is not readable.\n"); + } + # Iterate over all files in the specified directory. + my $max = 0; + opendir(DIR, $Dir); + my @FILES = grep { -d "$Dir/$_" } readdir(DIR); + closedir(DIR); + + foreach my $f (@FILES) { + # Strip the prefix '$Prog-' if we are dumping files to /tmp. + if ($TmpMode) { + next if (!($f =~ /^$Prog-(.+)/)); + $f = $1; + } + + my @x = split/-/, $f; + next if (scalar(@x) != 4); + next if ($x[0] != $year); + next if ($x[1] != $month); + next if ($x[2] != $day); + + if ($x[3] > $max) { + $max = $x[3]; + } + } + + $RunNumber = $max + 1; + } + else { + + if (-x $Dir) { + DieDiag("'$Dir' exists but is not a directory.\n"); + } + + if ($TmpMode) { + DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n"); + } + + # $Dir does not exist. It will be automatically created by the + # clang driver. Set the run number to 1. + + $RunNumber = 1; + } + + die "RunNumber must be defined!" if (!defined $RunNumber); + + # Append the run number. + my $NewDir; + if ($TmpMode) { + $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; + } + else { + $NewDir = "$Dir/$DateString-$RunNumber"; + } + system 'mkdir','-p',$NewDir; + return $NewDir; +} + +sub SetHtmlEnv { + + die "Wrong number of arguments." if (scalar(@_) != 2); + + my $Args = shift; + my $Dir = shift; + + die "No build command." if (scalar(@$Args) == 0); + + my $Cmd = $$Args[0]; + + if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) { + return; + } + + if ($Verbose) { + Diag("Emitting reports for this run to '$Dir'.\n"); + } + + $ENV{'CCC_ANALYZER_HTML'} = $Dir; +} + +##----------------------------------------------------------------------------## +# ComputeDigest - Compute a digest of the specified file. +##----------------------------------------------------------------------------## + +sub ComputeDigest { + my $FName = shift; + DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); + + # Use Digest::MD5. We don't have to be cryptographically secure. We're + # just looking for duplicate files that come from a non-malicious source. + # We use Digest::MD5 because it is a standard Perl module that should + # come bundled on most systems. + open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); + binmode FILE; + my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; + close(FILE); + + # Return the digest. + return $Result; +} + +##----------------------------------------------------------------------------## +# UpdatePrefix - Compute the common prefix of files. +##----------------------------------------------------------------------------## + +my $Prefix; + +sub UpdatePrefix { + my $x = shift; + my $y = basename($x); + $x =~ s/\Q$y\E$//; + + if (!defined $Prefix) { + $Prefix = $x; + return; + } + + chop $Prefix while (!($x =~ /^\Q$Prefix/)); +} + +sub GetPrefix { + return $Prefix; +} + +##----------------------------------------------------------------------------## +# UpdateInFilePath - Update the path in the report file. +##----------------------------------------------------------------------------## + +sub UpdateInFilePath { + my $fname = shift; + my $regex = shift; + my $newtext = shift; + + open (RIN, $fname) or die "cannot open $fname"; + open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; + + while (<RIN>) { + s/$regex/$newtext/; + print ROUT $_; + } + + close (ROUT); + close (RIN); + system("mv", "$fname.tmp", $fname); +} + +##----------------------------------------------------------------------------## +# AddStatLine - Decode and insert a statistics line into the database. +##----------------------------------------------------------------------------## + +sub AddStatLine { + my $Line = shift; + my $Stats = shift; + + print $Line . "\n"; + + my $Regex = qr/(.*?)\ :\ (.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable + \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList: + \ (yes|no)/x; + + if ($Line !~ $Regex) { + return; + } + + # Create a hash of the interesting fields + my $Row = { + Filename => $1, + Function => $2, + Total => $3, + Unreachable => $4, + Aborted => $5, + Empty => $6 + }; + + # Add them to the stats array + push @$Stats, $Row; +} + +##----------------------------------------------------------------------------## +# ScanFile - Scan a report file for various identifying attributes. +##----------------------------------------------------------------------------## + +# Sometimes a source file is scanned more than once, and thus produces +# multiple error reports. We use a cache to solve this problem. + +my %AlreadyScanned; + +sub ScanFile { + + my $Index = shift; + my $Dir = shift; + my $FName = shift; + my $Stats = shift; + + # Compute a digest for the report file. Determine if we have already + # scanned a file that looks just like it. + + my $digest = ComputeDigest("$Dir/$FName"); + + if (defined $AlreadyScanned{$digest}) { + # Redundant file. Remove it. + system ("rm", "-f", "$Dir/$FName"); + return; + } + + $AlreadyScanned{$digest} = 1; + + # At this point the report file is not world readable. Make it happen. + system ("chmod", "644", "$Dir/$FName"); + + # Scan the report file for tags. + open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); + + my $BugType = ""; + my $BugFile = ""; + my $BugCategory = ""; + my $BugDescription = ""; + my $BugPathLength = 1; + my $BugLine = 0; + + while (<IN>) { + last if (/<!-- BUGMETAEND -->/); + + if (/<!-- BUGTYPE (.*) -->$/) { + $BugType = $1; + } + elsif (/<!-- BUGFILE (.*) -->$/) { + $BugFile = abs_path($1); + UpdatePrefix($BugFile); + } + elsif (/<!-- BUGPATHLENGTH (.*) -->$/) { + $BugPathLength = $1; + } + elsif (/<!-- BUGLINE (.*) -->$/) { + $BugLine = $1; + } + elsif (/<!-- BUGCATEGORY (.*) -->$/) { + $BugCategory = $1; + } + elsif (/<!-- BUGDESC (.*) -->$/) { + $BugDescription = $1; + } + } + + close(IN); + + if (!defined $BugCategory) { + $BugCategory = "Other"; + } + + # Don't add internal statistics to the bug reports + if ($BugCategory =~ /statistics/i) { + AddStatLine($BugDescription, $Stats); + return; + } + + push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine, + $BugPathLength ]; +} + +##----------------------------------------------------------------------------## +# CopyFiles - Copy resource files to target directory. +##----------------------------------------------------------------------------## + +sub CopyFiles { + + my $Dir = shift; + + my $JS = Cwd::realpath("$RealBin/sorttable.js"); + + DieDiag("Cannot find 'sorttable.js'.\n") + if (! -r $JS); + + system ("cp", $JS, "$Dir"); + + DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") + if (! -r "$Dir/sorttable.js"); + + my $CSS = Cwd::realpath("$RealBin/scanview.css"); + + DieDiag("Cannot find 'scanview.css'.\n") + if (! -r $CSS); + + system ("cp", $CSS, "$Dir"); + + DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") + if (! -r $CSS); +} + +##----------------------------------------------------------------------------## +# CalcStats - Calculates visitation statistics and returns the string. +##----------------------------------------------------------------------------## + +sub CalcStats { + my $Stats = shift; + + my $TotalBlocks = 0; + my $UnreachedBlocks = 0; + my $TotalFunctions = scalar(@$Stats); + my $BlockAborted = 0; + my $WorkListAborted = 0; + my $Aborted = 0; + + # Calculate the unique files + my $FilesHash = {}; + + foreach my $Row (@$Stats) { + $FilesHash->{$Row->{Filename}} = 1; + $TotalBlocks += $Row->{Total}; + $UnreachedBlocks += $Row->{Unreachable}; + $BlockAborted++ if $Row->{Aborted} eq 'yes'; + $WorkListAborted++ if $Row->{Empty} eq 'no'; + $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no'; + } + + my $TotalFiles = scalar(keys(%$FilesHash)); + + # Calculations + my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100); + my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions + * 100); + my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted / + $TotalFunctions * 100); + my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks + * 100); + + my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions" + . " in $TotalFiles files\n" + . "$Aborted functions aborted early ($PercentAborted%)\n" + . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n" + . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n" + . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n"; + + return $StatsString; +} + +##----------------------------------------------------------------------------## +# Postprocess - Postprocess the results of an analysis scan. +##----------------------------------------------------------------------------## + +sub Postprocess { + + my $Dir = shift; + my $BaseDir = shift; + my $AnalyzerStats = shift; + + die "No directory specified." if (!defined $Dir); + + if (! -d $Dir) { + Diag("No bugs found.\n"); + return 0; + } + + opendir(DIR, $Dir); + my @files = grep { /^report-.*\.html$/ } readdir(DIR); + closedir(DIR); + + if (scalar(@files) == 0 and ! -e "$Dir/failures") { + Diag("Removing directory '$Dir' because it contains no reports.\n"); + system ("rm", "-fR", $Dir); + return 0; + } + + # Scan each report file and build an index. + my @Index; + my @Stats; + foreach my $file (@files) { ScanFile(\@Index, $Dir, $file, \@Stats); } + + # Scan the failures directory and use the information in the .info files + # to update the common prefix directory. + my @failures; + my @attributes_ignored; + if (-d "$Dir/failures") { + opendir(DIR, "$Dir/failures"); + @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR); + closedir(DIR); + opendir(DIR, "$Dir/failures"); + @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR); + closedir(DIR); + foreach my $file (@failures) { + open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n"); + my $Path = <IN>; + if (defined $Path) { UpdatePrefix($Path); } + close IN; + } + } + + # Generate an index.html file. + my $FName = "$Dir/index.html"; + open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); + + # Print out the header. + +print OUT <<ENDTEXT; +<html> +<head> +<title>${HtmlTitle}</title> +<link type="text/css" rel="stylesheet" href="scanview.css"/> +<script src="sorttable.js"></script> +<script language='javascript' type="text/javascript"> +function SetDisplay(RowClass, DisplayVal) +{ + var Rows = document.getElementsByTagName("tr"); + for ( var i = 0 ; i < Rows.length; ++i ) { + if (Rows[i].className == RowClass) { + Rows[i].style.display = DisplayVal; + } + } +} + +function CopyCheckedStateToCheckButtons(SummaryCheckButton) { + var Inputs = document.getElementsByTagName("input"); + for ( var i = 0 ; i < Inputs.length; ++i ) { + if (Inputs[i].type == "checkbox") { + if(Inputs[i] != SummaryCheckButton) { + Inputs[i].checked = SummaryCheckButton.checked; + Inputs[i].onclick(); + } + } + } +} + +function returnObjById( id ) { + if (document.getElementById) + var returnVar = document.getElementById(id); + else if (document.all) + var returnVar = document.all[id]; + else if (document.layers) + var returnVar = document.layers[id]; + return returnVar; +} + +var NumUnchecked = 0; + +function ToggleDisplay(CheckButton, ClassName) { + if (CheckButton.checked) { + SetDisplay(ClassName, ""); + if (--NumUnchecked == 0) { + returnObjById("AllBugsCheck").checked = true; + } + } + else { + SetDisplay(ClassName, "none"); + NumUnchecked++; + returnObjById("AllBugsCheck").checked = false; + } +} +</script> +<!-- SUMMARYENDHEAD --> +</head> +<body> +<h1>${HtmlTitle}</h1> + +<table> +<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr> +<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr> +<tr><th>Command Line:</th><td>${CmdArgs}</td></tr> +<tr><th>Date:</th><td>${Date}</td></tr> +ENDTEXT + +print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n" + if (defined($BuildName) && defined($BuildDate)); + +print OUT <<ENDTEXT; +</table> +ENDTEXT + + if (scalar(@files)) { + # Print out the summary table. + my %Totals; + + for my $row ( @Index ) { + my $bug_type = ($row->[2]); + my $bug_category = ($row->[1]); + my $key = "$bug_category:$bug_type"; + + if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } + else { $Totals{$key}->[0]++; } + } + + print OUT "<h2>Bug Summary</h2>"; + + if (defined $BuildName) { + print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n" + } + + my $TotalBugs = scalar(@Index); +print OUT <<ENDTEXT; +<table> +<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead> +<tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr> +ENDTEXT + + my $last_category; + + for my $key ( + sort { + my $x = $Totals{$a}; + my $y = $Totals{$b}; + my $res = $x->[1] cmp $y->[1]; + $res = $x->[2] cmp $y->[2] if ($res == 0); + $res + } keys %Totals ) + { + my $val = $Totals{$key}; + my $category = $val->[1]; + if (!defined $last_category or $last_category ne $category) { + $last_category = $category; + print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n"; + } + my $x = lc $key; + $x =~ s/[ ,'":\/()]+/_/g; + print OUT "<tr><td class=\"SUMM_DESC\">"; + print OUT $val->[2]; + print OUT "</td><td class=\"Q\">"; + print OUT $val->[0]; + print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n"; + } + + # Print out the table of errors. + +print OUT <<ENDTEXT; +</table> +<h2>Reports</h2> + +<table class="sortable" style="table-layout:automatic"> +<thead><tr> + <td>Bug Group</td> + <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind"> ▾</span></td> + <td>File</td> + <td class="Q">Line</td> + <td class="Q">Path Length</td> + <td class="sorttable_nosort"></td> + <!-- REPORTBUGCOL --> +</tr></thead> +<tbody> +ENDTEXT + + my $prefix = GetPrefix(); + my $regex; + my $InFileRegex; + my $InFilePrefix = "File:</td><td>"; + + if (defined $prefix) { + $regex = qr/^\Q$prefix\E/is; + $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; + } + + for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { + my $x = "$row->[1]:$row->[2]"; + $x = lc $x; + $x =~ s/[ ,'":\/()]+/_/g; + + my $ReportFile = $row->[0]; + + print OUT "<tr class=\"bt_$x\">"; + print OUT "<td class=\"DESC\">"; + print OUT $row->[1]; + print OUT "</td>"; + print OUT "<td class=\"DESC\">"; + print OUT $row->[2]; + print OUT "</td>"; + + # Update the file prefix. + my $fname = $row->[3]; + + if (defined $regex) { + $fname =~ s/$regex//; + UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) + } + + print OUT "<td>"; + my @fname = split /\//,$fname; + if ($#fname > 0) { + while ($#fname >= 0) { + my $x = shift @fname; + print OUT $x; + if ($#fname >= 0) { + print OUT "<span class=\"W\"> </span>/"; + } + } + } + else { + print OUT $fname; + } + print OUT "</td>"; + + # Print out the quantities. + for my $j ( 4 .. 5 ) { + print OUT "<td class=\"Q\">$row->[$j]</td>"; + } + + # Print the rest of the columns. + for (my $j = 6; $j <= $#{$row}; ++$j) { + print OUT "<td>$row->[$j]</td>" + } + + # Emit the "View" link. + print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>"; + + # Emit REPORTBUG markers. + print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n"; + + # End the row. + print OUT "</tr>\n"; + } + + print OUT "</tbody>\n</table>\n\n"; + } + + if (scalar (@failures) || scalar(@attributes_ignored)) { + print OUT "<h2>Analyzer Failures</h2>\n"; + + if (scalar @attributes_ignored) { + print OUT "The analyzer's parser ignored the following attributes:<p>\n"; + print OUT "<table>\n"; + print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; + foreach my $file (sort @attributes_ignored) { + die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/)); + my $attribute = $1; + # Open the attribute file to get the first file that failed. + next if (!open (ATTR, "$Dir/failures/$file")); + my $ppfile = <ATTR>; + chomp $ppfile; + close ATTR; + next if (! -e "$Dir/failures/$ppfile"); + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$ppfile.info.txt") or + die "Cannot open $Dir/failures/$ppfile.info.txt\n"; + my $srcfile = <INFO>; + chomp $srcfile; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; + } + print OUT "</table>\n"; + } + + if (scalar @failures) { + print OUT "<p>The analyzer had problems processing the following files:</p>\n"; + print OUT "<table>\n"; + print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; + foreach my $file (sort @failures) { + $file =~ /(.+).info.txt$/; + # Get the preprocessed file. + my $ppfile = $1; + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$file") or + die "Cannot open $Dir/failures/$file\n"; + my $srcfile = <INFO>; + chomp $srcfile; + my $problem = <INFO>; + chomp $problem; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; + } + print OUT "</table>\n"; + } + print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n"; + } + + print OUT "</body></html>\n"; + close(OUT); + CopyFiles($Dir); + + # Make sure $Dir and $BaseDir are world readable/executable. + system("chmod", "755", $Dir); + if (defined $BaseDir) { system("chmod", "755", $BaseDir); } + + # Print statistics + print CalcStats(\@Stats) if $AnalyzerStats; + + my $Num = scalar(@Index); + Diag("$Num bugs found.\n"); + if ($Num > 0 && -r "$Dir/index.html") { + Diag("Run 'scan-view $Dir' to examine bug reports.\n"); + } + + DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored); + + return $Num; +} + +##----------------------------------------------------------------------------## +# RunBuildCommand - Run the build command. +##----------------------------------------------------------------------------## + +sub AddIfNotPresent { + my $Args = shift; + my $Arg = shift; + my $found = 0; + + foreach my $k (@$Args) { + if ($k eq $Arg) { + $found = 1; + last; + } + } + + if ($found == 0) { + push @$Args, $Arg; + } +} + +sub RunBuildCommand { + + my $Args = shift; + my $IgnoreErrors = shift; + my $Cmd = $Args->[0]; + my $CCAnalyzer = shift; + my $CXXAnalyzer = shift; + + # Get only the part of the command after the last '/'. + if ($Cmd =~ /\/([^\/]+)$/) { + $Cmd = $1; + } + + if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?cc[^\/]*$)/ or + $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?clang$)/ or + $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) { + + if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) { + $ENV{"CCC_CC"} = $1; + } + + shift @$Args; + unshift @$Args, $CCAnalyzer; + } + elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or + $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or + $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or + $Cmd =~ /(.*\/?clang\+\+$)/ or + $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) { + if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) { + $ENV{"CCC_CXX"} = $1; + } + shift @$Args; + unshift @$Args, $CXXAnalyzer; + } + elsif ($IgnoreErrors) { + if ($Cmd eq "make" or $Cmd eq "gmake") { + AddIfNotPresent($Args, "CC=$CCAnalyzer"); + AddIfNotPresent($Args, "CXX=$CXXAnalyzer"); + AddIfNotPresent($Args,"-k"); + AddIfNotPresent($Args,"-i"); + } + elsif ($Cmd eq "xcodebuild") { + AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); + } + } + + if ($Cmd eq "xcodebuild") { + # Check if using iPhone SDK 3.0 (simulator). If so the compiler being + # used should be gcc-4.2. + if (!defined $ENV{"CCC_CC"}) { + for (my $i = 0 ; $i < scalar(@$Args); ++$i) { + if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) { + if (@$Args[$i+1] =~ /^iphonesimulator3/) { + $ENV{"CCC_CC"} = "gcc-4.2"; + $ENV{"CCC_CXX"} = "g++-4.2"; + } + } + } + } + + # Disable PCH files until clang supports them. + AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); + + # When 'CC' is set, xcodebuild uses it to do all linking, even if we are + # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' + # (via c++-analyzer) when linking such files. + $ENV{"LDPLUSPLUS"} = $CXXAnalyzer; + } + + return (system(@$Args) >> 8); +} + +##----------------------------------------------------------------------------## +# DisplayHelp - Utility function to display all help options. +##----------------------------------------------------------------------------## + +sub DisplayHelp { + +print <<ENDTEXT; +USAGE: $Prog [options] <build command> [build options] + +ENDTEXT + + if (defined $BuildName) { + print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; + } + +print <<ENDTEXT; +OPTIONS: + + -analyze-headers - Also analyze functions in #included files. + + -o - Target directory for HTML report files. Subdirectories + will be created as needed to represent separate "runs" of + the analyzer. If this option is not specified, a directory + is created in /tmp (TMPDIR on Mac OS X) to store the reports. + + -h - Display this message. + --help + + -k - Add a "keep on going" option to the specified build command. + --keep-going This option currently supports make and xcodebuild. + This is a convenience option; one can specify this + behavior directly using build options. + + --html-title [title] - Specify the title used on generated HTML pages. + --html-title=[title] If not specified, a default title will be used. + + -plist - By default the output of scan-build is a set of HTML files. + This option outputs the results as a set of .plist files. + + -plist-html - By default the output of scan-build is a set of HTML files. + This option outputs the results as a set of HTML + and .plist files. + + --status-bugs - By default, the exit status of $Prog is the same as the + executed build command. Specifying this option causes the + exit status of $Prog to be 1 if it found potential bugs + and 0 otherwise. + + --use-cc [compiler path] - $Prog attempts to guess the default compiler for + --use-cc=[compiler path] your C and Objective-C code. Use this option + to specify an alternate compiler. + + --use-c++ [compiler path] - $Prog attempts to guess the default compiler for + --use-c++=[compiler path] your C++ and Objective-C++ code. Use this option + to specify an alternate compiler. + + -v - Verbose output from $Prog and the analyzer. + A second and third '-v' increases verbosity. + + -V - View analysis results in a web browser when the build + --view completes. + +ADVANCED OPTIONS: + + -constraints [model] - Specify the contraint engine used by the analyzer. + By default the 'range' model is used. Specifying + 'basic' uses a simpler, less powerful constraint model + used by checker-0.160 and earlier. + + -store [model] - Specify the store model used by the analyzer. By default, + the 'region' store model is used. 'region' specifies a field- + sensitive store model. Users can also specify 'basic', which + is far less precise but can more quickly analyze code. + 'basic' was the default store model for checker-0.221 and + earlier. + + -no-failure-reports - Do not create a 'failures' subdirectory that includes + analyzer crash reports and preprocessed source files. + + -stats - Generates visitation statistics for the project being analyzed. + + -maxloop N - specifiy the number of times a block can be visited before giving + up. Default is 4. Increase for more comprehensive coverage at a + cost of speed. + +CONTROLLING CHECKERS: + + A default group of checkers are always run unless explicitly disabled. + Checkers may be enabled/disabled using the following options: + + -enable-checker [checker name] + -disable-checker [checker name] +ENDTEXT + +# Query clang for list of checkers that are enabled. +my %EnabledCheckers; +foreach my $lang ("c", "objective-c", "objective-c++", "c++") { + pipe(FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Clang, ('--analyze', '-x', $lang, '-', '-###'); + } + close(TO_PARENT); + while(<FROM_CHILD>) { + foreach my $val (split /\s+/) { + $val =~ s/\"//g; + if ($val =~ /-analyzer-checker\=([^\s]+)/) { + $EnabledCheckers{$1} = 1; + } + } + } + waitpid($pid,0); + close(FROM_CHILD); +} + +# Query clang for complete list of checkers. +pipe(FROM_CHILD, TO_PARENT); +my $pid = fork(); +if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Clang, ('-cc1', '-analyzer-checker-help'); +} +close(TO_PARENT); +my $foundCheckers = 0; +while(<FROM_CHILD>) { + if (/CHECKERS:/) { + $foundCheckers = 1; + last; + } +} +if (!$foundCheckers) { + print " *** Could not query Clang for the list of available checkers."; +} +else { + print("\nAVAILABLE CHECKERS:\n\n"); + my $skip = 0; + while(<FROM_CHILD>) { + if (/experimental/) { + $skip = 1; + next; + } + if ($skip) { + next if (!/^\s\s[^\s]/); + $skip = 0; + } + s/^\s\s//; + if (/^([^\s]+)/) { + # Is the checker enabled? + my $checker = $1; + my $enabled = 0; + my $aggregate = ""; + foreach my $domain (split /\./, $checker) { + $aggregate .= $domain; + if ($EnabledCheckers{$aggregate}) { + $enabled =1; + last; + } + } + + if ($enabled) { + print " + "; + } + else { + print " "; + } + } + else { + print " "; + } + print $_; + } +} +waitpid($pid,0); +close(FROM_CHILD); + +print <<ENDTEXT + + NOTE: "+" indicates that an analysis is enabled by default. + +BUILD OPTIONS + + You can specify any build option acceptable to the build command. + +EXAMPLE + + $Prog -o /tmp/myhtmldir make -j4 + + The above example causes analysis reports to be deposited into + a subdirectory of "/tmp/myhtmldir" and to run "make" with the "-j4" option. + A different subdirectory is created each time $Prog analyzes a project. + The analyzer should support most parallel builds, but not distributed builds. + +ENDTEXT +} + +##----------------------------------------------------------------------------## +# HtmlEscape - HTML entity encode characters that are special in HTML +##----------------------------------------------------------------------------## + +sub HtmlEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + my $tmp = $arg; + $tmp =~ s/&/&/g; + $tmp =~ s/</</g; + $tmp =~ s/>/>/g; + return $tmp; +} + +##----------------------------------------------------------------------------## +# ShellEscape - backslash escape characters that are special to the shell +##----------------------------------------------------------------------------## + +sub ShellEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + if ($arg =~ /["\s]/) { return "'" . $arg . "'"; } + return $arg; +} + +##----------------------------------------------------------------------------## +# Process command-line arguments. +##----------------------------------------------------------------------------## + +my $AnalyzeHeaders = 0; +my $HtmlDir; # Parent directory to store HTML files. +my $IgnoreErrors = 0; # Ignore build errors. +my $ViewResults = 0; # View results when the build terminates. +my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found +my @AnalysesToRun; +my $StoreModel; +my $ConstraintsModel; +my $OutputFormat = "html"; +my $AnalyzerStats = 0; +my $MaxLoop = 0; + +if (!@ARGV) { + DisplayHelp(); + exit 1; +} + + +my $displayHelp = 0; + +while (@ARGV) { + + # Scan for options we recognize. + + my $arg = $ARGV[0]; + + if ($arg eq "-h" or $arg eq "--help") { + $displayHelp = 1; + shift @ARGV; + next; + } + + if ($arg eq '-analyze-headers') { + shift @ARGV; + $AnalyzeHeaders = 1; + next; + } + + if ($arg eq "-o") { + shift @ARGV; + + if (!@ARGV) { + DieDiag("'-o' option requires a target directory name.\n"); + } + + # Construct an absolute path. Uses the current working directory + # as a base if the original path was not absolute. + $HtmlDir = abs_path(shift @ARGV); + + next; + } + + if ($arg =~ /^--html-title(=(.+))?$/) { + shift @ARGV; + + if (!defined $2 || $2 eq '') { + if (!@ARGV) { + DieDiag("'--html-title' option requires a string.\n"); + } + + $HtmlTitle = shift @ARGV; + } else { + $HtmlTitle = $2; + } + + next; + } + + if ($arg eq "-k" or $arg eq "--keep-going") { + shift @ARGV; + $IgnoreErrors = 1; + next; + } + + if ($arg =~ /^--use-cc(=(.+))?$/) { + shift @ARGV; + my $cc; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--use-cc' option requires a compiler executable name.\n"); + } + $cc = shift @ARGV; + } + else { + $cc = $2; + } + + $ENV{"CCC_CC"} = $cc; + next; + } + + if ($arg =~ /^--use-c\+\+(=(.+))?$/) { + shift @ARGV; + my $cxx; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--use-c++' option requires a compiler executable name.\n"); + } + $cxx = shift @ARGV; + } + else { + $cxx = $2; + } + + $ENV{"CCC_CXX"} = $cxx; + next; + } + + if ($arg eq "-v") { + shift @ARGV; + $Verbose++; + next; + } + + if ($arg eq "-V" or $arg eq "--view") { + shift @ARGV; + $ViewResults = 1; + next; + } + + if ($arg eq "--status-bugs") { + shift @ARGV; + $ExitStatusFoundBugs = 1; + next; + } + + if ($arg eq "-store") { + shift @ARGV; + $StoreModel = shift @ARGV; + next; + } + + if ($arg eq "-constraints") { + shift @ARGV; + $ConstraintsModel = shift @ARGV; + next; + } + + if ($arg eq "-plist") { + shift @ARGV; + $OutputFormat = "plist"; + next; + } + if ($arg eq "-plist-html") { + shift @ARGV; + $OutputFormat = "plist-html"; + next; + } + + if ($arg eq "-no-failure-reports") { + $ENV{"CCC_REPORT_FAILURES"} = 0; + next; + } + if ($arg eq "-stats") { + shift @ARGV; + $AnalyzerStats = 1; + next; + } + if ($arg eq "-maxloop") { + shift @ARGV; + $MaxLoop = shift @ARGV; + next; + } + if ($arg eq "-enable-checker") { + shift @ARGV; + push @AnalysesToRun, "-analyzer-checker", shift @ARGV; + next; + } + if ($arg eq "-disable-checker") { + shift @ARGV; + push @AnalysesToRun, "-analyzer-disable-checker", shift @ARGV; + next; + } + + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); + + last; +} + +if (!@ARGV and $displayHelp == 0) { + Diag("No build command specified.\n\n"); + $displayHelp = 1; +} + +if ($displayHelp) { + DisplayHelp(); + exit 1; +} + +# Determine where results go. +$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); +$HtmlTitle = "${CurrentDirSuffix} - scan-build results" + unless (defined($HtmlTitle)); + +# Determine the output directory for the HTML reports. +my $BaseDir = $HtmlDir; +$HtmlDir = GetHTMLRunDir($HtmlDir); + +# Determine the location of ccc-analyzer. +my $AbsRealBin = Cwd::realpath($RealBin); +my $Cmd = "$AbsRealBin/libexec/ccc-analyzer"; +my $CmdCXX = "$AbsRealBin/libexec/c++-analyzer"; + +if (!defined $Cmd || ! -x $Cmd) { + $Cmd = "$AbsRealBin/ccc-analyzer"; + DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd); +} +if (!defined $CmdCXX || ! -x $CmdCXX) { + $CmdCXX = "$AbsRealBin/c++-analyzer"; + DieDiag("Executable 'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -x $CmdCXX); +} + +if (!defined $ClangSB || ! -x $ClangSB) { + Diag("'clang' executable not found in '$RealBin/bin'.\n"); + Diag("Using 'clang' from path: $Clang\n"); +} + +# Set the appropriate environment variables. +SetHtmlEnv(\@ARGV, $HtmlDir); +$ENV{'CC'} = $Cmd; +$ENV{'CXX'} = $CmdCXX; +$ENV{'CLANG'} = $Clang; +$ENV{'CLANG_CXX'} = $ClangCXX; +if ($Verbose >= 2) { + $ENV{'CCC_ANALYZER_VERBOSE'} = 1; +} +if ($Verbose >= 3) { + $ENV{'CCC_ANALYZER_LOG'} = 1; +} +if ($AnalyzeHeaders) { + push @AnalysesToRun,"-analyzer-opt-analyze-headers"; +} +if ($AnalyzerStats) { + push @AnalysesToRun, '-analyzer-checker', 'debug.Stats'; +} +if ($MaxLoop > 0) { + push @AnalysesToRun, '-analyzer-max-loop ' . $MaxLoop; +} + +$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun; + +if (defined $StoreModel) { + $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel; +} +if (defined $ConstraintsModel) { + $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel; +} +if (defined $OutputFormat) { + $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat; +} + +# Run the build. +my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd, $CmdCXX); + +if (defined $OutputFormat) { + if ($OutputFormat =~ /plist/) { + Diag "Analysis run complete.\n"; + Diag "Analysis results (plist files) deposited in '$HtmlDir'\n"; + } + elsif ($OutputFormat =~ /html/) { + # Postprocess the HTML directory. + my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats); + + if ($ViewResults and -r "$HtmlDir/index.html") { + Diag "Analysis run complete.\n"; + Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n"; + my $ScanView = Cwd::realpath("$RealBin/scan-view"); + if (! -x $ScanView) { $ScanView = "scan-view"; } + exec $ScanView, "$HtmlDir"; + } + + if ($ExitStatusFoundBugs) { + exit 1 if ($NumBugs > 0); + exit 0; + } + } +} + +exit $ExitStatus; + diff --git a/clang/tools/scan-build/scanview.css b/clang/tools/scan-build/scanview.css new file mode 100644 index 0000000..a0406f3 --- /dev/null +++ b/clang/tools/scan-build/scanview.css @@ -0,0 +1,62 @@ +body { color:#000000; background-color:#ffffff } +body { font-family: Helvetica, sans-serif; font-size:9pt } +h1 { font-size: 14pt; } +h2 { font-size: 12pt; } +table { font-size:9pt } +table { border-spacing: 0px; border: 1px solid black } +th, table thead { + background-color:#eee; color:#666666; + font-weight: bold; cursor: default; + text-align:center; + font-weight: bold; font-family: Verdana; + white-space:nowrap; +} +.W { font-size:0px } +th, td { padding:5px; padding-left:8px; text-align:left } +td.SUMM_DESC { padding-left:12px } +td.DESC { white-space:pre } +td.Q { text-align:right } +td { text-align:left } +tbody.scrollContent { overflow:auto } + +table.form_group { + background-color: #ccc; + border: 1px solid #333; + padding: 2px; +} + +table.form_inner_group { + background-color: #ccc; + border: 1px solid #333; + padding: 0px; +} + +table.form { + background-color: #999; + border: 1px solid #333; + padding: 2px; +} + +td.form_label { + text-align: right; + vertical-align: top; +} +/* For one line entires */ +td.form_clabel { + text-align: right; + vertical-align: center; +} +td.form_value { + text-align: left; + vertical-align: top; +} +td.form_submit { + text-align: right; + vertical-align: top; +} + +h1.SubmitFail { + color: #f00; +} +h1.SubmitOk { +} diff --git a/clang/tools/scan-build/set-xcode-analyzer b/clang/tools/scan-build/set-xcode-analyzer new file mode 100755 index 0000000..06e1d85 --- /dev/null +++ b/clang/tools/scan-build/set-xcode-analyzer @@ -0,0 +1,89 @@ +#!/usr/bin/python + +# [PR 11661] Note that we hardwire to /usr/bin/python because we +# want to the use the system version of Python on Mac OS X. +# This one has the scripting bridge enabled. + +import os +import subprocess +import sys +import re +import tempfile +import shutil +import stat +from AppKit import * + +def FindClangSpecs(path): + print "(+) Searching for xcspec file in: ", path + for root, dirs, files in os.walk(path): + for f in files: + if f.endswith(".xcspec") and f.startswith("Clang LLVM"): + yield os.path.join(root, f) + +def ModifySpec(path, pathToChecker): + t = tempfile.NamedTemporaryFile(delete=False) + foundAnalyzer = False + with open(path) as f: + for line in f: + if not foundAnalyzer: + if line.find("Static Analyzer") >= 0: + foundAnalyzer = True + else: + m = re.search('^(\s*ExecPath\s*=\s*")', line) + if m: + line = "".join([m.group(0), pathToChecker, '";\n']) + t.write(line) + t.close() + print "(+) processing:", path + try: + shutil.copy(t.name, path) + os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + except IOError, why: + print " (-) Cannot update file:", why, "\n" + except OSError, why: + print " (-) Cannot update file:", why, "\n" + os.unlink(t.name) + +def main(): + from optparse import OptionParser + parser = OptionParser('usage: %prog [options]') + parser.set_description(__doc__) + parser.add_option("--use-checker-build", dest="path", + help="Use the Clang located at the provided absolute path, e.g. /Users/foo/checker-1") + parser.add_option("--use-xcode-clang", action="store_const", + const="$(CLANG)", dest="default", + help="Use the Clang bundled with Xcode") + (options, args) = parser.parse_args() + if options.path is None and options.default is None: + parser.error("You must specify a version of Clang to use for static analysis. Specify '-h' for details") + + # determine if Xcode is running + for x in NSWorkspace.sharedWorkspace().runningApplications(): + if x.localizedName().find("Xcode") >= 0: + print "(-) You must quit Xcode first before modifying its configuration files." + return + + if options.path: + # Expand tildes. + path = os.path.expanduser(options.path) + if not path.endswith("clang"): + print "(+) Using Clang bundled with checker build:", path + path = os.path.join(path, "bin", "clang"); + else: + print "(+) Using Clang located at:", path + else: + print "(+) Using the Clang bundled with Xcode" + path = options.default + + xcode_path = subprocess.check_output(["xcode-select", "-print-path"]) + if (re.search("Xcode.app", xcode_path)): + # Cut off the 'Developer' dir, as the xcspec lies in another part + # of the Xcode.app subtree. + xcode_path = os.path.dirname(xcode_path) + + for x in FindClangSpecs(xcode_path): + ModifySpec(x, path) + +if __name__ == '__main__': + main() + diff --git a/clang/tools/scan-build/sorttable.js b/clang/tools/scan-build/sorttable.js new file mode 100644 index 0000000..4352d3b --- /dev/null +++ b/clang/tools/scan-build/sorttable.js @@ -0,0 +1,493 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=0; i<headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j<rows.length; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i=0; i<table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i=newrows.length-1; i>=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + sorttable.init(); // call the onload handler + } + }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff + var _timer = setInterval(function() { + if (/loaded|complete/.test(document.readyState)) { + sorttable.init(); // call the onload handler + } + }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; + diff --git a/clang/tools/scan-view/Reporter.py b/clang/tools/scan-view/Reporter.py new file mode 100644 index 0000000..9560fc1 --- /dev/null +++ b/clang/tools/scan-view/Reporter.py @@ -0,0 +1,248 @@ +"""Methods for reporting bugs.""" + +import subprocess, sys, os + +__all__ = ['ReportFailure', 'BugReport', 'getReporters'] + +# + +class ReportFailure(Exception): + """Generic exception for failures in bug reporting.""" + def __init__(self, value): + self.value = value + +# Collect information about a bug. + +class BugReport: + def __init__(self, title, description, files): + self.title = title + self.description = description + self.files = files + +# Reporter interfaces. + +import os + +import email, mimetypes, smtplib +from email import encoders +from email.message import Message +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +#===------------------------------------------------------------------------===# +# ReporterParameter +#===------------------------------------------------------------------------===# + +class ReporterParameter: + def __init__(self, n): + self.name = n + def getName(self): + return self.name + def getValue(self,r,bugtype,getConfigOption): + return getConfigOption(r.getName(),self.getName()) + def saveConfigValue(self): + return True + +class TextParameter (ReporterParameter): + def getHTML(self,r,bugtype,getConfigOption): + return """\ +<tr> +<td class="form_clabel">%s:</td> +<td class="form_value"><input type="text" name="%s_%s" value="%s"></td> +</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption)) + +class SelectionParameter (ReporterParameter): + def __init__(self, n, values): + ReporterParameter.__init__(self,n) + self.values = values + + def getHTML(self,r,bugtype,getConfigOption): + default = self.getValue(r,bugtype,getConfigOption) + return """\ +<tr> +<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s"> +%s +</select></td>"""%(self.getName(),r.getName(),self.getName(),'\n'.join(["""\ +<option value="%s"%s>%s</option>"""%(o[0], + o[0] == default and ' selected="selected"' or '', + o[1]) for o in self.values])) + +#===------------------------------------------------------------------------===# +# Reporters +#===------------------------------------------------------------------------===# + +class EmailReporter: + def getName(self): + return 'Email' + + def getParameters(self): + return map(lambda x:TextParameter(x),['To', 'From', 'SMTP Server', 'SMTP Port']) + + # Lifted from python email module examples. + def attachFile(self, outer, path): + # Guess the content type based on the file's extension. Encoding + # will be ignored, although we should check for simple things like + # gzip'd or compressed files. + ctype, encoding = mimetypes.guess_type(path) + if ctype is None or encoding is not None: + # No guess could be made, or the file is encoded (compressed), so + # use a generic bag-of-bits type. + ctype = 'application/octet-stream' + maintype, subtype = ctype.split('/', 1) + if maintype == 'text': + fp = open(path) + # Note: we should handle calculating the charset + msg = MIMEText(fp.read(), _subtype=subtype) + fp.close() + else: + fp = open(path, 'rb') + msg = MIMEBase(maintype, subtype) + msg.set_payload(fp.read()) + fp.close() + # Encode the payload using Base64 + encoders.encode_base64(msg) + # Set the filename parameter + msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path)) + outer.attach(msg) + + def fileReport(self, report, parameters): + mainMsg = """\ +BUG REPORT +--- +Title: %s +Description: %s +"""%(report.title, report.description) + + if not parameters.get('To'): + raise ReportFailure('No "To" address specified.') + if not parameters.get('From'): + raise ReportFailure('No "From" address specified.') + + msg = MIMEMultipart() + msg['Subject'] = 'BUG REPORT: %s'%(report.title) + # FIXME: Get config parameters + msg['To'] = parameters.get('To') + msg['From'] = parameters.get('From') + msg.preamble = mainMsg + + msg.attach(MIMEText(mainMsg, _subtype='text/plain')) + for file in report.files: + self.attachFile(msg, file) + + try: + s = smtplib.SMTP(host=parameters.get('SMTP Server'), + port=parameters.get('SMTP Port')) + s.sendmail(msg['From'], msg['To'], msg.as_string()) + s.close() + except: + raise ReportFailure('Unable to send message via SMTP.') + + return "Message sent!" + +class BugzillaReporter: + def getName(self): + return 'Bugzilla' + + def getParameters(self): + return map(lambda x:TextParameter(x),['URL','Product']) + + def fileReport(self, report, parameters): + raise NotImplementedError + + +class RadarClassificationParameter(SelectionParameter): + def __init__(self): + SelectionParameter.__init__(self,"Classification", + [['1', 'Security'], ['2', 'Crash/Hang/Data Loss'], + ['3', 'Performance'], ['4', 'UI/Usability'], + ['6', 'Serious Bug'], ['7', 'Other']]) + + def saveConfigValue(self): + return False + + def getValue(self,r,bugtype,getConfigOption): + if bugtype.find("leak") != -1: + return '3' + elif bugtype.find("dereference") != -1: + return '2' + elif bugtype.find("missing ivar release") != -1: + return '3' + else: + return '7' + +class RadarReporter: + @staticmethod + def isAvailable(): + # FIXME: Find this .scpt better + path = os.path.join(os.path.dirname(__file__),'Resources/GetRadarVersion.scpt') + try: + p = subprocess.Popen(['osascript',path], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + data,err = p.communicate() + res = p.wait() + # FIXME: Check version? Check for no errors? + return res == 0 + + def getName(self): + return 'Radar' + + def getParameters(self): + return [ TextParameter('Component'), TextParameter('Component Version'), + RadarClassificationParameter() ] + + def fileReport(self, report, parameters): + component = parameters.get('Component', '') + componentVersion = parameters.get('Component Version', '') + classification = parameters.get('Classification', '') + personID = "" + diagnosis = "" + config = "" + + if not component.strip(): + component = 'Bugs found by clang Analyzer' + if not componentVersion.strip(): + componentVersion = 'X' + + script = os.path.join(os.path.dirname(__file__),'Resources/FileRadar.scpt') + args = ['osascript', script, component, componentVersion, classification, personID, report.title, + report.description, diagnosis, config] + map(os.path.abspath, report.files) +# print >>sys.stderr, args + try: + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + raise ReportFailure("Unable to file radar (AppleScript failure).") + data, err = p.communicate() + res = p.wait() + + if res: + raise ReportFailure("Unable to file radar (AppleScript failure).") + + try: + values = eval(data) + except: + raise ReportFailure("Unable to process radar results.") + + # We expect (int: bugID, str: message) + if len(values) != 2 or not isinstance(values[0], int): + raise ReportFailure("Unable to process radar results.") + + bugID,message = values + bugID = int(bugID) + + if not bugID: + raise ReportFailure(message) + + return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID) + +### + +def getReporters(): + reporters = [] + if RadarReporter.isAvailable(): + reporters.append(RadarReporter()) + reporters.append(EmailReporter()) + return reporters + diff --git a/clang/tools/scan-view/Resources/FileRadar.scpt b/clang/tools/scan-view/Resources/FileRadar.scpt Binary files differnew file mode 100644 index 0000000..1c74552 --- /dev/null +++ b/clang/tools/scan-view/Resources/FileRadar.scpt diff --git a/clang/tools/scan-view/Resources/GetRadarVersion.scpt b/clang/tools/scan-view/Resources/GetRadarVersion.scpt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/clang/tools/scan-view/Resources/GetRadarVersion.scpt diff --git a/clang/tools/scan-view/Resources/bugcatcher.ico b/clang/tools/scan-view/Resources/bugcatcher.ico Binary files differnew file mode 100644 index 0000000..22d39b5 --- /dev/null +++ b/clang/tools/scan-view/Resources/bugcatcher.ico diff --git a/clang/tools/scan-view/ScanView.py b/clang/tools/scan-view/ScanView.py new file mode 100644 index 0000000..c6dddba --- /dev/null +++ b/clang/tools/scan-view/ScanView.py @@ -0,0 +1,770 @@ +import BaseHTTPServer +import SimpleHTTPServer +import os +import sys +import urllib, urlparse +import posixpath +import StringIO +import re +import shutil +import threading +import time +import socket +import itertools + +import Reporter +import ConfigParser + +### +# Various patterns matched or replaced by server. + +kReportFileRE = re.compile('(.*/)?report-(.*)\\.html') + +kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') + +# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> + +kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') +kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') + +kReportReplacements = [] + +# Add custom javascript. +kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ +<script language="javascript" type="text/javascript"> +function load(url) { + if (window.XMLHttpRequest) { + req = new XMLHttpRequest(); + } else if (window.ActiveXObject) { + req = new ActiveXObject("Microsoft.XMLHTTP"); + } + if (req != undefined) { + req.open("GET", url, true); + req.send(""); + } +} +</script>""")) + +# Insert additional columns. +kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'), + '<td></td><td></td>')) + +# Insert report bug and open file links. +kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), + ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' + + '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) + +kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), + '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) + +kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), + '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) + +# Insert report crashes link. + +# Disabled for the time being until we decide exactly when this should +# be enabled. Also the radar reporter needs to be fixed to report +# multiple files. + +#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), +# '<br>These files will automatically be attached to ' + +# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) + +### +# Other simple parameters + +kResources = posixpath.join(posixpath.dirname(__file__), 'Resources') +kConfigPath = os.path.expanduser('~/.scanview.cfg') + +### + +__version__ = "0.1" + +__all__ = ["create_server"] + +class ReporterThread(threading.Thread): + def __init__(self, report, reporter, parameters, server): + threading.Thread.__init__(self) + self.report = report + self.server = server + self.reporter = reporter + self.parameters = parameters + self.success = False + self.status = None + + def run(self): + result = None + try: + if self.server.options.debug: + print >>sys.stderr, "%s: SERVER: submitting bug."%(sys.argv[0],) + self.status = self.reporter.fileReport(self.report, self.parameters) + self.success = True + time.sleep(3) + if self.server.options.debug: + print >>sys.stderr, "%s: SERVER: submission complete."%(sys.argv[0],) + except Reporter.ReportFailure,e: + self.status = e.value + except Exception,e: + s = StringIO.StringIO() + import traceback + print >>s,'<b>Unhandled Exception</b><br><pre>' + traceback.print_exc(e,file=s) + print >>s,'</pre>' + self.status = s.getvalue() + +class ScanViewServer(BaseHTTPServer.HTTPServer): + def __init__(self, address, handler, root, reporters, options): + BaseHTTPServer.HTTPServer.__init__(self, address, handler) + self.root = root + self.reporters = reporters + self.options = options + self.halted = False + self.config = None + self.load_config() + + def load_config(self): + self.config = ConfigParser.RawConfigParser() + + # Add defaults + self.config.add_section('ScanView') + for r in self.reporters: + self.config.add_section(r.getName()) + for p in r.getParameters(): + if p.saveConfigValue(): + self.config.set(r.getName(), p.getName(), '') + + # Ignore parse errors + try: + self.config.read([kConfigPath]) + except: + pass + + # Save on exit + import atexit + atexit.register(lambda: self.save_config()) + + def save_config(self): + # Ignore errors (only called on exit). + try: + f = open(kConfigPath,'w') + self.config.write(f) + f.close() + except: + pass + + def halt(self): + self.halted = True + if self.options.debug: + print >>sys.stderr, "%s: SERVER: halting." % (sys.argv[0],) + + def serve_forever(self): + while not self.halted: + if self.options.debug > 1: + print >>sys.stderr, "%s: SERVER: waiting..." % (sys.argv[0],) + try: + self.handle_request() + except OSError,e: + print 'OSError',e.errno + + def finish_request(self, request, client_address): + if self.options.autoReload: + import ScanView + self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler + BaseHTTPServer.HTTPServer.finish_request(self, request, client_address) + + def handle_error(self, request, client_address): + # Ignore socket errors + info = sys.exc_info() + if info and isinstance(info[1], socket.error): + if self.options.debug > 1: + print >>sys.stderr, "%s: SERVER: ignored socket error." % (sys.argv[0],) + return + BaseHTTPServer.HTTPServer.handle_error(self, request, client_address) + +# Borrowed from Quixote, with simplifications. +def parse_query(qs, fields=None): + if fields is None: + fields = {} + for chunk in filter(None, qs.split('&')): + if '=' not in chunk: + name = chunk + value = '' + else: + name, value = chunk.split('=', 1) + name = urllib.unquote(name.replace('+', ' ')) + value = urllib.unquote(value.replace('+', ' ')) + item = fields.get(name) + if item is None: + fields[name] = [value] + else: + item.append(value) + return fields + +class ScanViewRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + server_version = "ScanViewServer/" + __version__ + dynamic_mtime = time.time() + + def do_HEAD(self): + try: + SimpleHTTPServer.SimpleHTTPRequestHandler.do_HEAD(self) + except Exception,e: + self.handle_exception(e) + + def do_GET(self): + try: + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + except Exception,e: + self.handle_exception(e) + + def do_POST(self): + """Serve a POST request.""" + try: + length = self.headers.getheader('content-length') or "0" + try: + length = int(length) + except: + length = 0 + content = self.rfile.read(length) + fields = parse_query(content) + f = self.send_head(fields) + if f: + self.copyfile(f, self.wfile) + f.close() + except Exception,e: + self.handle_exception(e) + + def log_message(self, format, *args): + if self.server.options.debug: + sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % + (sys.argv[0], + self.address_string(), + self.log_date_time_string(), + format%args)) + + def load_report(self, report): + path = os.path.join(self.server.root, 'report-%s.html'%report) + data = open(path).read() + keys = {} + for item in kBugKeyValueRE.finditer(data): + k,v = item.groups() + keys[k] = v + return keys + + def load_crashes(self): + path = posixpath.join(self.server.root, 'index.html') + data = open(path).read() + problems = [] + for item in kReportCrashEntryRE.finditer(data): + fieldData = item.group(1) + fields = dict([i.groups() for i in + kReportCrashEntryKeyValueRE.finditer(fieldData)]) + problems.append(fields) + return problems + + def handle_exception(self, exc): + import traceback + s = StringIO.StringIO() + print >>s, "INTERNAL ERROR\n" + traceback.print_exc(exc, s) + f = self.send_string(s.getvalue(), 'text/plain') + if f: + self.copyfile(f, self.wfile) + f.close() + + def get_scalar_field(self, name): + if name in self.fields: + return self.fields[name][0] + else: + return None + + def submit_bug(self, c): + title = self.get_scalar_field('title') + description = self.get_scalar_field('description') + report = self.get_scalar_field('report') + reporterIndex = self.get_scalar_field('reporter') + files = [] + for fileID in self.fields.get('files',[]): + try: + i = int(fileID) + except: + i = None + if i is None or i<0 or i>=len(c.files): + return (False, 'Invalid file ID') + files.append(c.files[i]) + + if not title: + return (False, "Missing title.") + if not description: + return (False, "Missing description.") + try: + reporterIndex = int(reporterIndex) + except: + return (False, "Invalid report method.") + + # Get the reporter and parameters. + reporter = self.server.reporters[reporterIndex] + parameters = {} + for o in reporter.getParameters(): + name = '%s_%s'%(reporter.getName(),o.getName()) + if name not in self.fields: + return (False, + 'Missing field "%s" for %s report method.'%(name, + reporter.getName())) + parameters[o.getName()] = self.get_scalar_field(name) + + # Update config defaults. + if report != 'None': + self.server.config.set('ScanView', 'reporter', reporterIndex) + for o in reporter.getParameters(): + if o.saveConfigValue(): + name = o.getName() + self.server.config.set(reporter.getName(), name, parameters[name]) + + # Create the report. + bug = Reporter.BugReport(title, description, files) + + # Kick off a reporting thread. + t = ReporterThread(bug, reporter, parameters, self.server) + t.start() + + # Wait for thread to die... + while t.isAlive(): + time.sleep(.25) + submitStatus = t.status + + return (t.success, t.status) + + def send_report_submit(self): + report = self.get_scalar_field('report') + c = self.get_report_context(report) + if c.reportSource is None: + reportingFor = "Report Crashes > " + fileBug = """\ +<a href="/report_crashes">File Bug</a> > """%locals() + else: + reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, + report) + fileBug = '<a href="/report/%s">File Bug</a> > ' % report + title = self.get_scalar_field('title') + description = self.get_scalar_field('description') + + res,message = self.submit_bug(c) + + if res: + statusClass = 'SubmitOk' + statusName = 'Succeeded' + else: + statusClass = 'SubmitFail' + statusName = 'Failed' + + result = """ +<head> + <title>Bug Submission</title> + <link rel="stylesheet" type="text/css" href="/scanview.css" /> +</head> +<body> +<h3> +<a href="/">Summary</a> > +%(reportingFor)s +%(fileBug)s +Submit</h3> +<form name="form" action=""> +<table class="form"> +<tr><td> +<table class="form_group"> +<tr> + <td class="form_clabel">Title:</td> + <td class="form_value"> + <input type="text" name="title" size="50" value="%(title)s" disabled> + </td> +</tr> +<tr> + <td class="form_label">Description:</td> + <td class="form_value"> +<textarea rows="10" cols="80" name="description" disabled> +%(description)s +</textarea> + </td> +</table> +</td></tr> +</table> +</form> +<h1 class="%(statusClass)s">Submission %(statusName)s</h1> +%(message)s +<p> +<hr> +<a href="/">Return to Summary</a> +</body> +</html>"""%locals() + return self.send_string(result) + + def send_open_report(self, report): + try: + keys = self.load_report(report) + except IOError: + return self.send_error(400, 'Invalid report.') + + file = keys.get('FILE') + if not file or not posixpath.exists(file): + return self.send_error(400, 'File does not exist: "%s"' % file) + + import startfile + if self.server.options.debug: + print >>sys.stderr, '%s: SERVER: opening "%s"'%(sys.argv[0], + file) + + status = startfile.open(file) + if status: + res = 'Opened: "%s"' % file + else: + res = 'Open failed: "%s"' % file + + return self.send_string(res, 'text/plain') + + def get_report_context(self, report): + class Context: + pass + if report is None or report == 'None': + data = self.load_crashes() + # Don't allow empty reports. + if not data: + raise ValueError, 'No crashes detected!' + c = Context() + c.title = 'clang static analyzer failures' + + stderrSummary = "" + for item in data: + if 'stderr' in item: + path = posixpath.join(self.server.root, item['stderr']) + if os.path.exists(path): + lns = itertools.islice(open(path), 0, 10) + stderrSummary += '%s\n--\n%s' % (item.get('src', + '<unknown>'), + ''.join(lns)) + + c.description = """\ +The clang static analyzer failed on these inputs: +%s + +STDERR Summary +-------------- +%s +""" % ('\n'.join([item.get('src','<unknown>') for item in data]), + stderrSummary) + c.reportSource = None + c.navMarkup = "Report Crashes > " + c.files = [] + for item in data: + c.files.append(item.get('src','')) + c.files.append(posixpath.join(self.server.root, + item.get('file',''))) + c.files.append(posixpath.join(self.server.root, + item.get('clangfile',''))) + c.files.append(posixpath.join(self.server.root, + item.get('stderr',''))) + c.files.append(posixpath.join(self.server.root, + item.get('info',''))) + # Just in case something failed, ignore files which don't + # exist. + c.files = [f for f in c.files + if os.path.exists(f) and os.path.isfile(f)] + else: + # Check that this is a valid report. + path = posixpath.join(self.server.root, 'report-%s.html' % report) + if not posixpath.exists(path): + raise ValueError, 'Invalid report ID' + keys = self.load_report(report) + c = Context() + c.title = keys.get('DESC','clang error (unrecognized') + c.description = """\ +Bug reported by the clang static analyzer. + +Description: %s +File: %s +Line: %s +"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) + c.reportSource = 'report-%s.html' % report + c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, + report) + + c.files = [path] + return c + + def send_report(self, report, configOverrides=None): + def getConfigOption(section, field): + if (configOverrides is not None and + section in configOverrides and + field in configOverrides[section]): + return configOverrides[section][field] + return self.server.config.get(section, field) + + # report is None is used for crashes + try: + c = self.get_report_context(report) + except ValueError, e: + return self.send_error(400, e.message) + + title = c.title + description= c.description + reportingFor = c.navMarkup + if c.reportSource is None: + extraIFrame = "" + else: + extraIFrame = """\ +<iframe src="/%s" width="100%%" height="40%%" + scrolling="auto" frameborder="1"> + <a href="/%s">View Bug Report</a> +</iframe>""" % (c.reportSource, c.reportSource) + + reporterSelections = [] + reporterOptions = [] + + try: + active = int(getConfigOption('ScanView','reporter')) + except: + active = 0 + for i,r in enumerate(self.server.reporters): + selected = (i == active) + if selected: + selectedStr = ' selected' + else: + selectedStr = '' + reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) + options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) + display = ('none','')[selected] + reporterOptions.append("""\ +<tr id="%sReporterOptions" style="display:%s"> + <td class="form_label">%s Options</td> + <td class="form_value"> + <table class="form_inner_group"> +%s + </table> + </td> +</tr> +"""%(r.getName(),display,r.getName(),options)) + reporterSelections = '\n'.join(reporterSelections) + reporterOptionsDivs = '\n'.join(reporterOptions) + reportersArray = '[%s]'%(','.join([`r.getName()` for r in self.server.reporters])) + + if c.files: + fieldSize = min(5, len(c.files)) + attachFileOptions = '\n'.join(["""\ +<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) + attachFileRow = """\ +<tr> + <td class="form_label">Attach:</td> + <td class="form_value"> +<select style="width:100%%" name="files" multiple size=%d> +%s +</select> + </td> +</tr> +""" % (min(5, len(c.files)), attachFileOptions) + else: + attachFileRow = "" + + result = """<html> +<head> + <title>File Bug</title> + <link rel="stylesheet" type="text/css" href="/scanview.css" /> +</head> +<script language="javascript" type="text/javascript"> +var reporters = %(reportersArray)s; +function updateReporterOptions() { + index = document.getElementById('reporter').selectedIndex; + for (var i=0; i < reporters.length; ++i) { + o = document.getElementById(reporters[i] + "ReporterOptions"); + if (i == index) { + o.style.display = ""; + } else { + o.style.display = "none"; + } + } +} +</script> +<body onLoad="updateReporterOptions()"> +<h3> +<a href="/">Summary</a> > +%(reportingFor)s +File Bug</h3> +<form name="form" action="/report_submit" method="post"> +<input type="hidden" name="report" value="%(report)s"> + +<table class="form"> +<tr><td> +<table class="form_group"> +<tr> + <td class="form_clabel">Title:</td> + <td class="form_value"> + <input type="text" name="title" size="50" value="%(title)s"> + </td> +</tr> +<tr> + <td class="form_label">Description:</td> + <td class="form_value"> +<textarea rows="10" cols="80" name="description"> +%(description)s +</textarea> + </td> +</tr> + +%(attachFileRow)s + +</table> +<br> +<table class="form_group"> +<tr> + <td class="form_clabel">Method:</td> + <td class="form_value"> + <select id="reporter" name="reporter" onChange="updateReporterOptions()"> + %(reporterSelections)s + </select> + </td> +</tr> +%(reporterOptionsDivs)s +</table> +<br> +</td></tr> +<tr><td class="form_submit"> + <input align="right" type="submit" name="Submit" value="Submit"> +</td></tr> +</table> +</form> + +%(extraIFrame)s + +</body> +</html>"""%locals() + + return self.send_string(result) + + def send_head(self, fields=None): + if (self.server.options.onlyServeLocal and + self.client_address[0] != '127.0.0.1'): + return self.send_error(401, 'Unauthorized host.') + + if fields is None: + fields = {} + self.fields = fields + + o = urlparse.urlparse(self.path) + self.fields = parse_query(o.query, fields) + path = posixpath.normpath(urllib.unquote(o.path)) + + # Split the components and strip the root prefix. + components = path.split('/')[1:] + + # Special case some top-level entries. + if components: + name = components[0] + if len(components)==2: + if name=='report': + return self.send_report(components[1]) + elif name=='open': + return self.send_open_report(components[1]) + elif len(components)==1: + if name=='quit': + self.server.halt() + return self.send_string('Goodbye.', 'text/plain') + elif name=='report_submit': + return self.send_report_submit() + elif name=='report_crashes': + overrides = { 'ScanView' : {}, + 'Radar' : {}, + 'Email' : {} } + for i,r in enumerate(self.server.reporters): + if r.getName() == 'Radar': + overrides['ScanView']['reporter'] = i + break + overrides['Radar']['Component'] = 'llvm - checker' + overrides['Radar']['Component Version'] = 'X' + return self.send_report(None, overrides) + elif name=='favicon.ico': + return self.send_path(posixpath.join(kResources,'bugcatcher.ico')) + + # Match directory entries. + if components[-1] == '': + components[-1] = 'index.html' + + suffix = '/'.join(components) + + # The summary may reference source files on disk using rooted + # paths. Make sure these resolve correctly for now. + # FIXME: This isn't a very good idea... we should probably + # mark rooted paths somehow. + if os.path.exists(posixpath.join('/', suffix)): + path = posixpath.join('/', suffix) + else: + path = posixpath.join(self.server.root, suffix) + + if self.server.options.debug > 1: + print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0], + path) + return self.send_path(path) + + def send_404(self): + self.send_error(404, "File not found") + return None + + def send_path(self, path): + ctype = self.guess_type(path) + if ctype.startswith('text/'): + # Patch file instead + return self.send_patched_file(path, ctype) + else: + mode = 'rb' + try: + f = open(path, mode) + except IOError: + return self.send_404() + return self.send_file(f, ctype) + + def send_file(self, f, ctype): + # Patch files to add links, but skip binary files. + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + return f + + def send_string(self, s, ctype='text/html', headers=True, mtime=None): + if headers: + self.send_response(200) + self.send_header("Content-type", ctype) + self.send_header("Content-Length", str(len(s))) + if mtime is None: + mtime = self.dynamic_mtime + self.send_header("Last-Modified", self.date_time_string(mtime)) + self.end_headers() + return StringIO.StringIO(s) + + def send_patched_file(self, path, ctype): + # Allow a very limited set of variables. This is pretty gross. + variables = {} + variables['report'] = '' + m = kReportFileRE.match(path) + if m: + variables['report'] = m.group(2) + + try: + f = open(path,'r') + except IOError: + return self.send_404() + fs = os.fstat(f.fileno()) + data = f.read() + for a,b in kReportReplacements: + data = a.sub(b % variables, data) + return self.send_string(data, ctype, mtime=fs.st_mtime) + + +def create_server(address, options, root): + import Reporter + + reporters = Reporter.getReporters() + + return ScanViewServer(address, ScanViewRequestHandler, + root, + reporters, + options) diff --git a/clang/tools/scan-view/scan-view b/clang/tools/scan-view/scan-view new file mode 100755 index 0000000..fb27da6 --- /dev/null +++ b/clang/tools/scan-view/scan-view @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +"""The clang static analyzer results viewer. +""" + +import sys +import posixpath +import thread +import time +import urllib +import webbrowser + +# How long to wait for server to start. +kSleepTimeout = .05 +kMaxSleeps = int(60 / kSleepTimeout) + +# Default server parameters + +kDefaultHost = '127.0.0.1' +kDefaultPort = 8181 +kMaxPortsToTry = 100 + +### + +def url_is_up(url): + try: + o = urllib.urlopen(url) + except IOError: + return False + o.close() + return True + +def start_browser(port, options): + import urllib, webbrowser + + url = 'http://%s:%d'%(options.host, port) + + # Wait for server to start... + if options.debug: + sys.stderr.write('%s: Waiting for server.' % sys.argv[0]) + sys.stderr.flush() + for i in range(kMaxSleeps): + if url_is_up(url): + break + if options.debug: + sys.stderr.write('.') + sys.stderr.flush() + time.sleep(kSleepTimeout) + else: + print >>sys.stderr,'WARNING: Unable to detect that server started.' + + if options.debug: + print >>sys.stderr,'%s: Starting webbrowser...' % sys.argv[0] + webbrowser.open(url) + +def run(port, options, root): + import ScanView + try: + print 'Starting scan-view at: http://%s:%d'%(options.host, + port) + print ' Use Ctrl-C to exit.' + httpd = ScanView.create_server((options.host, port), + options, root) + httpd.serve_forever() + except KeyboardInterrupt: + pass + +def port_is_open(port): + import SocketServer + try: + t = SocketServer.TCPServer((kDefaultHost,port),None) + except: + return False + t.server_close() + return True + +def main(): + from optparse import OptionParser + parser = OptionParser('usage: %prog [options] <results directory>') + parser.set_description(__doc__) + parser.add_option( + '--host', dest="host", default=kDefaultHost, type="string", + help="Host interface to listen on. (default=%s)" % kDefaultHost) + parser.add_option( + '--port', dest="port", default=None, type="int", + help="Port to listen on. (default=%s)" % kDefaultPort) + parser.add_option("--debug", dest="debug", default=0, + action="count", + help="Print additional debugging information.") + parser.add_option("--auto-reload", dest="autoReload", default=False, + action="store_true", + help="Automatically update module for each request.") + parser.add_option("--no-browser", dest="startBrowser", default=True, + action="store_false", + help="Don't open a webbrowser on startup.") + parser.add_option("--allow-all-hosts", dest="onlyServeLocal", default=True, + action="store_false", + help='Allow connections from any host (access restricted to "127.0.0.1" by default)') + (options, args) = parser.parse_args() + + if len(args) != 1: + parser.error('No results directory specified.') + root, = args + + # Make sure this directory is in a reasonable state to view. + if not posixpath.exists(posixpath.join(root,'index.html')): + parser.error('Invalid directory, analysis results not found!') + + # Find an open port. We aren't particularly worried about race + # conditions here. Note that if the user specified a port we only + # use that one. + if options.port is not None: + port = options.port + else: + for i in range(kMaxPortsToTry): + if port_is_open(kDefaultPort + i): + port = kDefaultPort + i + break + else: + parser.error('Unable to find usable port in [%d,%d)'%(kDefaultPort, + kDefaultPort+kMaxPortsToTry)) + + # Kick off thread to wait for server and start web browser, if + # requested. + if options.startBrowser: + t = thread.start_new_thread(start_browser, (port,options)) + + run(port, options, root) + +if __name__ == '__main__': + main() diff --git a/clang/tools/scan-view/startfile.py b/clang/tools/scan-view/startfile.py new file mode 100644 index 0000000..e8fbe2d --- /dev/null +++ b/clang/tools/scan-view/startfile.py @@ -0,0 +1,203 @@ +"""Utility for opening a file using the default application in a cross-platform +manner. Modified from http://code.activestate.com/recipes/511443/. +""" + +__version__ = '1.1x' +__all__ = ['open'] + +import os +import sys +import webbrowser +import subprocess + +_controllers = {} +_open = None + + +class BaseController(object): + '''Base class for open program controllers.''' + + def __init__(self, name): + self.name = name + + def open(self, filename): + raise NotImplementedError + + +class Controller(BaseController): + '''Controller for a generic open program.''' + + def __init__(self, *args): + super(Controller, self).__init__(os.path.basename(args[0])) + self.args = list(args) + + def _invoke(self, cmdline): + if sys.platform[:3] == 'win': + closefds = False + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + else: + closefds = True + startupinfo = None + + if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or + sys.platform == 'darwin'): + inout = file(os.devnull, 'r+') + else: + # for TTY programs, we need stdin/out + inout = None + + # if possible, put the child precess in separate process group, + # so keyboard interrupts don't affect child precess as well as + # Python + setsid = getattr(os, 'setsid', None) + if not setsid: + setsid = getattr(os, 'setpgrp', None) + + pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout, + stderr=inout, close_fds=closefds, + preexec_fn=setsid, startupinfo=startupinfo) + + # It is assumed that this kind of tools (gnome-open, kfmclient, + # exo-open, xdg-open and open for OSX) immediately exit after lauching + # the specific application + returncode = pipe.wait() + if hasattr(self, 'fixreturncode'): + returncode = self.fixreturncode(returncode) + return not returncode + + def open(self, filename): + if isinstance(filename, basestring): + cmdline = self.args + [filename] + else: + # assume it is a sequence + cmdline = self.args + filename + try: + return self._invoke(cmdline) + except OSError: + return False + + +# Platform support for Windows +if sys.platform[:3] == 'win': + + class Start(BaseController): + '''Controller for the win32 start progam through os.startfile.''' + + def open(self, filename): + try: + os.startfile(filename) + except WindowsError: + # [Error 22] No application is associated with the specified + # file for this operation: '<URL>' + return False + else: + return True + + _controllers['windows-default'] = Start('start') + _open = _controllers['windows-default'].open + + +# Platform support for MacOS +elif sys.platform == 'darwin': + _controllers['open']= Controller('open') + _open = _controllers['open'].open + + +# Platform support for Unix +else: + + import commands + + # @WARNING: use the private API of the webbrowser module + from webbrowser import _iscommand + + class KfmClient(Controller): + '''Controller for the KDE kfmclient program.''' + + def __init__(self, kfmclient='kfmclient'): + super(KfmClient, self).__init__(kfmclient, 'exec') + self.kde_version = self.detect_kde_version() + + def detect_kde_version(self): + kde_version = None + try: + info = commands.getoutput('kde-config --version') + + for line in info.splitlines(): + if line.startswith('KDE'): + kde_version = line.split(':')[-1].strip() + break + except (OSError, RuntimeError): + pass + + return kde_version + + def fixreturncode(self, returncode): + if returncode is not None and self.kde_version > '3.5.4': + return returncode + else: + return os.EX_OK + + def detect_desktop_environment(): + '''Checks for known desktop environments + + Return the desktop environments name, lowercase (kde, gnome, xfce) + or "generic" + + ''' + + desktop_environment = 'generic' + + if os.environ.get('KDE_FULL_SESSION') == 'true': + desktop_environment = 'kde' + elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): + desktop_environment = 'gnome' + else: + try: + info = commands.getoutput('xprop -root _DT_SAVE_MODE') + if ' = "xfce4"' in info: + desktop_environment = 'xfce' + except (OSError, RuntimeError): + pass + + return desktop_environment + + + def register_X_controllers(): + if _iscommand('kfmclient'): + _controllers['kde-open'] = KfmClient() + + for command in ('gnome-open', 'exo-open', 'xdg-open'): + if _iscommand(command): + _controllers[command] = Controller(command) + + def get(): + controllers_map = { + 'gnome': 'gnome-open', + 'kde': 'kde-open', + 'xfce': 'exo-open', + } + + desktop_environment = detect_desktop_environment() + + try: + controller_name = controllers_map[desktop_environment] + return _controllers[controller_name].open + + except KeyError: + if _controllers.has_key('xdg-open'): + return _controllers['xdg-open'].open + else: + return webbrowser.open + + + if os.environ.get("DISPLAY"): + register_X_controllers() + _open = get() + + +def open(filename): + '''Open a file or an URL in the registered default application.''' + + return _open(filename) |