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);
|
||||
}
|
||||
|
||||
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
|
||||
\brief The MacroUse class represents the usage of a macro in a
|
||||
|
@@ -11,15 +11,14 @@
|
||||
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QFuture>
|
||||
#include <QAtomicInt>
|
||||
#include <QByteArrayList>
|
||||
#include <QDateTime>
|
||||
#include <QFuture>
|
||||
#include <QHash>
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace CPlusPlus {
|
||||
|
||||
class Macro;
|
||||
class MacroArgumentReference;
|
||||
class LookupContext;
|
||||
|
||||
@@ -55,6 +54,9 @@ public:
|
||||
void addUndefinedMacroUse(const QByteArray &name,
|
||||
int bytesOffset, int utf16charsOffset);
|
||||
|
||||
void appendPragma(const Pragma &pragma) { _pragmas << pragma; }
|
||||
int pragmaOnceLine() const;
|
||||
|
||||
Control *control() const { return _control; }
|
||||
Control *swapControl(Control *newControl);
|
||||
TranslationUnit *translationUnit() const { return _translationUnit; }
|
||||
@@ -353,6 +355,8 @@ private:
|
||||
/// the macro name of the include guard, if there is one.
|
||||
QByteArray _includeGuardMacroName;
|
||||
|
||||
QList<Pragma> _pragmas;
|
||||
|
||||
QByteArray m_fingerprint;
|
||||
|
||||
QByteArray _source;
|
||||
|
@@ -85,6 +85,12 @@ void FastPreprocessor::macroAdded(const Macro ¯o)
|
||||
_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)
|
||||
{
|
||||
if (Document::Ptr d = s.document(m.filePath())) {
|
||||
|
@@ -40,7 +40,7 @@ public:
|
||||
const Utils::FilePaths &initialIncludes = {});
|
||||
|
||||
virtual void macroAdded(const Macro &);
|
||||
|
||||
virtual void pragmaAdded(const Pragma &pragma);
|
||||
virtual void passedMacroDefinitionCheck(int, int, int, const Macro &);
|
||||
virtual void failedMacroDefinitionCheck(int, int, const ByteArrayRef &);
|
||||
|
||||
|
@@ -157,4 +157,11 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
class CPLUSPLUS_EXPORT Pragma
|
||||
{
|
||||
public:
|
||||
QByteArrayList tokens;
|
||||
int line;
|
||||
};
|
||||
|
||||
} // CPlusPlus
|
||||
|
@@ -14,6 +14,7 @@ namespace CPlusPlus {
|
||||
|
||||
class ByteArrayRef;
|
||||
class Macro;
|
||||
class Pragma;
|
||||
|
||||
class CPLUSPLUS_EXPORT MacroArgumentReference
|
||||
{
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
virtual ~Client() = 0;
|
||||
|
||||
virtual void macroAdded(const Macro ¯o) = 0;
|
||||
virtual void pragmaAdded(const Pragma &pragma) = 0;
|
||||
|
||||
virtual void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
||||
int line, const Macro ¯o) = 0;
|
||||
|
@@ -1626,10 +1626,10 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk)
|
||||
static const QByteArray ppInclude("include");
|
||||
static const QByteArray ppIncludeNext("include_next");
|
||||
static const QByteArray ppImport("import");
|
||||
static const QByteArray ppPragma("pragma");
|
||||
//### TODO:
|
||||
// line
|
||||
// error
|
||||
// pragma
|
||||
|
||||
if (tk->is(T_IDENTIFIER)) {
|
||||
const ByteArrayRef directive = tk->asByteArrayRef();
|
||||
@@ -1640,6 +1640,8 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk)
|
||||
handleIfDefDirective(true, tk);
|
||||
} else if (directive == ppEndIf) {
|
||||
handleEndIfDirective(tk, poundToken);
|
||||
} else if (directive == ppPragma) {
|
||||
handlePragmaDirective(tk);
|
||||
} else {
|
||||
m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken);
|
||||
|
||||
@@ -1866,6 +1868,23 @@ void Preprocessor::handleDefineDirective(PPToken *tk)
|
||||
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)
|
||||
{
|
||||
unsigned line = tk->lineno;
|
||||
|
@@ -210,6 +210,7 @@ private:
|
||||
void handlePreprocessorDirective(PPToken *tk);
|
||||
void handleIncludeDirective(PPToken *tk, bool includeNext);
|
||||
void handleDefineDirective(PPToken *tk);
|
||||
void handlePragmaDirective(PPToken *tk);
|
||||
QByteArray expand(PPToken *tk, PPToken *lastConditionToken = nullptr);
|
||||
const Internal::PPToken evalExpression(PPToken *tk, Value &result);
|
||||
void handleIfDirective(PPToken *tk);
|
||||
|
@@ -298,6 +298,13 @@ void CppSourceProcessor::macroAdded(const CPlusPlus::Macro ¯o)
|
||||
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,
|
||||
int line, const CPlusPlus::Macro ¯o)
|
||||
{
|
||||
|
@@ -68,6 +68,7 @@ private:
|
||||
|
||||
// Client interface
|
||||
void macroAdded(const CPlusPlus::Macro ¯o) override;
|
||||
void pragmaAdded(const CPlusPlus::Pragma &pragma) override;
|
||||
void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
|
||||
int line, const CPlusPlus::Macro ¯o) override;
|
||||
void failedMacroDefinitionCheck(int bytesOffset, int utf16charOffset,
|
||||
|
@@ -216,15 +216,22 @@ int LineForNewIncludeDirective::findInsertLineForVeryFirstInclude(unsigned *newL
|
||||
{
|
||||
int insertLine = 1;
|
||||
|
||||
// If there is an include guard, insert right after that one
|
||||
const QByteArray includeGuardMacroName = m_cppDocument->includeGuardMacroName();
|
||||
if (!includeGuardMacroName.isEmpty()) {
|
||||
for (const Macro &definedMacro : m_cppDocument->definedMacros()) {
|
||||
if (definedMacro.name() == includeGuardMacroName) {
|
||||
const auto appendAndPrependNewline = [&] {
|
||||
if (newLinesToPrepend)
|
||||
*newLinesToPrepend = 1;
|
||||
if (newLinesToAppend)
|
||||
*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;
|
||||
}
|
||||
}
|
||||
|
@@ -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 =
|
||||
"\n"
|
||||
"// comment\n"
|
||||
|
@@ -108,6 +108,11 @@ public:
|
||||
m_definedMacrosLine.append(macro.line());
|
||||
}
|
||||
|
||||
void pragmaAdded(const Pragma &pragma) override
|
||||
{
|
||||
m_pragmas.append(pragma);
|
||||
}
|
||||
|
||||
void passedMacroDefinitionCheck(int /*bytesOffset*/,
|
||||
int /*utf16charsOffset*/,
|
||||
int line,
|
||||
@@ -264,6 +269,8 @@ public:
|
||||
const QMap<QByteArray, QVector<MacroArgumentReference >> usedMacros() const
|
||||
{ return m_usedMacros; }
|
||||
|
||||
const QList<Pragma> &pragmas() const { return m_pragmas; }
|
||||
|
||||
private:
|
||||
Environment *m_env;
|
||||
QByteArray *m_output;
|
||||
@@ -282,6 +289,7 @@ private:
|
||||
QSet<QByteArray> m_unresolvedDefines;
|
||||
QList<int> m_macroArgsCount;
|
||||
QMap<QByteArray, QVector<MacroArgumentReference >> m_usedMacros;
|
||||
QList<Pragma> m_pragmas;
|
||||
};
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -381,6 +389,7 @@ private slots:
|
||||
void trigraph();
|
||||
void nested_arguments_expansion();
|
||||
void preprocessorSymbolsAsMacroArguments();
|
||||
void pragmas();
|
||||
};
|
||||
|
||||
// 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"));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Environment env;
|
||||
|
Reference in New Issue
Block a user