From a0d6df7bd85bf88e4ac3643d97d35e04389f6309 Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@digia.com>
Date: Thu, 11 Apr 2013 12:51:06 +0200
Subject: [PATCH] C++: also refresh files not in the project when project
 changes.

System headers and other file which are not explicitly mentioned in the
project must be reparsed when the project changes.

Task-number: QTCREATORBUG-9056
Change-Id: I32f1206d241a078a4d9b15fac5813f365a1ba303
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
---
 src/libs/cplusplus/CppDocument.cpp            | 42 +++++++++-------
 src/libs/cplusplus/CppDocument.h              |  4 +-
 src/plugins/cpptools/cppmodelmanager.cpp      |  8 +++-
 src/plugins/cpptools/cppmodelmanager_test.cpp | 48 +++++++++++++++++++
 src/plugins/cpptools/cpptoolsplugin.h         |  1 +
 .../cpptools/modelmanagertesthelper.cpp       | 17 +++++++
 src/plugins/cpptools/modelmanagertesthelper.h |  9 ++++
 .../sources/test_modelmanager_refresh.cpp     |  1 +
 .../sources/test_modelmanager_refresh.h       |  4 ++
 9 files changed, 116 insertions(+), 18 deletions(-)
 create mode 100644 tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.cpp
 create mode 100644 tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.h

diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index 2385238b9c7..13c0b3f6ad8 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -705,30 +705,40 @@ Document::Ptr Snapshot::documentFromSource(const QByteArray &preprocessedCode,
     return newDoc;
 }
 
-Document::Ptr Snapshot::document(const QString &fileName) const
+QSet<QString> Snapshot::allIncludesForDocument(const QString &fileName) const
 {
-    return _documents.value(fileName);
+    QSet<QString> result;
+    allIncludesForDocument_helper(fileName, result);
+    return result;
 }
 
-Snapshot Snapshot::simplified(Document::Ptr doc) const
+void Snapshot::allIncludesForDocument_helper(const QString &fileName, QSet<QString> &result) const
 {
-    Snapshot snapshot;
-    simplified_helper(doc, &snapshot);
-    return snapshot;
+    if (Document::Ptr doc = document(fileName)) {
+        foreach (const QString &inc, doc->includedFiles()) {
+            if (!result.contains(inc)) {
+                result.insert(inc);
+                allIncludesForDocument_helper(inc, result);
+            }
+        }
+    }
 }
 
-void Snapshot::simplified_helper(Document::Ptr doc, Snapshot *snapshot) const
+Document::Ptr Snapshot::document(const QString &fileName) const
 {
-    if (! doc)
-        return;
+    return _documents.value(fileName);
+}
 
-    if (! snapshot->contains(doc->fileName())) {
-        snapshot->insert(doc);
+Snapshot Snapshot::simplified(Document::Ptr doc) const
+{
+    Snapshot snapshot;
 
-        foreach (const Document::Include &incl, doc->includes()) {
-            Document::Ptr includedDoc = document(incl.fileName());
-            simplified_helper(includedDoc, snapshot);
-        }
+    if (doc) {
+        snapshot.insert(doc);
+        foreach (const QString &fileName, allIncludesForDocument(doc->fileName()))
+            if (Document::Ptr inc = document(fileName))
+                snapshot.insert(inc);
     }
-}
 
+    return snapshot;
+}
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 9d2cd128e21..589e2b9503a 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -389,8 +389,10 @@ public:
     Document::Ptr documentFromSource(const QByteArray &preprocessedDocument,
                                      const QString &fileName) const;
 
+    QSet<QString> allIncludesForDocument(const QString &fileName) const;
+
 private:
-    void simplified_helper(Document::Ptr doc, Snapshot *snapshot) const;
+    void allIncludesForDocument_helper(const QString &fileName, QSet<QString> &result) const;
 
 private:
     _Base _documents;
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index e382171c398..cef667fec0e 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -970,10 +970,16 @@ void CppModelManager::updateProjectInfo(const ProjectInfo &pinfo)
 
         foreach (const ProjectInfo &projectInfo, m_projects) {
             foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
-                foreach (const ProjectFile &cxxFile, projectPart->files)
+                foreach (const ProjectFile &cxxFile, projectPart->files) {
                     m_srcToProjectPart[cxxFile.path].append(projectPart);
+                    foreach (const QString &fileName, m_snapshot.allIncludesForDocument(cxxFile.path))
+                        m_snapshot.remove(fileName);
+                    m_snapshot.remove(cxxFile.path);
+                }
             }
         }
+
+        m_snapshot.remove(configurationFileName());
     }
 
     if (!qgetenv("QTCREATOR_DUMP_PROJECT_INFO").isEmpty())
diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp
index 4525b95a35f..2a9462674f4 100644
--- a/src/plugins/cpptools/cppmodelmanager_test.cpp
+++ b/src/plugins/cpptools/cppmodelmanager_test.cpp
@@ -138,3 +138,51 @@ void CppToolsPlugin::test_modelmanager_framework_headers()
         QVERIFY(chars.startsWith("success"));
     }
 }
+
+/// QTCREATORBUG-9056
+void CppToolsPlugin::test_modelmanager_refresh()
+{
+    ModelManagerTestHelper helper;
+    CppModelManager *mm = CppModelManager::instance();
+
+    const QString testCpp(testSource(QLatin1String("test_modelmanager_refresh.cpp")));
+    const QString testHeader(testSource(QLatin1String("test_modelmanager_refresh.h")));
+
+    Project *project = helper.createProject(QLatin1String("test_modelmanager_refresh"));
+    ProjectInfo pi = mm->projectInfo(project);
+    QCOMPARE(pi.project().data(), project);
+
+    ProjectPart::Ptr part(new ProjectPart);
+    pi.appendProjectPart(part);
+    part->cxxVersion = ProjectPart::CXX98;
+    part->qtVersion = ProjectPart::Qt5;
+    part->defines = QByteArray("#define OH_BEHAVE -1\n");
+    part->includePaths = QStringList() << testIncludeDir(false);
+    part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource));
+
+    mm->updateProjectInfo(pi);
+    mm->updateSourceFiles(QStringList() << testCpp);
+
+    QStringList refreshedFiles = helper.waitForRefreshedSourceFiles();
+
+    QCOMPARE(refreshedFiles.size(), 1);
+    QVERIFY(refreshedFiles.contains(testCpp));
+    CPlusPlus::Snapshot snapshot = mm->snapshot();
+    QVERIFY(snapshot.contains(testHeader));
+    QVERIFY(snapshot.contains(testCpp));
+
+    part->defines = QByteArray();
+    mm->updateProjectInfo(pi);
+    snapshot = mm->snapshot();
+    QVERIFY(!snapshot.contains(testHeader));
+    QVERIFY(!snapshot.contains(testCpp));
+
+    mm->updateSourceFiles(QStringList() << testCpp);
+    refreshedFiles = helper.waitForRefreshedSourceFiles();
+
+    QCOMPARE(refreshedFiles.size(), 1);
+    QVERIFY(refreshedFiles.contains(testCpp));
+    snapshot = mm->snapshot();
+    QVERIFY(snapshot.contains(testHeader));
+    QVERIFY(snapshot.contains(testCpp));
+}
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 4e6ead7b0a9..dfce31798ab 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -137,6 +137,7 @@ private slots:
 
     void test_modelmanager_paths();
     void test_modelmanager_framework_headers();
+    void test_modelmanager_refresh();
 
 private:
     void test_completion();
diff --git a/src/plugins/cpptools/modelmanagertesthelper.cpp b/src/plugins/cpptools/modelmanagertesthelper.cpp
index 54dc946bbad..46f2a917239 100644
--- a/src/plugins/cpptools/modelmanagertesthelper.cpp
+++ b/src/plugins/cpptools/modelmanagertesthelper.cpp
@@ -54,6 +54,7 @@ ModelManagerTestHelper::ModelManagerTestHelper(QObject *parent) :
 
     connect(this, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)), mm, SLOT(onAboutToRemoveProject(ProjectExplorer::Project*)));
     connect(this, SIGNAL(projectAdded(ProjectExplorer::Project*)), mm, SLOT(onProjectAdded(ProjectExplorer::Project*)));
+    connect(mm, SIGNAL(sourceFilesRefreshed(QStringList)), this, SLOT(sourceFilesRefreshed(QStringList)));
 
     cleanup();
     verifyClean();
@@ -97,3 +98,19 @@ ModelManagerTestHelper::Project *ModelManagerTestHelper::createProject(const QSt
     return tp;
 }
 
+QStringList ModelManagerTestHelper::waitForRefreshedSourceFiles()
+{
+    m_refreshHappened = false;
+
+    while (!m_refreshHappened)
+        QCoreApplication::processEvents();
+
+    return m_lastRefreshedSourceFiles;
+}
+
+
+void ModelManagerTestHelper::sourceFilesRefreshed(const QStringList &files)
+{
+    m_lastRefreshedSourceFiles = files;
+    m_refreshHappened = true;
+}
diff --git a/src/plugins/cpptools/modelmanagertesthelper.h b/src/plugins/cpptools/modelmanagertesthelper.h
index ade58b32f64..8a228cac42a 100644
--- a/src/plugins/cpptools/modelmanagertesthelper.h
+++ b/src/plugins/cpptools/modelmanagertesthelper.h
@@ -83,9 +83,18 @@ public:
 
     Project *createProject(const QString &name);
 
+    QStringList waitForRefreshedSourceFiles();
+
 signals:
     void aboutToRemoveProject(ProjectExplorer::Project *project);
     void projectAdded(ProjectExplorer::Project*);
+
+public slots:
+    void sourceFilesRefreshed(const QStringList &files);
+
+private:
+    bool m_refreshHappened;
+    QStringList m_lastRefreshedSourceFiles;
 };
 
 } // namespace Internal
diff --git a/tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.cpp b/tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.cpp
new file mode 100644
index 00000000000..ad92e705f6f
--- /dev/null
+++ b/tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.cpp
@@ -0,0 +1 @@
+#include "test_modelmanager_refresh.h"
diff --git a/tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.h b/tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.h
new file mode 100644
index 00000000000..43bcd865776
--- /dev/null
+++ b/tests/cppmodelmanager/testdata/sources/test_modelmanager_refresh.h
@@ -0,0 +1,4 @@
+#ifndef test_modelmanager_refresh_h
+#define test_modelmanager_refresh_h
+
+#endif // test_modelmanager_refresh_h
-- 
GitLab