aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/cplusplus/CppDocument.h12
-rw-r--r--src/libs/cplusplus/FastPreprocessor.h1
-rw-r--r--src/libs/cplusplus/PreprocessorClient.h3
-rw-r--r--src/libs/cplusplus/cplusplus-lib.pri2
-rw-r--r--src/libs/cplusplus/pp-engine.cpp173
-rw-r--r--src/libs/cplusplus/pp-engine.h64
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 &macro) = 0;
+ /// Mark the given macro name as the include guard for the current file.
+ virtual void markAsIncludeGuard(const QByteArray &macroName) = 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 &poundToken)
--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);