owenv updated this revision to Diff 265294.
owenv added a comment.

Updated terminology to generalize the feature so Clang diagnostics can adopt it 
in the future.
Also switched from local paths to URLs so local and remote documentation can 
both be supported if desired


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D80126/new/

https://reviews.llvm.org/D80126

Files:
  clang/include/clang-c/Index.h
  clang/include/clang/Frontend/SerializedDiagnosticReader.h
  clang/include/clang/Frontend/SerializedDiagnostics.h
  clang/lib/Frontend/SerializedDiagnosticPrinter.cpp
  clang/lib/Frontend/SerializedDiagnosticReader.cpp
  clang/test/Misc/Inputs/serialized-diags-documentation.dia
  clang/test/Misc/serialized-diags-doumentation.c
  clang/tools/c-index-test/c-index-test.c
  clang/tools/libclang/CIndexDiagnostic.cpp
  clang/tools/libclang/CIndexDiagnostic.h
  clang/tools/libclang/CXLoadedDiagnostic.cpp
  clang/tools/libclang/CXLoadedDiagnostic.h
  clang/tools/libclang/CXStoredDiagnostic.cpp
  clang/tools/libclang/libclang.exports

Index: clang/tools/libclang/libclang.exports
===================================================================
--- clang/tools/libclang/libclang.exports
+++ clang/tools/libclang/libclang.exports
@@ -219,6 +219,7 @@
 clang_getDiagnosticCategory
 clang_getDiagnosticCategoryName
 clang_getDiagnosticCategoryText
+clang_getDiagnosticDocumentationURLs
 clang_getDiagnosticFixIt
 clang_getDiagnosticInSet
 clang_getDiagnosticLocation
Index: clang/tools/libclang/CXStoredDiagnostic.cpp
===================================================================
--- clang/tools/libclang/CXStoredDiagnostic.cpp
+++ clang/tools/libclang/CXStoredDiagnostic.cpp
@@ -109,3 +109,7 @@
   return cxstring::createDup(Hint.CodeToInsert);
 }
 
+CXStringSet *CXStoredDiagnostic::getDocumentationURLs() const {
+  // Documentation URLs are not yet implemented for native Clang diagnostics.
+  return cxstring::createSet({});
+}
Index: clang/tools/libclang/CXLoadedDiagnostic.h
===================================================================
--- clang/tools/libclang/CXLoadedDiagnostic.h
+++ clang/tools/libclang/CXLoadedDiagnostic.h
@@ -58,6 +58,9 @@
   CXString getFixIt(unsigned FixIt,
                     CXSourceRange *ReplacementRange) const override;
 
+  /// Return the documentation URLs associated with the diagnostic.
+  virtual CXStringSet *getDocumentationURLs() const override;
+
   static bool classof(const CXDiagnosticImpl *D) {
     return D->getKind() == LoadedDiagnosticKind;
   }
@@ -82,6 +85,7 @@
 
   std::vector<CXSourceRange> Ranges;
   std::vector<std::pair<CXSourceRange, const char *> > FixIts;
+  std::vector<std::string> DocumentationURLs;
   const char *Spelling;
   llvm::StringRef DiagOption;
   llvm::StringRef CategoryText;
Index: clang/tools/libclang/CXLoadedDiagnostic.cpp
===================================================================
--- clang/tools/libclang/CXLoadedDiagnostic.cpp
+++ clang/tools/libclang/CXLoadedDiagnostic.cpp
@@ -41,7 +41,8 @@
   Strings Categories;
   Strings WarningFlags;
   Strings FileNames;
-  
+  Strings DocumentationURLs;
+
   FileSystemOptions FO;
   FileManager FakeFiles;
   llvm::DenseMap<unsigned, const FileEntry *> Files;
@@ -145,6 +146,10 @@
   return cxstring::createRef(FixIts[FixIt].second);
 }
 
+CXStringSet *CXLoadedDiagnostic::getDocumentationURLs() const {
+  return cxstring::createSet(DocumentationURLs);
+}
+
 void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location,
                                         CXFile *file,
                                         unsigned int *line,
@@ -233,6 +238,10 @@
   visitSourceRangeRecord(const serialized_diags::Location &Start,
                          const serialized_diags::Location &End) override;
 
+  std::error_code visitDocumentationURLRecord(unsigned ID,
+                                              StringRef Path) override;
+  std::error_code visitDocumentationRecord(unsigned ID) override;
+
 public:
   DiagLoader(enum CXLoadDiag_Error *e, CXString *es)
       : SerializedDiagnosticReader(), error(e), errorString(es) {
@@ -347,6 +356,15 @@
   return std::error_code();
 }
 
+std::error_code DiagLoader::visitDocumentationURLRecord(unsigned ID,
+                                                        StringRef Path) {
+  // FIXME: Why do we care about long strings?
+  if (Path.size() > 65536)
+    return reportInvalidFile("Out-of-bounds string in documentation URL.");
+  TopDiags->DocumentationURLs[ID] = TopDiags->copyString(Path);
+  return std::error_code();
+}
+
 std::error_code
 DiagLoader::visitSourceRangeRecord(const serialized_diags::Location &Start,
                                    const serialized_diags::Location &End) {
@@ -372,6 +390,12 @@
   return std::error_code();
 }
 
+std::error_code DiagLoader::visitDocumentationRecord(unsigned ID) {
+  CurrentDiags.back()->DocumentationURLs.push_back(
+      TopDiags->DocumentationURLs[ID]);
+  return std::error_code();
+}
+
 std::error_code DiagLoader::visitDiagnosticRecord(
     unsigned Severity, const serialized_diags::Location &Location,
     unsigned Category, unsigned Flag, StringRef Message) {
Index: clang/tools/libclang/CIndexDiagnostic.h
===================================================================
--- clang/tools/libclang/CIndexDiagnostic.h
+++ clang/tools/libclang/CIndexDiagnostic.h
@@ -89,6 +89,9 @@
   virtual CXString getFixIt(unsigned FixIt,
                             CXSourceRange *ReplacementRange) const = 0;
 
+  /// Return the documentation URLs associated with the diagnostic.
+  virtual CXStringSet *getDocumentationURLs() const = 0;
+
   Kind getKind() const { return K; }
   
   CXDiagnosticSetImpl &getChildDiagnostics() {
@@ -150,6 +153,9 @@
   CXString getFixIt(unsigned FixIt,
                     CXSourceRange *ReplacementRange) const override;
 
+  /// Return the documentation URLs associated with the diagnostic.
+  CXStringSet *getDocumentationURLs() const override;
+
   static bool classof(const CXDiagnosticImpl *D) {
     return D->getKind() == StoredDiagnosticKind;
   }
Index: clang/tools/libclang/CIndexDiagnostic.cpp
===================================================================
--- clang/tools/libclang/CIndexDiagnostic.cpp
+++ clang/tools/libclang/CIndexDiagnostic.cpp
@@ -77,6 +77,9 @@
       *ReplacementRange = clang_getNullRange();
     return cxstring::createEmpty();
   }
+  CXStringSet *getDocumentationURLs() const override {
+    return cxstring::createSet({});
+  }
 };    
     
 class CXDiagnosticRenderer : public DiagnosticNoteRenderer {
@@ -438,6 +441,12 @@
   return D->getFixIt(FixIt, ReplacementRange);
 }
 
+CXStringSet *clang_getDiagnosticDocumentationURLs(CXDiagnostic Diagnostic) {
+  if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diagnostic))
+    return D->getDocumentationURLs();
+  return cxstring::createSet({});
+}
+
 void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) {
   if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) {
     if (D->isExternallyManaged())
Index: clang/tools/c-index-test/c-index-test.c
===================================================================
--- clang/tools/c-index-test/c-index-test.c
+++ clang/tools/c-index-test/c-index-test.c
@@ -1189,10 +1189,11 @@
   FILE *out = stderr;
   CXFile file;
   CXString Msg;
+  CXStringSet *DocumentationURLs = NULL;
   unsigned display_opts = CXDiagnostic_DisplaySourceLocation
     | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges
     | CXDiagnostic_DisplayOption;
-  unsigned i, num_fixits;
+  unsigned i, count;
 
   if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored)
     return;
@@ -1206,9 +1207,9 @@
   if (!file)
     return;
 
-  num_fixits = clang_getDiagnosticNumFixIts(Diagnostic);
-  fprintf(stderr, "Number FIX-ITs = %d\n", num_fixits);
-  for (i = 0; i != num_fixits; ++i) {
+  count = clang_getDiagnosticNumFixIts(Diagnostic);
+  fprintf(stderr, "Number FIX-ITs = %d\n", count);
+  for (i = 0; i != count; ++i) {
     CXSourceRange range;
     CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range);
     CXSourceLocation start = clang_getRangeStart(range);
@@ -1240,6 +1241,14 @@
     }
     clang_disposeString(insertion_text);
   }
+
+  DocumentationURLs = clang_getDiagnosticDocumentationURLs(Diagnostic);
+  if (DocumentationURLs) {
+    for (i = 0, count = DocumentationURLs->Count; i < count; ++i)
+      printf("[Documentation URL: %s]\n",
+             clang_getCString(DocumentationURLs->Strings[i]));
+    clang_disposeStringSet(DocumentationURLs);
+  }
 }
 
 void PrintDiagnosticSet(CXDiagnosticSet Set) {
@@ -4669,6 +4678,17 @@
   }  
 }
 
+static void printDocumentationURLs(CXDiagnostic D, unsigned indent) {
+  unsigned i, count;
+  CXStringSet *DocumentationURLs = clang_getDiagnosticDocumentationURLs(D);
+  for (i = 0, count = DocumentationURLs->Count; i < count; ++i) {
+    printIndent(indent);
+    printf("[Documentation URL: %s]\n",
+           clang_getCString(DocumentationURLs->Strings[i]));
+  }
+  clang_disposeStringSet(DocumentationURLs);
+}
+
 static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) {
   unsigned i, n;
 
@@ -4716,7 +4736,8 @@
     
     printRanges(D, indent);
     printFixIts(D, indent);
-    
+    printDocumentationURLs(D, indent);
+
     /* Print subdiagnostics. */
     printDiagnosticSet(clang_getChildDiagnostics(D), indent+2);
 
Index: clang/test/Misc/serialized-diags-doumentation.c
===================================================================
--- /dev/null
+++ clang/test/Misc/serialized-diags-doumentation.c
@@ -0,0 +1,4 @@
+// RUN: c-index-test -read-diagnostics %S/Inputs/serialized-diags-documentation.dia 2>&1 | FileCheck %s
+
+// CHECK: hello.swift:1:1: error: non-nominal type '(Int, Int)' cannot be extended [] []
+// CHECK: [Documentation URL: /Users/owenvoorhees/Documents/Development/swift-source/build/Ninja-ReleaseAssert/swift-macosx-x86_64/share/doc/swift/diagnostics/nominal-types.md]
Index: clang/lib/Frontend/SerializedDiagnosticReader.cpp
===================================================================
--- clang/lib/Frontend/SerializedDiagnosticReader.cpp
+++ clang/lib/Frontend/SerializedDiagnosticReader.cpp
@@ -318,6 +318,20 @@
       if ((EC = visitVersionRecord(Record[0])))
         return EC;
       continue;
+    case RECORD_DOCUMENTATION_URL:
+      // A documentation URL has an ID and path size.
+      if (Record.size() != 2)
+        return SDError::MalformedDiagnosticRecord;
+      if ((EC = visitDocumentationURLRecord(Record[0], Blob)))
+        return EC;
+      continue;
+    case RECORD_DOCUMENTATION:
+      // A documentation record just has an ID.
+      if (Record.size() != 1)
+        return SDError::MalformedDiagnosticRecord;
+      if ((EC = visitDocumentationRecord(Record[0])))
+        return EC;
+      continue;
     }
   }
 }
Index: clang/lib/Frontend/SerializedDiagnosticPrinter.cpp
===================================================================
--- clang/lib/Frontend/SerializedDiagnosticPrinter.cpp
+++ clang/lib/Frontend/SerializedDiagnosticPrinter.cpp
@@ -93,6 +93,7 @@
   AbbrevLookup FileLookup;
   AbbrevLookup CategoryLookup;
   AbbrevLookup DiagFlagLookup;
+  AbbrevLookup DocumentationLookup;
 
 public:
   SDiagsMerger(SDiagsWriter &Writer)
@@ -120,6 +121,10 @@
   visitSourceRangeRecord(const serialized_diags::Location &Start,
                          const serialized_diags::Location &End) override;
 
+  std::error_code visitDocumentationURLRecord(unsigned ID,
+                                              StringRef URL) override;
+  std::error_code visitDocumentationRecord(unsigned ID) override;
+
 private:
   std::error_code adjustSourceLocFilename(RecordData &Record,
                                           unsigned int offset);
@@ -197,6 +202,9 @@
                        ArrayRef<FixItHint> Hints,
                        const SourceManager &SM);
 
+  /// Emit record(s) for diagnostic documentation URLs.
+  void EmitDocumentation(ArrayRef<StringRef> URLs);
+
   /// Emit a record for a CharSourceRange.
   void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM);
 
@@ -212,6 +220,9 @@
   /// Emit (lazily) the file string and retrieved the file identifier.
   unsigned getEmitFile(const char *Filename);
 
+  /// Lazily emit a diagnostic documentation URL.
+  unsigned getEmitDocumentationURL(StringRef URL);
+
   /// Add SourceLocation information the specified record.
   void AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc,
                       RecordDataImpl &Record, unsigned TokSize = 0);
@@ -273,6 +284,9 @@
     /// The collection of files used.
     llvm::DenseMap<const char *, unsigned> Files;
 
+    /// The collection of documentation URLs used.
+    llvm::DenseMap<const char *, unsigned> DocumentationURLs;
+
     typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> >
     DiagFlagsTy;
 
@@ -452,6 +466,8 @@
   EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record);
   EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record);
   EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record);
+  EmitRecordID(RECORD_DOCUMENTATION_URL, "DocumentationURL", Stream, Record);
+  EmitRecordID(RECORD_DOCUMENTATION_URL, "Documentation", Stream, Record);
 
   // Emit abbreviation for RECORD_DIAG.
   Abbrev = std::make_shared<BitCodeAbbrev>();
@@ -508,6 +524,23 @@
   Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG,
                                                        Abbrev));
 
+  // Emit the abbreviation for RECORD_DOCUMENTATION_URL.
+  Abbrev = std::make_shared<BitCodeAbbrev>();
+  Abbrev->Add(BitCodeAbbrevOp(RECORD_DOCUMENTATION_URL));
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped doc ID.
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // URL size.
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));      // URL text.
+  Abbrevs.set(RECORD_DOCUMENTATION_URL,
+              Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
+
+  // Emit the abbreviation for RECORD_DOCUMENTATION.
+  Abbrev = std::make_shared<BitCodeAbbrev>();
+  Abbrev->Add(BitCodeAbbrevOp(RECORD_DOCUMENTATION));
+  Abbrev->Add(
+      BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped doc note ID.
+  Abbrevs.set(RECORD_DOCUMENTATION,
+              Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev));
+
   Stream.ExitBlock();
 }
 
@@ -566,6 +599,24 @@
   return entry.first;
 }
 
+unsigned SDiagsWriter::getEmitDocumentationURL(StringRef URL) {
+  if (URL.empty())
+    return 0;
+
+  unsigned &entry = State->DocumentationURLs[URL.data()];
+  if (entry)
+    return entry;
+
+  // Lazily generate the record for the URL.
+  entry = State->DocumentationURLs.size();
+  RecordData::value_type Record[] = {RECORD_DOCUMENTATION_URL, entry,
+                                     URL.size()};
+  State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DOCUMENTATION_URL),
+                                   Record, URL);
+
+  return entry;
+}
+
 void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                                     const Diagnostic &Info) {
   // Enter the block for a non-note diagnostic immediately, rather than waiting
@@ -718,6 +769,21 @@
   Writer.EmitCodeContext(Ranges, Hints, Loc.getManager());
 }
 
+void SDiagsWriter::EmitDocumentation(ArrayRef<StringRef> URLs) {
+  llvm::BitstreamWriter &Stream = State->Stream;
+  RecordData &Record = State->Record;
+  AbbreviationMap &Abbrevs = State->Abbrevs;
+
+  for (ArrayRef<StringRef>::iterator I = URLs.begin(), E = URLs.end(); I != E;
+       ++I) {
+    unsigned ID = getEmitDocumentationURL(*I);
+    Record.clear();
+    Record.push_back(ID);
+    Stream.EmitRecord(RECORD_DOCUMENTATION, Record,
+                      Abbrevs.get(RECORD_DOCUMENTATION));
+  }
+}
+
 void SDiagsRenderer::emitNote(FullSourceLoc Loc, StringRef Message) {
   Writer.EnterDiagBlock();
   PresumedLoc PLoc = Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc();
@@ -858,3 +924,17 @@
   DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name);
   return std::error_code();
 }
+
+std::error_code SDiagsMerger::visitDocumentationURLRecord(unsigned ID,
+                                                          StringRef URL) {
+  DocumentationLookup[ID] = Writer.getEmitDocumentationURL(URL);
+  return std::error_code();
+}
+
+std::error_code SDiagsMerger::visitDocumentationRecord(unsigned ID) {
+  RecordData::value_type Record[] = {DocumentationLookup[ID]};
+  Writer.State->Stream.EmitRecord(
+      RECORD_DOCUMENTATION, Record,
+      Writer.State->Abbrevs.get(RECORD_DOCUMENTATION));
+  return std::error_code();
+}
Index: clang/include/clang/Frontend/SerializedDiagnostics.h
===================================================================
--- clang/include/clang/Frontend/SerializedDiagnostics.h
+++ clang/include/clang/Frontend/SerializedDiagnostics.h
@@ -32,8 +32,10 @@
   RECORD_CATEGORY,
   RECORD_FILENAME,
   RECORD_FIXIT,
+  RECORD_DOCUMENTATION_URL,
+  RECORD_DOCUMENTATION,
   RECORD_FIRST = RECORD_VERSION,
-  RECORD_LAST = RECORD_FIXIT
+  RECORD_LAST = RECORD_DOCUMENTATION
 };
 
 /// A stable version of DiagnosticIDs::Level.
Index: clang/include/clang/Frontend/SerializedDiagnosticReader.h
===================================================================
--- clang/include/clang/Frontend/SerializedDiagnosticReader.h
+++ clang/include/clang/Frontend/SerializedDiagnosticReader.h
@@ -121,6 +121,13 @@
     return {};
   }
 
+  /// Visit a diagnostic documentation URL.
+  virtual std::error_code visitDocumentationURLRecord(unsigned ID,
+                                                      StringRef Path) {
+    return {};
+  }
+  virtual std::error_code visitDocumentationRecord(unsigned ID) { return {}; }
+
   /// Visit the version of the set of diagnostics.
   virtual std::error_code visitVersionRecord(unsigned Version) { return {}; }
 };
Index: clang/include/clang-c/Index.h
===================================================================
--- clang/include/clang-c/Index.h
+++ clang/include/clang-c/Index.h
@@ -1103,6 +1103,20 @@
 CINDEX_LINKAGE CXString clang_getDiagnosticFixIt(
     CXDiagnostic Diagnostic, unsigned FixIt, CXSourceRange *ReplacementRange);
 
+/**
+ * Retrieve the documentation URLs associated with the given diagnostic.
+ *
+ * Linked documentation is intended to be displayed alongside emitted
+ * diagnostics to teach users about relevant language concepts or describe
+ * common problems and solutions when encountering errors.
+ *
+ * \param Diagnostic The diagnostic whose documentation URLs are being queried.
+ *
+ * \returns A set of strings which represent URLs to external documentation.
+ */
+CINDEX_LINKAGE CXStringSet *
+clang_getDiagnosticDocumentationURLs(CXDiagnostic Diagnostic);
+
 /**
  * @}
  */
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to