kdev-clang not finding included files

David Nolden david.nolden.kdevelop at art-master.de
Tue Jul 12 10:12:14 UTC 2016


The attached patch works around the clang parsing problem for clang
versions that don't have the fix.

Greetings, David

2016-07-11 23:16 GMT+02:00 Sven Brauch <mail at svenbrauch.de>:
> Ok, follow-up on our IRC talk:
>
> clang is invoked like this:
> Invocation: clang -ferror-limit=100 -fspell-checking -Wdocumentation
> -Wunused-parameter -Wunreachable-code -Wall -std=c++11 -nostdinc
> -nostdinc++ -xc++ -isystem
> /home/sven/install/share/kdevclangsupport/wrappedQtHeaders -isystem
> /home/sven/install/share/kdevclangsupport/wrappedQtHeaders/QtCore
> -isystem/usr/include/qt -isystem/usr/include/qt/QtCore
> -isystem/usr/lib/qt/mkspecs/linux-g++
> -isystem/usr/include/KF5/KTextEditor -isystem/usr/include/KF5
> -isystem/usr/include/KF5/KParts -isystem/usr/include/KF5/KIOWidgets
> -isystem/usr/include/KF5/KIOCore -isystem/usr/include/KF5/KCoreAddons
> -isystem/usr/include/KF5/KService -isystem/usr/include/KF5/KConfigCore
> -isystem/usr/include/KF5/KJobWidgets -isystem/usr/include/qt/QtWidgets
> -isystem/usr/include/qt/QtGui -isystem/usr/include/qt/QtNetwork
> -isystem/usr/include/KF5/KCompletion
> -isystem/usr/include/KF5/KWidgetsAddons -isystem/usr/include/KF5/KXmlGui
> -isystem/usr/include/qt/QtDBus -isystem/usr/include/qt/QtXml
> -isystem/usr/include/KF5/KConfigWidgets -isystem/usr/include/KF5/KCodecs
> -isystem/usr/include/KF5/KConfigGui -isystem/usr/include/KF5/KAuth
> -isystem/usr/include/KF5/KTextWidgets -isystem/usr/include/KF5/SonnetUi
> -isystem/usr/include/KF5/KI18n -isystem/usr/include/KF5/ThreadWeaver
> -isystem/home/sven/install/include/kdevplatform
> -isystem/home/sven/install/include -isystem/usr/include/KF5/KItemModels
> -isystem/usr/include -isystem/usr/include/c++/6.1.1
> -isystem/usr/include/c++/6.1.1/x86_64-pc-linux-gnu
> -isystem/usr/include/c++/6.1.1/backward -isystem/usr/local/include
> -isystem/usr/lib/clang/3.8.0/include -isystem/usr/include
> -I/home/sven/Projekte/kde/kdevelop/build/languages/clang
> -I/home/sven/Projekte/kde/kdevelop/languages/clang
> -I/home/sven/Projekte/kde/kdevelop
> -I/home/sven/Projekte/kde/kdevelop/build
> -I/home/sven/Projekte/kde/kdevelop/languages/clang/libs
> -I/home/sven/Projekte/kde/kdevelop/languages/plugin -imacros
> /tmp/kdevelop.pJ1057
> /home/sven/Projekte/kde/kdevelop/languages/clang/duchain/parsesession.cpp
>
> Doing that on command line reproduces the issue. The issue is that
> -isystem/usr/include is there twice, once as first and once as last path
> in the standard include paths list. If you remove the _first_ occurence,
> it works. I suspect some kind of circular #include or something if you
> have it in the first place.
>
> Do you clang people know what the reason for this is?
>
> Best,
> Sven
>
>
> _______________________________________________
> KDevelop-devel mailing list
> KDevelop-devel at kde.org
> https://mail.kde.org/mailman/listinfo/kdevelop-devel
>
-------------- next part --------------
commit 0b52400c6e6f7bf268607c3f4f47991920c2984f
Author: David Nolden <david.nolden.kde at art-master.de>
Date:   Mon Jul 4 09:35:40 2016 +0200

    annihilate missing includes

diff --git a/languages/clang/clangparsejob.cpp b/languages/clang/clangparsejob.cpp
index 8375eb5..1c65d10 100644
--- a/languages/clang/clangparsejob.cpp
+++ b/languages/clang/clangparsejob.cpp
@@ -280,6 +280,10 @@ void ClangParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread*
         session.setData(createSessionData());
     }
 
+    if (session.unit()) {
+        session.annihilateMissingIncludes(m_unsavedFiles, m_environment);
+    }
+
     if (!session.unit()) {
         // failed to parse file, unpin and don't try again
         clang()->index()->unpinTranslationUnitForUrl(document());
diff --git a/languages/clang/duchain/parsesession.cpp b/languages/clang/duchain/parsesession.cpp
index aae0661..84dbfea 100644
--- a/languages/clang/duchain/parsesession.cpp
+++ b/languages/clang/duchain/parsesession.cpp
@@ -352,13 +353,70 @@ IndexedString ParseSession::languageString()
     return lang;
 }
 
+void ParseSession::annihilateMissingIncludes(
+    QVector<UnsavedFile>& unsavedFiles, const ClangParsingEnvironment& environment)
+{
+    d->m_staticProblems.clear();
+
+    // unfortunately clang dumps just 1 missing include per parse, so we can
+    // do only one correction per iteration
+    const int maxReparse = 20;
+    for (uint iReparse = 0; iReparse < maxReparse; ++iReparse) {
+        bool fixedMissing = false;
+        const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit);
+        for (uint i = 0; i < numDiagnostics; ++i) {
+            auto diagnostic = clang_getDiagnostic(d->m_unit, i);
+
+            CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
+            if (ClangDiagnosticEvaluator::diagnosticType(diagnostic) == ClangDiagnosticEvaluator::IncludeFileNotFoundProblem) {
+                CXFile diagnosticFile;
+                uint line = 0;
+                clang_getFileLocation(location, &diagnosticFile, &line, nullptr, nullptr);
+                QString fileName = ClangString(clang_getFileName(diagnosticFile)).toString();
+                QStringList fileContents;
+                for (int i = 0; i < unsavedFiles.count(); ++i) {
+                    if (unsavedFiles[i].filename() == fileName) {
+                        fileContents = unsavedFiles[i].contents();
+                        unsavedFiles.removeAt(i);
+                        break;
+                    }
+                }
+                if (fileContents.isEmpty()) {   // need to read the file contents from disk
+                    QFile file(fileName);
+                    if (file.open(QIODevice::ReadOnly)) {
+                        fileContents = QString::fromLocal8Bit(file.readAll()).split(QChar::fromLatin1('\n'));
+                    }
+                }
+                line -= 1; // clang seems to start counting at 1
+                if (line < (uint)fileContents.count() && fileContents[line].simplified().contains(QLatin1String("#include "))) {
+                    fileContents[line] = QString::fromLatin1("// REMOVED BY ANNIHILATION: ")+fileContents[line];
+                    unsavedFiles << UnsavedFile(fileName, fileContents);
+                    fixedMissing = true;
+                    ProblemPointer problem = ProblemPointer(ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit));
+                    d->m_staticProblems[fileName] << problem;
+                    d->m_staticProblems[ClangString(clang_getFileName(d->m_file)).toString()] << problem;
+                    qDebug(KDEV_CLANG) << "annihilated missing include: " << problem->toString();
+                }
+            }
+        }
+        if (!fixedMissing) {
+            break;
+        }
+        qDebug(KDEV_CLANG) << "reparsing to fix missing includes";
+
+        if (!reparse(unsavedFiles, environment)) {
+            break;
+        }
+    }
+}
+
 QList<ProblemPointer> ParseSession::problemsForFile(CXFile file) const
 {
     if (!d) {
         return {};
     }
 
-    QList<ProblemPointer> problems;
+    QList<ProblemPointer> problems = d->m_staticProblems[ClangString(clang_getFileName(file)).toString()];
 
     // extra clang diagnostics
     const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit);
@@ -369,9 +427,7 @@ QList<ProblemPointer> ParseSession::problemsForFile(CXFile file) const
         CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
         CXFile diagnosticFile;
         clang_getFileLocation(location, &diagnosticFile, nullptr, nullptr, nullptr);
-        // missing-include problems are so severe in clang that we always propagate
-        // them to this document, to ensure that the user will see the error.
-        if (diagnosticFile != file && ClangDiagnosticEvaluator::diagnosticType(diagnostic) != ClangDiagnosticEvaluator::IncludeFileNotFoundProblem) {
+        if (diagnosticFile != file) {
             continue;
         }
 
@@ -434,9 +490,12 @@ bool ParseSession::reparse(const QVector<UnsavedFile>& unsavedFiles, const Clang
 
     const auto code = clang_reparseTranslationUnit(d->m_unit, unsaved.size(), unsaved.data(),
                                                    clang_defaultReparseOptions(d->m_unit));
-    if (code != CXError_Success) {
-        qWarning() << "clang_reparseTranslationUnit return with error code" << code;
+    if (code == CXError_Success) {
+        d->setUnit(d->m_unit);
+        return true;
+    } else {
         // if error code != 0 => clang_reparseTranslationUnit invalidates the old translation unit => clean up
+        qWarning() << "clang_reparseTranslationUnit return with error code" << code;
         clang_disposeTranslationUnit(d->m_unit);
         d->setUnit(nullptr);
         return false;
diff --git a/languages/clang/duchain/parsesession.h b/languages/clang/duchain/parsesession.h
index cd57c8a..e77617f 100644
--- a/languages/clang/duchain/parsesession.h
+++ b/languages/clang/duchain/parsesession.h
@@ -80,6 +80,7 @@ private:
     /// TODO: share this file for all TUs that use the same defines (probably most in a project)
     ///       best would be a PCH, if possible
     QTemporaryFile m_definesFile;
+    QMap<QString, QList<KDevelop::ProblemPointer>> m_staticProblems;
 };
 
 /**
@@ -127,6 +128,14 @@ public:
 
     bool reparse(const QVector<UnsavedFile>& unsavedFiles, const ClangParsingEnvironment& environment);
 
+    /**
+     * If an #include was not found, then this adapts the unsaved-file data
+     * so that the problematic #include code line is removed.
+     * @return The corresponding problem-pointer. The problem-pointer is invalid
+     * if no problem as found.
+     */
+    void annihilateMissingIncludes(QVector<UnsavedFile>& unsavedFiles, const ClangParsingEnvironment& environment);
+
     ClangParsingEnvironment environment() const;
 
 private:
diff --git a/languages/clang/duchain/unsavedfile.cpp b/languages/clang/duchain/unsavedfile.cpp
index bef86df..8ab7b42 100644
--- a/languages/clang/duchain/unsavedfile.cpp
+++ b/languages/clang/duchain/unsavedfile.cpp
@@ -33,6 +33,17 @@ UnsavedFile::UnsavedFile(const QString& fileName, const QStringList& contents)
 {
 }
 
+QStringList UnsavedFile::contents() const
+{
+    Q_ASSERT(m_contentsUtf8.isEmpty() || !m_contents.isEmpty());
+    return m_contents;
+}
+
+QString UnsavedFile::filename() const
+{
+    return m_fileName;
+}
+
 CXUnsavedFile UnsavedFile::toClangApi() const
 {
     if (m_fileNameUtf8.isEmpty()) {
diff --git a/languages/clang/duchain/unsavedfile.h b/languages/clang/duchain/unsavedfile.h
index a3442c7..e2050fa 100644
--- a/languages/clang/duchain/unsavedfile.h
+++ b/languages/clang/duchain/unsavedfile.h
@@ -38,6 +38,10 @@ public:
 
     CXUnsavedFile toClangApi() const;
 
+    QStringList contents() const;
+
+    QString filename() const;
+
 private:
     QString m_fileName;
     QStringList m_contents;


More information about the KDevelop-devel mailing list