forked from qt-creator/qt-creator
CppEditor: Consider #pragma once when inserting includes
Fixes: QTCREATORBUG-30808 Change-Id: Ib9f2ed1e428abfaa608b9dc42bc09dd2d403ee56 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -356,6 +356,15 @@ void Document::addUndefinedMacroUse(const QByteArray &name,
|
|||||||
_undefinedMacroUses.append(use);
|
_undefinedMacroUses.append(use);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Document::pragmaOnceLine() const
|
||||||
|
{
|
||||||
|
for (const Pragma &p : _pragmas) {
|
||||||
|
if (p.tokens.size() == 1 && p.tokens.first() == "once")
|
||||||
|
return p.line;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class Document::MacroUse
|
\class Document::MacroUse
|
||||||
\brief The MacroUse class represents the usage of a macro in a
|
\brief The MacroUse class represents the usage of a macro in a
|
||||||
|
@@ -11,15 +11,14 @@
|
|||||||
|
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QFuture>
|
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInt>
|
||||||
|
#include <QByteArrayList>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
namespace CPlusPlus {
|
namespace CPlusPlus {
|
||||||
|
|
||||||
class Macro;
|
|
||||||
class MacroArgumentReference;
|
class MacroArgumentReference;
|
||||||
class LookupContext;
|
class LookupContext;
|
||||||
|
|
||||||
@@ -55,6 +54,9 @@ public:
|
|||||||
void addUndefinedMacroUse(const QByteArray &name,
|
void addUndefinedMacroUse(const QByteArray &name,
|
||||||
int bytesOffset, int utf16charsOffset);
|
int bytesOffset, int utf16charsOffset);
|
||||||
|
|
||||||
|
void appendPragma(const Pragma &pragma) { _pragmas << pragma; }
|
||||||
|
int pragmaOnceLine() const;
|
||||||
|
|
||||||
Control *control() const { return _control; }
|
Control *control() const { return _control; }
|
||||||
Control *swapControl(Control *newControl);
|
Control *swapControl(Control *newControl);
|
||||||
TranslationUnit *translationUnit() const { return _translationUnit; }
|
TranslationUnit *translationUnit() const { return _translationUnit; }
|
||||||
@@ -353,6 +355,8 @@ private:
|
|||||||
/// the macro name of the include guard, if there is one.
|
/// the macro name of the include guard, if there is one.
|
||||||
QByteArray _includeGuardMacroName;
|
QByteArray _includeGuardMacroName;
|
||||||
|
|
||||||
|
QList<Pragma> _pragmas;
|
||||||
|
|
||||||
QByteArray m_fingerprint;
|
QByteArray m_fingerprint;
|
||||||
|
|
||||||
QByteArray _source;
|
QByteArray _source;
|
||||||
|
@@ -85,6 +85,12 @@ void FastPreprocessor::macroAdded(const Macro ¯o)
|
|||||||
_currentDoc->appendMacro(macro);
|
_currentDoc->appendMacro(macro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FastPreprocessor::pragmaAdded(const Pragma &pragma)
|
||||||
|
{
|
||||||
|
Q_ASSERT(_currentDoc);
|
||||||
|
_currentDoc->appendPragma(pragma);
|
||||||
|
}
|
||||||
|
|
||||||
static const Macro revision(const Snapshot &s, const Macro &m)
|
static const Macro revision(const Snapshot &s, const Macro &m)
|
||||||
{
|
{
|
||||||
if (Document::Ptr d = s.document(m.filePath())) {
|
if (Document::Ptr d = s.document(m.filePath())) {
|
||||||
|
@@ -40,7 +40,7 @@ public:
|
|||||||
const Utils::FilePaths &initialIncludes = {});
|
const Utils::FilePaths &initialIncludes = {});
|
||||||
|
|
||||||
virtual void macroAdded(const Macro &);
|
virtual void macroAdded(const Macro &);
|
||||||
|
virtual void pragmaAdded(const Pragma &pragma);
|
||||||
virtual void passedMacroDefinitionCheck(int, int, int, const Macro &);
|
virtual void passedMacroDefinitionCheck(int, int, int, const Macro &);
|
||||||
virtual void failedMacroDefinitionCheck(int, int, const ByteArrayRef &);
|
virtual void failedMacroDefinitionCheck(int, int, const ByteArrayRef &);
|
||||||
|
|
||||||
|
@@ -157,4 +157,11 @@ private:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CPLUSPLUS_EXPORT Pragma
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QByteArrayList tokens;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
} // CPlusPlus
|
} // CPlusPlus
|
||||||
|
@@ -14,6 +14,7 @@ namespace CPlusPlus {
|
|||||||
|
|
||||||
class ByteArrayRef;
|
class ByteArrayRef;
|
||||||
class Macro;
|
class Macro;
|
||||||
|
class Pragma;
|
||||||
|
|
||||||
class CPLUSPLUS_EXPORT MacroArgumentReference
|
class CPLUSPLUS_EXPORT MacroArgumentReference
|
||||||
{
|
{
|
||||||
@@ -61,6 +62,7 @@ public:
|
|||||||
virtual ~Client() = 0;
|
virtual ~Client() = 0;
|
||||||
|
|
||||||
virtual void macroAdded(const Macro ¯o) = 0;
|
virtual void macroAdded(const Macro ¯o) = 0;
|
||||||
|
virtual void pragmaAdded(const Pragma &pragma) = 0;
|
||||||
|
|
||||||
virtual void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
virtual void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
||||||
int line, const Macro ¯o) = 0;
|
int line, const Macro ¯o) = 0;
|
||||||
|
@@ -1626,10 +1626,10 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk)
|
|||||||
static const QByteArray ppInclude("include");
|
static const QByteArray ppInclude("include");
|
||||||
static const QByteArray ppIncludeNext("include_next");
|
static const QByteArray ppIncludeNext("include_next");
|
||||||
static const QByteArray ppImport("import");
|
static const QByteArray ppImport("import");
|
||||||
|
static const QByteArray ppPragma("pragma");
|
||||||
//### TODO:
|
//### TODO:
|
||||||
// line
|
// line
|
||||||
// error
|
// error
|
||||||
// pragma
|
|
||||||
|
|
||||||
if (tk->is(T_IDENTIFIER)) {
|
if (tk->is(T_IDENTIFIER)) {
|
||||||
const ByteArrayRef directive = tk->asByteArrayRef();
|
const ByteArrayRef directive = tk->asByteArrayRef();
|
||||||
@@ -1640,6 +1640,8 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk)
|
|||||||
handleIfDefDirective(true, tk);
|
handleIfDefDirective(true, tk);
|
||||||
} else if (directive == ppEndIf) {
|
} else if (directive == ppEndIf) {
|
||||||
handleEndIfDirective(tk, poundToken);
|
handleEndIfDirective(tk, poundToken);
|
||||||
|
} else if (directive == ppPragma) {
|
||||||
|
handlePragmaDirective(tk);
|
||||||
} else {
|
} else {
|
||||||
m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken);
|
m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken);
|
||||||
|
|
||||||
@@ -1866,6 +1868,23 @@ void Preprocessor::handleDefineDirective(PPToken *tk)
|
|||||||
m_client->macroAdded(macro);
|
m_client->macroAdded(macro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Preprocessor::handlePragmaDirective(PPToken *tk)
|
||||||
|
{
|
||||||
|
Pragma pragma;
|
||||||
|
pragma.line = tk->lineno;
|
||||||
|
lex(tk); // consume "pragma" token
|
||||||
|
|
||||||
|
while (isContinuationToken(*tk)) {
|
||||||
|
if (!consumeComments(tk))
|
||||||
|
return;
|
||||||
|
pragma.tokens << tk->asByteArrayRef().toByteArray();
|
||||||
|
lex(tk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_client)
|
||||||
|
m_client->pragmaAdded(pragma);
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray Preprocessor::expand(PPToken *tk, PPToken *lastConditionToken)
|
QByteArray Preprocessor::expand(PPToken *tk, PPToken *lastConditionToken)
|
||||||
{
|
{
|
||||||
unsigned line = tk->lineno;
|
unsigned line = tk->lineno;
|
||||||
|
@@ -210,6 +210,7 @@ private:
|
|||||||
void handlePreprocessorDirective(PPToken *tk);
|
void handlePreprocessorDirective(PPToken *tk);
|
||||||
void handleIncludeDirective(PPToken *tk, bool includeNext);
|
void handleIncludeDirective(PPToken *tk, bool includeNext);
|
||||||
void handleDefineDirective(PPToken *tk);
|
void handleDefineDirective(PPToken *tk);
|
||||||
|
void handlePragmaDirective(PPToken *tk);
|
||||||
QByteArray expand(PPToken *tk, PPToken *lastConditionToken = nullptr);
|
QByteArray expand(PPToken *tk, PPToken *lastConditionToken = nullptr);
|
||||||
const Internal::PPToken evalExpression(PPToken *tk, Value &result);
|
const Internal::PPToken evalExpression(PPToken *tk, Value &result);
|
||||||
void handleIfDirective(PPToken *tk);
|
void handleIfDirective(PPToken *tk);
|
||||||
|
@@ -298,6 +298,13 @@ void CppSourceProcessor::macroAdded(const CPlusPlus::Macro ¯o)
|
|||||||
m_currentDoc->appendMacro(macro);
|
m_currentDoc->appendMacro(macro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CppSourceProcessor::pragmaAdded(const CPlusPlus::Pragma &pragma)
|
||||||
|
{
|
||||||
|
if (!m_currentDoc)
|
||||||
|
return;
|
||||||
|
m_currentDoc->appendPragma(pragma);
|
||||||
|
}
|
||||||
|
|
||||||
void CppSourceProcessor::passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
void CppSourceProcessor::passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
||||||
int line, const CPlusPlus::Macro ¯o)
|
int line, const CPlusPlus::Macro ¯o)
|
||||||
{
|
{
|
||||||
|
@@ -68,6 +68,7 @@ private:
|
|||||||
|
|
||||||
// Client interface
|
// Client interface
|
||||||
void macroAdded(const CPlusPlus::Macro ¯o) override;
|
void macroAdded(const CPlusPlus::Macro ¯o) override;
|
||||||
|
void pragmaAdded(const CPlusPlus::Pragma &pragma) override;
|
||||||
void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
||||||
int line, const CPlusPlus::Macro ¯o) override;
|
int line, const CPlusPlus::Macro ¯o) override;
|
||||||
void failedMacroDefinitionCheck(int bytesOffset, int utf16charOffset,
|
void failedMacroDefinitionCheck(int bytesOffset, int utf16charOffset,
|
||||||
|
@@ -216,15 +216,22 @@ int LineForNewIncludeDirective::findInsertLineForVeryFirstInclude(unsigned *newL
|
|||||||
{
|
{
|
||||||
int insertLine = 1;
|
int insertLine = 1;
|
||||||
|
|
||||||
// If there is an include guard, insert right after that one
|
const auto appendAndPrependNewline = [&] {
|
||||||
const QByteArray includeGuardMacroName = m_cppDocument->includeGuardMacroName();
|
|
||||||
if (!includeGuardMacroName.isEmpty()) {
|
|
||||||
for (const Macro &definedMacro : m_cppDocument->definedMacros()) {
|
|
||||||
if (definedMacro.name() == includeGuardMacroName) {
|
|
||||||
if (newLinesToPrepend)
|
if (newLinesToPrepend)
|
||||||
*newLinesToPrepend = 1;
|
*newLinesToPrepend = 1;
|
||||||
if (newLinesToAppend)
|
if (newLinesToAppend)
|
||||||
*newLinesToAppend += 1;
|
*newLinesToAppend += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If there is an include guard or a "#pragma once", insert right after that one
|
||||||
|
if (const int pragmaOnceLine = m_cppDocument->pragmaOnceLine(); pragmaOnceLine != -1) {
|
||||||
|
appendAndPrependNewline();
|
||||||
|
insertLine = pragmaOnceLine + 1;
|
||||||
|
} else if (const QByteArray includeGuardMacroName = m_cppDocument->includeGuardMacroName();
|
||||||
|
!includeGuardMacroName.isEmpty()) {
|
||||||
|
for (const Macro &definedMacro : m_cppDocument->definedMacros()) {
|
||||||
|
if (definedMacro.name() == includeGuardMacroName) {
|
||||||
|
appendAndPrependNewline();
|
||||||
insertLine = definedMacro.line() + 1;
|
insertLine = definedMacro.line() + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1145,6 +1145,25 @@ private slots:
|
|||||||
|
|
||||||
// -------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
original =
|
||||||
|
"#pragma once\n"
|
||||||
|
"void @f();\n"
|
||||||
|
;
|
||||||
|
expected =
|
||||||
|
"#pragma once\n"
|
||||||
|
"\n"
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"void f();\n"
|
||||||
|
;
|
||||||
|
testDocuments << CppTestDocument::create("file.cpp", original, expected);
|
||||||
|
QTest::newRow("inserting_onlyPragmaOnce")
|
||||||
|
<< TestIncludePaths::globalIncludePath()
|
||||||
|
<< testDocuments << firstRefactoringOperation << "\"file.h\"";
|
||||||
|
testDocuments.clear();
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
original =
|
original =
|
||||||
"\n"
|
"\n"
|
||||||
"// comment\n"
|
"// comment\n"
|
||||||
|
@@ -108,6 +108,11 @@ public:
|
|||||||
m_definedMacrosLine.append(macro.line());
|
m_definedMacrosLine.append(macro.line());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pragmaAdded(const Pragma &pragma) override
|
||||||
|
{
|
||||||
|
m_pragmas.append(pragma);
|
||||||
|
}
|
||||||
|
|
||||||
void passedMacroDefinitionCheck(int /*bytesOffset*/,
|
void passedMacroDefinitionCheck(int /*bytesOffset*/,
|
||||||
int /*utf16charsOffset*/,
|
int /*utf16charsOffset*/,
|
||||||
int line,
|
int line,
|
||||||
@@ -264,6 +269,8 @@ public:
|
|||||||
const QMap<QByteArray, QVector<MacroArgumentReference >> usedMacros() const
|
const QMap<QByteArray, QVector<MacroArgumentReference >> usedMacros() const
|
||||||
{ return m_usedMacros; }
|
{ return m_usedMacros; }
|
||||||
|
|
||||||
|
const QList<Pragma> &pragmas() const { return m_pragmas; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Environment *m_env;
|
Environment *m_env;
|
||||||
QByteArray *m_output;
|
QByteArray *m_output;
|
||||||
@@ -282,6 +289,7 @@ private:
|
|||||||
QSet<QByteArray> m_unresolvedDefines;
|
QSet<QByteArray> m_unresolvedDefines;
|
||||||
QList<int> m_macroArgsCount;
|
QList<int> m_macroArgsCount;
|
||||||
QMap<QByteArray, QVector<MacroArgumentReference >> m_usedMacros;
|
QMap<QByteArray, QVector<MacroArgumentReference >> m_usedMacros;
|
||||||
|
QList<Pragma> m_pragmas;
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
@@ -381,6 +389,7 @@ private slots:
|
|||||||
void trigraph();
|
void trigraph();
|
||||||
void nested_arguments_expansion();
|
void nested_arguments_expansion();
|
||||||
void preprocessorSymbolsAsMacroArguments();
|
void preprocessorSymbolsAsMacroArguments();
|
||||||
|
void pragmas();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove all #... lines, and 'simplify' string, to allow easily comparing the result
|
// Remove all #... lines, and 'simplify' string, to allow easily comparing the result
|
||||||
@@ -2090,6 +2099,28 @@ void tst_Preprocessor::preprocessorSymbolsAsMacroArguments()
|
|||||||
QVERIFY(preprocess.run(QLatin1String("<stdin>"), input).startsWith("# 1 \"<stdin>\"\n"));
|
QVERIFY(preprocess.run(QLatin1String("<stdin>"), input).startsWith("# 1 \"<stdin>\"\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_Preprocessor::pragmas()
|
||||||
|
{
|
||||||
|
Environment env;
|
||||||
|
QByteArray output;
|
||||||
|
MockClient client(&env, &output);
|
||||||
|
Preprocessor preprocess(&client, &env);
|
||||||
|
QByteArray input =
|
||||||
|
"#pragma once\n"
|
||||||
|
"#include <iostream>\n"
|
||||||
|
"#pragma pack(/*distraction*/push)\n"
|
||||||
|
"struct S { bool b1; int i; short s; bool b2; };\n"
|
||||||
|
"#pragma pack(pop)\n";
|
||||||
|
preprocess.run(QLatin1String("<stdin>"), input);
|
||||||
|
QCOMPARE(client.pragmas().size(), 3);
|
||||||
|
QCOMPARE(client.pragmas().at(0).line, 1);
|
||||||
|
QCOMPARE(client.pragmas().at(0).tokens, QByteArrayList{"once"});
|
||||||
|
QCOMPARE(client.pragmas().at(1).line, 3);
|
||||||
|
QCOMPARE(client.pragmas().at(1).tokens, (QByteArrayList{"pack", "(", "push", ")"}));
|
||||||
|
QCOMPARE(client.pragmas().at(2).line, 5);
|
||||||
|
QCOMPARE(client.pragmas().at(2).tokens, (QByteArrayList{"pack", "(", "pop", ")"}));
|
||||||
|
}
|
||||||
|
|
||||||
void tst_Preprocessor::excessive_nesting()
|
void tst_Preprocessor::excessive_nesting()
|
||||||
{
|
{
|
||||||
Environment env;
|
Environment env;
|
||||||
|
Reference in New Issue
Block a user