File indexing completed on 2024-05-19 15:44:56
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 }