From 024cfda06cc4f4fb7112344ee7b27d2e1d99978d Mon Sep 17 00:00:00 2001
From: Nikolai Kosjar <nikolai.kosjar@qt.io>
Date: Tue, 22 Aug 2017 14:51:34 +0200
Subject: [PATCH] Clang: Ensure that an unparsed translation unit is not
 suspended

Currently this might happen when registerTranslationUnitsForEditor is
called with visible documents that are not the ones that are registered.

Change-Id: I9ae5f75c8bbff6a11161a3387633726066001062
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
---
 .../clangbackend/ipcsource/clangdocument.cpp  |  5 ++++
 .../clangbackend/ipcsource/clangdocument.h    |  1 +
 .../clangdocumentsuspenderresumer.cpp         |  3 ++-
 .../clangdocumentsuspenderresumer-test.cpp    | 27 +++++++++++++++++++
 4 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/src/tools/clangbackend/ipcsource/clangdocument.cpp b/src/tools/clangbackend/ipcsource/clangdocument.cpp
index a4f95d17b84..cbbaf51b332 100644
--- a/src/tools/clangbackend/ipcsource/clangdocument.cpp
+++ b/src/tools/clangbackend/ipcsource/clangdocument.cpp
@@ -150,6 +150,11 @@ bool Document::isIntact() const
         && !d->hasParseOrReparseFailed;
 }
 
+bool Document::isParsed() const
+{
+    return d->translationUnits.areAllTranslationUnitsParsed();
+}
+
 Utf8String Document::filePath() const
 {
     checkIfNull();
diff --git a/src/tools/clangbackend/ipcsource/clangdocument.h b/src/tools/clangbackend/ipcsource/clangdocument.h
index 260705c1354..415deefbe9d 100644
--- a/src/tools/clangbackend/ipcsource/clangdocument.h
+++ b/src/tools/clangbackend/ipcsource/clangdocument.h
@@ -77,6 +77,7 @@ public:
 
     bool isNull() const;
     bool isIntact() const;
+    bool isParsed() const;
 
     Utf8String filePath() const;
     Utf8StringVector fileArguments() const;
diff --git a/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp
index 8edde48fd62..bae98893187 100644
--- a/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp
+++ b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp
@@ -97,7 +97,8 @@ static bool isSuspendable(const Document &document)
 {
     return isFineDocument(document)
         && !document.isSuspended()
-        && !document.isVisibleInEditor();
+        && !document.isVisibleInEditor()
+        && document.isParsed();
 }
 
 static bool isResumable(const Document &document)
diff --git a/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp b/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp
index 8ab78ebded6..823a84482ca 100644
--- a/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp
+++ b/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp
@@ -73,6 +73,7 @@ protected:
     Document getDocument(const Utf8String &filePath);
     void categorizeDocuments(int hotDocumentsSize);
     SuspendResumeJobs createSuspendResumeJobs(int hotDocumentsSize = -1);
+    static void setParsed(Document &document);
 
 protected:
     ClangBackEnd::ProjectParts projects;
@@ -176,6 +177,8 @@ TEST_F(DocumentSuspenderResumer, CreateSuspendJobForInvisible)
     Document document = documents.create({fileContainer1})[0];
     document.setIsSuspended(false);
     document.setIsVisibleInEditor(false, Clock::now());
+    setParsed(document);
+
     const SuspendResumeJobs expectedJobs = {
         {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed}
     };
@@ -196,12 +199,24 @@ TEST_F(DocumentSuspenderResumer, DoNotCreateSuspendJobForVisible)
     ASSERT_THAT(jobs, ContainerEq(SuspendResumeJobs()));
 }
 
+TEST_F(DocumentSuspenderResumer, DoNotCreateSuspendJobForUnparsed)
+{
+    Document document = documents.create({fileContainer1})[0];
+    document.setIsSuspended(false);
+    document.setIsVisibleInEditor(true, Clock::now());
+
+    const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0);
+
+    ASSERT_THAT(jobs, ContainerEq(SuspendResumeJobs()));
+}
+
 TEST_F(DocumentSuspenderResumer, CreateSuspendJobsForDocumentWithSupportiveTranslationUnit)
 {
     Document document = documents.create({fileContainer1})[0];
     document.setIsSuspended(false);
     document.setIsVisibleInEditor(false, Clock::now());
     document.translationUnits().createAndAppend(); // Add supportive translation unit
+    setParsed(document);
     const SuspendResumeJobs expectedJobs = {
         {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed},
         {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::PreviouslyParsed},
@@ -258,6 +273,7 @@ TEST_F(DocumentSuspenderResumer, CreateSuspendAndResumeJobs)
     Document hotDocument = documents.create({fileContainer1})[0];
     hotDocument.setIsSuspended(true);
     Document coldDocument = documents.create({fileContainer2})[0];
+    setParsed(coldDocument);
     coldDocument.setIsSuspended(false);
     documents.setVisibleInEditors({filePath1});
     const SuspendResumeJobs expectedJobs = {
@@ -292,4 +308,15 @@ DocumentSuspenderResumer::createSuspendResumeJobs(int hotDocumentsSize)
     return ClangBackEnd::createSuspendResumeJobs(documents.documents(), hotDocumentsSize);
 }
 
+void DocumentSuspenderResumer::setParsed(ClangBackEnd::Document &document)
+{
+    const Utf8String first = document.translationUnit().id();
+    document.translationUnits().updateParseTimePoint(first, Clock::now());
+
+    const Utf8String second
+            = document.translationUnit(PreferredTranslationUnit::LastUninitialized).id();
+    if (second != first)
+        document.translationUnits().updateParseTimePoint(second, Clock::now());
+}
+
 } // anonymous
-- 
GitLab