Commit a3664297 authored by Nikolai Kosjar's avatar Nikolai Kosjar

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: default avatarChristian Stenger <christian.stenger@digia.com>
parent eefcd23c
......@@ -151,3 +151,11 @@ void FastPreprocessor::startExpandingMacro(unsigned bytesOffset, unsigned utf16c
utf16charsOffset, macro.nameToQString().size(),
line, actuals);
}
void FastPreprocessor::markAsIncludeGuard(const QByteArray &macroName)
{
if (!_currentDoc)
return;
_currentDoc->setIncludeGuardMacroName(macroName);
}
......@@ -73,7 +73,7 @@ public:
const Macro &,
const QVector<MacroArgumentReference> &);
virtual void stopExpandingMacro(unsigned, const Macro &) {}
virtual void markAsIncludeGuard(const QByteArray &) {}
virtual void markAsIncludeGuard(const QByteArray &macroName);
virtual void startSkippingBlocks(unsigned) {}
virtual void stopSkippingBlocks(unsigned) {}
......
......@@ -169,6 +169,7 @@ private slots:
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_mixedIncludeTypes3();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_mixedIncludeTypes4();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_noinclude();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_onlyIncludeGuard();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCppStyleCommentOnTop();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_veryFirstIncludeCStyleCommentOnTop();
void test_quickfix_AddIncludeForUndefinedIdentifier_inserting_checkQSomethingInQtIncludePaths();
......
......@@ -2828,6 +2828,36 @@ void CppEditorPlugin::test_quickfix_AddIncludeForUndefinedIdentifier_inserting_n
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
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!).
void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr file)
void insertNewIncludeDirective(const QString &include, CppRefactoringFilePtr file,
const CPlusPlus::Document::Ptr &cppDocument)
{
// Find optimal position
using namespace IncludeUtils;
LineForNewIncludeDirective finder(file->document(), file->cppDocument()->resolvedIncludes(),
LineForNewIncludeDirective finder(file->document(), cppDocument,
LineForNewIncludeDirective::IgnoreMocIncludes,
LineForNewIncludeDirective::AutoDetect);
unsigned newLinesToPrepend = 0;
......@@ -1583,7 +1584,7 @@ public:
best = headerFile;
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());
CppRefactoringFilePtr file = refactoring.file(fileName());
insertNewIncludeDirective(m_include, file);
insertNewIncludeDirective(m_include, file, semanticInfo().doc);
}
namespace {
......
......@@ -35,6 +35,7 @@
#include <cplusplus/PreprocessorEnvironment.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
#include <QDir>
......@@ -80,15 +81,57 @@ QString includeDir(const QString &include)
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
LineForNewIncludeDirective::LineForNewIncludeDirective(const QTextDocument *textDocument,
QList<Document::Include> includes,
const Document::Ptr cppDocument,
MocIncludeMode mocIncludeMode,
IncludeStyle includeStyle)
: m_textDocument(textDocument)
, m_cppDocument(cppDocument)
, m_includeStyle(includeStyle)
{
const QList<Document::Include> includes = cppDocument->resolvedIncludes();
// Ignore *.moc includes if requested
if (mocIncludeMode == IgnoreMocIncludes) {
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,
unsigned *newLinesToPrepend,
unsigned *newLinesToAppend)
......@@ -144,51 +224,8 @@ int LineForNewIncludeDirective::operator()(const QString &newIncludeFileName,
: Client::IncludeGlobal;
// Handle no includes
if (m_includes.empty()) {
unsigned insertLine = 0;
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;
}
if (m_includes.empty())
return findInsertLineForVeryFirstInclude(newLinesToPrepend, newLinesToAppend);
typedef QList<IncludeGroup> IncludeGroups;
......
......@@ -90,7 +90,7 @@ public:
enum IncludeStyle { LocalBeforeGlobal, GlobalBeforeLocal, AutoDetect };
LineForNewIncludeDirective(const QTextDocument *textDocument,
QList<Include> includes,
const CPlusPlus::Document::Ptr cppDocument,
MocIncludeMode mocIncludeMode = IgnoreMocIncludes,
IncludeStyle includeStyle = AutoDetect);
......@@ -100,10 +100,13 @@ public:
unsigned *newLinesToAppend = 0);
private:
int findInsertLineForVeryFirstInclude(unsigned *newLinesToPrepend, unsigned *newLinesToAppend);
QList<IncludeGroup> getGroupsByIncludeType(const QList<IncludeGroup> &groups,
IncludeType includeType);
const QTextDocument *m_textDocument;
const CPlusPlus::Document::Ptr m_cppDocument;
IncludeStyle m_includeStyle;
QList<Include> m_includes;
};
......
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