diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp index 1748a322e68..f6010994d89 100644 --- a/src/libs/cplusplus/pp-engine.cpp +++ b/src/libs/cplusplus/pp-engine.cpp @@ -539,6 +539,7 @@ Preprocessor::State::State() , m_inCondition(false) , m_inDefine(false) , m_offsetRef(0) + , m_envLineRef(1) { m_skipping[m_ifLevel] = false; m_trueTest[m_ifLevel] = false; @@ -687,13 +688,16 @@ _Lagain: m_state.m_lexer->scan(tk); } -// if (tk->isValid() && !tk->generated() && !tk->is(T_EOF_SYMBOL)) -// m_env->currentLine = tk->lineno; - _Lclassify: if (! m_state.m_inPreprocessorDirective) { + // Bellow, during directive and identifier handling the current environment line is + // updated in accordance to "global" context in order for clients to rely on consistent + // information. Afterwards, it's restored until output is eventually processed. if (tk->newline() && tk->is(T_POUND)) { + unsigned envLine = m_env->currentLine; + m_env->currentLine = tk->lineno + m_state.m_envLineRef - 1; handlePreprocessorDirective(tk); + m_env->currentLine = envLine; goto _Lclassify; } else if (tk->newline() && skipping()) { ScopedBoolSwap s(m_state.m_inPreprocessorDirective, true); @@ -702,10 +706,16 @@ _Lclassify: } while (isValidToken(*tk)); goto _Lclassify; } else if (tk->is(T_IDENTIFIER) && !isQtReservedWord(tk->asByteArrayRef())) { - if (m_state.m_inCondition && tk->asByteArrayRef() == "defined") + if (m_state.m_inCondition && tk->asByteArrayRef() == "defined") { handleDefined(tk); - else if (handleIdentifier(tk)) - goto _Lagain; + } else { + unsigned envLine = m_env->currentLine; + m_env->currentLine = tk->lineno + m_state.m_envLineRef - 1; + bool willExpand = handleIdentifier(tk); + m_env->currentLine = envLine; + if (willExpand) + goto _Lagain; + } } } } @@ -803,7 +813,8 @@ bool Preprocessor::handleIdentifier(PPToken *tk) argTks.last().begin() + argTks.last().length() - argTks.first().begin())); } - m_client->startExpandingMacro(idTk.offset, *macro, macroNameRef, argRefs); + m_client->startExpandingMacro(m_state.m_offsetRef + idTk.offset, *macro, macroNameRef, + argRefs); } if (!handleFunctionLikeMacro(tk, macro, body, !m_state.m_inDefine, allArgTks)) { @@ -812,7 +823,7 @@ bool Preprocessor::handleIdentifier(PPToken *tk) return false; } } else if (m_client && !idTk.generated()) { - m_client->startExpandingMacro(idTk.offset, *macro, macroNameRef); + m_client->startExpandingMacro(m_state.m_offsetRef + idTk.offset, *macro, macroNameRef); } if (body.isEmpty()) { @@ -929,7 +940,7 @@ exitNicely: void Preprocessor::preprocess(const QString &fileName, const QByteArray &source, QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition, - unsigned offset) + unsigned offsetRef, unsigned envLineRef) { if (source.isEmpty()) return; @@ -948,7 +959,8 @@ void Preprocessor::preprocess(const QString &fileName, const QByteArray &source, m_state.m_noLines = noLines; m_state.m_markGeneratedTokens = markGeneratedTokens; m_state.m_inCondition = inCondition; - m_state.m_offsetRef = offset; + m_state.m_offsetRef = offsetRef; + m_state.m_envLineRef = envLineRef; const QString previousFileName = m_env->currentFile; m_env->currentFile = fileName; @@ -1249,8 +1261,12 @@ void Preprocessor::handleDefineDirective(PPToken *tk) bodyTokens.push_back(*tk); lex(tk); if (eagerExpansion) - while (tk->is(T_IDENTIFIER) && !isQtReservedWord(tk->asByteArrayRef()) && handleIdentifier(tk)) + while (tk->is(T_IDENTIFIER) + && (!tk->newline() || tk->joined()) + && !isQtReservedWord(tk->asByteArrayRef()) + && handleIdentifier(tk)) { lex(tk); + } } if (isQtReservedWord(ByteArrayRef(¯oName))) { @@ -1305,7 +1321,8 @@ QByteArray Preprocessor::expand(PPToken *tk, PPToken *lastConditionToken) // qDebug("*** Condition before: [%s]", condition.constData()); QByteArray result; result.reserve(256); - preprocess(m_state.m_currentFileName, condition, &result, true, false, true, begin); + preprocess(m_state.m_currentFileName, condition, &result, true, false, true, begin, + m_env->currentLine); result.squeeze(); // qDebug("*** Condition after: [%s]", result.constData()); diff --git a/src/libs/cplusplus/pp-engine.h b/src/libs/cplusplus/pp-engine.h index 12afd1f9b83..c310a9e3e3a 100644 --- a/src/libs/cplusplus/pp-engine.h +++ b/src/libs/cplusplus/pp-engine.h @@ -92,7 +92,7 @@ private: void preprocess(const QString &filename, const QByteArray &source, QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition, - unsigned offset = 0); + unsigned offsetRef = 0, unsigned envLineRef = 1); enum { MAX_LEVEL = 512 }; @@ -121,6 +121,7 @@ private: bool m_inDefine; unsigned m_offsetRef; + unsigned m_envLineRef; }; void handleDefined(PPToken *tk); diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 249b7c8181d..0fae9871b46 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -524,9 +524,7 @@ void CppPreprocessor::startExpandingMacro(unsigned offset, if (! m_currentDoc) return; - //qDebug() << "start expanding:" << macro.name() << "text:" << originalText; - m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine, - actuals); + m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine, actuals); } void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &) diff --git a/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp b/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp index a6775f6b6b8..0dc116072d1 100644 --- a/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp +++ b/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp @@ -32,6 +32,7 @@ #include #include +#include //TESTED_COMPONENT=src/libs/cplusplus using namespace CPlusPlus; @@ -117,13 +118,14 @@ public: virtual void failedMacroDefinitionCheck(unsigned /*offset*/, const ByteArrayRef &/*name*/) {} virtual void startExpandingMacro(unsigned offset, - const Macro &/*macro*/, + const Macro ¯o, const ByteArrayRef &originalText, const QVector &/*actuals*/ = QVector()) { m_expandedMacros.append(QByteArray(originalText.start(), originalText.length())); m_expandedMacrosOffset.append(offset); + m_macroUsesLine.insert(macro.name(), m_env->currentLine); } virtual void stopExpandingMacro(unsigned /*offset*/, const Macro &/*macro*/) {} @@ -227,6 +229,9 @@ public: QList definedMacrosLine() const { return m_definedMacrosLine; } + QHash macroUsesLine() const + { return m_macroUsesLine; } + private: Environment *m_env; QByteArray *m_output; @@ -239,6 +244,7 @@ private: QList m_expandedMacrosOffset; QList m_definedMacros; QList m_definedMacrosLine; + QHash m_macroUsesLine; }; QT_BEGIN_NAMESPACE @@ -304,6 +310,7 @@ private slots: void objmacro_expanding_as_fnmacro_notification(); void macro_definition_lineno(); void macro_uses(); + void macro_uses_lines(); void macro_arguments_notificatin(); void unfinished_function_like_macro_call(); void nasty_macro_expansion(); @@ -466,6 +473,51 @@ void tst_Preprocessor::macro_uses() QCOMPARE(client.definedMacrosLine(), QList() << 2 << 3); } +void tst_Preprocessor::macro_uses_lines() +{ + QByteArray buffer("#define FOO\n" + "FOO\n" + "\n" + "#define HEADER \n" + "#include HEADER\n" + "\n" + "#define DECLARE(C, V) struct C {}; C V;\n" + "#define ABC X\n" + "DECLARE(Test, test)\n" + "\n" + "int abc;\n" + "#define NOTHING(C)\n" + "NOTHING(abc)\n" + "\n" + "#define ENABLE(FEATURE) (defined ENABLE_##FEATURE && ENABLE_##FEATURE)\n" + "#define ENABLE_COOL 1\n" + "void fill();\n" + "#if ENABLE(COOL)\n" + "class Cool {};\n" + "#endif\n" + "int cool = ENABLE_COOL;\n"); + + QByteArray output; + Environment env; + MockClient client(&env, &output); + Preprocessor preprocess(&client, &env); + preprocess.run(QLatin1String(""), buffer); + + QCOMPARE(client.macroUsesLine().value("FOO"), 2U); + QCOMPARE(client.macroUsesLine().value("HEADER"), 5U); + QCOMPARE(client.macroUsesLine().value("DECLARE"), 9U); + QCOMPARE(client.macroUsesLine().value("NOTHING"), 13U); + QCOMPARE(client.macroUsesLine().value("ENABLE"), 18U); + QCOMPARE(client.macroUsesLine().value("ENABLE_COOL"), 21U); + QCOMPARE(client.expandedMacrosOffset(), QList() + << buffer.lastIndexOf("FOO") + << buffer.lastIndexOf("HEADER") + << buffer.lastIndexOf("DECLARE") + << buffer.lastIndexOf("NOTHING") + << buffer.lastIndexOf("ENABLE(COOL)") + << buffer.lastIndexOf("ENABLE_COOL")); +} + void tst_Preprocessor::macro_definition_lineno() { Client *client = 0; // no client.