diff --git a/languages/clang/codegen/clangclasshelper.cpp b/languages/clang/codegen/clangclasshelper.cpp --- a/languages/clang/codegen/clangclasshelper.cpp +++ b/languages/clang/codegen/clangclasshelper.cpp @@ -24,12 +24,14 @@ #include "clangclasshelper.h" #include "util/clangdebug.h" +#include "duchain/unknowndeclarationproblem.h" #include #include #include #include #include #include +#include #include #include @@ -139,37 +141,33 @@ variables[QStringLiteral("signals")] = CodeDescription::toVariantList(signalDescriptions); variables[QStringLiteral("needs_qobject_macro")] = !slotDescriptions.isEmpty() || !signalDescriptions.isEmpty(); - // TODO: port to KDev-clang -#if 0 QStringList includedFiles; DUChainReadLocker locker(DUChain::lock()); QUrl sourceUrl; QHash urls = fileUrls(); if (!urls.isEmpty()) { sourceUrl = urls.constBegin().value(); - } - else { - // includeDirectiveFromUrl() expects a header URL + } else { + // includeDirectiveArgumentFromPath() expects a path to the folder where includes are used from sourceUrl = baseUrl(); - sourceUrl.setPath(sourceUrl.path() + '/' + QLatin1String(".h")); + sourceUrl.setPath(sourceUrl.path() + QLatin1String("/.h")); } for (const auto& base : directBaseClasses()) { if (!base) { continue; } clangDebug() << "Looking for includes for class" << base->identifier().toString(); - QExplicitlySharedDataPointer item = Cpp::includeDirectiveFromUrl(sourceUrl, IndexedDeclaration(base.data())); - if(item) - { - clangDebug() << "Found one in" << item->m_canonicalPath; - includedFiles << item->m_addedInclude; + + const QString includeDirective = + UnknownDeclarationProblem::includeDirectiveArgumentFromPath(Path(sourceUrl), base); + if (!includeDirective.isEmpty()) { + includedFiles << includeDirective; } } variables[QStringLiteral("included_files")] = includedFiles; -#endif return variables; } diff --git a/languages/clang/duchain/unknowndeclarationproblem.h b/languages/clang/duchain/unknowndeclarationproblem.h --- a/languages/clang/duchain/unknowndeclarationproblem.h +++ b/languages/clang/duchain/unknowndeclarationproblem.h @@ -29,6 +29,10 @@ #include +namespace KDevelop { +class Path; +} + class KDEVCLANGPRIVATE_EXPORT UnknownDeclarationProblem : public ClangProblem { public: @@ -41,6 +45,9 @@ KDevelop::IAssistant::Ptr solutionAssistant() const override; + static QString includeDirectiveArgumentFromPath(const KDevelop::Path& file, + const KDevelop::DeclarationPointer& declaration); + private: KDevelop::QualifiedIdentifier m_identifier; }; diff --git a/languages/clang/duchain/unknowndeclarationproblem.cpp b/languages/clang/duchain/unknowndeclarationproblem.cpp --- a/languages/clang/duchain/unknowndeclarationproblem.cpp +++ b/languages/clang/duchain/unknowndeclarationproblem.cpp @@ -46,7 +46,6 @@ #include #include -#include #include @@ -318,7 +317,7 @@ /** * Takes a filepath and the include paths and determines what directive to use. */ -ClangFixit directiveForFile( const QString& includefile, const KDevelop::Path::List& includepaths, const KDevelop::Path& source ) +ClangFixit directiveForFileFixIt(const QString& includefile, const Path::List& includepaths, const Path& source) { const auto sourceFolder = source.parent(); const Path canonicalFile( QFileInfo( includefile ).canonicalFilePath() ); @@ -364,6 +363,45 @@ return ClangFixit{directive + QLatin1Char('\n'), range, QObject::tr("Insert \'%1\'").arg(directive)}; } +QString includeArgumentForFile(const QString& includefile, const Path::List& includePaths, + const Path& source) +{ + const auto sourceFolder = source.parent(); + const Path canonicalFile(QFileInfo(includefile).canonicalFilePath()); + + QString shortestDirective; + bool isRelative = false; + + // we can include the file directly + if (sourceFolder == canonicalFile.parent()) { + shortestDirective = canonicalFile.lastPathSegment(); + isRelative = true; + } else { + // find the include directive with the shortest length + for (const auto& includePath : includePaths) { + QString relative = includePath.relativePath(canonicalFile); + if (relative.startsWith(QLatin1String("./"))) { + relative.remove(0, 2); + } + + if (shortestDirective.isEmpty() || relative.length() < shortestDirective.length()) { + shortestDirective = relative; + isRelative = (includePath == sourceFolder); + } + } + } + + // Item not found in include path? + if (shortestDirective.isEmpty()) { + return {}; + } + + if (isRelative) { + return QLatin1Char('\"') + shortestDirective + QLatin1Char('\"'); + } + return QLatin1Char('<') + shortestDirective + QLatin1Char('>'); +} + KDevelop::Path::List includePaths( const KDevelop::Path& file ) { // Find project's custom include paths @@ -489,10 +527,11 @@ } const auto includepaths = includePaths( file ); + clangDebug() << "found include paths for" << file << ":" << includepaths; /* create fixits for candidates */ for( const auto& includeFile : includefiles ) { - const auto fixit = directiveForFile( includeFile, includepaths, file /* UP */ ); + const auto fixit = directiveForFileFixIt(includeFile, includepaths, file /* UP */); if (!fixit.range.isValid()) { clangDebug() << "unable to create directive for" << includeFile << "in" << file.toLocalFile(); continue; @@ -520,6 +559,56 @@ return symbol; } +struct IncludeArgumentShorterThan +{ + bool operator()(const QString& lhs, const QString& rhs) + { + return lhs.length() < rhs.length(); + } +}; + +} + +// TODO: this method is an experimental hook to get access to all the logic in this file, where to put this best? +QString UnknownDeclarationProblem::includeDirectiveArgumentFromPath(const Path& file, + const DeclarationPointer& declaration) +{ +/* + TODO: includePaths() will use ICore::self()->projectController()->projectModel()->itemForPath(IndexedString(source)) + on the file passed. but those files might not exist yet, are only planned to be created by the generator. + What happens exactly in this call then and what to do? +*/ + const auto includepaths = includePaths(file); + if (includepaths.isEmpty()) { + clangDebug() << "Include path is empty"; + return {}; + } + + clangDebug() << "found include paths for" << file << ":" << includepaths; + + const auto includefiles = findMatchingIncludeFiles(QVector() << declaration.data()); + if (includefiles.isEmpty()) { + // return early as the computation of the include paths is quite expensive + return {}; + } + + // create include arguments for all candidates + QStringList includeArguments; + for (const auto& includeFile : includefiles) { + const auto includeArgument = includeArgumentForFile(includeFile, includepaths, file); + if (includeArgument.isEmpty()) { + clangDebug() << "unable to create include argument for" << includeFile << "in" << file.toLocalFile(); + } + includeArguments << includeArgument; + } + + if (includeArguments.isEmpty()) { + return {}; + } + + std::sort(includeArguments.begin(), includeArguments.end(), IncludeArgumentShorterThan()); + + return includeArguments.at(0); } UnknownDeclarationProblem::UnknownDeclarationProblem(CXDiagnostic diagnostic, CXTranslationUnit unit) diff --git a/languages/clang/kdevclangsupport.json b/languages/clang/kdevclangsupport.json --- a/languages/clang/kdevclangsupport.json +++ b/languages/clang/kdevclangsupport.json @@ -53,7 +53,7 @@ "X-KDevelop-Interfaces": [ "ILanguageSupport" ], - "X-KDevelop-Language": "C", + "X-KDevelop-Language": "C++", "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-SupportedMimeTypes": [