From ca7ac8c0358efb0067c47f3135b22f26ea66b2a1 Mon Sep 17 00:00:00 2001 From: Leandro Melo Date: Tue, 29 May 2012 12:37:55 +0200 Subject: [PATCH] C++: Fix macro uses line info Make sure the environment line is consistent during preprocessor directives and identifier handling so clients can rely on consistent information. Particularly important for macro usages. New tests also added. Change-Id: I962a39a86cd17b8d945d2959c2c95e2d258ea3e6 Reviewed-by: hjk --- src/libs/cplusplus/pp-engine.cpp | 41 +++++++++----- src/libs/cplusplus/pp-engine.h | 3 +- src/plugins/cpptools/cppmodelmanager.cpp | 4 +- .../preprocessor/tst_preprocessor.cpp | 54 ++++++++++++++++++- 4 files changed, 85 insertions(+), 17 deletions(-) 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.