diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp index 9faabe29b13fde6f762878112c520c11bf393753..675cf6edf91a6a95ae683e2084ac0cc3be241c68 100644 --- a/src/libs/cplusplus/CppDocument.cpp +++ b/src/libs/cplusplus/CppDocument.cpp @@ -44,6 +44,11 @@ #include <QtCore/QBitArray> #include <QtCore/QtDebug> +/*! + \namespace CPlusPlus + The namespace for C++ related tools. +*/ + using namespace CPlusPlus; namespace { @@ -101,6 +106,7 @@ private: } // anonymous namespace + Document::Document(const QString &fileName) : _fileName(fileName), _globalNamespace(0), @@ -166,9 +172,10 @@ void Document::appendMacro(const Macro ¯o) } void Document::addMacroUse(const Macro ¯o, unsigned offset, unsigned length, - const QVector<MacroArgumentReference> &actuals) + const QVector<MacroArgumentReference> &actuals, bool inCondition) { MacroUse use(macro, offset, offset + length); + use.setInCondition(inCondition); foreach (const MacroArgumentReference &actual, actuals) { const Block arg(actual.position(), actual.position() + actual.length()); @@ -179,6 +186,60 @@ void Document::addMacroUse(const Macro ¯o, unsigned offset, unsigned length, _macroUses.append(use); } +void Document::addUndefinedMacroUse(const QByteArray &name, unsigned offset) +{ + QByteArray copy(name.data(), name.size()); + UndefinedMacroUse use(copy, offset); + _undefinedMacroUses.append(use); +} + +/*! + \class Document::MacroUse + \brief Represents the usage of a macro in a \l {Document}. + \sa Document::UndefinedMacroUse +*/ + +/*! + \class Document::UndefinedMacroUse + \brief Represents a macro that was looked up, but not found. + + Holds data about the reference to a macro in an \tt{#ifdef} or \tt{#ifndef} + or argument to the \tt{defined} operator inside an \tt{#if} or \tt{#elif} that does + not exist. + + \sa Document::undefinedMacroUses(), Document::MacroUse, Macro +*/ + +/*! + \fn QByteArray Document::UndefinedMacroUse::name() const + + Returns the name of the macro that was not found. +*/ + +/*! + \fn QList<UndefinedMacroUse> Document::undefinedMacroUses() const + + Returns a list of referenced but undefined macros. + + \sa Document::macroUses(), Document::definedMacros(), Macro +*/ + +/*! + \fn QList<MacroUse> Document::macroUses() const + + Returns a list of macros used. + + \sa Document::undefinedMacroUses(), Document::definedMacros(), Macro +*/ + +/*! + \fn QList<Macro> Document::definedMacros() const + + Returns the list of macros defined. + + \sa Document::macroUses(), Document::undefinedMacroUses() +*/ + TranslationUnit *Document::translationUnit() const { return _translationUnit; diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h index 7308e04e6007db324fd25ff21135ef7f760cb609..ca15bd1986d761cfa29b215fe6940638b296f2de 100644 --- a/src/libs/cplusplus/CppDocument.h +++ b/src/libs/cplusplus/CppDocument.h @@ -70,7 +70,8 @@ public: void appendMacro(const Macro ¯o); void addMacroUse(const Macro ¯o, unsigned offset, unsigned length, - const QVector<MacroArgumentReference> &range); + const QVector<MacroArgumentReference> &range, bool inCondition); + void addUndefinedMacroUse(const QByteArray &name, unsigned offset); Control *control() const; TranslationUnit *translationUnit() const; @@ -220,13 +221,15 @@ public: class MacroUse: public Block { Macro _macro; QVector<Block> _arguments; + bool _inCondition; public: inline MacroUse(const Macro ¯o, unsigned begin = 0, unsigned end = 0) : Block(begin, end), - _macro(macro) + _macro(macro), + _inCondition(false) { } const Macro ¯o() const @@ -238,11 +241,37 @@ public: QVector<Block> arguments() const { return _arguments; } + bool isInCondition() const + { return _inCondition; } + + private: void setArguments(const QVector<Block> &arguments) { _arguments = arguments; } void addArgument(const Block &block) { _arguments.append(block); } + + void setInCondition(bool set) + { _inCondition = set; } + + friend class Document; + }; + + class UndefinedMacroUse: public Block { + QByteArray _name; + + public: + inline UndefinedMacroUse( + const QByteArray &name, + unsigned begin = 0) + : Block(begin, begin + name.length()), + _name(name) + { } + + QByteArray name() const + { + return _name; + } }; QList<Include> includes() const @@ -254,6 +283,9 @@ public: QList<MacroUse> macroUses() const { return _macroUses; } + QList<UndefinedMacroUse> undefinedMacroUses() const + { return _undefinedMacroUses; } + private: Symbol *findSymbolAt(unsigned line, unsigned column, Scope *scope) const; @@ -267,6 +299,7 @@ private: QList<Macro> _definedMacros; QList<Block> _skippedBlocks; QList<MacroUse> _macroUses; + QList<UndefinedMacroUse> _undefinedMacroUses; QByteArray _source; unsigned _revision; diff --git a/src/libs/cplusplus/FastPreprocessor.h b/src/libs/cplusplus/FastPreprocessor.h index 0d82d54253f69175a6126de19abf227fef5c73f6..36f3685fc13915d42b742f4a6240336050680fd0 100644 --- a/src/libs/cplusplus/FastPreprocessor.h +++ b/src/libs/cplusplus/FastPreprocessor.h @@ -58,9 +58,13 @@ public: virtual void macroAdded(const Macro &) {} + virtual void passedMacroDefinitionCheck(unsigned, const Macro &) {} + virtual void failedMacroDefinitionCheck(unsigned, const QByteArray &) {} + virtual void startExpandingMacro(unsigned, const Macro &, const QByteArray &, + bool, const QVector<MacroArgumentReference> &) {} virtual void stopExpandingMacro(unsigned, const Macro &) {} diff --git a/src/libs/cplusplus/PreprocessorClient.cpp b/src/libs/cplusplus/PreprocessorClient.cpp index 6fe1b2a75b9eadf00c2e5fbc5e89ca24c44e4b0b..5b57baa7b2eb0a65863f5f70d42ab86983b73ba9 100644 --- a/src/libs/cplusplus/PreprocessorClient.cpp +++ b/src/libs/cplusplus/PreprocessorClient.cpp @@ -31,6 +31,44 @@ using namespace CPlusPlus; +/*! + \class Client + \brief A notification interface for for C++ preprocessor. +*/ + +/*! + \fn void Client::macroAdded(const Macro ¯o) + + Called whenever a new macro is defined. +*/ + +/*! + \fn void Client::passedMacroDefinitionCheck(unsigned offset, const Macro ¯o) + + Called when the preprocessor checks whether a macro is defined or not and the + result is positive. + + \sa failedMacroDefinitionCheck() +*/ + +/*! + \fn void Client::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name) + + Called when the preprocessor checks whether a macro is defined or not and the + result is negative. + + \sa passedMacroDefinitionCheck() +*/ + +/*! + \fn void Client::startExpandingMacro(unsigned offset, const Macro ¯o, const QByteArray &originalText, bool inCondition = false, const QVector<MacroArgumentReference> &actuals = QVector<MacroArgumentReference>()) + + Called when starting to expand a macro. The parameter \a inCondition indicates whether the + expansion is happening inside a preprocessor conditional. + + \sa stopExpandingMacro() +*/ + Client::Client() { } diff --git a/src/libs/cplusplus/PreprocessorClient.h b/src/libs/cplusplus/PreprocessorClient.h index 84cb0b73ac372c0d980f8ab4eeee512d5d74aa21..d89ce71355d6bd17152c152d293a555b0d8fa562 100644 --- a/src/libs/cplusplus/PreprocessorClient.h +++ b/src/libs/cplusplus/PreprocessorClient.h @@ -75,12 +75,14 @@ public: virtual ~Client(); virtual void macroAdded(const Macro ¯o) = 0; - virtual void sourceNeeded(QString &fileName, IncludeType mode, - unsigned line) = 0; // ### FIX the signature. + + virtual void passedMacroDefinitionCheck(unsigned offset, const Macro ¯o) = 0; + virtual void failedMacroDefinitionCheck(unsigned offset, const QByteArray &name) = 0; virtual void startExpandingMacro(unsigned offset, const Macro ¯o, const QByteArray &originalText, + bool inCondition = false, const QVector<MacroArgumentReference> &actuals = QVector<MacroArgumentReference>()) = 0; @@ -89,6 +91,9 @@ public: virtual void startSkippingBlocks(unsigned offset) = 0; virtual void stopSkippingBlocks(unsigned offset) = 0; + + virtual void sourceNeeded(QString &fileName, IncludeType mode, + unsigned line) = 0; // ### FIX the signature. }; } // namespace CPlusPlus diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp index 347f4999d26d2eb2e8d1bf62138552ec802ff726..dcb6d4932223f7df5b1adb2da49583e1a2932e9b 100644 --- a/src/libs/cplusplus/pp-engine.cpp +++ b/src/libs/cplusplus/pp-engine.cpp @@ -138,6 +138,18 @@ using namespace CPlusPlus; namespace { +bool isMacroDefined(QByteArray name, unsigned offset, Environment *env, Client *client) +{ + Macro *m = env->resolve(name); + if (client) { + if (m) + client->passedMacroDefinitionCheck(offset, *m); + else + client->failedMacroDefinitionCheck(offset, name); + } + return m != 0; +} + class RangeLexer { const Token *first; @@ -193,8 +205,8 @@ class ExpressionEvaluator void operator = (const ExpressionEvaluator &other); public: - ExpressionEvaluator(Environment *env) - : env(env), _lex(0) + ExpressionEvaluator(Client *client, Environment *env) + : client(client), env(env), _lex(0) { } Value operator()(const Token *firstToken, const Token *lastToken, @@ -255,13 +267,13 @@ protected: } else if (isTokenDefined()) { ++(*_lex); if ((*_lex)->is(T_IDENTIFIER)) { - _value.set_long(env->resolve(tokenSpell()) != 0); + _value.set_long(isMacroDefined(tokenSpell(), (*_lex)->offset, env, client)); ++(*_lex); return true; } else if ((*_lex)->is(T_LPAREN)) { ++(*_lex); if ((*_lex)->is(T_IDENTIFIER)) { - _value.set_long(env->resolve(tokenSpell()) != 0); + _value.set_long(isMacroDefined(tokenSpell(), (*_lex)->offset, env, client)); ++(*_lex); if ((*_lex)->is(T_RPAREN)) { ++(*_lex); @@ -519,6 +531,7 @@ protected: } private: + Client *client; Environment *env; QByteArray source; RangeLexer *_lex; @@ -983,7 +996,7 @@ void Preprocessor::expandObjectLikeMacro(TokenIterator identifierToken, { if (client) client->startExpandingMacro(identifierToken->offset, - *m, spell); + *m, spell, false); m->setHidden(true); expand(m->definition(), result); @@ -1007,7 +1020,7 @@ void Preprocessor::expandFunctionLikeMacro(TokenIterator identifierToken, endOfText - beginOfText); client->startExpandingMacro(identifierToken->offset, - *m, text, actuals); + *m, text, false, actuals); } const bool was = markGeneratedTokens(true, identifierToken); @@ -1253,7 +1266,7 @@ void Preprocessor::processIf(TokenIterator firstToken, TokenIterator lastToken) const char *first = startOfToken(*tk); const char *last = startOfToken(*lastToken); - MacroExpander expandCondition (env); + MacroExpander expandCondition (env, 0, client, tk.dot()->offset); QByteArray condition; condition.reserve(256); expandCondition(first, last, &condition); @@ -1297,7 +1310,7 @@ void Preprocessor::processElif(TokenIterator firstToken, TokenIterator lastToken const char *first = startOfToken(*tk); const char *last = startOfToken(*lastToken); - MacroExpander expandCondition (env); + MacroExpander expandCondition (env, 0, client, tk.dot()->offset); QByteArray condition; condition.reserve(256); expandCondition(first, last, &condition); @@ -1338,7 +1351,7 @@ void Preprocessor::processIfdef(bool checkUndefined, if (testIfLevel()) { if (tk->is(T_IDENTIFIER)) { const QByteArray macroName = tokenSpell(*tk); - bool value = env->resolve(macroName) != 0 || env->isBuiltinMacro(macroName); + bool value = isMacroDefined(macroName, tk->offset, env, client) || env->isBuiltinMacro(macroName); if (checkUndefined) value = ! value; @@ -1437,7 +1450,7 @@ int Preprocessor::skipping() const Value Preprocessor::evalExpression(TokenIterator firstToken, TokenIterator lastToken, const QByteArray &source) const { - ExpressionEvaluator eval(env); + ExpressionEvaluator eval(client, env); const Value result = eval(firstToken, lastToken, source); return result; } diff --git a/src/libs/cplusplus/pp-macro-expander.cpp b/src/libs/cplusplus/pp-macro-expander.cpp index 4902158c761e00afc10c28f93a4a5e391e1538b6..42be634562d445eafebffe413820cd8c969d2300 100644 --- a/src/libs/cplusplus/pp-macro-expander.cpp +++ b/src/libs/cplusplus/pp-macro-expander.cpp @@ -66,9 +66,11 @@ inline static bool comment_p (const char *__first, const char *__last) return (*__first == '/' || *__first == '*'); } -MacroExpander::MacroExpander(Environment *env, pp_frame *frame) +MacroExpander::MacroExpander(Environment *env, pp_frame *frame, Client *client, unsigned start_offset) : env(env), frame(frame), + client(client), + start_offset(start_offset), lines(0) { } @@ -97,6 +99,7 @@ const char *MacroExpander::operator()(const char *first, const char *last, const char *MacroExpander::expand(const char *__first, const char *__last, QByteArray *__result) { + const char *start = __first; __first = skip_blanks (__first, __last); lines = skip_blanks.lines; @@ -284,6 +287,9 @@ const char *MacroExpander::expand(const char *__first, const char *__last, if (! macro->definition().isEmpty()) { + if (client) + client->startExpandingMacro(start_offset + (name_begin-start), *macro, fast_name, true); + macro->setHidden(true); QByteArray __tmp; @@ -310,6 +316,9 @@ const char *MacroExpander::expand(const char *__first, const char *__last, } macro->setHidden(false); + + if (client) + client->stopExpandingMacro(start_offset + (name_begin-start), *macro); } if (! m) @@ -330,6 +339,7 @@ const char *MacroExpander::expand(const char *__first, const char *__last, } QVector<QByteArray> actuals; + QVector<MacroArgumentReference> actuals_ref; actuals.reserve (5); ++arg_it; // skip '(' @@ -338,6 +348,7 @@ const char *MacroExpander::expand(const char *__first, const char *__last, const char *arg_end = skip_argument_variadics (actuals, macro, arg_it, __last); if (arg_it != arg_end) { + actuals_ref.append(MacroArgumentReference(start_offset + (arg_it-start), arg_end - arg_it)); const QByteArray actual (arg_it, arg_end - arg_it); QByteArray expanded; expand_actual (actual.constBegin (), actual.constEnd (), &expanded); @@ -350,6 +361,7 @@ const char *MacroExpander::expand(const char *__first, const char *__last, ++arg_it; // skip ',' arg_end = skip_argument_variadics (actuals, macro, arg_it, __last); + actuals_ref.append(MacroArgumentReference(start_offset + (arg_it-start), arg_end - arg_it)); const QByteArray actual (arg_it, arg_end - arg_it); QByteArray expanded; expand_actual (actual.constBegin (), actual.constEnd (), &expanded); @@ -363,11 +375,17 @@ const char *MacroExpander::expand(const char *__first, const char *__last, ++arg_it; // skip ')' __first = arg_it; + if (client) + client->startExpandingMacro(start_offset + (name_begin-start), *macro, fast_name, true, actuals_ref); + pp_frame frame (macro, actuals); MacroExpander expand_macro (env, &frame); macro->setHidden(true); expand_macro (macro->definition(), __result); macro->setHidden(false); + + if (client) + client->stopExpandingMacro(start_offset + (name_begin-start), *macro); } else __result->append(*__first++); diff --git a/src/libs/cplusplus/pp-macro-expander.h b/src/libs/cplusplus/pp-macro-expander.h index f566ca809bb0488d4ffc109a165b094fc8a1c5a2..4f8730d2db8e92f0a5cd90a322bdbd6129774c8a 100644 --- a/src/libs/cplusplus/pp-macro-expander.h +++ b/src/libs/cplusplus/pp-macro-expander.h @@ -56,6 +56,7 @@ namespace CPlusPlus { class Environment; +class Client; struct pp_frame; @@ -63,6 +64,8 @@ class MacroExpander { Environment *env; pp_frame *frame; + Client *client; + unsigned start_offset; pp_skip_number skip_number; pp_skip_identifier skip_identifier; @@ -76,7 +79,7 @@ class MacroExpander const QByteArray *resolve_formal(const QByteArray &name); public: - MacroExpander(Environment *env, pp_frame *frame = 0); + MacroExpander(Environment *env, pp_frame *frame = 0, Client *client = 0, unsigned start_offset = 0); const char *operator()(const char *first, const char *last, QByteArray *result); diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 5d76824ff9b017a9e574a006ac6a5066389a80dd..6a859d16cc7bc04f9991f1f15e2745015e81bb24 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -196,9 +196,12 @@ protected: void mergeEnvironment(CPlusPlus::Document::Ptr doc); virtual void macroAdded(const Macro ¯o); + virtual void passedMacroDefinitionCheck(unsigned offset, const Macro ¯o); + virtual void failedMacroDefinitionCheck(unsigned offset, const QByteArray &name); virtual void startExpandingMacro(unsigned offset, const Macro ¯o, const QByteArray &originalText, + bool inCondition, const QVector<MacroArgumentReference> &actuals); virtual void stopExpandingMacro(unsigned offset, const Macro ¯o); virtual void startSkippingBlocks(unsigned offset); @@ -446,16 +449,34 @@ void CppPreprocessor::macroAdded(const Macro ¯o) m_currentDoc->appendMacro(macro); } +void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro ¯o) +{ + if (! m_currentDoc) + return; + + m_currentDoc->addMacroUse(macro, offset, macro.name().length(), + QVector<MacroArgumentReference>(), true); +} + +void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name) +{ + if (! m_currentDoc) + return; + + m_currentDoc->addUndefinedMacroUse(name, offset); +} + void CppPreprocessor::startExpandingMacro(unsigned offset, const Macro ¯o, const QByteArray &originalText, + bool inCondition, const QVector<MacroArgumentReference> &actuals) { if (! m_currentDoc) return; - //qDebug() << "start expanding:" << macro.name << "text:" << originalText; - m_currentDoc->addMacroUse(macro, offset, originalText.length(), actuals); + //qDebug() << "start expanding:" << macro.name() << "text:" << originalText; + m_currentDoc->addMacroUse(macro, offset, originalText.length(), actuals, inCondition); } void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)