diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 3e65e3dde81..85ca917e954 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -3771,6 +3771,20 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_data() << TestIncludePaths::globalQtCoreIncludePath() << testDocuments << firstRefactoringOperation << ""; testDocuments.clear(); + + original = + "std::s@tring s;\n" + ; + expected = + "#include \n" + "\n" + "std::string s;\n" + ; + testDocuments << QuickFixTestDocument::create("file.cpp", original, expected); + QTest::newRow("inserting_std::string") + << TestIncludePaths::globalIncludePath() + << testDocuments << firstRefactoringOperation << ""; + testDocuments.clear(); } void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier() diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 1753f9ff070..149d1023961 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -58,6 +58,7 @@ #include #include +#include #include #include #include @@ -1916,25 +1917,20 @@ QString findShortestInclude(const QString currentDocumentFilePath, return result; } -QString findQtIncludeWithSameName(const QString &className, - const ProjectExplorer::HeaderPaths &headerPaths) +QString findMatchingInclude(const QString &className, + const ProjectExplorer::HeaderPaths &headerPaths) { - QString result; - - // Check for a header file with the same name in the Qt include paths - foreach (const ProjectExplorer::HeaderPath &headerPath, headerPaths) { - if (!headerPath.path.contains(QLatin1String("/Qt"))) // "QtCore", "QtGui" etc... - continue; - - const QString headerPathCandidate = headerPath.path + QLatin1Char('/') + className; - const QFileInfo fileInfo(headerPathCandidate); - if (fileInfo.exists() && fileInfo.isFile()) { - result = QLatin1Char('<') + className + QLatin1Char('>'); - break; + const QStringList candidateFileNames{className, className + ".h", className + ".hpp", + className.toLower(), className.toLower() + ".h", className.toLower() + ".hpp"}; + for (const QString &fileName : candidateFileNames) { + for (const ProjectExplorer::HeaderPath &headerPath : headerPaths) { + const QString headerPathCandidate = headerPath.path + QLatin1Char('/') + fileName; + const QFileInfo fileInfo(headerPathCandidate); + if (fileInfo.exists() && fileInfo.isFile()) + return '<' + fileName + '>'; } } - - return result; + return {}; } ProjectExplorer::HeaderPaths relevantHeaderPaths(const QString &filePath) @@ -2030,17 +2026,11 @@ Snapshot forwardingHeaders(const CppQuickFixInterface &interface) return result; } -bool looksLikeAQtClass(const QString &identifier) -{ - return identifier.size() > 2 - && identifier.at(0) == QLatin1Char('Q') - && identifier.at(1).isUpper(); -} - bool matchName(const Name *name, QList *matches, QString *className) { if (!name) return false; + QString simpleName; if (Core::ILocatorFilter *classesFilter = CppTools::CppModelManager::instance()->classesFilter()) { QFutureInterface dummy; @@ -2051,7 +2041,8 @@ bool matchName(const Name *name, QList *matches, QStri if (const TemplateNameId *templateName = name->asTemplateNameId()) { *className = templateNameAsString(templateName); } else { - *className = oo.prettyName(name); + simpleName = oo.prettyName(name); + *className = simpleName; *matches = classesFilter->matchesFor(dummy, *className); if (matches->empty()) { if (const Name *name = qualifiedName->base()) { @@ -2070,6 +2061,8 @@ bool matchName(const Name *name, QList *matches, QStri if (matches->empty()) *matches = classesFilter->matchesFor(dummy, *className); + if (matches->empty() && !simpleName.isEmpty()) + *className = simpleName; } return !matches->empty(); @@ -2092,7 +2085,7 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa QList matches; const QString currentDocumentFilePath = interface.semanticInfo().doc->fileName(); const ProjectExplorer::HeaderPaths headerPaths = relevantHeaderPaths(currentDocumentFilePath); - bool qtHeaderFileIncludeOffered = false; + QList headers; // Find an include file through the locator if (matchName(nameAst->name, &matches, &className)) { @@ -2124,11 +2117,9 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa else if (headerFileName.at(1).isUpper()) priority = 1; - if (looksLikeAQtClass(include.mid(1, include.size() - 2))) - qtHeaderFileIncludeOffered = true; - result << new AddIncludeForUndefinedIdentifierOp(interface, priority, include); + headers << header; } } } @@ -2158,12 +2149,16 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa if (className.isEmpty()) return; - // The header file we are looking for might not be (yet) included in any file we have parsed. - // As such, it will not be findable via locator. At least for Qt classes, check also for - // headers with the same name. - if (!qtHeaderFileIncludeOffered && looksLikeAQtClass(className)) { - const QString include = findQtIncludeWithSameName(className, headerPaths); - if (!include.isEmpty()) + // Fallback: Check the include paths for files that look like candidates + // for the given name. + if (!Utils::contains(headers, + [&className](const Utils::FilePath &fp) { return fp.fileName() == className; })) { + const QString include = findMatchingInclude(className, headerPaths); + const auto matcher = [&include](const QuickFixOperation::Ptr &o) { + const auto includeOp = o.dynamicCast(); + return includeOp && includeOp->include() == include; + }; + if (!include.isEmpty() && !Utils::contains(result, matcher)) result << new AddIncludeForUndefinedIdentifierOp(interface, 1, include); } } diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h index 83fdbaeca26..520b1ec40c6 100644 --- a/src/plugins/cppeditor/cppquickfixes.h +++ b/src/plugins/cppeditor/cppquickfixes.h @@ -67,6 +67,8 @@ public: const QString &include); void perform() override; + QString include() const { return m_include; } + private: QString m_include; };