forked from qt-creator/qt-creator
CppEditor: Add very first include after include guard
...when adding an include for an undefined identifier. Change-Id: Ia338e924901262a847d3bd7ed9733d8e66c631dd Task-number: QTCREATORBUG-10391 Reviewed-by: Christian Stenger <christian.stenger@digia.com>
This commit is contained in:
@@ -151,3 +151,11 @@ void FastPreprocessor::startExpandingMacro(unsigned bytesOffset, unsigned utf16c
|
|||||||
utf16charsOffset, macro.nameToQString().size(),
|
utf16charsOffset, macro.nameToQString().size(),
|
||||||
line, actuals);
|
line, actuals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FastPreprocessor::markAsIncludeGuard(const QByteArray ¯oName)
|
||||||
|
{
|
||||||
|
if (!_currentDoc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_currentDoc->setIncludeGuardMacroName(macroName);
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public:
|
|||||||
const Macro &,
|
const Macro &,
|
||||||
const QVector<MacroArgumentReference> &);
|
const QVector<MacroArgumentReference> &);
|
||||||
virtual void stopExpandingMacro(unsigned, const Macro &) {}
|
virtual void stopExpandingMacro(unsigned, const Macro &) {}
|
||||||
virtual void markAsIncludeGuard(const QByteArray &) {}
|
virtual void markAsIncludeGuard(const QByteArray ¯oName);
|
||||||
|
|
||||||
virtual void startSkippingBlocks(unsigned) {}
|
virtual void startSkippingBlocks(unsigned) {}
|
||||||
virtual void stopSkippingBlocks(unsigned) {}
|
virtual void stopSkippingBlocks(unsigned) {}
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ private slots:
|
|||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_mixedIncludeTypes3();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_mixedIncludeTypes3();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_mixedIncludeTypes4();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_mixedIncludeTypes4();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_noinclude();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_noinclude();
|
||||||
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_onlyIncludeGuard();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCppStyleCommentOnTop();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCppStyleCommentOnTop();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCStyleCommentOnTop();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCStyleCommentOnTop();
|
||||||
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_checkQSomethingInQtIncludePaths();
|
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_checkQSomethingInQtIncludePaths();
|
||||||
|
|||||||
@@ -2828,6 +2828,36 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_n
|
|||||||
QuickFixTestCase::run(testFiles, &factory, TestIncludePaths::globalIncludePath());
|
QuickFixTestCase::run(testFiles, &factory, TestIncludePaths::globalIncludePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check: Insert very first include after include guard
|
||||||
|
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_onlyIncludeGuard()
|
||||||
|
{
|
||||||
|
QList<QuickFixTestDocument::Ptr> testFiles;
|
||||||
|
|
||||||
|
QByteArray original;
|
||||||
|
QByteArray expected;
|
||||||
|
|
||||||
|
original =
|
||||||
|
"#ifndef FOO_H\n"
|
||||||
|
"#define FOO_H\n"
|
||||||
|
"void @f();\n"
|
||||||
|
"#endif\n"
|
||||||
|
;
|
||||||
|
expected =
|
||||||
|
"#ifndef FOO_H\n"
|
||||||
|
"#define FOO_H\n"
|
||||||
|
"\n"
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"void f();\n"
|
||||||
|
"#endif\n"
|
||||||
|
;
|
||||||
|
testFiles << QuickFixTestDocument::create(TestIncludePaths::directoryOfTestFile().toUtf8()
|
||||||
|
+ "/file.cpp", original, expected);
|
||||||
|
|
||||||
|
AddIncludeForUndefinedIdentifierTestFactory factory(QLatin1String("\"file.h\""));
|
||||||
|
QuickFixTestCase::run(testFiles, &factory, TestIncludePaths::globalIncludePath());
|
||||||
|
}
|
||||||
|
|
||||||
/// Check: Insert very first include if there is a c++ style comment on top
|
/// Check: Insert very first include if there is a c++ style comment on top
|
||||||
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCppStyleCommentOnTop()
|
void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCppStyleCommentOnTop()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -259,11 +259,12 @@ Namespace *isNamespaceFunction(const LookupContext &context, Function *function)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Given include is e.g. "afile.h" or <afile.h> (quotes/angle brackets included!).
|
// Given include is e.g. "afile.h" or <afile.h> (quotes/angle brackets included!).
|
||||||
void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr file)
|
void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr file,
|
||||||
|
const CPlusPlus::Document::Ptr &cppDocument)
|
||||||
{
|
{
|
||||||
// Find optimal position
|
// Find optimal position
|
||||||
using namespace IncludeUtils;
|
using namespace IncludeUtils;
|
||||||
LineForNewIncludeDirective finder(file->document(), file->cppDocument()->resolvedIncludes(),
|
LineForNewIncludeDirective finder(file->document(), cppDocument,
|
||||||
LineForNewIncludeDirective::IgnoreMocIncludes,
|
LineForNewIncludeDirective::IgnoreMocIncludes,
|
||||||
LineForNewIncludeDirective::AutoDetect);
|
LineForNewIncludeDirective::AutoDetect);
|
||||||
unsigned newLinesToPrepend = 0;
|
unsigned newLinesToPrepend = 0;
|
||||||
@@ -1583,7 +1584,7 @@ public:
|
|||||||
best = headerFile;
|
best = headerFile;
|
||||||
|
|
||||||
const QString include = QString::fromLatin1("<%1>").arg(QFileInfo(best).fileName());
|
const QString include = QString::fromLatin1("<%1>").arg(QFileInfo(best).fileName());
|
||||||
insertNewIncludeDirective(include, currentFile);
|
insertNewIncludeDirective(include, currentFile, semanticInfo().doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1826,7 +1827,7 @@ void AddIncludeForUndefinedIdentifierOp::perform()
|
|||||||
CppRefactoringChanges refactoring(snapshot());
|
CppRefactoringChanges refactoring(snapshot());
|
||||||
CppRefactoringFilePtr file = refactoring.file(fileName());
|
CppRefactoringFilePtr file = refactoring.file(fileName());
|
||||||
|
|
||||||
insertNewIncludeDirective(m_include, file);
|
insertNewIncludeDirective(m_include, file, semanticInfo().doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include <cplusplus/PreprocessorEnvironment.h>
|
#include <cplusplus/PreprocessorEnvironment.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -80,15 +81,57 @@ QString includeDir(const QString &include)
|
|||||||
return dirPrefix;
|
return dirPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lineAfterFirstComment(const QTextDocument *textDocument)
|
||||||
|
{
|
||||||
|
int insertLine = -1;
|
||||||
|
|
||||||
|
QTextBlock block = textDocument->firstBlock();
|
||||||
|
while (block.isValid()) {
|
||||||
|
const QString trimmedText = block.text().trimmed();
|
||||||
|
|
||||||
|
// Only skip the first comment!
|
||||||
|
if (trimmedText.startsWith(QLatin1String("/*"))) {
|
||||||
|
do {
|
||||||
|
const int pos = block.text().indexOf(QLatin1String("*/"));
|
||||||
|
if (pos > -1) {
|
||||||
|
insertLine = block.blockNumber() + 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
block = block.next();
|
||||||
|
} while (block.isValid());
|
||||||
|
break;
|
||||||
|
} else if (trimmedText.startsWith(QLatin1String("//"))) {
|
||||||
|
block = block.next();
|
||||||
|
while (block.isValid()) {
|
||||||
|
if (!block.text().trimmed().startsWith(QLatin1String("//"))) {
|
||||||
|
insertLine = block.blockNumber() + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
block = block.next();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trimmedText.isEmpty())
|
||||||
|
break;
|
||||||
|
block = block.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return insertLine;
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
LineForNewIncludeDirective::LineForNewIncludeDirective(const QTextDocument *textDocument,
|
LineForNewIncludeDirective::LineForNewIncludeDirective(const QTextDocument *textDocument,
|
||||||
QList<Document::Include> includes,
|
const Document::Ptr cppDocument,
|
||||||
MocIncludeMode mocIncludeMode,
|
MocIncludeMode mocIncludeMode,
|
||||||
IncludeStyle includeStyle)
|
IncludeStyle includeStyle)
|
||||||
: m_textDocument(textDocument)
|
: m_textDocument(textDocument)
|
||||||
|
, m_cppDocument(cppDocument)
|
||||||
, m_includeStyle(includeStyle)
|
, m_includeStyle(includeStyle)
|
||||||
{
|
{
|
||||||
|
const QList<Document::Include> includes = cppDocument->resolvedIncludes();
|
||||||
|
|
||||||
// Ignore *.moc includes if requested
|
// Ignore *.moc includes if requested
|
||||||
if (mocIncludeMode == IgnoreMocIncludes) {
|
if (mocIncludeMode == IgnoreMocIncludes) {
|
||||||
foreach (const Document::Include &include, includes) {
|
foreach (const Document::Include &include, includes) {
|
||||||
@@ -129,6 +172,43 @@ LineForNewIncludeDirective::LineForNewIncludeDirective(const QTextDocument *text
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LineForNewIncludeDirective::findInsertLineForVeryFirstInclude(unsigned *newLinesToPrepend,
|
||||||
|
unsigned *newLinesToAppend)
|
||||||
|
{
|
||||||
|
int insertLine = 1;
|
||||||
|
|
||||||
|
// If there is an include guard, insert right after that one
|
||||||
|
const QByteArray includeGuardMacroName = m_cppDocument->includeGuardMacroName();
|
||||||
|
if (!includeGuardMacroName.isEmpty()) {
|
||||||
|
const QList<Macro> definedMacros = m_cppDocument->definedMacros();
|
||||||
|
foreach (const Macro &definedMacro, definedMacros) {
|
||||||
|
if (definedMacro.name() == includeGuardMacroName) {
|
||||||
|
if (newLinesToPrepend)
|
||||||
|
*newLinesToPrepend = 1;
|
||||||
|
if (newLinesToAppend)
|
||||||
|
*newLinesToAppend += 1;
|
||||||
|
insertLine = definedMacro.line() + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QTC_CHECK(insertLine != 1);
|
||||||
|
} else {
|
||||||
|
// Otherwise, if there is a comment, insert right after it
|
||||||
|
insertLine = lineAfterFirstComment(m_textDocument);
|
||||||
|
if (insertLine != -1) {
|
||||||
|
if (newLinesToPrepend)
|
||||||
|
*newLinesToPrepend = 1;
|
||||||
|
|
||||||
|
// Otherwise, insert at top of file
|
||||||
|
} else {
|
||||||
|
if (newLinesToAppend)
|
||||||
|
*newLinesToAppend += 1;
|
||||||
|
insertLine = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return insertLine;
|
||||||
|
}
|
||||||
|
|
||||||
int LineForNewIncludeDirective::operator()(const QString &newIncludeFileName,
|
int LineForNewIncludeDirective::operator()(const QString &newIncludeFileName,
|
||||||
unsigned *newLinesToPrepend,
|
unsigned *newLinesToPrepend,
|
||||||
unsigned *newLinesToAppend)
|
unsigned *newLinesToAppend)
|
||||||
@@ -144,51 +224,8 @@ int LineForNewIncludeDirective::operator()(const QString &newIncludeFileName,
|
|||||||
: Client::IncludeGlobal;
|
: Client::IncludeGlobal;
|
||||||
|
|
||||||
// Handle no includes
|
// Handle no includes
|
||||||
if (m_includes.empty()) {
|
if (m_includes.empty())
|
||||||
unsigned insertLine = 0;
|
return findInsertLineForVeryFirstInclude(newLinesToPrepend, newLinesToAppend);
|
||||||
|
|
||||||
QTextBlock block = m_textDocument->firstBlock();
|
|
||||||
while (block.isValid()) {
|
|
||||||
const QString trimmedText = block.text().trimmed();
|
|
||||||
|
|
||||||
// Only skip the first comment!
|
|
||||||
if (trimmedText.startsWith(QLatin1String("/*"))) {
|
|
||||||
do {
|
|
||||||
const int pos = block.text().indexOf(QLatin1String("*/"));
|
|
||||||
if (pos > -1) {
|
|
||||||
insertLine = block.blockNumber() + 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
block = block.next();
|
|
||||||
} while (block.isValid());
|
|
||||||
break;
|
|
||||||
} else if (trimmedText.startsWith(QLatin1String("//"))) {
|
|
||||||
block = block.next();
|
|
||||||
while (block.isValid()) {
|
|
||||||
if (!block.text().trimmed().startsWith(QLatin1String("//"))) {
|
|
||||||
insertLine = block.blockNumber() + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
block = block.next();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!trimmedText.isEmpty())
|
|
||||||
break;
|
|
||||||
block = block.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (insertLine == 0) {
|
|
||||||
if (newLinesToAppend)
|
|
||||||
*newLinesToAppend += 1;
|
|
||||||
insertLine = 1;
|
|
||||||
} else {
|
|
||||||
if (newLinesToPrepend)
|
|
||||||
*newLinesToPrepend = 1;
|
|
||||||
}
|
|
||||||
return insertLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef QList<IncludeGroup> IncludeGroups;
|
typedef QList<IncludeGroup> IncludeGroups;
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public:
|
|||||||
enum IncludeStyle { LocalBeforeGlobal, GlobalBeforeLocal, AutoDetect };
|
enum IncludeStyle { LocalBeforeGlobal, GlobalBeforeLocal, AutoDetect };
|
||||||
|
|
||||||
LineForNewIncludeDirective(const QTextDocument *textDocument,
|
LineForNewIncludeDirective(const QTextDocument *textDocument,
|
||||||
QList<Include> includes,
|
const CPlusPlus::Document::Ptr cppDocument,
|
||||||
MocIncludeMode mocIncludeMode = IgnoreMocIncludes,
|
MocIncludeMode mocIncludeMode = IgnoreMocIncludes,
|
||||||
IncludeStyle includeStyle = AutoDetect);
|
IncludeStyle includeStyle = AutoDetect);
|
||||||
|
|
||||||
@@ -100,10 +100,13 @@ public:
|
|||||||
unsigned *newLinesToAppend = 0);
|
unsigned *newLinesToAppend = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int findInsertLineForVeryFirstInclude(unsigned *newLinesToPrepend, unsigned *newLinesToAppend);
|
||||||
QList<IncludeGroup> getGroupsByIncludeType(const QList<IncludeGroup> &groups,
|
QList<IncludeGroup> getGroupsByIncludeType(const QList<IncludeGroup> &groups,
|
||||||
IncludeType includeType);
|
IncludeType includeType);
|
||||||
|
|
||||||
const QTextDocument *m_textDocument;
|
const QTextDocument *m_textDocument;
|
||||||
|
const CPlusPlus::Document::Ptr m_cppDocument;
|
||||||
|
|
||||||
IncludeStyle m_includeStyle;
|
IncludeStyle m_includeStyle;
|
||||||
QList<Include> m_includes;
|
QList<Include> m_includes;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user