File indexing completed on 2024-05-12 04:39:13

0001 /*
0002     SPDX-FileCopyrightText: 2013 Olivier de Gaalon <olivier.jg@gmail.com>
0003     SPDX-FileCopyrightText: 2013 Milian Wolff <mail@milianw.de>
0004     SPDX-FileCopyrightText: 2013 Kevin Funk <kfunk@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "parsesession.h"
0010 #include <QStandardPaths>
0011 #include "clangdiagnosticevaluator.h"
0012 #include "todoextractor.h"
0013 #include "clanghelpers.h"
0014 #include "clangindex.h"
0015 #include "clangparsingenvironment.h"
0016 #include "util/clangdebug.h"
0017 #include "util/clangtypes.h"
0018 #include "util/clangutils.h"
0019 #include "headerguardassistant.h"
0020 
0021 #include <language/duchain/duchainlock.h>
0022 #include <language/duchain/duchain.h>
0023 #include <language/codegen/coderepresentation.h>
0024 
0025 #include <KShell>
0026 
0027 #include <QDir>
0028 #include <QFileInfo>
0029 #include <QMimeDatabase>
0030 #include <QMimeType>
0031 
0032 #include <algorithm>
0033 
0034 using namespace KDevelop;
0035 
0036 namespace {
0037 
0038 QVector<QByteArray> extraArgs()
0039 {
0040     const auto extraArgsString = QString::fromLatin1(qgetenv("KDEV_CLANG_EXTRA_ARGUMENTS"));
0041     const auto extraArgs = KShell::splitArgs(extraArgsString);
0042 
0043     // transform to list of QByteArrays
0044     QVector<QByteArray> result;
0045     result.reserve(extraArgs.size());
0046     for (const QString& arg : extraArgs) {
0047         result << arg.toLatin1();
0048     }
0049     clangDebug() << "Passing extra arguments to clang:" << result;
0050 
0051     return result;
0052 }
0053 
0054 void removeHarmfulArguments(QVector<QByteArray>& arguments)
0055 {
0056     const auto shouldRemove = [](const QByteArray& argument) {
0057         constexpr const char* toRemove[] = {
0058             "-Werror",
0059             "-Wlogical-op", // prevent runtime -Wunknown-warning-option warning because Clang does not support this flag
0060 #if CINDEX_VERSION_MINOR < 100 // FIXME https://bugs.llvm.org/show_bug.cgi?id=35333
0061             "-Wdocumentation",
0062 #endif
0063         };
0064         return std::any_of(std::cbegin(toRemove), std::cend(toRemove), [&argument](const char* rm) {
0065             return argument == rm;
0066         });
0067     };
0068 
0069     const auto end = arguments.end();
0070     const auto logicalEnd = std::remove_if(arguments.begin(), end, shouldRemove);
0071     arguments.erase(logicalEnd, end);
0072 }
0073 
0074 void sanitizeArguments(QVector<QByteArray>& arguments)
0075 {
0076     removeHarmfulArguments(arguments);
0077 
0078     // We remove the -Werror flag, and replace -Werror=foo by -Wfoo.
0079     // Warning as error may cause problem to the clang parser.
0080     const auto asError = QByteArrayLiteral("-Werror=");
0081     // Silence common warning that arises when we parse as a GCC-lookalike.
0082     // Note how clang warns us about emulating GCC, which is exactly what we want here.
0083     const auto noGnuZeroVaridicMacroArguments = QByteArrayLiteral("-Wno-gnu-zero-variadic-macro-arguments");
0084     bool noGnuZeroVaridicMacroArgumentsFound = false;
0085     bool isCpp = false;
0086     const auto sizedDealloc = QByteArrayLiteral("-fsized-deallocation");
0087     bool sizedDeallocFound = false;
0088     for (auto& argument : arguments) {
0089         if (argument.startsWith(asError)) {
0090             // replace -Werror=foo by -Wfoo
0091             argument.remove(2, asError.length() - 2);
0092         } else if (!noGnuZeroVaridicMacroArgumentsFound && argument == noGnuZeroVaridicMacroArguments) {
0093             noGnuZeroVaridicMacroArgumentsFound = true;
0094         } else if (!isCpp && argument == "-xc++") {
0095             isCpp = true;
0096         } else if (!sizedDeallocFound && argument == sizedDealloc) {
0097             sizedDeallocFound = true;
0098         }
0099     }
0100 
0101     if (!noGnuZeroVaridicMacroArgumentsFound) {
0102         arguments.append(noGnuZeroVaridicMacroArguments);
0103     }
0104     if (isCpp && !sizedDeallocFound) {
0105         // see e.g.
0106         // https://youtrack.jetbrains.com/issue/CPP-29091/In-template-call-to-builtinoperatordelete-selects-non-usual-deallocation-function-gcc-12#focus=Comments-27-6067190.0-0
0107         arguments.append(sizedDealloc);
0108     }
0109 }
0110 
0111 QVector<QByteArray> argsForSession(const QString& path, ParseSessionData::Options options, const ParserSettings& parserSettings)
0112 {
0113     QMimeDatabase db;
0114     if (db.mimeTypeForFile(path).name() == QLatin1String("text/x-objcsrc")) {
0115         return {QByteArrayLiteral("-xobjective-c++")};
0116     }
0117 
0118     // TODO: No proper mime type detection possible yet
0119     // cf. https://bugs.freedesktop.org/show_bug.cgi?id=26913
0120     if (path.endsWith(QLatin1String(".cl"), Qt::CaseInsensitive)) {
0121         return {QByteArrayLiteral("-xcl")};
0122     }
0123 
0124     // TODO: No proper mime type detection possible yet
0125     // cf. https://bugs.freedesktop.org/show_bug.cgi?id=23700
0126     if (path.endsWith(QLatin1String(".cu"), Qt::CaseInsensitive) ||
0127         path.endsWith(QLatin1String(".cuh"), Qt::CaseInsensitive)) {
0128         auto result = parserSettings.toClangAPI();
0129         result.append(QByteArrayLiteral("-xcuda"));
0130         return result;
0131     }
0132 
0133     if (parserSettings.parserOptions.isEmpty()) {
0134         // The parserOptions can be empty for some unit tests that use ParseSession directly
0135         auto defaultArguments = ClangSettingsManager::self()->parserSettings(path).toClangAPI();
0136 
0137         defaultArguments.append(QByteArrayLiteral("-nostdinc"));
0138         defaultArguments.append(QByteArrayLiteral("-nostdinc++"));
0139         defaultArguments.append(QByteArrayLiteral("-xc++"));
0140 
0141         sanitizeArguments(defaultArguments);
0142         return defaultArguments;
0143     }
0144 
0145     auto result = parserSettings.toClangAPI();
0146     result.append(QByteArrayLiteral("-nostdinc"));
0147     if (parserSettings.isCpp()) {
0148         result.append(QByteArrayLiteral("-nostdinc++"));
0149     }
0150 
0151     if (options & ParseSessionData::PrecompiledHeader) {
0152         result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++-header") : QByteArrayLiteral("-xc-header"));
0153 
0154         sanitizeArguments(result);
0155         return result;
0156     }
0157 
0158     result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++") : QByteArrayLiteral("-xc"));
0159 
0160     sanitizeArguments(result);
0161     return result;
0162 }
0163 
0164 void addIncludes(QVector<const char*>* args, QVector<QByteArray>* otherArgs,
0165                  const Path::List& includes, const char* cliSwitch)
0166 {
0167     for (const Path& url : includes) {
0168         if (url.isEmpty()) {
0169             continue;
0170         }
0171 
0172         QFileInfo info(url.toLocalFile());
0173         QByteArray path = url.toLocalFile().toUtf8();
0174 
0175         if (info.isFile()) {
0176             path.prepend("-include");
0177         } else {
0178             path.prepend(cliSwitch);
0179         }
0180         otherArgs->append(path);
0181         args->append(path.constData());
0182     }
0183 }
0184 
0185 void addFrameworkDirectories(QVector<const char*>* args, QVector<QByteArray>* otherArgs,
0186                  const Path::List& frameworkDirectories, const char* cliSwitch)
0187 {
0188     for (const Path& url : frameworkDirectories) {
0189         if (url.isEmpty()) {
0190             continue;
0191         }
0192 
0193         QFileInfo info(url.toLocalFile());
0194         if (!info.isDir()) {
0195             qCWarning(KDEV_CLANG) << "supposed framework directory is not a directory:" << url.pathOrUrl();
0196             continue;
0197         }
0198         QByteArray path = url.toLocalFile().toUtf8();
0199 
0200         otherArgs->append(cliSwitch);
0201         otherArgs->append(path);
0202         args->append(cliSwitch);
0203         args->append(path.constData());
0204     }
0205 }
0206 
0207 QVector<CXUnsavedFile> toClangApi(const QVector<UnsavedFile>& unsavedFiles)
0208 {
0209     QVector<CXUnsavedFile> unsaved;
0210     unsaved.reserve(unsavedFiles.size());
0211     std::transform(unsavedFiles.begin(), unsavedFiles.end(),
0212                 std::back_inserter(unsaved),
0213                 [] (const UnsavedFile& file) { return file.toClangApi(); });
0214     return unsaved;
0215 }
0216 
0217 bool notClangAndGccVersionAtLeast(const QMap<QString, QString>& defines, int minGccMajorVersion)
0218 {
0219     return !defines.contains(QStringLiteral("__clang__"))
0220         && defines.value(QStringLiteral("__GNUC__")).toInt() >= minGccMajorVersion;
0221 }
0222 
0223 bool hasQtIncludes(const Path::List& includePaths)
0224 {
0225     return std::find_if(includePaths.begin(), includePaths.end(), [] (const Path& path) {
0226         return path.lastPathSegment() == QLatin1String("QtCore");
0227     }) != includePaths.end();
0228 }
0229 
0230 }
0231 
0232 ParseSessionData::ParseSessionData(const QVector<UnsavedFile>& unsavedFiles, ClangIndex* index,
0233                                    const ClangParsingEnvironment& environment, Options options)
0234     : m_file(nullptr)
0235     , m_unit(nullptr)
0236 {
0237     unsigned int flags = CXTranslationUnit_DetailedPreprocessingRecord
0238 #if CINDEX_VERSION_MINOR >= 34
0239         | CXTranslationUnit_KeepGoing
0240 #endif
0241     ;
0242     if (options.testFlag(SkipFunctionBodies)) {
0243         flags |= CXTranslationUnit_SkipFunctionBodies;
0244     }
0245     if (options.testFlag(PrecompiledHeader)) {
0246         flags |= CXTranslationUnit_ForSerialization;
0247     } else if (environment.quality() == ClangParsingEnvironment::Unknown) {
0248         flags |= CXTranslationUnit_Incomplete;
0249     }
0250     if (options.testFlag(OpenedInEditor)) {
0251         flags |= CXTranslationUnit_CacheCompletionResults
0252 #if CINDEX_VERSION_MINOR >= 32
0253               |  CXTranslationUnit_CreatePreambleOnFirstParse
0254 #endif
0255               |  CXTranslationUnit_PrecompiledPreamble;
0256     }
0257 
0258     const auto tuUrl = environment.translationUnitUrl();
0259     Q_ASSERT(!tuUrl.isEmpty());
0260 
0261     const auto arguments = argsForSession(tuUrl.str(), options, environment.parserSettings());
0262     QVector<const char*> clangArguments;
0263 
0264     const auto& includes = environment.includes();
0265     const auto& pchInclude = environment.pchInclude();
0266 
0267     // uses QByteArray as smart-pointer for const char* ownership
0268     QVector<QByteArray> smartArgs;
0269     smartArgs.reserve(includes.system.size() + includes.project.size()
0270                       + pchInclude.isValid() + arguments.size() + 1);
0271     clangArguments.reserve(smartArgs.size());
0272 
0273     std::transform(arguments.constBegin(), arguments.constEnd(),
0274                    std::back_inserter(clangArguments),
0275                    [] (const QByteArray &argument) { return argument.constData(); });
0276 
0277     // NOTE: the PCH include must come before all other includes!
0278     if (pchInclude.isValid()) {
0279         clangArguments << "-include";
0280         QByteArray pchFile = pchInclude.toLocalFile().toUtf8();
0281         smartArgs << pchFile;
0282         clangArguments << pchFile.constData();
0283     }
0284 
0285     if (notClangAndGccVersionAtLeast(environment.defines(), 7)) {
0286         const auto compatFile =
0287             QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0288                                    QStringLiteral("kdevclangsupport/gccCompatibility/additional_floating_types.h"))
0289                 .toUtf8();
0290         if (!compatFile.isEmpty()) {
0291             smartArgs << compatFile;
0292             clangArguments << "-include" << compatFile.constData();
0293         }
0294     }
0295 
0296     if (hasQtIncludes(includes.system)) {
0297         const auto wrappedQtHeaders = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0298                                                              QStringLiteral("kdevclangsupport/wrappedQtHeaders"),
0299                                                              QStandardPaths::LocateDirectory).toUtf8();
0300         if (!wrappedQtHeaders.isEmpty()) {
0301             smartArgs << wrappedQtHeaders;
0302             clangArguments << "-isystem" << wrappedQtHeaders.constData();
0303             const QByteArray qtCore = wrappedQtHeaders + "/QtCore";
0304             smartArgs << qtCore;
0305             clangArguments << "-isystem" << qtCore.constData();
0306         }
0307     }
0308 
0309     addIncludes(&clangArguments, &smartArgs, includes.system, "-isystem");
0310     addIncludes(&clangArguments, &smartArgs, includes.project, "-I");
0311 
0312     const auto& frameworkDirectories = environment.frameworkDirectories();
0313     addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.system, "-iframework");
0314     addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.project, "-F");
0315 
0316     // libclang cannot find it's builtin dir automatically, we have to specify it manually
0317     smartArgs << ClangHelpers::clangBuiltinIncludePath().toUtf8();
0318     clangArguments << "-isystem" << smartArgs.last().constData();
0319 
0320     {
0321         smartArgs << writeDefinesFile(environment.defines());
0322         clangArguments << "-imacros" << smartArgs.last().constData();
0323     }
0324 
0325     if (!environment.workingDirectory().isEmpty()) {
0326         QByteArray workingDirectory = environment.workingDirectory().toLocalFile().toUtf8();
0327         workingDirectory.prepend("-working-directory");
0328         smartArgs << workingDirectory;
0329         clangArguments << workingDirectory.constData();
0330     }
0331 
0332     // append extra args from environment variable
0333     static const auto extraArgs = ::extraArgs();
0334     for (const QByteArray& arg : extraArgs) {
0335         clangArguments << arg.constData();
0336     }
0337 
0338     QVector<CXUnsavedFile> unsaved;
0339     //For PrecompiledHeader, we don't want unsaved contents (and contents.isEmpty())
0340     if (!options.testFlag(PrecompiledHeader)) {
0341         unsaved = toClangApi(unsavedFiles);
0342     }
0343 
0344     // debugging: print hypothetical clang invocation including args (for easy c&p for local testing)
0345     if (qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_ARGS")) {
0346         QTextStream out(stdout);
0347         out << "Invocation: clang";
0348         for (const auto& arg : qAsConst(clangArguments)) {
0349             out << " " << arg;
0350         }
0351         out << " " << tuUrl.byteArray().constData() << "\n";
0352     }
0353 
0354     const CXErrorCode code = clang_parseTranslationUnit2(
0355         index->index(), tuUrl.byteArray().constData(),
0356         clangArguments.constData(), clangArguments.size(),
0357         unsaved.data(), unsaved.size(),
0358         flags,
0359         &m_unit
0360     );
0361     if (code != CXError_Success) {
0362         qCWarning(KDEV_CLANG) << "clang_parseTranslationUnit2 return with error code" << code;
0363         if (!qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DIAGS")) {
0364             qCWarning(KDEV_CLANG) << "  (start KDevelop with `KDEV_CLANG_DISPLAY_DIAGS=1 kdevelop` to see more diagnostics)";
0365         }
0366     }
0367 
0368     if (m_unit) {
0369         setUnit(m_unit);
0370         m_environment = environment;
0371 
0372         if (options.testFlag(PrecompiledHeader)) {
0373             clang_saveTranslationUnit(m_unit, QByteArray(tuUrl.byteArray() + ".pch").constData(), CXSaveTranslationUnit_None);
0374         }
0375     } else {
0376         qCWarning(KDEV_CLANG) << "Failed to parse translation unit:" << tuUrl;
0377     }
0378 }
0379 
0380 ParseSessionData::~ParseSessionData()
0381 {
0382     clang_disposeTranslationUnit(m_unit);
0383 }
0384 
0385 QByteArray ParseSessionData::writeDefinesFile(const QMap<QString, QString>& defines)
0386 {
0387     m_definesFile.open();
0388     Q_ASSERT(m_definesFile.isWritable());
0389 
0390     {
0391         QTextStream definesStream(&m_definesFile);
0392         // don't show warnings about redefined macros
0393         definesStream << "#pragma clang system_header\n";
0394         for (auto it = defines.begin(); it != defines.end(); ++it) {
0395             if (it.key().startsWith(QLatin1String("__has_include("))
0396                 || it.key().startsWith(QLatin1String("__has_include_next(")))
0397             {
0398                 continue;
0399             }
0400             definesStream << QLatin1String("#define ") << it.key() << ' ' << it.value() << '\n';
0401         }
0402         if (notClangAndGccVersionAtLeast(defines, 11)) {
0403             /* fake GCC compatibility for __malloc__ attribute with arguments to silence warnings like this:
0404 
0405              /usr/include/stdlib.h:566:5: error: use of undeclared identifier '__builtin_free'; did you mean
0406              '__builtin_frexp'? /usr/include/stdlib.h:566:5: note: '__builtin_frexp' declared here
0407              /usr/include/stdlib.h:566:5: error: '__malloc__' attribute takes no arguments
0408              /usr/include/stdlib.h:570:14: error: '__malloc__' attribute takes no arguments
0409              /usr/include/stdlib.h:799:6: error: use of undeclared identifier '__builtin_free'; did you mean
0410              '__builtin_frexp'? /usr/include/stdlib.h:566:5: note: '__builtin_frexp' declared here
0411              /usr/include/stdlib.h:799:6: error: '__malloc__' attribute takes no arguments
0412              */
0413             definesStream << QLatin1String("#define __malloc__(...) __malloc__\n");
0414         }
0415     }
0416     m_definesFile.close();
0417 
0418     if (qEnvironmentVariableIsSet("KDEV_CLANG_DISPLAY_DEFINES")) {
0419         QFile f(m_definesFile.fileName());
0420         f.open(QIODevice::ReadOnly);
0421         Q_ASSERT(f.isReadable());
0422         QTextStream out(stdout);
0423         out << "Defines file: " << f.fileName() << "\n"
0424             << f.readAll() << f.size()
0425             << "\n VS defines:" << defines.size() << "\n";
0426     }
0427 
0428     return m_definesFile.fileName().toUtf8();
0429 }
0430 
0431 void ParseSessionData::setUnit(CXTranslationUnit unit)
0432 {
0433     m_unit = unit;
0434     m_diagnosticsCache.clear();
0435     if (m_unit) {
0436         const ClangString unitFile(clang_getTranslationUnitSpelling(unit));
0437         m_file = clang_getFile(m_unit, unitFile.c_str());
0438     } else {
0439         m_file = nullptr;
0440     }
0441 }
0442 
0443 ClangParsingEnvironment ParseSessionData::environment() const
0444 {
0445     return m_environment;
0446 }
0447 
0448 ParseSession::ParseSession(const ParseSessionData::Ptr& data)
0449     : d(data)
0450 {
0451     if (d) {
0452         ENSURE_CHAIN_NOT_LOCKED
0453         d->m_mutex.lock();
0454     }
0455 }
0456 
0457 ParseSession::~ParseSession()
0458 {
0459     if (d) {
0460         d->m_mutex.unlock();
0461     }
0462 }
0463 
0464 void ParseSession::setData(const ParseSessionData::Ptr& data)
0465 {
0466     if (data == d) {
0467         return;
0468     }
0469 
0470     if (d) {
0471         d->m_mutex.unlock();
0472     }
0473 
0474     d = data;
0475 
0476     if (d) {
0477         ENSURE_CHAIN_NOT_LOCKED
0478         d->m_mutex.lock();
0479     }
0480 }
0481 
0482 ParseSessionData::Ptr ParseSession::data() const
0483 {
0484     return d;
0485 }
0486 
0487 IndexedString ParseSession::languageString()
0488 {
0489     static const IndexedString lang("Clang");
0490     return lang;
0491 }
0492 
0493 ClangProblem::Ptr ParseSession::getOrCreateProblem(int indexInTU, CXDiagnostic diagnostic) const
0494 {
0495     if (!d) {
0496         return {};
0497     }
0498 
0499     auto& problem = d->m_diagnosticsCache[indexInTU];
0500     if (!problem) {
0501         problem = ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit);
0502     }
0503     return problem;
0504 }
0505 
0506 ClangProblem::Ptr ParseSession::createExternalProblem(int indexInTU,
0507                                                       CXDiagnostic diagnostic,
0508                                                       const KLocalizedString& descriptionTemplate,
0509                                                       int childProblemFinalLocationIndex) const
0510 {
0511     // Make a copy of the original (cached) problem since it is modified later
0512     auto problem = ClangProblem::Ptr(new ClangProblem(*getOrCreateProblem(indexInTU, diagnostic)));
0513 
0514     // Insert a copy of the parent problem (without child problems) as the first
0515     // child problem to preserve its location.
0516     auto* problemCopy = new ClangProblem();
0517     problemCopy->setSource(problem->source());
0518     problemCopy->setFinalLocation(problem->finalLocation());
0519     problemCopy->setFinalLocationMode(problem->finalLocationMode());
0520     problemCopy->setDescription(problem->description());
0521     problemCopy->setExplanation(problem->explanation());
0522     problemCopy->setSeverity(problem->severity());
0523 
0524     auto childProblems = problem->diagnostics();
0525     childProblems.prepend(IProblem::Ptr(problemCopy));
0526     problem->setDiagnostics(childProblems);
0527 
0528     // Override the problem's finalLocation with that of the child problem in this document.
0529     // This is required to make the problem show up in the problem reporter for this
0530     // file, since it filters by finalLocation. It will also lead the user to the correct
0531     // location when clicking the problem and cause proper error highlighting.
0532     int index = (childProblemFinalLocationIndex >= 0) ?
0533                 (1 + childProblemFinalLocationIndex) :
0534                 (childProblems.size() - 1);
0535     problem->setFinalLocation(childProblems[index]->finalLocation());
0536 
0537     problem->setDescription(descriptionTemplate.subs(problem->description()).toString());
0538 
0539     return problem;
0540 }
0541 
0542 QList<ClangProblem::Ptr> ParseSession::createRequestedHereProblems(int indexInTU, CXDiagnostic diagnostic, CXFile file) const
0543 {
0544     QList<ClangProblem::Ptr> results;
0545 
0546     auto childDiagnostics = clang_getChildDiagnostics(diagnostic);
0547     auto numChildDiagnostics = clang_getNumDiagnosticsInSet(childDiagnostics);
0548     for (uint j = 0; j < numChildDiagnostics; ++j) {
0549         auto childDiagnostic = clang_getDiagnosticInSet(childDiagnostics, j);
0550         CXSourceLocation childLocation = clang_getDiagnosticLocation(childDiagnostic);
0551         CXFile childDiagnosticFile;
0552         clang_getFileLocation(childLocation, &childDiagnosticFile, nullptr, nullptr, nullptr);
0553         if (childDiagnosticFile == file) {
0554             QString description = ClangString(clang_getDiagnosticSpelling(childDiagnostic)).toString();
0555             if (description.endsWith(QLatin1String("requested here"))) {
0556                 // Note: Using the index j here assumes a 1:1 mapping from clang child diagnostics to KDevelop
0557                 // problem diagnostics (i.e., child problems). If we wanted to avoid making this assumption, we'd have
0558                 // to use ClangDiagnosticEvaluator::createProblem() first and then search within its
0559                 // child problems to find the correct index.
0560                 results << createExternalProblem(indexInTU, diagnostic, ki18n("Requested here: %1"), j);
0561             }
0562         }
0563     }
0564 
0565     return results;
0566 }
0567 
0568 QList<ProblemPointer> ParseSession::problemsForFile(CXFile file) const
0569 {
0570     if (!d) {
0571         return {};
0572     }
0573 
0574     QList<ProblemPointer> problems;
0575 
0576     // extra clang diagnostics
0577     const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit);
0578     problems.reserve(numDiagnostics);
0579     d->m_diagnosticsCache.resize(numDiagnostics);
0580 
0581     for (uint i = 0; i < numDiagnostics; ++i) {
0582         auto diagnostic = clang_getDiagnostic(d->m_unit, i);
0583 
0584         CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
0585         CXFile diagnosticFile;
0586         clang_getFileLocation(location, &diagnosticFile, nullptr, nullptr, nullptr);
0587 
0588         const auto requestedHereProblems = createRequestedHereProblems(i, diagnostic, file);
0589         for (const auto& ptr : requestedHereProblems) {
0590             problems.append(static_cast<const ProblemPointer&>(ptr));
0591         }
0592 
0593         // missing-include problems are so severe in clang that we always propagate
0594         // them to this document, to ensure that the user will see the error.
0595         if (diagnosticFile != file && ClangDiagnosticEvaluator::diagnosticType(diagnostic) != ClangDiagnosticEvaluator::IncludeFileNotFoundProblem) {
0596             continue;
0597         }
0598 
0599         problems << ((diagnosticFile == file) ?
0600                      getOrCreateProblem(i, diagnostic) :
0601                      createExternalProblem(i, diagnostic, ki18n("In included file: %1")));
0602 
0603         clang_disposeDiagnostic(diagnostic);
0604     }
0605 
0606     // other problem sources
0607 
0608     TodoExtractor extractor(unit(), file);
0609     problems << extractor.problems();
0610 
0611 #if CINDEX_VERSION_MINOR > 30
0612     // note that the below warning is triggered on every reparse when there is a precompiled preamble
0613     // see also TestDUChain::testReparseIncludeGuard
0614     const QString path = QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath();
0615     const IndexedString indexedPath(path);
0616     const auto location = clang_getLocationForOffset(d->m_unit, file, 0);
0617     if (ClangHelpers::isHeader(path) && !clang_isFileMultipleIncludeGuarded(unit(), file)
0618         && !clang_Location_isInSystemHeader(location)
0619         // clang_isFileMultipleIncludeGuarded always returns 0 in case our only file is the header
0620         && !clang_Location_isFromMainFile(location))
0621     {
0622         QExplicitlySharedDataPointer<StaticAssistantProblem> problem(new StaticAssistantProblem);
0623         problem->setSeverity(IProblem::Warning);
0624         problem->setDescription(i18n("Header is not guarded against multiple inclusions"));
0625         problem->setExplanation(i18n("The given header is not guarded against multiple inclusions, "
0626             "either with the conventional #ifndef/#define/#endif macro guards or with #pragma once."));
0627         const KTextEditor::Range problemRange(0, 0, KDevelop::createCodeRepresentation(indexedPath)->lines(), 0);
0628         problem->setFinalLocation(DocumentRange{indexedPath, problemRange});
0629         problem->setSource(IProblem::Preprocessor);
0630         problem->setSolutionAssistant(KDevelop::IAssistant::Ptr(new HeaderGuardAssistant(d->m_unit, file)));
0631         problems << problem;
0632     }
0633 #endif
0634 
0635     return problems;
0636 }
0637 
0638 CXTranslationUnit ParseSession::unit() const
0639 {
0640     return d ? d->m_unit : nullptr;
0641 }
0642 
0643 CXFile ParseSession::file(const QByteArray& path) const
0644 {
0645     return clang_getFile(unit(), path.constData());
0646 }
0647 
0648 CXFile ParseSession::mainFile() const
0649 {
0650     return d ? d->m_file : nullptr;
0651 }
0652 
0653 bool ParseSession::reparse(const QVector<UnsavedFile>& unsavedFiles, const ClangParsingEnvironment& environment)
0654 {
0655     if (!d || environment != d->m_environment) {
0656         return false;
0657     }
0658 
0659     auto unsaved = toClangApi(unsavedFiles);
0660 
0661     const auto code = clang_reparseTranslationUnit(d->m_unit, unsaved.size(), unsaved.data(),
0662                                                    clang_defaultReparseOptions(d->m_unit));
0663     if (code != CXError_Success) {
0664         qCWarning(KDEV_CLANG) << "clang_reparseTranslationUnit return with error code" << code;
0665         // if error code != 0 => clang_reparseTranslationUnit invalidates the old translation unit => clean up
0666         clang_disposeTranslationUnit(d->m_unit);
0667         d->setUnit(nullptr);
0668         return false;
0669     }
0670 
0671     // update state
0672     d->setUnit(d->m_unit);
0673     return true;
0674 }
0675 
0676 ClangParsingEnvironment ParseSession::environment() const
0677 {
0678     if (!d) {
0679         return {};
0680     }
0681     return d->m_environment;
0682 }