Commit 22ed0255 authored by Christian Kamm's avatar Christian Kamm

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: default avatarRoberto Raggi <roberto.raggi@nokia.com>
parent 90470771
......@@ -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 &macro)
}
void Document::addMacroUse(const Macro &macro, 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 &macro, 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 &macro);
void addMacroUse(const Macro &macro, 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 &macro,
unsigned begin = 0,
unsigned end = 0)
: Block(begin, end),
_macro(macro)
_macro(macro),
_inCondition(false)
{ }
const Macro &macro() 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 &macro)
Called whenever a new macro is defined.
*/
/*!
\fn void Client::passedMacroDefinitionCheck(unsigned offset, const Macro &macro)
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 &macro, 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 &macro) = 0;
virtual void sourceNeeded(QString &fileName, IncludeType mode,
unsigned line) = 0; // ### FIX the signature.
virtual void passedMacroDefinitionCheck(unsigned offset, const Macro &macro) = 0;
virtual void failedMacroDefinitionCheck(unsigned offset, const QByteArray &name) = 0;
virtual void startExpandingMacro(unsigned offset,
const Macro &macro,
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);
......
......@@ -196,9 +196,12 @@ protected:
void mergeEnvironment(CPlusPlus::Document::Ptr doc);
virtual void macroAdded(const Macro &macro);
virtual void passedMacroDefinitionCheck(unsigned offset, const Macro &macro);
virtual void failedMacroDefinitionCheck(unsigned offset, const QByteArray &name);
virtual void startExpandingMacro(unsigned offset,
const Macro &macro,
const QByteArray &originalText,
bool inCondition,
const QVector<MacroArgumentReference> &actuals);
virtual void stopExpandingMacro(unsigned offset, const Macro &macro);
virtual void startSkippingBlocks(unsigned offset);
......@@ -446,16 +449,34 @@ void CppPreprocessor::macroAdded(const Macro &macro)
m_currentDoc->appendMacro(macro);
}
void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro &macro)
{
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 &macro,
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 &)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment