tom-anders created this revision.
Herald added subscribers: usaxena95, kadircet, arphaman, mgorny.
Herald added a project: All.
tom-anders requested review of this revision.
Herald added subscribers: cfe-commits, llvm-commits, MaskRay, ilya-biryukov.
Herald added projects: LLVM, clang-tools-extra.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129971

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/CodeComplete.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.cpp
  clang-tools-extra/clangd/CodeCompletionStrings.h
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  clang-tools-extra/clangd/SymbolDocumentation.cpp
  clang-tools-extra/clangd/SymbolDocumentation.h
  clang-tools-extra/clangd/index/SymbolCollector.cpp
  llvm/utils/gn/secondary/clang-tools-extra/clangd/BUILD.gn

Index: llvm/utils/gn/secondary/clang-tools-extra/clangd/BUILD.gn
===================================================================
--- llvm/utils/gn/secondary/clang-tools-extra/clangd/BUILD.gn
+++ llvm/utils/gn/secondary/clang-tools-extra/clangd/BUILD.gn
@@ -113,6 +113,7 @@
     "SemanticHighlighting.cpp",
     "SemanticSelection.cpp",
     "SourceCode.cpp",
+    "SymbolDocumentation.cpp"
     "TUScheduler.cpp",
     "TidyProvider.cpp",
     "URI.cpp",
Index: clang-tools-extra/clangd/index/SymbolCollector.cpp
===================================================================
--- clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -914,7 +914,8 @@
       /*IncludeBriefComments*/ false);
   std::string Documentation =
       formatDocumentation(*CCS, getDocComment(Ctx, SymbolCompletion,
-                                              /*CommentsFromHeaders=*/true));
+                                              /*CommentsFromHeaders=*/true)
+                                    .CommentText);
   if (!(S.Flags & Symbol::IndexedForCodeCompletion)) {
     if (Opts.StoreAllDocumentation)
       S.Documentation = Documentation;
Index: clang-tools-extra/clangd/SymbolDocumentation.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/SymbolDocumentation.h
@@ -0,0 +1,71 @@
+//===--- SymbolDocumentation.h ==---------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Class to parse doxygen comments into a flat structure for consumption
+// in e.g. Hover and Code Completion
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/CommentVisitor.h"
+
+namespace clang {
+namespace clangd {
+
+struct ParameterDocumentation {
+  llvm::StringRef Name;
+  std::string Description;
+  llvm::Optional<comments::ParamCommandComment::PassDirection> PassDirection;
+};
+
+struct ExceptionDocumentation {
+  llvm::StringRef Exception;
+  std::string Description;
+};
+
+struct SymbolDocumentation {
+  SymbolDocumentation(const RawComment &RC, const ASTContext &Ctx,
+                      const Preprocessor *PP, const Decl *D);
+
+  void visitFullComment(const comments::FullComment *FC);
+  void visitParamCommandComment(const comments::ParamCommandComment *P);
+
+  SymbolDocumentation() = default;
+  SymbolDocumentation(const std::string &Doc) : CommentText(Doc) {}
+
+  bool empty() const { return CommentText.empty(); }
+
+  std::string Brief;
+
+  std::string Returns;
+
+  llvm::SmallVector<std::string> Notes;
+  llvm::SmallVector<std::string> Warnings;
+
+  /// \\param commands that match a parameter in the function declaration
+  std::map<unsigned, ParameterDocumentation> MatchingParameterDocs;
+
+  llvm::SmallVector<ParameterDocumentation> UnmatchedParameterDocs;
+
+  llvm::SmallVector<ExceptionDocumentation> Throws;
+
+  /// All the paragraphs we don't have any special handling for, e.g. \details
+  std::string Description;
+
+  /// The full text of the doxygen comment
+  std::string CommentText;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLDOCUMENTATION_H
Index: clang-tools-extra/clangd/SymbolDocumentation.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/SymbolDocumentation.cpp
@@ -0,0 +1,149 @@
+//===--- SymbolDocumentation.cpp ==-------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolDocumentation.h"
+#include "clang/AST/TextNodeDumper.h"
+#include "llvm/Support/JSON.h"
+
+namespace clang {
+namespace clangd {
+
+/// Converts a BlockCommentToString,
+class BlockCommentToString
+    : public comments::ConstCommentVisitor<BlockCommentToString> {
+public:
+  BlockCommentToString(std::string &Out, const ASTContext &Ctx)
+      : Out(Out), Ctx(Ctx) {}
+
+  void visitParagraphComment(const comments::ParagraphComment *C) {
+    for (const auto *Child = C->child_begin(); Child != C->child_end();
+         ++Child) {
+      visit(*Child);
+    }
+    Out << "\n";
+  }
+
+  void visitBlockCommandComment(const comments::BlockCommandComment *B) {
+    Out << (B->getCommandMarker() == (comments::CommandMarkerKind::CMK_At)
+                ? '@'
+                : '\\')
+        << B->getCommandName(Ctx.getCommentCommandTraits());
+
+    visit(B->getParagraph());
+  }
+
+  void visitTextComment(const comments::TextComment *C) { Out << C->getText(); }
+
+  void visitInlineCommandComment(const comments::InlineCommandComment *C) {
+    const std::string SurroundWith = [C] {
+      switch (C->getRenderKind()) {
+      case comments::InlineCommandComment::RenderKind::RenderMonospaced:
+        return "`";
+      case comments::InlineCommandComment::RenderKind::RenderBold:
+        return "**";
+      case comments::InlineCommandComment::RenderKind::RenderEmphasized:
+        return "*";
+      default:
+        return "";
+      }
+    }();
+
+    Out << SurroundWith;
+    for (unsigned I = 0; I < C->getNumArgs(); ++I) {
+      Out << C->getArgText(I);
+    }
+    Out << SurroundWith;
+  }
+
+private:
+  llvm::raw_string_ostream Out;
+  const ASTContext &Ctx;
+};
+
+class CommentToSymbolDocumentation
+    : public comments::ConstCommentVisitor<CommentToSymbolDocumentation> {
+public:
+  CommentToSymbolDocumentation(const RawComment &RC, const ASTContext &Ctx,
+                               const Preprocessor *PP, const Decl *D,
+                               SymbolDocumentation &Doc)
+      : FullComment(RC.parse(Ctx, PP, D)), Output(Doc), Ctx(Ctx) {
+
+    Output.CommentText =
+        RC.getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
+
+    for (auto *Block : FullComment->getBlocks()) {
+      visit(Block);
+    }
+  }
+
+  void visitBlockCommandComment(const comments::BlockCommandComment *B) {
+    const llvm::StringRef CommandName =
+        B->getCommandName(Ctx.getCommentCommandTraits());
+
+    // Visit B->getParagraph() for commands that we have special fields for,
+    // so that the command name won't be included in the string.
+    // Otherwise, we want to keep the command name, so visit B itself.
+    if (CommandName == "brief")
+      BlockCommentToString(Output.Brief, Ctx).visit(B->getParagraph());
+    else if (CommandName == "return")
+      BlockCommentToString(Output.Returns, Ctx).visit(B->getParagraph());
+    else if (CommandName == "throw" || CommandName == "throws" ||
+             CommandName == "exception") {
+      ExceptionDocumentation Doc;
+      // TODO Looks like comments::Parser does not parse this correctly,
+      // getNumArgs() is always zero and the exception argument is contained in
+      // the Paragraph instead.
+      if (B->getNumArgs() > 0)
+        Doc.Exception = B->getArgText(0);
+      BlockCommentToString(Doc.Description, Ctx).visit(B->getParagraph());
+      Output.Throws.emplace_back(std::move(Doc));
+    } else if (CommandName == "warning")
+      BlockCommentToString(Output.Warnings.emplace_back(), Ctx)
+          .visit(B->getParagraph());
+    else if (CommandName == "note")
+      BlockCommentToString(Output.Notes.emplace_back(), Ctx)
+          .visit(B->getParagraph());
+    else
+      BlockCommentToString(Output.Description, Ctx).visit(B);
+  }
+
+  void visitParagraphComment(const comments::ParagraphComment *P) {
+    BlockCommentToString(Output.Description, Ctx).visit(P);
+  }
+
+  void visitParamCommandComment(const comments::ParamCommandComment *P) {
+    ParameterDocumentation ParamDoc;
+    ParamDoc.Name = P->getParamNameAsWritten();
+    if (P->isDirectionExplicit())
+      ParamDoc.PassDirection = P->getDirection();
+
+    BlockCommentToString(ParamDoc.Description, Ctx).visit(P->getParagraph());
+
+    if (P->isParamIndexValid() && !P->isVarArgParam()) {
+      Output.MatchingParameterDocs.insert(
+          {P->getParamIndex(), std::move(ParamDoc)});
+    } else {
+      Output.UnmatchedParameterDocs.emplace_back(std::move(ParamDoc));
+    }
+  }
+
+private:
+  comments::FullComment *FullComment;
+  SymbolDocumentation &Output;
+  const ASTContext &Ctx;
+};
+
+SymbolDocumentation::SymbolDocumentation(const RawComment &RC,
+                                         const ASTContext &Ctx,
+                                         const Preprocessor *PP,
+                                         const Decl *D) {
+  CommentToSymbolDocumentation(RC, Ctx, PP, D, *this);
+}
+
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/Hover.h
===================================================================
--- clang-tools-extra/clangd/Hover.h
+++ clang-tools-extra/clangd/Hover.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HOVER_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HOVER_H
 
+#include "CodeCompletionStrings.h"
 #include "ParsedAST.h"
 #include "Protocol.h"
 #include "support/Markup.h"
@@ -68,7 +69,7 @@
   std::string Name;
   llvm::Optional<Range> SymRange;
   index::SymbolKind Kind = index::SymbolKind::Unknown;
-  std::string Documentation;
+  SymbolDocumentation Documentation;
   /// Source code containing the definition of the symbol.
   std::string Definition;
   const char *DefinitionLanguage = "cpp";
Index: clang-tools-extra/clangd/Hover.cpp
===================================================================
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -332,7 +332,7 @@
   LookupRequest Req;
   Req.IDs.insert(ID);
   Index->lookup(Req, [&](const Symbol &S) {
-    Hover.Documentation = std::string(S.Documentation);
+    Hover.Documentation.CommentText = std::string(S.Documentation);
   });
 }
 
@@ -1089,6 +1089,10 @@
 
   // Put a linebreak after header to increase readability.
   Output.addRuler();
+
+  if (!Documentation.Brief.empty())
+    Output.addParagraph().appendText(Documentation.Brief);
+
   // Print Types on their own lines to reduce chances of getting line-wrapped by
   // editor, as they might be long.
   if (ReturnType) {
@@ -1097,15 +1101,36 @@
     // Parameters:
     // - `bool param1`
     // - `int param2 = 5`
-    Output.addParagraph().appendText("→ ").appendCode(
-        llvm::to_string(*ReturnType));
+    Output.addParagraph()
+        .appendText("→ ")
+        .appendCode(llvm::to_string(*ReturnType))
+        .appendText(": ")
+        .appendText(Documentation.Returns);
   }
-
   if (Parameters && !Parameters->empty()) {
     Output.addParagraph().appendText("Parameters: ");
     markup::BulletList &L = Output.addBulletList();
-    for (const auto &Param : *Parameters)
-      L.addItem().addParagraph().appendCode(llvm::to_string(Param));
+    unsigned ParamIndex = 0;
+    for (const auto &Param : *Parameters) {
+      auto &Paragraph = L.addItem().addParagraph();
+      Paragraph.appendCode(llvm::to_string(Param));
+
+      const auto Parameter =
+          Documentation.MatchingParameterDocs.find(ParamIndex);
+      if (Parameter != Documentation.MatchingParameterDocs.end()) {
+        Paragraph.appendText(": ").appendText(Parameter->second.Description);
+      }
+
+      ++ParamIndex;
+    }
+
+    for (const auto &ParamDoc : Documentation.UnmatchedParameterDocs) {
+      L.addItem()
+          .addParagraph()
+          .appendCode(ParamDoc.Name)
+          .appendText(": ")
+          .appendText(ParamDoc.Description);
+    }
   }
 
   // Don't print Type after Parameters or ReturnType as this will just duplicate
@@ -1149,8 +1174,42 @@
     Output.addParagraph().appendText(OS.str());
   }
 
-  if (!Documentation.empty())
-    parseDocumentation(Documentation, Output);
+  if (!Documentation.Description.empty())
+    parseDocumentation(Documentation.Description, Output);
+
+  if (!Documentation.Throws.empty()) {
+    Output.addRuler();
+    Output.addParagraph().appendText("Throws: ");
+    markup::BulletList &L = Output.addBulletList();
+    for (const auto &Throws : Documentation.Throws)
+      L.addItem()
+          .addParagraph()
+          .appendCode(Throws.Exception)
+          .appendText(": ")
+          .appendText(Throws.Description);
+  }
+
+  if (!Documentation.Warnings.empty()) {
+    Output.addRuler();
+    Output.addParagraph()
+        .appendText("Warning")
+        .appendText(Documentation.Warnings.size() > 1 ? "s" : "")
+        .appendText(": ");
+    markup::BulletList &L = Output.addBulletList();
+    for (const auto &Warning : Documentation.Warnings)
+      L.addItem().addParagraph().appendText(Warning);
+  }
+
+  if (!Documentation.Notes.empty()) {
+    Output.addRuler();
+    Output.addParagraph()
+        .appendText("Note")
+        .appendText(Documentation.Notes.size() > 1 ? "s" : "")
+        .appendText(": ");
+    markup::BulletList &L = Output.addBulletList();
+    for (const auto &Note : Documentation.Notes)
+      L.addItem().addParagraph().appendText(Note);
+  }
 
   if (!Definition.empty()) {
     Output.addRuler();
Index: clang-tools-extra/clangd/CodeCompletionStrings.h
===================================================================
--- clang-tools-extra/clangd/CodeCompletionStrings.h
+++ clang-tools-extra/clangd/CodeCompletionStrings.h
@@ -14,13 +14,17 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H
 
+#include "clang/AST/CommentVisitor.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
 
+#include "SymbolDocumentation.h"
+
 namespace clang {
 class ASTContext;
 
 namespace clangd {
 
+/// TODO Update docs
 /// Gets a minimally formatted documentation comment of \p Result, with comment
 /// markers stripped. See clang::RawComment::getFormattedText() for the detailed
 /// explanation of how the comment text is transformed.
@@ -28,12 +32,11 @@
 /// If \p CommentsFromHeaders parameter is set, only comments from the main
 /// file will be returned. It is used to workaround crashes when parsing
 /// comments in the stale headers, coming from completion preamble.
-std::string getDocComment(const ASTContext &Ctx,
-                          const CodeCompletionResult &Result,
-                          bool CommentsFromHeaders);
+SymbolDocumentation getDocComment(const ASTContext &Ctx,
+                                  const CodeCompletionResult &Result,
+                                  bool CommentsFromHeaders);
 
-/// Similar to getDocComment, but returns the comment for a NamedDecl.
-std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &D);
+SymbolDocumentation getDeclComment(const ASTContext &Ctx, const NamedDecl &D);
 
 /// Formats the signature for an item, as a display string and snippet.
 /// e.g. for const_reference std::vector<T>::at(size_type) const, this returns:
Index: clang-tools-extra/clangd/CodeCompletionStrings.cpp
===================================================================
--- clang-tools-extra/clangd/CodeCompletionStrings.cpp
+++ clang-tools-extra/clangd/CodeCompletionStrings.cpp
@@ -58,39 +58,48 @@
 
 } // namespace
 
-std::string getDocComment(const ASTContext &Ctx,
-                          const CodeCompletionResult &Result,
-                          bool CommentsFromHeaders) {
+SymbolDocumentation getDocComment(const ASTContext &Ctx,
+                                  const CodeCompletionResult &Result,
+                                  bool CommentsFromHeaders) {
+  // FIXME: CommentsFromHeaders seems to be unused? Is this a bug?
+
   // FIXME: clang's completion also returns documentation for RK_Pattern if they
   // contain a pattern for ObjC properties. Unfortunately, there is no API to
   // get this declaration, so we don't show documentation in that case.
   if (Result.Kind != CodeCompletionResult::RK_Declaration)
-    return "";
+    return {};
   return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration())
-                                 : "";
+                                 : SymbolDocumentation{};
 }
 
-std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) {
+SymbolDocumentation getDeclComment(const ASTContext &Ctx,
+                                   const NamedDecl &Decl) {
   if (isa<NamespaceDecl>(Decl)) {
     // Namespaces often have too many redecls for any particular redecl comment
     // to be useful. Moreover, we often confuse file headers or generated
     // comments with namespace comments. Therefore we choose to just ignore
     // the comments for namespaces.
-    return "";
+    return {};
   }
   const RawComment *RC = getCompletionComment(Ctx, &Decl);
   if (!RC)
-    return "";
+    return {};
   // Sanity check that the comment does not come from the PCH. We choose to not
   // write them into PCH, because they are racy and slow to load.
   assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc()));
-  std::string Doc =
-      RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
-  if (!looksLikeDocComment(Doc))
-    return "";
+
+  // TODO Figure out where to get the Preprocessor from (3rd arg)...?
+  SymbolDocumentation Doc(*RC, Ctx, nullptr, &Decl);
+
+  if (!looksLikeDocComment(Doc.CommentText))
+    return {};
+
+  // TODO where should be put this check now?
   // Clang requires source to be UTF-8, but doesn't enforce this in comments.
-  if (!llvm::json::isUTF8(Doc))
-    Doc = llvm::json::fixUTF8(Doc);
+  //
+  // if (!llvm::json::isUTF8(Doc))
+  //   Doc = llvm::json::fixUTF8(Doc);
+
   return Doc;
 }
 
Index: clang-tools-extra/clangd/CodeComplete.cpp
===================================================================
--- clang-tools-extra/clangd/CodeComplete.cpp
+++ clang-tools-extra/clangd/CodeComplete.cpp
@@ -424,7 +424,7 @@
       } else if (C.SemaResult) {
         const auto DocComment = getDocComment(*ASTCtx, *C.SemaResult,
                                               /*CommentsFromHeaders=*/false);
-        SetDoc(formatDocumentation(*SemaCCS, DocComment));
+        SetDoc(formatDocumentation(*SemaCCS, DocComment.CommentText));
       }
     }
     if (Completion.Deprecated) {
@@ -978,7 +978,7 @@
           Candidate, *CCS,
           Candidate.getFunction()
               ? getDeclComment(S.getASTContext(), *Candidate.getFunction())
-              : ""));
+              : SymbolDocumentation{}));
     }
 
     // Sema does not load the docs from the preamble, so we need to fetch extra
@@ -1100,15 +1100,17 @@
 
   // FIXME(ioeric): consider moving CodeCompletionString logic here to
   // CompletionString.h.
-  ScoredSignature processOverloadCandidate(const OverloadCandidate &Candidate,
-                                           const CodeCompletionString &CCS,
-                                           llvm::StringRef DocComment) const {
+  ScoredSignature
+  processOverloadCandidate(const OverloadCandidate &Candidate,
+                           const CodeCompletionString &CCS,
+                           const SymbolDocumentation &DocComment) const {
     SignatureInformation Signature;
     SignatureQualitySignals Signal;
     const char *ReturnType = nullptr;
 
     markup::Document OverloadComment;
-    parseDocumentation(formatDocumentation(CCS, DocComment), OverloadComment);
+    parseDocumentation(formatDocumentation(CCS, DocComment.CommentText),
+                       OverloadComment);
     Signature.documentation = renderDoc(OverloadComment, DocumentationFormat);
     Signal.Kind = Candidate.getKind();
 
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -97,6 +97,7 @@
   SemanticHighlighting.cpp
   SemanticSelection.cpp
   SourceCode.cpp
+  SymbolDocumentation.cpp
   QueryDriverDatabase.cpp
   TidyProvider.cpp
   TUScheduler.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D129971: [clangd][WIP... Tom Praschan via Phabricator via cfe-commits

Reply via email to