Commit 57279c0d authored by Nikolai Kosjar's avatar Nikolai Kosjar

CppEditor: Trigger AddIncludeForUndefinedIdentifier on qualified name base

Now there is no need to position the cursor on "C" of "Namespace::C" to
trigger the quick fix. It will already trigger if the cursor is
somewhere within the name.

Change-Id: Ic99325f95b5cf5551fc74b55606c402461849fb0
Reviewed-by: default avatarChristian Stenger <christian.stenger@digia.com>
parent cb320e1b
......@@ -150,6 +150,7 @@ private slots:
void test_quickfix_AddIncludeForUndefinedIdentifier_onSimpleName();
void test_quickfix_AddIncludeForUndefinedIdentifier_onNameOfQualifiedName();
void test_quickfix_AddIncludeForUndefinedIdentifier_onBaseOfQualifiedName();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_ignoremoc();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_sortingTop();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_sortingMiddle();
......
......@@ -2286,6 +2286,45 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_onNameOfQua
QuickFixTestCase::run(testFiles, &factory, TestIncludePaths::globalIncludePath());
}
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_onBaseOfQualifiedName()
{
QList<QuickFixTestDocument::Ptr> testFiles;
QByteArray original;
QByteArray expected;
// Header File
original = "namespace N { class Foo {}; }\n";
expected = original;
testFiles << QuickFixTestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8() + "/afile.h",
original, expected);
// Source File
original =
"#include \"header.h\"\n"
"\n"
"void f()\n"
"{\n"
" @N::Foo foo;\n"
"}\n"
;
expected =
"#include \"afile.h\"\n"
"#include \"header.h\"\n"
"\n"
"void f()\n"
"{\n"
" N::Foo foo;\n"
"}\n"
;
testFiles << QuickFixTestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8()
+ "/afile.cpp", original, expected);
// Do not use the test factory, at least once we want to go through the "full stack".
AddIncludeForUndefinedIdentifier factory;
QuickFixTestCase::run(testFiles, &factory, TestIncludePaths::globalIncludePath());
}
/// Check: Ignore *.moc includes
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_ignoremoc()
{
......
......@@ -1900,59 +1900,78 @@ ProjectPart::HeaderPaths relevantHeaderPaths(const QString &filePath)
return headerPaths;
}
} // anonymous namespace
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
QuickFixOperations &result)
NameAST *nameUnderCursor(const QList<AST *> &path)
{
CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::getObject<CppClassesFilter>();
if (!classesFilter)
return;
const QList<AST *> &path = interface.path();
if (path.isEmpty())
return;
return 0;
// find the largest enclosing Name
const NameAST *enclosingName = 0;
const SimpleNameAST *innermostName = 0;
NameAST *nameAst = 0;
for (int i = path.size() - 1; i >= 0; --i) {
if (NameAST *nameAst = path.at(i)->asName()) {
enclosingName = nameAst;
if (!innermostName) {
innermostName = nameAst->asSimpleName();
if (!innermostName)
return;
}
} else {
AST * const ast = path.at(i);
if (SimpleNameAST *simpleName = ast->asSimpleName()) {
nameAst = simpleName;
} else if (QualifiedNameAST *qualifiedName = ast->asQualifiedName()) {
nameAst = qualifiedName;
break;
}
}
if (!enclosingName || !enclosingName->name)
return;
// find the enclosing scope
return nameAst;
}
bool canLookup(const CppQuickFixInterface &interface, const NameAST *nameAst)
{
QTC_ASSERT(nameAst, return false);
// Find the enclosing scope
unsigned line, column;
const Document::Ptr &doc = interface.semanticInfo().doc;
doc->translationUnit()->getTokenStartPosition(enclosingName->firstToken(), &line, &column);
doc->translationUnit()->getTokenStartPosition(nameAst->firstToken(), &line, &column);
Scope *scope = doc->scopeAt(line, column);
if (!scope)
return false;
// Check if the name resolves to something
const Name *name = nameAst->name;
const QList<LookupItem> existingResults = interface.context().lookup(name, scope);
return !existingResults.isEmpty();
}
QString unqualifiedName(const Name *name)
{
QTC_ASSERT(name, return QString());
const Overview oo;
if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId())
return oo.prettyName(qualifiedName->name());
else
return oo.prettyName(name);
}
} // anonymous namespace
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
QuickFixOperations &result)
{
CppClassesFilter *classesFilter = ExtensionSystem::PluginManager::getObject<CppClassesFilter>();
if (!classesFilter)
return;
// check if the name resolves to something
QList<LookupItem> existingResults = interface.context().lookup(enclosingName->name, scope);
if (!existingResults.isEmpty())
const NameAST *nameAst = nameUnderCursor(interface.path());
if (!nameAst)
return;
const QString &className = Overview().prettyName(innermostName->name);
if (canLookup(interface, nameAst))
return; // There are results, so include isn't needed
const QString className = unqualifiedName(nameAst->name);
if (className.isEmpty())
return;
// find the include paths
const ProjectPart::HeaderPaths headerPaths = relevantHeaderPaths(doc->fileName());
const QString currentDocumentFilePath = interface.semanticInfo().doc->fileName();
const ProjectPart::HeaderPaths headerPaths = relevantHeaderPaths(currentDocumentFilePath);
// find a include file through the locator
// Find an include file through the locator
QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
QList<Core::LocatorFilterEntry> matches = classesFilter->matchesFor(dummyInterface, className);
bool classFoundInLocator = false;
......@@ -1962,8 +1981,9 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
continue;
classFoundInLocator = true;
// find the shortest way to include fileName given the includePaths
const QString include = findShortestInclude(doc->fileName(), info->fileName(), headerPaths);
// Find the shortest way to include fileName given the includePaths
const QString include = findShortestInclude(currentDocumentFilePath, info->fileName(),
headerPaths);
if (!include.isEmpty())
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 0, include));
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment