forked from qt-creator/qt-creator
Track more macro uses.
In particular macros that are only checked for definition or are expanded during the evaluation of an #if or #elif directive are now also added to the list available through Document::macroUses(). The names of undefined macros that are interesting (because they're used in an #ifdef or a defined(...)) are now available through Document::undefinedMacroUses(). Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 &) {}
|
||||
|
||||
@@ -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()
|
||||
{ }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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++);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user