diff options
Diffstat (limited to 'src/libs')
-rw-r--r-- | src/libs/cplusplus/CppDocument.h | 12 | ||||
-rw-r--r-- | src/libs/cplusplus/FastPreprocessor.h | 1 | ||||
-rw-r--r-- | src/libs/cplusplus/PreprocessorClient.h | 3 | ||||
-rw-r--r-- | src/libs/cplusplus/cplusplus-lib.pri | 2 | ||||
-rw-r--r-- | src/libs/cplusplus/pp-engine.cpp | 173 | ||||
-rw-r--r-- | src/libs/cplusplus/pp-engine.h | 64 |
6 files changed, 215 insertions, 40 deletions
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h index 484a1b3d9e9..3a537478ed1 100644 --- a/src/libs/cplusplus/CppDocument.h +++ b/src/libs/cplusplus/CppDocument.h @@ -312,6 +312,11 @@ public: QList<UndefinedMacroUse> undefinedMacroUses() const { return _undefinedMacroUses; } + void setIncludeGuardMacroName(const QByteArray &includeGuardMacroName) + { _includeGuardMacroName = includeGuardMacroName; } + QByteArray includeGuardMacroName() const + { return _includeGuardMacroName; } + const Macro *findMacroDefinitionAt(unsigned line) const; const MacroUse *findMacroUseAt(unsigned offset) const; const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const; @@ -327,12 +332,19 @@ private: Control *_control; TranslationUnit *_translationUnit; Namespace *_globalNamespace; + + /// All messages generated during lexical/syntactic/semantic analysis. QList<DiagnosticMessage> _diagnosticMessages; + QList<Include> _includes; QList<Macro> _definedMacros; QList<Block> _skippedBlocks; QList<MacroUse> _macroUses; QList<UndefinedMacroUse> _undefinedMacroUses; + + /// the macro name of the include guard, if there is one. + QByteArray _includeGuardMacroName; + QByteArray _source; QDateTime _lastModified; QAtomicInt _keepSourceAndASTCount; diff --git a/src/libs/cplusplus/FastPreprocessor.h b/src/libs/cplusplus/FastPreprocessor.h index 14168ad2e6b..223acaa05f0 100644 --- a/src/libs/cplusplus/FastPreprocessor.h +++ b/src/libs/cplusplus/FastPreprocessor.h @@ -71,6 +71,7 @@ public: const Macro &, const QVector<MacroArgumentReference> &); virtual void stopExpandingMacro(unsigned, const Macro &) {} + virtual void markAsIncludeGuard(const QByteArray &) {} virtual void startSkippingBlocks(unsigned) {} virtual void stopSkippingBlocks(unsigned) {} diff --git a/src/libs/cplusplus/PreprocessorClient.h b/src/libs/cplusplus/PreprocessorClient.h index f7e9932dd2d..f7f18a75cb3 100644 --- a/src/libs/cplusplus/PreprocessorClient.h +++ b/src/libs/cplusplus/PreprocessorClient.h @@ -90,6 +90,9 @@ public: = QVector<MacroArgumentReference>()) = 0; virtual void stopExpandingMacro(unsigned offset, const Macro ¯o) = 0; + /// Mark the given macro name as the include guard for the current file. + virtual void markAsIncludeGuard(const QByteArray ¯oName) = 0; + /// Start skipping from the given offset. virtual void startSkippingBlocks(unsigned offset) = 0; virtual void stopSkippingBlocks(unsigned offset) = 0; diff --git a/src/libs/cplusplus/cplusplus-lib.pri b/src/libs/cplusplus/cplusplus-lib.pri index f5d1b0a5112..a2ccabdfb42 100644 --- a/src/libs/cplusplus/cplusplus-lib.pri +++ b/src/libs/cplusplus/cplusplus-lib.pri @@ -10,6 +10,8 @@ include(../3rdparty/cplusplus/cplusplus.pri) greaterThan(QT_MAJOR_VERSION, 4): QT += concurrent +#DEFINES += DEBUG_INCLUDE_GUARD_TRACKING + contains(QT, gui) { HEADERS += \ $$PWD/Icons.h \ diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp index 705ea1b1a38..b1571f0c201 100644 --- a/src/libs/cplusplus/pp-engine.cpp +++ b/src/libs/cplusplus/pp-engine.cpp @@ -80,6 +80,7 @@ enum { } namespace { +/// RAII object to save a value, and restore it when the scope is left. template<typename _T> class ScopedSwap { @@ -100,7 +101,6 @@ public: } }; typedef ScopedSwap<bool> ScopedBoolSwap; -typedef ScopedSwap<unsigned> ScopedUnsignedSwap; } // anonymous namespace namespace CPlusPlus { @@ -557,6 +557,7 @@ Preprocessor::State::State() , m_offsetRef(0) , m_lineRef(1) , m_expansionStatus(NotExpanding) + , m_includeGuardState(IncludeGuardState_BeforeIfndef) { m_skipping[m_ifLevel] = false; m_trueTest[m_ifLevel] = false; @@ -596,6 +597,88 @@ void Preprocessor::State::popTokenBuffer() --m_tokenBufferDepth; } +#ifdef DEBUG_INCLUDE_GUARD_TRACKING +QString Preprocessor::State::guardStateToString(int guardState) +{ + switch (guardState) { + case IncludeGuardState_NoGuard: return QLatin1String("NoGuard"); + case IncludeGuardState_BeforeIfndef: return QLatin1String("BeforeIfndef"); + case IncludeGuardState_AfterIfndef: return QLatin1String("AfterIfndef"); + case IncludeGuardState_AfterDefine: return QLatin1String("AfterDefine"); + case IncludeGuardState_AfterEndif: return QLatin1String("AfterEndif"); + default: return QLatin1String("UNKNOWN"); + } +} +#endif // DEBUG_INCLUDE_GUARD_TRACKING + +/** + * @brief Update the include-guard tracking state. + * + * Include guards are the #ifdef/#define/#endif sequence typically found in + * header files to prevent repeated definition of the contents of that header + * file. So, for a file to have an include guard, it must look like this: + * \code + * #ifndef SOME_ID + * ... all declarations/definitions/etc. go here ... + * #endif + * \endcode + * + * SOME_ID is an identifier, and is also the include guard. The only tokens + * allowed before the #ifndef and after the #endif are comments (in any form) + * or #line directives. The only other requirement is that a #define SOME_ID + * occurs inside the #ifndef block, but not nested inside other + * #if/#ifdef/#ifndef blocks. + * + * This method tracks the state, and is called from \c updateIncludeGuardState + * which handles the most common no-op cases. + * + * @param hint indicates what kind of token is encountered in the input + * @param idToken the identifier token that ought to be in the input + * after a #ifndef or a #define . + */ +void Preprocessor::State::updateIncludeGuardState_helper(IncludeGuardStateHint hint, PPToken *idToken) +{ +#ifdef DEBUG_INCLUDE_GUARD_TRACKING + int oldIncludeGuardState = m_includeGuardState; + QByteArray oldIncludeGuardMacroName = m_includeGuardMacroName; +#endif // DEBUG_INCLUDE_GUARD_TRACKING + + switch (m_includeGuardState) { + case IncludeGuardState_NoGuard: + break; + case IncludeGuardState_BeforeIfndef: + if (hint == IncludeGuardStateHint_Ifndef + && idToken && idToken->is(T_IDENTIFIER)) { + m_includeGuardMacroName = idToken->asByteArrayRef().toByteArray(); + m_includeGuardState = IncludeGuardState_AfterIfndef; + } else { + m_includeGuardState = IncludeGuardState_NoGuard; + } + break; + case IncludeGuardState_AfterIfndef: + if (hint == IncludeGuardStateHint_Define + && idToken && idToken->is(T_IDENTIFIER) + && idToken->asByteArrayRef() == m_includeGuardMacroName) + m_includeGuardState = IncludeGuardState_AfterDefine; + break; + case IncludeGuardState_AfterDefine: + if (hint == IncludeGuardStateHint_Endif) + m_includeGuardState = IncludeGuardState_AfterEndif; + break; + case IncludeGuardState_AfterEndif: + m_includeGuardState = IncludeGuardState_NoGuard; + m_includeGuardMacroName.clear(); + break; + } + +#ifdef DEBUG_INCLUDE_GUARD_TRACKING + qDebug() << "***" << guardStateToString(oldIncludeGuardState) + << "->" << guardStateToString(m_includeGuardState) + << "hint:" << hint + << "guard:" << oldIncludeGuardMacroName << "->" << m_includeGuardMacroName; +#endif // DEBUG_INCLUDE_GUARD_TRACKING +} + const QString Preprocessor::configurationFileName = QLatin1String("<configuration>"); Preprocessor::Preprocessor(Client *client, Environment *env) @@ -618,8 +701,11 @@ QByteArray Preprocessor::run(const QString &fileName, { m_scratchBuffer.clear(); - QByteArray preprocessed; - preprocess(fileName, source, &preprocessed, noLines, markGeneratedTokens, false); + QByteArray preprocessed, includeGuardMacroName; + preprocess(fileName, source, &preprocessed, &includeGuardMacroName, noLines, + markGeneratedTokens, false); + if (!includeGuardMacroName.isEmpty()) + m_client->markAsIncludeGuard(includeGuardMacroName); return preprocessed; } @@ -736,6 +822,7 @@ _Lclassify: } while (isContinuationToken(*tk)); goto _Lclassify; } else if (tk->is(T_IDENTIFIER) && !isQtReservedWord(tk->asByteArrayRef())) { + m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken); if (m_state.m_inCondition && tk->asByteArrayRef() == "defined") { handleDefined(tk); } else { @@ -743,6 +830,8 @@ _Lclassify: if (handleIdentifier(tk)) goto _Lagain; } + } else if (tk->isNot(T_COMMENT) && tk->isNot(T_EOF_SYMBOL)) { + m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken); } } } @@ -1244,15 +1333,15 @@ void Preprocessor::enforceSpacing(const Preprocessor::PPToken &tk, bool forceSpa /// invalid pp-tokens are used as markers to force whitespace checks. void Preprocessor::preprocess(const QString &fileName, const QByteArray &source, - QByteArray *result, bool noLines, + QByteArray *result, QByteArray *includeGuardMacroName, + bool noLines, bool markGeneratedTokens, bool inCondition, unsigned offsetRef, unsigned lineRef) { if (source.isEmpty()) return; - const State savedState = m_state; - m_state = State(); + ScopedSwap<State> savedState(m_state, State()); m_state.m_currentFileName = fileName; m_state.m_source = source; m_state.m_lexer = new Lexer(source.constBegin(), source.constEnd()); @@ -1267,12 +1356,9 @@ void Preprocessor::preprocess(const QString &fileName, const QByteArray &source, m_state.m_offsetRef = offsetRef; m_state.m_lineRef = lineRef; - const QString previousFileName = m_env->currentFile; - m_env->currentFile = fileName; - m_env->currentFileUtf8 = fileName.toUtf8(); - - const unsigned previousCurrentLine = m_env->currentLine; - m_env->currentLine = 1; + ScopedSwap<QString> savedFileName(m_env->currentFile, fileName); + ScopedSwap<QByteArray> savedUtf8FileName(m_env->currentFileUtf8, fileName.toUtf8()); + ScopedSwap<unsigned> savedCurrentLine(m_env->currentLine, 1); if (!m_state.m_noLines) generateOutputLineMarker(1); @@ -1313,14 +1399,14 @@ void Preprocessor::preprocess(const QString &fileName, const QByteArray &source, removeTrailingOutputLines(); + if (includeGuardMacroName) { + if (m_state.m_includeGuardState == State::IncludeGuardState_AfterDefine + || m_state.m_includeGuardState == State::IncludeGuardState_AfterEndif) + *includeGuardMacroName = m_state.m_includeGuardMacroName; + } delete m_state.m_lexer; while (m_state.m_tokenBuffer) m_state.popTokenBuffer(); - m_state = savedState; - - m_env->currentFile = previousFileName; - m_env->currentFileUtf8 = previousFileName.toUtf8(); - m_env->currentLine = previousCurrentLine; } bool Preprocessor::collectActualArguments(PPToken *tk, QVector<QVector<PPToken> > *actuals) @@ -1426,27 +1512,31 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk) if (tk->is(T_IDENTIFIER)) { const ByteArrayRef directive = tk->asByteArrayRef(); - if (!skipping() && directive == ppDefine) + if (!skipping() && directive == ppDefine) { handleDefineDirective(tk); - else if (!skipping() && directive == ppUndef) - handleUndefDirective(tk); - else if (!skipping() && (directive == ppInclude - || directive == ppImport)) - handleIncludeDirective(tk, false); - else if (!skipping() && directive == ppIncludeNext) - handleIncludeDirective(tk, true); - else if (directive == ppIf) - handleIfDirective(tk); - else if (directive == ppIfDef) - handleIfDefDirective(false, tk); - else if (directive == ppIfNDef) + } else if (directive == ppIfNDef) { handleIfDefDirective(true, tk); - else if (directive == ppEndIf) + } else if (directive == ppEndIf) { handleEndIfDirective(tk, poundToken); - else if (directive == ppElse) - handleElseDirective(tk, poundToken); - else if (directive == ppElif) - handleElifDirective(tk, poundToken); + } else { + m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken); + + if (!skipping() && directive == ppUndef) + handleUndefDirective(tk); + else if (!skipping() && (directive == ppInclude + || directive == ppImport)) + handleIncludeDirective(tk, false); + else if (!skipping() && directive == ppIncludeNext) + handleIncludeDirective(tk, true); + else if (directive == ppIf) + handleIfDirective(tk); + else if (directive == ppIfDef) + handleIfDefDirective(false, tk); + else if (directive == ppElse) + handleElseDirective(tk, poundToken); + else if (directive == ppElif) + handleElifDirective(tk, poundToken); + } } skipPreprocesorDirective(tk); @@ -1506,6 +1596,8 @@ void Preprocessor::handleDefineDirective(PPToken *tk) macro.setName(macroName); macro.setOffset(tk->offset); + PPToken idToken(*tk); + lex(tk); if (isContinuationToken(*tk) && tk->is(T_LPAREN) && ! tk->whitespace()) { @@ -1540,6 +1632,9 @@ void Preprocessor::handleDefineDirective(PPToken *tk) } if (isContinuationToken(*tk) && tk->is(T_RPAREN)) lex(tk); // consume ")" token + } else { + if (m_state.m_ifLevel == 1) + m_state.updateIncludeGuardState(State::IncludeGuardStateHint_Define, &idToken); } QVector<PPToken> bodyTokens; @@ -1636,7 +1731,7 @@ 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, line); + preprocess(m_state.m_currentFileName, condition, &result, 0, true, false, true, begin, line); result.squeeze(); // qDebug("*** Condition after: [%s]", result.constData()); @@ -1757,6 +1852,9 @@ void Preprocessor::handleEndIfDirective(PPToken *tk, const PPToken £Token) --m_state.m_ifLevel; if (m_client && wasSkipping && !m_state.m_skipping[m_state.m_ifLevel]) m_client->stopSkippingBlocks(poundToken.offset - 1); + + if (m_state.m_ifLevel == 0) + m_state.updateIncludeGuardState(State::IncludeGuardStateHint_Endif); } lex(tk); // consume "endif" token @@ -1768,6 +1866,9 @@ void Preprocessor::handleIfDefDirective(bool checkUndefined, PPToken *tk) lex(tk); // consume "ifdef" token if (tk->is(T_IDENTIFIER)) { + if (checkUndefined && m_state.m_ifLevel == 0) + m_state.updateIncludeGuardState(State::IncludeGuardStateHint_Ifndef, tk); + bool value = false; const ByteArrayRef macroName = tk->asByteArrayRef(); if (Macro *macro = macroDefinition(macroName, tk->offset, tk->lineno, m_env, m_client)) { diff --git a/src/libs/cplusplus/pp-engine.h b/src/libs/cplusplus/pp-engine.h index 23ea7b84f9f..a3189f18183 100644 --- a/src/libs/cplusplus/pp-engine.h +++ b/src/libs/cplusplus/pp-engine.h @@ -81,7 +81,8 @@ public: Preprocessor(Client *client, Environment *env); QByteArray run(const QString &filename, const QString &source); - QByteArray run(const QString &filename, const QByteArray &source, bool noLines = false, bool markGeneratedTokens = true); + QByteArray run(const QString &filename, const QByteArray &source, + bool noLines = false, bool markGeneratedTokens = true); bool expandFunctionlikeMacros() const; void setExpandFunctionlikeMacros(bool expandFunctionlikeMacros); @@ -90,9 +91,9 @@ public: void setKeepComments(bool keepComments); private: - void preprocess(const QString &filename, - const QByteArray &source, - QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition, + void preprocess(const QString &filename, const QByteArray &source, + QByteArray *result, QByteArray *includeGuardMacroName, + bool noLines, bool markGeneratedTokens, bool inCondition, unsigned offsetRef = 0, unsigned lineRef = 1); enum { MAX_LEVEL = 512 }; @@ -133,6 +134,61 @@ private: ExpansionStatus m_expansionStatus; QByteArray m_expansionResult; QVector<QPair<unsigned, unsigned> > m_expandedTokensInfo; + + enum { + /// State to indicate that no guard is possible anymore. + IncludeGuardState_NoGuard = 0, + /// Initial state before the first non-comment token. + IncludeGuardState_BeforeIfndef, + /// State to indicate that the first interesting token was a + /// #ifndef token. + IncludeGuardState_AfterIfndef, + /// State to indicate that the only interesting tokens were + /// a #ifndef and a #define . + IncludeGuardState_AfterDefine, + /// State for after reading the #endif belonging to the #ifndef + IncludeGuardState_AfterEndif + } m_includeGuardState; + QByteArray m_includeGuardMacroName; + + enum IncludeGuardStateHint { + /// anything that is not a comment, a #ifndef, a #define, or a + /// #endif + IncludeGuardStateHint_OtherToken = 0, + /// we hit a #ifndef + IncludeGuardStateHint_Ifndef, + /// we hit a #define + IncludeGuardStateHint_Define, + /// we hit a #endif + IncludeGuardStateHint_Endif + }; + + /// Update the include-guard state. + /// + /// \param hint indicates what kind of token is encountered in the input + /// \param idToken the identifier token that ought to be in the input + /// after a #ifndef or a #define . + inline void updateIncludeGuardState(IncludeGuardStateHint hint, + PPToken *idToken = 0) + { + // some quick checks for the majority of the uninteresting cases: + if (m_includeGuardState == IncludeGuardState_NoGuard) + return; // no valid guard is possible + if (m_includeGuardState == IncludeGuardState_AfterDefine + && hint == IncludeGuardStateHint_OtherToken) + return; // after the #define of the guard, and before the + // #endif, any non-endif token won't change the state + if (m_inCondition) + return; // include guards can never occur in pp-conditions + + updateIncludeGuardState_helper(hint, idToken); + } + + private: +#ifdef DEBUG_INCLUDE_GUARD_TRACKING + static QString guardStateToString(int guardState); +#endif // DEBUG_INCLUDE_GUARD_TRACKING + void updateIncludeGuardState_helper(IncludeGuardStateHint hint, PPToken *idToken); }; void handleDefined(PPToken *tk); |