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

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 &)
......
Supports Markdown
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