[clang-tools-extra] r333189 - [clangd] Serve comments for headers decls from dynamic index only

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Thu May 24 07:49:24 PDT 2018


Author: ibiryukov
Date: Thu May 24 07:49:23 2018
New Revision: 333189

URL: http://llvm.org/viewvc/llvm-project?rev=333189&view=rev
Log:
[clangd] Serve comments for headers decls from dynamic index only

Summary:
To fix a crash in code completion that occurrs when reading doc
comments from files that were updated after the preamble was
computed. In that case, the files on disk could've been changed and we
can't rely on finding the comment text with the same range anymore.

The current workaround is to not provide comments from the headers at
all and rely on the dynamic index instead.

A more principled solution would be to store contents of the files
read inside the preamble, but it is way harder to implement properly,
given that it would definitely increase the sizes of the preamble.

Together with D47272, this should fix all preamble-related crashes
we're aware of.

Reviewers: sammccall

Reviewed By: sammccall

Subscribers: klimek, ioeric, MaskRay, jkorous, cfe-commits

Differential Revision: https://reviews.llvm.org/D47274

Modified:
    clang-tools-extra/trunk/clangd/CodeComplete.cpp
    clang-tools-extra/trunk/clangd/CodeCompletionStrings.cpp
    clang-tools-extra/trunk/clangd/CodeCompletionStrings.h
    clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp
    clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp

Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=333189&r1=333188&r2=333189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original)
+++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Thu May 24 07:49:23 2018
@@ -586,9 +586,11 @@ public:
       const auto *CCS = Candidate.CreateSignatureString(
           CurrentArg, S, *Allocator, CCTUInfo, true);
       assert(CCS && "Expected the CodeCompletionString to be non-null");
+      // FIXME: for headers, we need to get a comment from the index.
       SigHelp.signatures.push_back(ProcessOverloadCandidate(
           Candidate, *CCS,
-          getParameterDocComment(S.getASTContext(), Candidate, CurrentArg)));
+          getParameterDocComment(S.getASTContext(), Candidate, CurrentArg,
+                                 /*CommentsFromHeader=*/false)));
     }
   }
 
@@ -1030,7 +1032,8 @@ private:
       SemaCCS = Recorder->codeCompletionString(*SR);
       if (Opts.IncludeComments) {
         assert(Recorder->CCSema);
-        DocComment = getDocComment(Recorder->CCSema->getASTContext(), *SR);
+        DocComment = getDocComment(Recorder->CCSema->getASTContext(), *SR,
+                                   /*CommentsFromHeader=*/false);
       }
     }
     return Candidate.build(FileName, Scores, Opts, SemaCCS, Includes.get(), DocComment);

Modified: clang-tools-extra/trunk/clangd/CodeCompletionStrings.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeCompletionStrings.cpp?rev=333189&r1=333188&r2=333189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CodeCompletionStrings.cpp (original)
+++ clang-tools-extra/trunk/clangd/CodeCompletionStrings.cpp Thu May 24 07:49:23 2018
@@ -10,6 +10,7 @@
 #include "CodeCompletionStrings.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/RawCommentList.h"
+#include "clang/Basic/SourceManager.h"
 #include <utility>
 
 namespace clang {
@@ -122,10 +123,23 @@ void processSnippetChunks(const CodeComp
   }
 }
 
+std::string getFormattedComment(const ASTContext &Ctx, const RawComment &RC,
+                                bool CommentsFromHeaders) {
+  auto &SourceMgr = Ctx.getSourceManager();
+  // Parsing comments from invalid preamble can lead to crashes. So we only
+  // return comments from the main file when doing code completion. For
+  // indexing, we still read all the comments.
+  // FIXME: find a better fix, e.g. store file contents in the preamble or get
+  // doc comments from the index.
+  if (!CommentsFromHeaders && !SourceMgr.isWrittenInMainFile(RC.getLocStart()))
+    return "";
+  return RC.getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
+}
 } // namespace
 
 std::string getDocComment(const ASTContext &Ctx,
-                          const CodeCompletionResult &Result) {
+                          const CodeCompletionResult &Result,
+                          bool CommentsFromHeaders) {
   // 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.
@@ -137,20 +151,20 @@ std::string getDocComment(const ASTConte
   const RawComment *RC = getCompletionComment(Ctx, Decl);
   if (!RC)
     return "";
-  return RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
+  return getFormattedComment(Ctx, *RC, CommentsFromHeaders);
 }
 
 std::string
 getParameterDocComment(const ASTContext &Ctx,
                        const CodeCompleteConsumer::OverloadCandidate &Result,
-                       unsigned ArgIndex) {
+                       unsigned ArgIndex, bool CommentsFromHeaders) {
   auto Func = Result.getFunction();
   if (!Func)
     return "";
   const RawComment *RC = getParameterComment(Ctx, Result, ArgIndex);
   if (!RC)
     return "";
-  return RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
+  return getFormattedComment(Ctx, *RC, CommentsFromHeaders);
 }
 
 void getLabelAndInsertText(const CodeCompletionString &CCS, std::string *Label,

Modified: clang-tools-extra/trunk/clangd/CodeCompletionStrings.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeCompletionStrings.h?rev=333189&r1=333188&r2=333189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CodeCompletionStrings.h (original)
+++ clang-tools-extra/trunk/clangd/CodeCompletionStrings.h Thu May 24 07:49:23 2018
@@ -25,18 +25,25 @@ namespace clangd {
 /// markers stripped. See clang::RawComment::getFormattedText() for the detailed
 /// explanation of how the comment text is transformed.
 /// Returns empty string when no comment is available.
+/// 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);
+                          const CodeCompletionResult &Result,
+                          bool CommentsFromHeaders);
 
 /// Gets a minimally formatted documentation for parameter of \p Result,
 /// corresponding to argument number \p ArgIndex.
 /// This currently looks for comments attached to the parameter itself, and
 /// doesn't extract them from function documentation.
 /// Returns empty string when no comment is available.
+/// 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
 getParameterDocComment(const ASTContext &Ctx,
                        const CodeCompleteConsumer::OverloadCandidate &Result,
-                       unsigned ArgIndex);
+                       unsigned ArgIndex, bool CommentsFromHeaders);
 
 /// Gets label and insert text for a completion item. For example, for function
 /// `Foo`, this returns <"Foo(int x, int y)", "Foo"> without snippts enabled.

Modified: clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp?rev=333189&r1=333188&r2=333189&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/SymbolCollector.cpp Thu May 24 07:49:23 2018
@@ -389,7 +389,8 @@ const Symbol *SymbolCollector::addDeclar
                         /*EnableSnippets=*/false);
   std::string FilterText = getFilterText(*CCS);
   std::string Documentation =
-      formatDocumentation(*CCS, getDocComment(Ctx, SymbolCompletion));
+      formatDocumentation(*CCS, getDocComment(Ctx, SymbolCompletion,
+                                              /*CommentsFromHeaders=*/true));
   std::string CompletionDetail = getDetail(*CCS);
 
   std::string Include;

Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=333189&r1=333188&r2=333189&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Thu May 24 07:49:23 2018
@@ -17,6 +17,7 @@
 #include "SyncAPI.h"
 #include "TestFS.h"
 #include "index/MemIndex.h"
+#include "llvm/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -998,6 +999,46 @@ TEST(CompletionTest, NoIndexCompletionsI
   }
 }
 
+TEST(CompletionTest, DocumentationFromChangedFileCrash) {
+  MockFSProvider FS;
+  auto FooH = testPath("foo.h");
+  auto FooCpp = testPath("foo.cpp");
+  FS.Files[FooH] = R"cpp(
+    // this is my documentation comment.
+    int func();
+  )cpp";
+  FS.Files[FooCpp] = "";
+
+  MockCompilationDatabase CDB;
+  IgnoreDiagnostics DiagConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+
+  Annotations Source(R"cpp(
+    #include "foo.h"
+    int func() {
+      // This makes sure we have func from header in the AST.
+    }
+    int a = fun^
+  )cpp");
+  Server.addDocument(FooCpp, Source.code(), WantDiagnostics::Yes);
+  // We need to wait for preamble to build.
+  ASSERT_TRUE(Server.blockUntilIdleForTest());
+
+  // Change the header file. Completion will reuse the old preamble!
+  FS.Files[FooH] = R"cpp(
+    int func();
+  )cpp";
+
+  clangd::CodeCompleteOptions Opts;
+  Opts.IncludeComments = true;
+  CompletionList Completions =
+      cantFail(runCodeComplete(Server, FooCpp, Source.point(), Opts));
+  // We shouldn't crash. Unfortunately, current workaround is to not produce
+  // comments for symbols from headers.
+  EXPECT_THAT(Completions.items,
+              Contains(AllOf(Not(IsDocumented()), Named("func"))));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang




More information about the cfe-commits mailing list