]> granicus.if.org Git - clang/commitdiff
Implement a diagnostics callback for the C interface to Clang, so that
authorDouglas Gregor <dgregor@apple.com>
Thu, 28 Jan 2010 00:27:43 +0000 (00:27 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 28 Jan 2010 00:27:43 +0000 (00:27 +0000)
clients can format diagnostics as they wish rather than having to
parse standard error. All of the important parts of the front end's
diagnostics are exposed: text, severity, location, source ranges, and
fix-its. The diagnostics callback is now available with
clang_createTranslationUnitFromSource() and
clang_createTranslationUnit().

As part of this change, CXSourceLocation and CXSourceRange got one
pointer larger, since we need to hold on to the SourceManager and
LangOptions structures in the source location. This is the minimum
amount of information needed for the functions that operate on source
locations and ranges (as implemented now). Previously we held on to
the ASTContext, but the diagnostics callback can end up with source
locations when there is no ASTContext (or preprocessor).

Still to do:
  - Code completion needs to support the diagnostics callback, once we
  have the ability to (de-)serialize diagnostics.
  - Eliminate the "displayDiagnostics" argument to createIndex; we'll
  always pass diagnostics to the callback and let it deal with display.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94709 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang-c/Index.h
tools/CIndex/CIndex.cpp
tools/CIndex/CIndex.exports
tools/CIndex/CIndexDiagnostic.cpp [new file with mode: 0644]
tools/CIndex/CIndexDiagnostic.h [new file with mode: 0644]
tools/CIndex/CIndexer.h
tools/CIndex/CMakeLists.txt
tools/CIndex/CXSourceLocation.h
tools/c-index-test/c-index-test.c

index 0470592a0fdd6c89542a6b57c3d6fe5ca227c246..e26dff1eb18a5b0f7bbada1c618a465b79948bb4 100644 (file)
@@ -177,12 +177,6 @@ CINDEX_LINKAGE CXIndex clang_createIndex(int excludeDeclarationsFromPCH,
                           int displayDiagnostics);
 CINDEX_LINKAGE void clang_disposeIndex(CXIndex index);
 
-/**
- * \brief Get the original translation unit source file name.
- */
-CINDEX_LINKAGE CXString
-clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit);
-
 /**
  * \brief Request that AST's be generated externally for API calls which parse
  * source code on the fly, e.g. \see createTranslationUnitFromSourceFile.
@@ -195,54 +189,6 @@ clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit);
  */
 CINDEX_LINKAGE void clang_setUseExternalASTGeneration(CXIndex index,
                                                       int value);
-
-/**
- * \brief Create a translation unit from an AST file (-emit-ast).
- */
-CINDEX_LINKAGE CXTranslationUnit clang_createTranslationUnit(
-  CXIndex, const char *ast_filename
-);
-
-/**
- * \brief Destroy the specified CXTranslationUnit object.
- */
-CINDEX_LINKAGE void clang_disposeTranslationUnit(CXTranslationUnit);
-
-/**
- * \brief Return the CXTranslationUnit for a given source file and the provided
- * command line arguments one would pass to the compiler.
- *
- * Note: The 'source_filename' argument is optional.  If the caller provides a
- * NULL pointer, the name of the source file is expected to reside in the
- * specified command line arguments.
- *
- * Note: When encountered in 'clang_command_line_args', the following options
- * are ignored:
- *
- *   '-c'
- *   '-emit-ast'
- *   '-fsyntax-only'
- *   '-o <output file>'  (both '-o' and '<output file>' are ignored)
- *
- *
- * \param source_filename - The name of the source file to load, or NULL if the
- * source file is included in clang_command_line_args.
- *
- * \param num_unsaved_files the number of unsaved file entries in \p
- * unsaved_files.
- *
- * \param unsaved_files the files that have not yet been saved to disk
- * but may be required for code completion, including the contents of
- * those files.
- */
-CINDEX_LINKAGE CXTranslationUnit clang_createTranslationUnitFromSourceFile(
-                                        CXIndex CIdx,
-                                        const char *source_filename,
-                                        int num_clang_command_line_args,
-                                        const char **clang_command_line_args,
-                                        unsigned num_unsaved_files,
-                                        struct CXUnsavedFile *unsaved_files);
-
 /**
  * \defgroup CINDEX_FILES File manipulation routines
  *
@@ -303,7 +249,7 @@ CINDEX_LINKAGE CXFile clang_getFile(CXTranslationUnit tu,
  * particular file, line, and column.
  */
 typedef struct {
-  void *ptr_data;
+  void *ptr_data[2];
   unsigned int_data;
 } CXSourceLocation;
 
@@ -314,7 +260,7 @@ typedef struct {
  * starting and end locations from a source range, respectively.
  */
 typedef struct {
-  void *ptr_data;
+  void *ptr_data[2];
   unsigned begin_int_data;
   unsigned end_int_data;
 } CXSourceRange;
@@ -344,6 +290,11 @@ CINDEX_LINKAGE CXSourceLocation clang_getLocation(CXTranslationUnit tu,
                                                   unsigned line,
                                                   unsigned column);
 
+/**
+ * \brief Retrieve a NULL (invalid) source range.
+ */
+CINDEX_LINKAGE CXSourceRange clang_getNullRange();
+  
 /**
  * \brief Retrieve a source range given the beginning and ending source
  * locations.
@@ -392,6 +343,285 @@ CINDEX_LINKAGE CXSourceLocation clang_getRangeEnd(CXSourceRange range);
  * @}
  */
 
+/**
+ * \defgroup CINDEX_DIAG Diagnostic reporting
+ *
+ * @{
+ */
+
+/**
+ * \brief Describes the severity of a particular diagnostic.
+ */
+enum CXDiagnosticSeverity {
+  /**
+   * \brief A diagnostic that has been suppressed, e.g., by a command-line 
+   * option.
+   */
+  CXDiagnostic_Ignored = 0,
+  
+  /**
+   * \brief This diagnostic is a note that should be attached to the
+   * previous (non-note) diagnostic.
+   */
+  CXDiagnostic_Note    = 1,
+
+  /**
+   * \brief This diagnostic indicates suspicious code that may not be
+   * wrong.
+   */
+  CXDiagnostic_Warning = 2,
+
+  /**
+   * \brief This diagnostic indicates that the code is ill-formed.
+   */
+  CXDiagnostic_Error   = 3,
+
+  /**
+   * \brief This diagnostic indicates that the code is ill-formed such
+   * that future parser recovery is unlikely to produce useful
+   * results.
+   */
+  CXDiagnostic_Fatal   = 4
+};
+
+/**
+ * \brief Describes the kind of fix-it hint expressed within a
+ * diagnostic.
+ */
+enum CXFixItKind {
+  /**
+   * \brief A fix-it hint that inserts code at a particular position.
+   */
+  CXFixIt_Insertion   = 0,
+
+  /**
+   * \brief A fix-it hint that removes code within a range.
+   */
+  CXFixIt_Removal     = 1,
+
+  /**
+   * \brief A fix-it hint that replaces the code within a range with another
+   * string.
+   */
+  CXFixIt_Replacement = 2
+};
+
+/**
+ * \brief A single diagnostic, containing the diagnostic's severity,
+ * location, text, source ranges, and fix-it hints.
+ */
+typedef void *CXDiagnostic;
+
+/**
+ * \brief Callback function invoked for each diagnostic emitted during
+ * translation.
+ *
+ * \param Diagnostic the diagnostic emitted during translation. This
+ * diagnostic pointer is only valid during the execution of the
+ * callback.
+ *
+ * \param ClientData the callback client data.
+ */
+typedef void (*CXDiagnosticCallback)(CXDiagnostic Diagnostic,
+                                     CXClientData ClientData);
+
+/**
+ * \brief Determine the severity of the given diagnostic.
+ */
+CINDEX_LINKAGE enum CXDiagnosticSeverity 
+clang_getDiagnosticSeverity(CXDiagnostic);
+
+/**
+ * \brief Retrieve the source location of the given diagnostic.
+ *
+ * This location is where Clang would print the caret ('^') when
+ * displaying the diagnostic on the command line.
+ */
+CINDEX_LINKAGE CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic);
+
+/**
+ * \brief Retrieve the text of the given diagnostic.
+ */
+CINDEX_LINKAGE CXString clang_getDiagnosticSpelling(CXDiagnostic);
+  
+/**
+ * \brief Retrieve the source ranges associated with the diagnostic.
+ *
+ * These source ranges highlight important elements in the source
+ * code. On the command line, Clang displays source ranges by
+ * underlining them with '~' characters. 
+ *
+ * \param Diagnostic the diagnostic whose ranges are being extracted.
+ *
+ * \param Ranges [out] will be set to a newly-allocated array
+ * containing the source ranges of this diagnostic. These ranges must
+ * be freed with \c clang_disposeDiagnosticRanges().
+ *
+ * \param NumRanges [out] will be set to the number of source ranges
+ * in the \p Ranges array.
+ */
+CINDEX_LINKAGE void clang_getDiagnosticRanges(CXDiagnostic Diagnostic, 
+                                              CXSourceRange **Ranges, 
+                                              unsigned *NumRanges);
+
+/**
+ * \brief Free the source ranges returned by \c clang_getDiagnosticRanges().
+ */
+CINDEX_LINKAGE void clang_disposeDiagnosticRanges(CXSourceRange *Ranges, 
+                                                  unsigned NumRanges);
+
+/**
+ * \brief Determine the number of fix-it hints associated with the
+ * given diagnostic.
+ */
+CINDEX_LINKAGE unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diagnostic);
+
+/**
+ * \brief Retrieve the kind of the given fix-it.
+ *
+ * \param Diagnostic the diagnostic whose fix-its are being queried.
+ *
+ * \param FixIt the zero-based index of the fix-it to query.
+ */
+CINDEX_LINKAGE enum CXFixItKind 
+clang_getDiagnosticFixItKind(CXDiagnostic Diagnostic, unsigned FixIt);
+
+/**
+ * \brief Retrieve the insertion information for an insertion fix-it.
+ *
+ * For a fix-it that describes an insertion into a text buffer,
+ * retrieve the source location where the text should be inserted and
+ * the text to be inserted.
+ *
+ * \param Diagnostic the diagnostic whose fix-its are being queried.
+ *
+ * \param FixIt the zero-based index of the insertion fix-it.
+ *
+ * \param Location will be set to the location where text should be
+ * inserted.
+ *
+ * \returns the text string to insert at the given location.
+ */
+CINDEX_LINKAGE CXString
+clang_getDiagnosticFixItInsertion(CXDiagnostic Diagnostic, unsigned FixIt,
+                                  CXSourceLocation *Location);
+
+/**
+ * \brief Retrieve the removal information for a removal fix-it.
+ *
+ * For a fix-it that describes a removal from a text buffer, retrieve
+ * the source range that should be removed.
+ *
+ * \param Diagnostic the diagnostic whose fix-its are being queried.
+ *
+ * \param FixIt the zero-based index of the removal fix-it.
+ *
+ * \returns a source range describing the text that should be removed
+ * from the buffer.
+ */
+CINDEX_LINKAGE CXSourceRange
+clang_getDiagnosticFixItRemoval(CXDiagnostic Diagnostic, unsigned FixIt);
+
+/**
+ * \brief Retrieve the replacement information for an replacement fix-it.
+ *
+ * For a fix-it that describes replacement of text in the text buffer
+ * with alternative text.
+ *
+ * \param Diagnostic the diagnostic whose fix-its are being queried.
+ *
+ * \param FixIt the zero-based index of the replacement fix-it.
+ *
+ * \param Range will be set to the source range whose text should be
+ * replaced with the returned text.
+ *
+ * \returns the text string to use as replacement text.
+ */
+CINDEX_LINKAGE CXString
+clang_getDiagnosticFixItReplacement(CXDiagnostic Diagnostic, unsigned FixIt,
+                                    CXSourceRange *Range);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_TRANSLATION_UNIT Translation unit manipulation
+ *
+ * The routines in this group provide the ability to create and destroy
+ * translation units from files, either by parsing the contents of the files or
+ * by reading in a serialized representation of a translation unit.
+ *
+ * @{
+ */
+  
+/**
+ * \brief Get the original translation unit source file name.
+ */
+CINDEX_LINKAGE CXString
+clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit);
+
+/**
+ * \brief Return the CXTranslationUnit for a given source file and the provided
+ * command line arguments one would pass to the compiler.
+ *
+ * Note: The 'source_filename' argument is optional.  If the caller provides a
+ * NULL pointer, the name of the source file is expected to reside in the
+ * specified command line arguments.
+ *
+ * Note: When encountered in 'clang_command_line_args', the following options
+ * are ignored:
+ *
+ *   '-c'
+ *   '-emit-ast'
+ *   '-fsyntax-only'
+ *   '-o <output file>'  (both '-o' and '<output file>' are ignored)
+ *
+ *
+ * \param source_filename - The name of the source file to load, or NULL if the
+ * source file is included in clang_command_line_args.
+ *
+ * \param num_unsaved_files the number of unsaved file entries in \p
+ * unsaved_files.
+ *
+ * \param unsaved_files the files that have not yet been saved to disk
+ * but may be required for code completion, including the contents of
+ * those files.
+ *
+ * \param diag_callback callback function that will receive any diagnostics
+ * emitted while processing this source file. If NULL, diagnostics will be
+ * suppressed.
+ *
+ * \param diag_client_data client data that will be passed to the diagnostic
+ * callback function.
+ */
+CINDEX_LINKAGE CXTranslationUnit clang_createTranslationUnitFromSourceFile(
+                                         CXIndex CIdx,
+                                         const char *source_filename,
+                                         int num_clang_command_line_args,
+                                         const char **clang_command_line_args,
+                                         unsigned num_unsaved_files,
+                                         struct CXUnsavedFile *unsaved_files,
+                                         CXDiagnosticCallback diag_callback,
+                                         CXClientData diag_client_data);
+/**
+ * \brief Create a translation unit from an AST file (-emit-ast).
+ */
+CINDEX_LINKAGE CXTranslationUnit clang_createTranslationUnit(CXIndex, 
+                                             const char *ast_filename,
+                                             CXDiagnosticCallback diag_callback,
+                                             CXClientData diag_client_data);
+
+/**
+ * \brief Destroy the specified CXTranslationUnit object.
+ */
+CINDEX_LINKAGE void clang_disposeTranslationUnit(CXTranslationUnit);
+/**
+ * @}
+ */
+  
 /**
  * \brief Describes the kind of entity that a cursor refers to.
  */
index 10099ef0aa902ca1fd6c336815a506c1b3229d35..336a638b3aa9a26dad6d6f8c5b604c00ab2e1366 100644 (file)
@@ -15,6 +15,7 @@
 #include "CIndexer.h"
 #include "CXCursor.h"
 #include "CXSourceLocation.h"
+#include "CIndexDiagnostic.h"
 
 #include "clang/Basic/Version.h"
 #include "clang/AST/DeclVisitor.h"
@@ -913,13 +914,21 @@ void clang_setUseExternalASTGeneration(CXIndex CIdx, int value) {
   CXXIdx->setUseExternalASTGeneration(value);
 }
 
-// FIXME: need to pass back error info.
 CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx,
-                                              const char *ast_filename) {
+                                              const char *ast_filename,
+                                             CXDiagnosticCallback diag_callback,
+                                              CXClientData diag_client_data) {
   assert(CIdx && "Passed null CXIndex");
   CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx);
 
-  return ASTUnit::LoadFromPCHFile(ast_filename, CXXIdx->getDiags(),
+  // Configure the diagnostics.
+  DiagnosticOptions DiagOpts;
+  llvm::OwningPtr<Diagnostic> Diags;
+  Diags.reset(CompilerInstance::createDiagnostics(DiagOpts, 0, 0));
+  CIndexDiagnosticClient DiagClient(diag_callback, diag_client_data);
+  Diags->setClient(&DiagClient);
+  
+  return ASTUnit::LoadFromPCHFile(ast_filename, *Diags,
                                   CXXIdx->getOnlyLocalDecls(),
                                   /* UseBumpAllocator = */ true);
 }
@@ -930,10 +939,19 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx,
                                           int num_command_line_args,
                                           const char **command_line_args,
                                           unsigned num_unsaved_files,
-                                          struct CXUnsavedFile *unsaved_files) {
+                                          struct CXUnsavedFile *unsaved_files,
+                                          CXDiagnosticCallback diag_callback,
+                                          CXClientData diag_client_data) {
   assert(CIdx && "Passed null CXIndex");
   CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx);
 
+  // Configure the diagnostics.
+  DiagnosticOptions DiagOpts;
+  llvm::OwningPtr<Diagnostic> Diags;
+  Diags.reset(CompilerInstance::createDiagnostics(DiagOpts, 0, 0));
+  CIndexDiagnosticClient DiagClient(diag_callback, diag_client_data);
+  Diags->setClient(&DiagClient);
+                               
   llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;
   for (unsigned I = 0; I != num_unsaved_files; ++I) {
     const llvm::MemoryBuffer *Buffer 
@@ -955,7 +973,7 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx,
     Args.insert(Args.end(), command_line_args,
                 command_line_args + num_command_line_args);
 
-    unsigned NumErrors = CXXIdx->getDiags().getNumErrors();
+    unsigned NumErrors = Diags->getNumErrors();
     
 #ifdef USE_CRASHTRACER
     ArgsCrashTracerInfo ACTI(Args);
@@ -963,7 +981,7 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx,
     
     llvm::OwningPtr<ASTUnit> Unit(
       ASTUnit::LoadFromCommandLine(Args.data(), Args.data() + Args.size(),
-                                   CXXIdx->getDiags(),
+                                   *Diags, 
                                    CXXIdx->getClangResourcesPath(),
                                    CXXIdx->getOnlyLocalDecls(),
                                    /* UseBumpAllocator = */ true,
@@ -972,7 +990,7 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx,
     
     // FIXME: Until we have broader testing, just drop the entire AST if we
     // encountered an error.
-    if (NumErrors != CXXIdx->getDiags().getNumErrors())
+    if (NumErrors != Diags->getNumErrors())
       return 0;
 
     return Unit.take();
@@ -1050,7 +1068,7 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx,
     llvm::errs() << '\n';
   }
 
-  ASTUnit *ATU = ASTUnit::LoadFromPCHFile(astTmpFile, CXXIdx->getDiags(),
+  ASTUnit *ATU = ASTUnit::LoadFromPCHFile(astTmpFile, *Diags,
                                           CXXIdx->getOnlyLocalDecls(),
                                           /* UseBumpAllocator = */ true,
                                           RemappedFiles.data(),
@@ -1089,7 +1107,7 @@ CXCursor clang_getTranslationUnitCursor(CXTranslationUnit TU) {
 
 extern "C" {
 CXSourceLocation clang_getNullLocation() {
-  CXSourceLocation Result = { 0, 0 };
+  CXSourceLocation Result = { { 0, 0 }, 0 };
   return Result;
 }
 
@@ -1113,13 +1131,18 @@ CXSourceLocation clang_getLocation(CXTranslationUnit tu,
   return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc, false);
 }
 
+CXSourceRange clang_getNullRange() {
+  CXSourceRange Result = { { 0, 0 }, 0, 0 };
+  return Result;
+}
+  
 CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) {
-  if (begin.ptr_data != end.ptr_data) {
-    CXSourceRange Result = { 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, begin.int_data, end.int_data };
+  CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] }, 
+                           begin.int_data, end.int_data };
   return Result;
 }
 
@@ -1129,7 +1152,7 @@ void clang_getInstantiationLocation(CXSourceLocation location,
                                     unsigned *column,
                                     unsigned *offset) {
   cxloc::CXSourceLocationPtr Ptr
-    = cxloc::CXSourceLocationPtr::getFromOpaqueValue(location.ptr_data);
+    = cxloc::CXSourceLocationPtr::getFromOpaqueValue(location.ptr_data[0]);
   SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data);
 
   if (!Ptr.getPointer() || Loc.isInvalid()) {
@@ -1147,8 +1170,7 @@ void clang_getInstantiationLocation(CXSourceLocation location,
   // FIXME: This is largely copy-paste from
   ///TextDiagnosticPrinter::HighlightRange.  When it is clear that this is
   // what we want the two routines should be refactored.  
-  ASTContext &Context = *Ptr.getPointer();
-  SourceManager &SM = Context.getSourceManager();
+  const SourceManager &SM = *Ptr.getPointer();
   SourceLocation InstLoc = SM.getInstantiationLoc(Loc);
   
   if (Ptr.getInt()) {
@@ -1170,7 +1192,7 @@ void clang_getInstantiationLocation(CXSourceLocation location,
     // (CXXUnit), so that the preprocessor will be available here. At
     // that point, we can use Preprocessor::getLocForEndOfToken().
     unsigned Length = Lexer::MeasureTokenLength(InstLoc, SM, 
-                                                Context.getLangOptions());
+                             *static_cast<LangOptions *>(location.ptr_data[1]));
     if (Length > 0)
       InstLoc = InstLoc.getFileLocWithOffset(Length - 1);
   }
@@ -1186,15 +1208,17 @@ void clang_getInstantiationLocation(CXSourceLocation location,
 }
 
 CXSourceLocation clang_getRangeStart(CXSourceRange range) {
-  CXSourceLocation Result = { range.ptr_data, range.begin_int_data };
+  CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, 
+                              range.begin_int_data };
   return Result;
 }
 
 CXSourceLocation clang_getRangeEnd(CXSourceRange range) {
-  llvm::PointerIntPair<ASTContext *, 1, bool> Ptr;
-  Ptr.setPointer(static_cast<ASTContext *>(range.ptr_data));
+  cxloc::CXSourceLocationPtr Ptr;
+  Ptr.setPointer(static_cast<SourceManager *>(range.ptr_data[0]));
   Ptr.setInt(true);
-  CXSourceLocation Result = { Ptr.getOpaqueValue(), range.end_int_data };
+  CXSourceLocation Result = { { Ptr.getOpaqueValue(), range.ptr_data[1] },
+                              range.end_int_data };
   return Result;
 }
 
@@ -1505,10 +1529,8 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) {
     return cxloc::translateSourceLocation(getCursorContext(C), 
                                    getLocationFromExpr(getCursorExpr(C)));
 
-  if (!getCursorDecl(C)) {
-    CXSourceLocation empty = { 0, 0 };
-    return empty;
-  }
+  if (!getCursorDecl(C))
+    return clang_getNullLocation();
 
   Decl *D = getCursorDecl(C);
   SourceLocation Loc = D->getLocation();
@@ -1558,10 +1580,8 @@ CXSourceRange clang_getCursorExtent(CXCursor C) {
     return cxloc::translateSourceRange(getCursorContext(C), 
                                 getCursorStmt(C)->getSourceRange());
   
-  if (!getCursorDecl(C)) {
-    CXSourceRange empty = { 0, 0, 0 };
-    return empty;
-  }
+  if (!getCursorDecl(C))
+    return clang_getNullRange();
   
   Decl *D = getCursorDecl(C);
   return cxloc::translateSourceRange(D->getASTContext(), D->getSourceRange());
@@ -1939,10 +1959,8 @@ CXSourceLocation clang_getTokenLocation(CXTranslationUnit TU, CXToken CXTok) {
 
 CXSourceRange clang_getTokenExtent(CXTranslationUnit TU, CXToken CXTok) {
   ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU);
-  if (!CXXUnit) {
-    CXSourceRange Result = { 0, 0, 0 };
-    return Result;
-  }
+  if (!CXXUnit)
+    return clang_getNullRange();
   
   return cxloc::translateSourceRange(CXXUnit->getASTContext(), 
                         SourceLocation::getFromRawEncoding(CXTok.int_data[1]));
index 12e2788e4aff2b2afd93e970768691878c72f37e..8af1897faa3984cacb13c60093db70abd0cbf9e4 100644 (file)
@@ -4,6 +4,7 @@ _clang_createIndex
 _clang_createTranslationUnit
 _clang_createTranslationUnitFromSourceFile
 _clang_disposeCodeCompleteResults
+_clang_disposeDiagnosticRanges
 _clang_disposeIndex
 _clang_disposeString
 _clang_disposeTokens
@@ -25,6 +26,15 @@ _clang_getCursorReferenced
 _clang_getCursorSpelling
 _clang_getCursorUSR
 _clang_getDefinitionSpellingAndExtent
+_clang_getDiagnosticFixItInsertion
+_clang_getDiagnosticFixItKind
+_clang_getDiagnosticFixItRemoval
+_clang_getDiagnosticFixItReplacement
+_clang_getDiagnosticLocation
+_clang_getDiagnosticNumFixIts
+_clang_getDiagnosticRanges
+_clang_getDiagnosticSeverity
+_clang_getDiagnosticSpelling
 _clang_getFile
 _clang_getFileName
 _clang_getFileTime
@@ -33,6 +43,7 @@ _clang_getInstantiationLocation
 _clang_getLocation
 _clang_getNullCursor
 _clang_getNullLocation
+_clang_getNullRange
 _clang_getNumCompletionChunks
 _clang_getRange
 _clang_getRangeEnd
diff --git a/tools/CIndex/CIndexDiagnostic.cpp b/tools/CIndex/CIndexDiagnostic.cpp
new file mode 100644 (file)
index 0000000..9a21f38
--- /dev/null
@@ -0,0 +1,198 @@
+/*===-- 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 "CXSourceLocation.h"
+
+using namespace clang;
+using namespace clang::cxloc;
+
+//-----------------------------------------------------------------------------
+// Opaque data structures                        
+//-----------------------------------------------------------------------------
+namespace {
+  /// \brief The storage behind a CXDiagnostic
+  struct CXStoredDiagnostic {
+    /// \brief The translation unit this diagnostic came from.
+    const LangOptions &LangOpts;
+    
+    /// \brief The severity level of this diagnostic.
+    Diagnostic::Level Level;
+    
+    /// \brief A reference to the diagnostic information.
+    const DiagnosticInfo &Info;
+  };
+}
+
+//-----------------------------------------------------------------------------
+// CIndex Diagnostic Client                        
+//-----------------------------------------------------------------------------
+CIndexDiagnosticClient::~CIndexDiagnosticClient() { }
+
+void CIndexDiagnosticClient::BeginSourceFile(const LangOptions &LangOpts,
+                                             const Preprocessor *PP) {
+  this->LangOpts = LangOpts;
+}
+
+void CIndexDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel,
+                                              const DiagnosticInfo &Info) {
+  if (!Callback)
+    return;
+  
+  CXStoredDiagnostic Stored = { this->LangOpts, DiagLevel, Info };
+  Callback(&Stored, ClientData);
+}
+
+//-----------------------------------------------------------------------------
+// C Interface Routines                        
+//-----------------------------------------------------------------------------
+extern "C" {
+  
+enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag)
+    return CXDiagnostic_Ignored;
+  
+  switch (StoredDiag->Level) {
+  case Diagnostic::Ignored: return CXDiagnostic_Ignored;
+  case Diagnostic::Note:    return CXDiagnostic_Note;
+  case Diagnostic::Warning: return CXDiagnostic_Warning;
+  case Diagnostic::Error:   return CXDiagnostic_Error;
+  case Diagnostic::Fatal:   return CXDiagnostic_Fatal;
+  }
+  llvm_unreachable("Invalid diagnostic level");
+  return CXDiagnostic_Ignored;
+}
+  
+CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag || StoredDiag->Info.getLocation().isInvalid())
+    return clang_getNullLocation();
+  
+  return translateSourceLocation(StoredDiag->Info.getLocation().getManager(),
+                                 StoredDiag->LangOpts,
+                                 StoredDiag->Info.getLocation());
+}
+
+CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag)
+    return CIndexer::createCXString("");
+  
+  llvm::SmallString<64> Spelling;
+  StoredDiag->Info.FormatDiagnostic(Spelling);
+  return CIndexer::createCXString(Spelling.str(), true);
+}
+
+void clang_getDiagnosticRanges(CXDiagnostic Diag, 
+                               CXSourceRange **Ranges, 
+                               unsigned *NumRanges) {
+  if (Ranges) 
+    *Ranges = 0;
+  if (NumRanges)
+    *NumRanges = 0;
+  
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag || !Ranges || !NumRanges || 
+      !StoredDiag->Info.getNumRanges() || 
+      StoredDiag->Info.getLocation().isInvalid())
+    return;
+  
+  unsigned N = StoredDiag->Info.getNumRanges();
+  *Ranges = (CXSourceRange *)malloc(sizeof(CXSourceRange) * N);
+  *NumRanges = N;
+  for (unsigned I = 0; I != N; ++I)
+    (*Ranges)[I] = translateSourceRange(
+                                    StoredDiag->Info.getLocation().getManager(),
+                                        StoredDiag->LangOpts,
+                                        StoredDiag->Info.getRange(I));
+}
+
+void clang_disposeDiagnosticRanges(CXSourceRange *Ranges, 
+                                   unsigned NumRanges) {
+  free(Ranges);
+}
+
+unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag)
+    return 0;
+  
+  return StoredDiag->Info.getNumCodeModificationHints();
+}
+
+enum CXFixItKind clang_getDiagnosticFixItKind(CXDiagnostic Diag, 
+                                              unsigned FixIt) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag || FixIt >= StoredDiag->Info.getNumCodeModificationHints())
+    return CXFixIt_Insertion;
+  
+  const CodeModificationHint &Hint
+    = StoredDiag->Info.getCodeModificationHint(FixIt);
+  if (Hint.RemoveRange.isInvalid())
+    return CXFixIt_Insertion;
+  if (Hint.InsertionLoc.isInvalid())
+    return CXFixIt_Removal;
+  
+  return CXFixIt_Replacement;  
+}
+
+CXString clang_getDiagnosticFixItInsertion(CXDiagnostic Diag, 
+                                           unsigned FixIt,
+                                           CXSourceLocation *Location) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag || FixIt >= StoredDiag->Info.getNumCodeModificationHints())
+    return CIndexer::createCXString("");
+  
+  const CodeModificationHint &Hint
+    = StoredDiag->Info.getCodeModificationHint(FixIt);
+  return CIndexer::createCXString(Hint.CodeToInsert);
+}
+
+CXSourceRange clang_getDiagnosticFixItRemoval(CXDiagnostic Diag, 
+                                              unsigned FixIt) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag || FixIt >= StoredDiag->Info.getNumCodeModificationHints() ||
+      StoredDiag->Info.getLocation().isInvalid())
+    return clang_getNullRange();
+  
+  const CodeModificationHint &Hint
+    = StoredDiag->Info.getCodeModificationHint(FixIt);
+  return translateSourceRange(StoredDiag->Info.getLocation().getManager(),
+                              StoredDiag->LangOpts,
+                              Hint.RemoveRange);
+}
+
+CXString clang_getDiagnosticFixItReplacement(CXDiagnostic Diag, 
+                                             unsigned FixIt,
+                                             CXSourceRange *Range) {
+  CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag);
+  if (!StoredDiag || FixIt >= StoredDiag->Info.getNumCodeModificationHints() ||
+      StoredDiag->Info.getLocation().isInvalid()) {
+    if (Range)
+      *Range = clang_getNullRange();
+    
+    return CIndexer::createCXString("");
+  }
+  
+  const CodeModificationHint &Hint
+    = StoredDiag->Info.getCodeModificationHint(FixIt);
+  if (Range)
+    *Range = translateSourceRange(StoredDiag->Info.getLocation().getManager(),
+                                  StoredDiag->LangOpts,
+                                  Hint.RemoveRange);
+  return CIndexer::createCXString(Hint.CodeToInsert);  
+}
+  
+} // end extern "C"
diff --git a/tools/CIndex/CIndexDiagnostic.h b/tools/CIndex/CIndexDiagnostic.h
new file mode 100644 (file)
index 0000000..f962041
--- /dev/null
@@ -0,0 +1,49 @@
+/*===-- 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 "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+
+namespace clang {
+
+class Preprocessor;
+  
+/**
+ * \brief Diagnostic client that translates Clang diagnostics into diagnostics
+ * for the C interface to Clang.
+ */
+class CIndexDiagnosticClient : public DiagnosticClient {
+  CXDiagnosticCallback Callback;
+  CXClientData ClientData;
+  LangOptions LangOpts;
+  
+public:
+  CIndexDiagnosticClient(CXDiagnosticCallback Callback,
+                         CXClientData ClientData)
+    : Callback(Callback), ClientData(ClientData), LangOpts() { }
+  
+  virtual ~CIndexDiagnosticClient();
+  
+  virtual void BeginSourceFile(const LangOptions &LangOpts,
+                               const Preprocessor *PP);
+  
+  virtual void HandleDiagnostic(Diagnostic::Level DiagLevel,
+                                const DiagnosticInfo &Info);
+};
+  
+} // end namespace clang
+
+#endif // LLVM_CLANG_CINDEX_DIAGNOSTIC_H
index aa63ec0238086e44dd671092b93eccc393a0576a..44eaaec8b56ce15d75a29dc4209cfd04711dc3a2 100644 (file)
 
 using namespace clang;
 
-/// IgnoreDiagnosticsClient - A DiagnosticsClient that just ignores emitted
-/// warnings and errors.
-class IgnoreDiagnosticsClient : public DiagnosticClient {
-public:
-  virtual ~IgnoreDiagnosticsClient() {}
-  virtual void HandleDiagnostic(Diagnostic::Level, const DiagnosticInfo &) {}
-};
-
 class CIndexer {
-  DiagnosticOptions DiagOpts;
-  IgnoreDiagnosticsClient IgnoreDiagClient;
-  llvm::OwningPtr<Diagnostic> TextDiags;
-  Diagnostic IgnoreDiags;
   bool UseExternalASTGeneration;
   bool OnlyLocalDecls;
   bool DisplayDiagnostics;
@@ -44,11 +32,9 @@ class CIndexer {
   llvm::sys::Path ClangPath;
   
 public:
-  CIndexer() : IgnoreDiags(&IgnoreDiagClient), UseExternalASTGeneration(false),
-               OnlyLocalDecls(false), DisplayDiagnostics(false) 
-  {
-    TextDiags.reset(CompilerInstance::createDiagnostics(DiagOpts, 0, 0));
-  }
+  CIndexer() 
+    : UseExternalASTGeneration(false), OnlyLocalDecls(false), 
+      DisplayDiagnostics(false) { }
   
   /// \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
@@ -66,10 +52,6 @@ public:
     UseExternalASTGeneration = Value;
   }
   
-  Diagnostic &getDiags() {
-    return DisplayDiagnostics ? *TextDiags : IgnoreDiags;
-  }
-  
   /// \brief Get the path of the clang binary.
   const llvm::sys::Path& getClangPath();
   
index f4d22253107d753449896ef0521d1832e48a08bf..26e1b3bb905c7dc3b69f31c77f790744c450fb60 100644 (file)
@@ -21,6 +21,7 @@ set( LLVM_LINK_COMPONENTS
 add_clang_library(CIndex
   CIndex.cpp
   CIndexCodeCompletion.cpp
+  CIndexDiagnostic.cpp
   CIndexInclusionStack.cpp
   CIndexUSRs.cpp
   CIndexer.cpp
index 1f15f0832ccb01243e8505e4985ec318a2330822..e1dc7ed9665cca62a7b1de72c4f8489461cab8a7 100644 (file)
@@ -16,6 +16,9 @@
 
 #include "clang-c/Index.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/AST/ASTContext.h"
 
 namespace clang {
       
@@ -23,25 +26,45 @@ class ASTContext;
 
 namespace cxloc {
   
-typedef llvm::PointerIntPair<ASTContext *, 1, bool> CXSourceLocationPtr;
+typedef llvm::PointerIntPair<const SourceManager *, 1, bool> 
+  CXSourceLocationPtr;
 
+/// \brief Translate a Clang source location into a CIndex source location.
+static inline CXSourceLocation 
+translateSourceLocation(const SourceManager &SM, const LangOptions &LangOpts,
+                        SourceLocation Loc, bool AtEnd = false) {
+  CXSourceLocationPtr Ptr(&SM, AtEnd);
+  CXSourceLocation Result = { { Ptr.getOpaqueValue(), (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,
                                                        bool AtEnd = false) {
-  CXSourceLocationPtr Ptr(&Context, AtEnd);
-  CXSourceLocation Result = { Ptr.getOpaqueValue(), Loc.getRawEncoding() };
-  return Result;
+  return translateSourceLocation(Context.getSourceManager(), 
+                                 Context.getLangOptions(),
+                                 Loc, AtEnd);
 }
 
 /// \brief Translate a Clang source range into a CIndex source range.
-static inline CXSourceRange translateSourceRange(ASTContext &Context,
+static inline CXSourceRange translateSourceRange(const SourceManager &SM, 
+                                                 const LangOptions &LangOpts,
                                                  SourceRange R) {
-  CXSourceRange Result = { &Context, 
+  CXSourceRange Result = { { (void *)&SM, (void *)&LangOpts },
                            R.getBegin().getRawEncoding(),
                            R.getEnd().getRawEncoding() };
   return Result;
 }
+  
+/// \brief Translate a Clang source range into a CIndex source range.
+static inline CXSourceRange translateSourceRange(ASTContext &Context,
+                                                 SourceRange R) {
+  return translateSourceRange(Context.getSourceManager(),
+                              Context.getLangOptions(),
+                              R);
+}
 
 static inline SourceLocation translateSourceLocation(CXSourceLocation L) {
   return SourceLocation::getFromRawEncoding(L.int_data);
index 2ad9da12bb65ee124995445b65f78e8eaeb5fce9..39c3446f046b66ba9875079c3d8e7a64509935c3 100644 (file)
@@ -28,10 +28,13 @@ char *basename(const char* path)
 extern char *basename(const char *);
 #endif
 
+static void PrintDiagnosticCallback(CXDiagnostic Diagnostic, 
+                                    CXClientData ClientData);
+
 static unsigned CreateTranslationUnit(CXIndex Idx, const char *file,
                                       CXTranslationUnit *TU) {
   
-  *TU = clang_createTranslationUnit(Idx, file);
+  *TU = clang_createTranslationUnit(Idx, file, PrintDiagnosticCallback, 0);
   if (!TU) {
     fprintf(stderr, "Unable to load translation unit from '%s'!\n", file);
     return 0;
@@ -177,6 +180,41 @@ static const char* GetCursorSource(CXCursor Cursor) {
 
 typedef void (*PostVisitTU)(CXTranslationUnit);
 
+static void PrintDiagnosticCallback(CXDiagnostic Diagnostic, 
+                                    CXClientData ClientData) {
+  FILE *out = (FILE *)ClientData;
+  CXFile file;
+  unsigned line, column;
+  CXString text;
+  enum CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(Diagnostic);
+  
+  /* Ignore diagnostics that should be ignored. */
+  if (severity == CXDiagnostic_Ignored)
+    return;
+  
+  /* Print file:line:column. */
+  clang_getInstantiationLocation(clang_getDiagnosticLocation(Diagnostic),
+                                 &file, &line, &column, 0);
+  if (file)
+    fprintf(out, "%s:%d:%d: ", clang_getFileName(file), line, column);
+  
+  /* Print warning/error/etc. */
+  switch (severity) {
+  case CXDiagnostic_Ignored: assert(0 && "impossible"); break;
+  case CXDiagnostic_Note: fprintf(out, "note: "); break;
+  case CXDiagnostic_Warning: fprintf(out, "warning: "); break;
+  case CXDiagnostic_Error: fprintf(out, "error: "); break;
+  case CXDiagnostic_Fatal: fprintf(out, "fatal error: "); break;
+  }
+  
+  text = clang_getDiagnosticSpelling(Diagnostic);
+  if (clang_getCString(text))
+    fprintf(out, "%s\n", clang_getCString(text));
+  else
+    fprintf(out, "<no diagnostic text>\n");
+  clang_disposeString(text);
+}
+
 /******************************************************************************/
 /* Logic for testing traversal.                                               */
 /******************************************************************************/
@@ -407,7 +445,9 @@ int perform_test_load_source(int argc, const char **argv,
                                                  argc - num_unsaved_files, 
                                                  argv + num_unsaved_files,
                                                  num_unsaved_files,
-                                                 unsaved_files);
+                                                 unsaved_files,
+                                                 PrintDiagnosticCallback,
+                                                 stderr);
   if (!TU) {
     fprintf(stderr, "Unable to load translation unit!\n");
     return 1;
@@ -722,7 +762,9 @@ int inspect_cursor_at(int argc, const char **argv) {
                                   argc - num_unsaved_files - 2 - NumLocations,
                                    argv + num_unsaved_files + 1 + NumLocations,
                                                  num_unsaved_files,
-                                                 unsaved_files);
+                                                 unsaved_files,
+                                                 PrintDiagnosticCallback,
+                                                 stderr);
   if (!TU) {
     fprintf(stderr, "unable to parse input\n");
     return -1;
@@ -779,7 +821,9 @@ int perform_token_annotation(int argc, const char **argv) {
                                                  argc - num_unsaved_files - 3,
                                                  argv + num_unsaved_files + 2,
                                                  num_unsaved_files,
-                                                 unsaved_files);
+                                                 unsaved_files,
+                                                 PrintDiagnosticCallback,
+                                                 stderr);
   if (!TU) {
     fprintf(stderr, "unable to parse input\n");
     clang_disposeIndex(CIdx);