forked from qt-creator/qt-creator
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: Christian Stenger <christian.stenger@digia.com>
This commit is contained in:
@@ -150,6 +150,7 @@ private slots:
|
|||||||
|
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_onSimpleName();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_onSimpleName();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_onNameOfQualifiedName();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_onNameOfQualifiedName();
|
||||||
|
void test_quickfix_AddIncludeForUndefinedIdentifier_onBaseOfQualifiedName();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_ignoremoc();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_ignoremoc();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_sortingTop();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_sortingTop();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_sortingMiddle();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_sortingMiddle();
|
||||||
|
|||||||
@@ -2286,6 +2286,45 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_onNameOfQua
|
|||||||
QuickFixTestCase::run(testFiles, &factory, TestIncludePaths::globalIncludePath());
|
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
|
/// Check: Ignore *.moc includes
|
||||||
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_ignoremoc()
|
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_ignoremoc()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1900,6 +1900,54 @@ ProjectPart::HeaderPaths relevantHeaderPaths(const QString &filePath)
|
|||||||
return headerPaths;
|
return headerPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NameAST *nameUnderCursor(const QList<AST *> &path)
|
||||||
|
{
|
||||||
|
if (path.isEmpty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
NameAST *nameAst = 0;
|
||||||
|
for (int i = path.size() - 1; i >= 0; --i) {
|
||||||
|
AST * const ast = path.at(i);
|
||||||
|
if (SimpleNameAST *simpleName = ast->asSimpleName()) {
|
||||||
|
nameAst = simpleName;
|
||||||
|
} else if (QualifiedNameAST *qualifiedName = ast->asQualifiedName()) {
|
||||||
|
nameAst = qualifiedName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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
|
} // anonymous namespace
|
||||||
|
|
||||||
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
|
void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interface,
|
||||||
@@ -1909,50 +1957,21 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
|
|||||||
if (!classesFilter)
|
if (!classesFilter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QList<AST *> &path = interface.path();
|
const NameAST *nameAst = nameUnderCursor(interface.path());
|
||||||
|
if (!nameAst)
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// find the largest enclosing Name
|
if (canLookup(interface, nameAst))
|
||||||
const NameAST *enclosingName = 0;
|
return; // There are results, so include isn't needed
|
||||||
const SimpleNameAST *innermostName = 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 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!enclosingName || !enclosingName->name)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// find the enclosing scope
|
const QString className = unqualifiedName(nameAst->name);
|
||||||
unsigned line, column;
|
|
||||||
const Document::Ptr &doc = interface.semanticInfo().doc;
|
|
||||||
doc->translationUnit()->getTokenStartPosition(enclosingName->firstToken(), &line, &column);
|
|
||||||
Scope *scope = doc->scopeAt(line, column);
|
|
||||||
if (!scope)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// check if the name resolves to something
|
|
||||||
QList<LookupItem> existingResults = interface.context().lookup(enclosingName->name, scope);
|
|
||||||
if (!existingResults.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const QString &className = Overview().prettyName(innermostName->name);
|
|
||||||
if (className.isEmpty())
|
if (className.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// find the include paths
|
const QString currentDocumentFilePath = interface.semanticInfo().doc->fileName();
|
||||||
const ProjectPart::HeaderPaths headerPaths = relevantHeaderPaths(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;
|
QFutureInterface<Core::LocatorFilterEntry> dummyInterface;
|
||||||
QList<Core::LocatorFilterEntry> matches = classesFilter->matchesFor(dummyInterface, className);
|
QList<Core::LocatorFilterEntry> matches = classesFilter->matchesFor(dummyInterface, className);
|
||||||
bool classFoundInLocator = false;
|
bool classFoundInLocator = false;
|
||||||
@@ -1962,8 +1981,9 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
|
|||||||
continue;
|
continue;
|
||||||
classFoundInLocator = true;
|
classFoundInLocator = true;
|
||||||
|
|
||||||
// find the shortest way to include fileName given the includePaths
|
// Find the shortest way to include fileName given the includePaths
|
||||||
const QString include = findShortestInclude(doc->fileName(), info->fileName(), headerPaths);
|
const QString include = findShortestInclude(currentDocumentFilePath, info->fileName(),
|
||||||
|
headerPaths);
|
||||||
if (!include.isEmpty())
|
if (!include.isEmpty())
|
||||||
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 0, include));
|
result.append(new AddIncludeForUndefinedIdentifierOp(interface, 0, include));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user