[C++] Rewrite of the preprocessor.

This rewrite fixes a couple of issues with the pre-processor. It now
supports:
- macros in macro bodies
- stringification of parameters [cpp.stringize]
- the concatenation operator [cpp.concat]
- #include MACRO_HERE
- defined() inside macro bodies used in pp-conditions.

Change-Id: Ifdb78041fb6afadf44f939a4bd66ce2832b8601f
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
Erik Verbruggen
2012-03-26 15:18:01 +02:00
parent 159058d9eb
commit 60db573660
50 changed files with 1843 additions and 1620 deletions

View File

@@ -198,6 +198,8 @@ protected:
};
#define DO_NOT_DUMP_ALL_PARSER_ERRORS
class DocumentDiagnosticClient : public DiagnosticClient
{
enum { MAX_MESSAGE_COUNT = 10 };
@@ -217,8 +219,10 @@ public:
if (level == Error) {
++errorCount;
#ifdef DO_NOT_DUMP_ALL_PARSER_ERRORS
if (errorCount >= MAX_MESSAGE_COUNT)
return; // ignore the error
#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
}
const QString fileName = QString::fromUtf8(fileId->chars(), fileId->size());
@@ -229,6 +233,16 @@ public:
QString message;
message.vsprintf(format, ap);
#ifndef DO_NOT_DUMP_ALL_PARSER_ERRORS
{
const char *levelStr = "Unknown level";
if (level == Document::DiagnosticMessage::Warning) levelStr = "Warning";
if (level == Document::DiagnosticMessage::Error) levelStr = "Error";
if (level == Document::DiagnosticMessage::Fatal) levelStr = "Fatal";
qDebug("%s:%u:%u: %s: %s", fileId->chars(), line, column, levelStr, message.toUtf8().constData());
}
#endif // DO_NOT_DUMP_ALL_PARSER_ERRORS
Document::DiagnosticMessage m(convertLevel(level), doc->fileName(),
line, column, message);
messages->append(m);
@@ -341,10 +355,9 @@ void Document::appendMacro(const Macro &macro)
void Document::addMacroUse(const Macro &macro, unsigned offset, unsigned length,
unsigned beginLine,
const QVector<MacroArgumentReference> &actuals, bool inCondition)
const QVector<MacroArgumentReference> &actuals)
{
MacroUse use(macro, offset, offset + length, beginLine);
use.setInCondition(inCondition);
foreach (const MacroArgumentReference &actual, actuals) {
const Block arg(actual.position(), actual.position() + actual.length());

View File

@@ -77,8 +77,7 @@ public:
void appendMacro(const Macro &macro);
void addMacroUse(const Macro &macro, unsigned offset, unsigned length,
unsigned beginLine, const QVector<MacroArgumentReference> &range,
bool inCondition);
unsigned beginLine, const QVector<MacroArgumentReference> &range);
void addUndefinedMacroUse(const QByteArray &name, unsigned offset);
Control *control() const;
@@ -247,7 +246,6 @@ public:
class MacroUse: public Block {
Macro _macro;
QVector<Block> _arguments;
bool _inCondition;
unsigned _beginLine;
public:
@@ -255,7 +253,6 @@ public:
unsigned begin, unsigned end, unsigned beginLine)
: Block(begin, end),
_macro(macro),
_inCondition(false),
_beginLine(beginLine)
{ }
@@ -268,9 +265,6 @@ public:
QVector<Block> arguments() const
{ return _arguments; }
bool isInCondition() const
{ return _inCondition; }
unsigned beginLine() const
{ return _beginLine; }
@@ -281,9 +275,6 @@ public:
void addArgument(const Block &block)
{ _arguments.append(block); }
void setInCondition(bool set)
{ _inCondition = set; }
friend class Document;
};

View File

@@ -69,7 +69,6 @@ public:
virtual void startExpandingMacro(unsigned,
const Macro &,
const QByteArray &,
bool,
const QVector<MacroArgumentReference> &) {}
virtual void stopExpandingMacro(unsigned, const Macro &) {}

View File

@@ -92,19 +92,11 @@ QString Macro::decoratedName() const
QString Macro::toString() const
{
QString text = decoratedName();
text.append(QString::fromUtf8(_definition.constData(), _definition.size()));
text.append(QString::fromUtf8(_definitionText.constData(), _definitionText.size()));
return text;
}
QString Macro::toStringWithLineBreaks() const
{
if (_lineBreaks.isEmpty())
return toString();
QString text = decoratedName();
QString definitionWithBreaks = QString::fromUtf8(_definition.constData(), _definition.size());
foreach (unsigned pos, _lineBreaks)
definitionWithBreaks[pos] = '\n';
text.append(definitionWithBreaks);
return text;
}

View File

@@ -52,6 +52,8 @@
#ifndef CPLUSPLUS_PP_MACRO_H
#define CPLUSPLUS_PP_MACRO_H
#include "PPToken.h"
#include <CPlusPlusForwardDeclarations.h>
#include <QByteArray>
@@ -60,8 +62,12 @@
namespace CPlusPlus {
class Environment;
class CPLUSPLUS_EXPORT Macro
{
typedef Internal::PPToken PPToken;
public:
Macro();
@@ -71,13 +77,16 @@ public:
void setName(const QByteArray &name)
{ _name = name; }
QByteArray definition() const
{ return _definition; }
const QByteArray definitionText() const
{ return _definitionText; }
void setDefinition(const QByteArray &definition)
{ _definition = definition; }
const QVector<PPToken> &definitionTokens() const
{ return _definitionTokens; }
QVector<QByteArray> formals() const
void setDefinition(const QByteArray &definitionText, const QVector<PPToken> &definitionTokens)
{ _definitionText = definitionText; _definitionTokens = definitionTokens; }
const QVector<QByteArray> &formals() const
{ return _formals; }
void addFormal(const QByteArray &formal)
@@ -125,16 +134,11 @@ public:
void setVariadic(bool isVariadic)
{ f._variadic = isVariadic; }
void setLineBreaks(const QList<unsigned> &breaks)
{ _lineBreaks = breaks; }
const QList<unsigned> &lineBreaks() const
{ return _lineBreaks; }
QString toString() const;
QString toStringWithLineBreaks() const;
// ### private
private:
friend class Environment;
Macro *_next;
unsigned _hashcode;
@@ -149,10 +153,10 @@ private:
};
QByteArray _name;
QByteArray _definition;
QByteArray _definitionText;
QVector<PPToken> _definitionTokens;
QVector<QByteArray> _formals;
QString _fileName;
QList<unsigned> _lineBreaks;
unsigned _line;
unsigned _offset;
unsigned _length;

View File

@@ -0,0 +1,45 @@
#include "PPToken.h"
#include <cstring>
using namespace CPlusPlus::Internal;
ByteArrayRef::ByteArrayRef()
: m_ref(0)
, m_offset(0)
, m_length(0)
{}
bool ByteArrayRef::startsWith(const char *s) const
{
int l = std::strlen(s);
if (l > m_length)
return false;
return !qstrncmp(start(), s, l);
}
int ByteArrayRef::count(char ch) const
{
if (!m_ref)
return 0;
int num = 0;
const char *b = start();
const char *i = b + m_length;
while (i != b)
if (*--i == ch)
++num;
return num;
}
PPToken::PPToken()
{}
void PPToken::squeeze()
{
if (isValid()) {
m_src = m_src.mid(offset, length());
m_src.squeeze();
offset = 0;
}
}

View File

@@ -0,0 +1,106 @@
#ifndef CPLUSPLUS_INTERNAL_PPTOKEN_H
#define CPLUSPLUS_INTERNAL_PPTOKEN_H
#include <CPlusPlus.h>
#include <Token.h>
#include <QByteArray>
namespace CPlusPlus {
namespace Internal {
class CPLUSPLUS_EXPORT ByteArrayRef
{
public:
ByteArrayRef();
ByteArrayRef(const QByteArray *ref)
: m_ref(ref)
, m_offset(0)
, m_length(ref->length())
{}
ByteArrayRef(const QByteArray *ref, int offset, int length)
: m_ref(ref)
, m_offset(offset)
, m_length(length)
{
Q_ASSERT(ref);
Q_ASSERT(offset >= 0);
Q_ASSERT(length >= 0);
Q_ASSERT(offset + length <= ref->size());
}
inline const char *start() const
{ return m_ref ? m_ref->constData() + m_offset : 0; }
inline int length() const
{ return m_length; }
inline int size() const
{ return length(); }
inline char at(int pos) const
{ return m_ref && pos >= 0 && pos < m_length ? m_ref->at(m_offset + pos) : '\0'; }
inline char operator[](int pos) const
{ return at(pos); }
QByteArray toByteArray() const
{ return m_ref ? QByteArray(m_ref->constData() + m_offset, m_length) : QByteArray(); }
bool operator==(const QByteArray &other) const
{ return m_ref ? (m_length == other.length() && !qstrncmp(m_ref->constData() + m_offset, other.constData(), m_length)) : false; }
bool operator!=(const QByteArray &other) const
{ return !this->operator==(other); }
bool startsWith(const char *ch) const;
int count(char c) const;
private:
const QByteArray *m_ref;
int m_offset;
int m_length;
};
inline bool operator==(const QByteArray &other, const ByteArrayRef &ref)
{ return ref == other; }
inline bool operator!=(const QByteArray &other, const ByteArrayRef &ref)
{ return ref != other; }
class CPLUSPLUS_EXPORT PPToken: public Token
{
public:
PPToken();
PPToken(const QByteArray &src)
: m_src(src)
{}
void setSource(const QByteArray &src)
{ m_src = src; }
const QByteArray &source() const
{ return m_src; }
const char *start() const
{ return m_src.constData() + offset; }
ByteArrayRef asByteArrayRef() const
{ return ByteArrayRef(&m_src, offset, length()); }
bool isValid() const
{ return !m_src.isEmpty(); }
void squeeze();
private:
QByteArray m_src;
};
} // namespace Internal
} // namespace CPlusPlus
#endif // CPLUSPLUS_INTERNAL_PPTOKEN_H

View File

@@ -75,7 +75,7 @@ public:
public:
Client();
virtual ~Client();
virtual ~Client() = 0;
virtual void macroAdded(const Macro &macro) = 0;
@@ -85,13 +85,13 @@ public:
virtual void startExpandingMacro(unsigned offset,
const Macro &macro,
const QByteArray &originalText,
bool inCondition = false,
const QVector<MacroArgumentReference> &actuals
= QVector<MacroArgumentReference>()) = 0;
virtual void stopExpandingMacro(unsigned offset,
const Macro &macro) = 0;
/// Start skipping from the given offset.
virtual void startSkippingBlocks(unsigned offset) = 0;
virtual void stopSkippingBlocks(unsigned offset) = 0;

View File

@@ -150,7 +150,7 @@ void Environment::reset()
_hash_count = 401;
}
bool Environment::isBuiltinMacro(const QByteArray &s)
bool Environment::isBuiltinMacro(const Internal::ByteArrayRef &s)
{
if (s.length() != 8)
return false;
@@ -236,6 +236,22 @@ Macro *Environment::resolve(const QByteArray &name) const
return it;
}
Macro *Environment::resolve(const Internal::ByteArrayRef &name) const
{
if (! _macros)
return 0;
Macro *it = _hash[hashCode(name) % _hash_count];
for (; it; it = it->_next) {
if (it->name() != name)
continue;
else if (it->isHidden())
return 0;
else break;
}
return it;
}
unsigned Environment::hashCode(const QByteArray &s)
{
unsigned hash_value = 0;
@@ -246,6 +262,16 @@ unsigned Environment::hashCode(const QByteArray &s)
return hash_value;
}
unsigned Environment::hashCode(const Internal::ByteArrayRef &s)
{
unsigned hash_value = 0;
for (int i = 0; i < s.length(); ++i)
hash_value = (hash_value << 5) - hash_value + s.at(i);
return hash_value;
}
void Environment::rehash()
{
if (_hash) {

View File

@@ -53,6 +53,7 @@
#define CPLUSPLUS_PP_ENVIRONMENT_H
#include "CPlusPlusForwardDeclarations.h"
#include "PPToken.h"
#include <QList>
#include <QByteArray>
@@ -78,6 +79,7 @@ public:
Macro *remove(const QByteArray &name);
Macro *resolve(const QByteArray &name) const;
Macro *resolve(const Internal::ByteArrayRef &name) const;
iterator firstMacro() const;
iterator lastMacro() const;
@@ -85,10 +87,11 @@ public:
void reset();
void addMacros(const QList<Macro> &macros);
static bool isBuiltinMacro(const QByteArray &name);
static bool isBuiltinMacro(const Internal::ByteArrayRef &name);
private:
static unsigned hashCode(const QByteArray &s);
static unsigned hashCode(const Internal::ByteArrayRef &s);
void rehash();
public:

View File

@@ -51,9 +51,9 @@ HEADERS += \
$$PWD/pp.h \
$$PWD/pp-cctype.h \
$$PWD/pp-engine.h \
$$PWD/pp-macro-expander.h \
$$PWD/pp-scanner.h \
$$PWD/findcdbbreakpoint.h
$$PWD/findcdbbreakpoint.h \
$$PWD/PPToken.h
SOURCES += \
$$PWD/SimpleLexer.cpp \
@@ -78,8 +78,8 @@ SOURCES += \
$$PWD/FastPreprocessor.cpp \
$$PWD/Macro.cpp \
$$PWD/pp-engine.cpp \
$$PWD/pp-macro-expander.cpp \
$$PWD/pp-scanner.cpp \
$$PWD/findcdbbreakpoint.cpp
$$PWD/findcdbbreakpoint.cpp \
$$PWD/PPToken.cpp
RESOURCES += $$PWD/cplusplus.qrc

View File

@@ -1,9 +1,9 @@
TEMPLATE = lib
TARGET = CPlusPlus
DEFINES += NDEBUG
unix:QMAKE_CXXFLAGS_DEBUG += -O2
#DEFINES += NDEBUG
#unix:QMAKE_CXXFLAGS_DEBUG += -O2
QMAKE_CXXFLAGS_DEBUG += -ggdb
include(../../qtcreatorlibrary.pri)
include(cplusplus-lib.pri)
include(../languageutils/languageutils.pri)

View File

@@ -149,8 +149,8 @@ DynamicLibrary {
"pp-cctype.h",
"pp-engine.cpp",
"pp-engine.h",
"pp-macro-expander.cpp",
"pp-macro-expander.h",
"PPToken.cpp",
"PPToken.h",
"pp-scanner.cpp",
"pp-scanner.h",
"pp.h",

File diff suppressed because it is too large Load Diff

View File

@@ -52,29 +52,39 @@
#ifndef CPLUSPLUS_PP_ENGINE_H
#define CPLUSPLUS_PP_ENGINE_H
#include "PPToken.h"
#include "PreprocessorClient.h"
#include "pp-macro-expander.h"
#include <Lexer.h>
#include <Token.h>
#include <QVector>
#include <QBitArray>
#include <QByteArray>
namespace CPlusPlus {
struct Value;
class Environment;
namespace Internal {
class PPToken;
struct TokenBuffer;
struct Value;
}
class CPLUSPLUS_EXPORT Preprocessor
{
typedef Internal::PPToken PPToken;
typedef Internal::Value Value;
public:
Preprocessor(Client *client, Environment *env);
QByteArray operator()(const QString &filename, const QString &source);
QByteArray operator()(const QString &filename, const QByteArray &source);
QByteArray operator()(const QString &filename, const QByteArray &source, bool noLines = false, bool markGeneratedTokens = true);
void preprocess(const QString &filename,
const QByteArray &source,
QByteArray *result);
QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition);
bool expandMacros() const;
void setExpandMacros(bool expandMacros);
@@ -85,126 +95,101 @@ public:
private:
enum { MAX_LEVEL = 512 };
enum PP_DIRECTIVE_TYPE
{
PP_UNKNOWN_DIRECTIVE,
PP_DEFINE,
PP_IMPORT,
PP_INCLUDE,
PP_INCLUDE_NEXT,
PP_ELIF,
PP_ELSE,
PP_ENDIF,
PP_IF,
PP_IFDEF,
PP_IFNDEF,
PP_UNDEF
};
typedef const CPlusPlus::Token *TokenIterator;
struct State {
QByteArray source;
QVector<CPlusPlus::Token> tokens;
TokenIterator dot;
State();
QString m_currentFileName;
QByteArray m_source;
Lexer *m_lexer;
QBitArray m_skipping;
QBitArray m_trueTest;
int m_ifLevel;
Internal::TokenBuffer *m_tokenBuffer;
bool m_inPreprocessorDirective;
QByteArray *m_result;
bool m_markGeneratedTokens;
bool m_noLines;
bool m_inCondition;
bool m_inDefine;
};
bool markGeneratedTokens(bool markGeneratedTokens, TokenIterator dot = 0);
bool markGeneratedTokens(bool markGeneratedTokens, int position, int extraLines=0, bool newline=false);
void handleDefined(PPToken *tk);
void pushToken(PPToken *tk);
void lex(PPToken *tk);
void skipPreprocesorDirective(PPToken *tk);
bool handleIdentifier(PPToken *tk);
bool handleFunctionLikeMacro(PPToken *tk, const Macro *macro, QVector<PPToken> &body, bool addWhitespaceMarker);
QByteArray expand(const QByteArray &source);
void expand(const QByteArray &source, QByteArray *result);
void expand(const char *first, const char *last, QByteArray *result);
void expandBuiltinMacro(TokenIterator identifierToken,
const QByteArray &spell);
void expandObjectLikeMacro(TokenIterator identifierToken,
const QByteArray &spell,
Macro *m, QByteArray *result);
void expandFunctionLikeMacro(TokenIterator identifierToken, Macro *m,
const QVector<MacroArgumentReference> &actuals);
void resetIfLevel();
bool testIfLevel();
int skipping() const;
PP_DIRECTIVE_TYPE classifyDirective(const QByteArray &directive) const;
Value evalExpression(TokenIterator firstToken,
TokenIterator lastToken,
const QByteArray &source) const;
bool skipping() const
{ return m_state.m_skipping[m_state.m_ifLevel]; }
QVector<CPlusPlus::Token> tokenize(const QByteArray &text) const;
const char *startOfToken(const CPlusPlus::Token &token) const;
const char *endOfToken(const CPlusPlus::Token &token) const;
bool collectActualArguments(PPToken *tk, QVector<QVector<PPToken> > *actuals);
void scanActualArgument(PPToken *tk, QVector<PPToken> *tokens);
QByteArray tokenSpell(const CPlusPlus::Token &token) const;
QByteArray tokenText(const CPlusPlus::Token &token) const; // does a deep copy
void handlePreprocessorDirective(PPToken *tk);
void handleIncludeDirective(PPToken *tk);
void handleDefineDirective(PPToken *tk);
QByteArray expand(PPToken *tk, PPToken *lastConditionToken = 0);
const Internal::PPToken evalExpression(PPToken *tk, Value &result);
void handleIfDirective(PPToken *tk);
void handleElifDirective(PPToken *tk, const PPToken &poundToken);
void handleElseDirective(PPToken *tk, const PPToken &poundToken);
void handleEndIfDirective(PPToken *tk, const PPToken &poundToken);
void handleIfDefDirective(bool checkUndefined, PPToken *tk);
void handleUndefDirective(PPToken *tk);
void collectActualArguments(QVector<MacroArgumentReference> *actuals);
MacroArgumentReference collectOneActualArgument();
static bool isQtReservedWord(const Internal::ByteArrayRef &name);
void processNewline(bool force = false, int extraLines = 0);
void processSkippingBlocks(bool skippingBlocks,
TokenIterator dot, TokenIterator lastToken);
Macro *processObjectLikeMacro(TokenIterator identifierToken,
const QByteArray &spell,
Macro *m);
void processDirective(TokenIterator dot, TokenIterator lastToken);
void processInclude(bool skipCurrentPath,
TokenIterator dot, TokenIterator lastToken,
bool acceptMacros = true);
void processDefine(TokenIterator dot, TokenIterator lastToken);
void processIf(TokenIterator dot, TokenIterator lastToken);
void processElse(TokenIterator dot, TokenIterator lastToken);
void processElif(TokenIterator dot, TokenIterator lastToken);
void processEndif(TokenIterator dot, TokenIterator lastToken);
void processIfdef(bool checkUndefined,
TokenIterator dot, TokenIterator lastToken);
void processUndef(TokenIterator dot, TokenIterator lastToken);
bool isQtReservedWord(const QByteArray &name) const;
State state() const;
void pushState(const State &state);
void pushState(const State &newState);
void popState();
State createStateFromSource(const QByteArray &source) const;
State createStateFromSource(const QString &fileName, const QByteArray &source, QByteArray *result, bool noLines, bool markGeneratedTokens, bool inCondition) const;
void out(const QByteArray &text);
void out(char ch);
void out(const char *s);
inline bool atStartOfOutputLine() const
{ return (m_state.m_result && !m_state.m_result->isEmpty()) ? m_state.m_result->end()[-1] == '\n' : true; }
inline void startNewOutputLine() const
{
if (m_state.m_result && !m_state.m_result->isEmpty() && m_state.m_result->end()[-1] != '\n')
out('\n');
}
inline void out(const QByteArray &text) const
{ if (m_state.m_result) m_state.m_result->append(text); }
inline void out(char ch) const
{ if (m_state.m_result) m_state.m_result->append(ch); }
inline void out(const char *s) const
{ if (m_state.m_result) m_state.m_result->append(s); }
inline void out(const Internal::ByteArrayRef &ref) const
{ if (m_state.m_result) m_state.m_result->append(ref.start(), ref.length()); }
QString string(const char *first, int len) const;
bool maybeAfterComment() const;
bool maybeMultilineToken(TokenIterator tok);
void skipToNextLine();
PPToken generateToken(enum Kind kind, const Internal::ByteArrayRef &content, unsigned lineno, bool addQuotes);
PPToken generateConcatenated(const PPToken &leftTk, const PPToken &rightTk);
void startSkippingBlocks(const PPToken &tk) const;
private:
Client *client;
Environment *env;
MacroExpander _expand;
Client *m_client;
Environment *m_env;
QByteArray m_scratchBuffer;
QBitArray _skipping; // ### move in state
QBitArray _trueTest; // ### move in state
int iflevel; // ### move in state
QList<State> m_savedStates;
QList<State> _savedStates;
QString m_originalSource;
bool m_expandMacros;
bool m_keepComments;
QByteArray _source;
QVector<CPlusPlus::Token> _tokens;
TokenIterator _dot;
QByteArray *_result;
bool _markGeneratedTokens;
QString _originalSource;
bool _expandMacros;
bool _keepComments;
State m_state;
};
} // namespace CPlusPlus

View File

@@ -1,449 +0,0 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "pp-macro-expander.h"
#include "pp.h"
#include "pp-cctype.h"
#include <QDateTime>
namespace CPlusPlus {
struct pp_frame
{
Macro *expanding_macro;
const QVector<QByteArray> actuals;
pp_frame(Macro *expanding_macro, const QVector<QByteArray> &actuals)
: expanding_macro (expanding_macro),
actuals (actuals)
{ }
};
} // namespace CPlusPlus
using namespace CPlusPlus;
inline static bool comment_p (const char *__first, const char *__last)
{
if (__first == __last)
return false;
if (*__first != '/')
return false;
if (++__first == __last)
return false;
return (*__first == '/' || *__first == '*');
}
MacroExpander::MacroExpander(Environment *env, pp_frame *frame, Client *client, unsigned start_offset)
: env(env),
frame(frame),
client(client),
start_offset(start_offset),
lines(0)
{ }
const QByteArray *MacroExpander::resolve_formal(const QByteArray &__name)
{
if (! (frame && frame->expanding_macro))
return 0;
const QVector<QByteArray> formals = frame->expanding_macro->formals();
for (int index = 0; index < formals.size(); ++index) {
const QByteArray formal = formals.at(index);
if (formal == __name && index < frame->actuals.size())
return &frame->actuals.at(index);
}
return 0;
}
const char *MacroExpander::operator()(const char *first, const char *last,
QByteArray *result)
{
return expand(first, last, result);
}
const char *MacroExpander::expand(const char *__first, const char *__last,
QByteArray *__result)
{
__first = skip_blanks (__first, __last);
lines = skip_blanks.lines;
while (__first != __last)
{
if (*__first == '\n')
{
__result->append("\n# ");
__result->append(QByteArray::number(env->currentLine));
__result->append(' ');
__result->append('"');
__result->append(env->currentFile.toUtf8());
__result->append('"');
__result->append('\n');
++lines;
__first = skip_blanks (++__first, __last);
lines += skip_blanks.lines;
if (__first != __last && *__first == '#')
break;
}
else if (*__first == '#')
{
__first = skip_blanks (++__first, __last);
lines += skip_blanks.lines;
const char *end_id = skip_identifier (__first, __last);
const QByteArray fast_name(__first, end_id - __first);
__first = end_id;
if (const QByteArray *actual = resolve_formal (fast_name))
{
__result->append('\"');
const char *actual_begin = actual->constData ();
const char *actual_end = actual_begin + actual->size ();
for (const char *it = skip_whitespaces (actual_begin, actual_end);
it != actual_end; ++it)
{
if (*it == '"' || *it == '\\')
{
__result->append('\\');
__result->append(*it);
}
else if (*it == '\n')
{
__result->append('"');
__result->append('\n');
__result->append('"');
}
else
__result->append(*it);
}
__result->append('\"');
}
else {
// ### warning message?
}
}
else if (*__first == '\"')
{
const char *next_pos = skip_string_literal (__first, __last);
lines += skip_string_literal.lines;
__result->append(__first, next_pos - __first);
__first = next_pos;
}
else if (*__first == '\'')
{
const char *next_pos = skip_char_literal (__first, __last);
lines += skip_char_literal.lines;
__result->append(__first, next_pos - __first);
__first = next_pos;
}
else if (*__first == '\\')
{
++__first;
if (__first != __last && *__first == '\n')
{
++lines;
++__first;
} else {
__result->append('\\');
}
}
else if (comment_p (__first, __last))
{
__first = skip_comment_or_divop (__first, __last);
int n = skip_comment_or_divop.lines;
lines += n;
while (n-- > 0)
__result->append('\n');
}
else if (pp_isspace (*__first))
{
for (; __first != __last; ++__first)
{
if (*__first == '\n' || !pp_isspace (*__first))
break;
}
__result->append(' ');
}
else if (pp_isdigit (*__first))
{
const char *next_pos = skip_number (__first, __last);
lines += skip_number.lines;
__result->append(__first, next_pos - __first);
__first = next_pos;
}
else if (pp_isalpha (*__first) || *__first == '_')
{
const char *name_begin = __first;
const char *name_end = skip_identifier (__first, __last);
__first = name_end; // advance
// search for the paste token
const char *next = skip_blanks (__first, __last);
bool paste = false;
bool need_comma = false;
if (next != __last && *next == ',') {
const char *x = skip_blanks(__first + 1, __last);
if (x != __last && *x == '#' && (x + 1) != __last && x[1] == '#') {
need_comma = true;
paste = true;
__first = skip_blanks(x + 2, __last);
}
}
if (next != __last && *next == '#')
{
paste = true;
++next;
if (next != __last && *next == '#')
__first = skip_blanks(++next, __last);
}
const QByteArray fast_name(name_begin, name_end - name_begin);
const QByteArray *actual = resolve_formal (fast_name);
if (actual)
{
const char *begin = actual->constData ();
const char *end = begin + actual->size ();
if (paste) {
for (--end; end != begin - 1; --end) {
if (! pp_isspace(*end))
break;
}
++end;
}
__result->append(begin, end - begin);
if (need_comma)
__result->append(',');
continue;
}
Macro *macro = env->resolve (fast_name);
if (! macro || macro->isHidden() || env->hideNext)
{
if (fast_name.size () == 7 && fast_name [0] == 'd' && fast_name == "defined")
env->hideNext = true;
else
env->hideNext = false;
if (fast_name.size () == 8 && fast_name [0] == '_' && fast_name [1] == '_')
{
if (fast_name == "__LINE__")
{
__result->append(QByteArray::number(env->currentLine + lines));
continue;
}
else if (fast_name == "__FILE__")
{
__result->append('"');
__result->append(env->currentFile.toUtf8());
__result->append('"');
continue;
}
else if (fast_name == "__DATE__")
{
__result->append('"');
__result->append(QDate::currentDate().toString().toUtf8());
__result->append('"');
continue;
}
else if (fast_name == "__TIME__")
{
__result->append('"');
__result->append(QTime::currentTime().toString().toUtf8());
__result->append('"');
continue;
}
}
__result->append(name_begin, name_end - name_begin);
continue;
}
if (! macro->isFunctionLike())
{
Macro *m = 0;
if (! macro->definition().isEmpty())
{
macro->setHidden(true);
QByteArray __tmp;
__tmp.reserve (256);
MacroExpander expand_macro (env);
expand_macro(macro->definition(), &__tmp);
if (! __tmp.isEmpty ())
{
const char *__tmp_begin = __tmp.constBegin();
const char *__tmp_end = __tmp.constEnd();
const char *__begin_id = skip_whitespaces (__tmp_begin, __tmp_end);
const char *__end_id = skip_identifier (__begin_id, __tmp_end);
if (__end_id == __tmp_end)
{
const QByteArray __id (__begin_id, __end_id - __begin_id);
m = env->resolve (__id);
}
if (! m)
*__result += __tmp;
}
macro->setHidden(false);
}
if (! m)
continue;
macro = m;
}
// function like macro
const char *arg_it = skip_whitespaces (__first, __last);
if (arg_it == __last || *arg_it != '(')
{
__result->append(name_begin, name_end - name_begin);
lines += skip_whitespaces.lines;
__first = arg_it;
continue;
}
QVector<QByteArray> actuals;
actuals.reserve(macro->formals().size());
++arg_it; // skip '('
MacroExpander expand_actual (env, frame);
const char *arg_end = skip_argument(arg_it, __last);
if (arg_it != arg_end || (arg_end != __last && *arg_end == ','))
{
const QByteArray actual(arg_it, arg_end - arg_it);
QByteArray expanded;
expand_actual(actual.constBegin (), actual.constEnd (), &expanded);
pushActuals(actuals, macro, expanded);
arg_it = arg_end;
}
while (arg_it != __last && *arg_end == ',')
{
++arg_it; // skip ','
arg_end = skip_argument(arg_it, __last);
const QByteArray actual(arg_it, arg_end - arg_it);
QByteArray expanded;
expand_actual(actual.constBegin (), actual.constEnd (), &expanded);
pushActuals(actuals, macro, expanded);
arg_it = arg_end;
}
if (! (arg_it != __last && *arg_it == ')'))
return __last;
++arg_it; // skip ')'
__first = arg_it;
pp_frame frame (macro, actuals);
MacroExpander expand_macro (env, &frame);
macro->setHidden(true);
expand_macro (macro->definition(), __result);
macro->setHidden(false);
}
else
__result->append(*__first++);
}
return __first;
}
const char *MacroExpander::skip_argument_variadics (QVector<QByteArray> const &__actuals,
Macro *__macro,
const char *__first, const char *__last)
{
const char *arg_end = skip_argument (__first, __last);
while (__macro->isVariadic() && __first != arg_end && arg_end != __last && *arg_end == ','
&& (__actuals.size () + 1) == __macro->formals().size ())
{
arg_end = skip_argument (++arg_end, __last);
}
return arg_end;
}
void MacroExpander::pushActuals(QVector<QByteArray> & actuals, Macro *__macro, const QByteArray& expanded)
{
if (__macro->isVariadic() && actuals.count() == __macro->formals().count()) {
//already enough params --> append to the last one
QByteArray& b = actuals.last();
b.append(",");
b.append(expanded.trimmed());
}
else {
const char * __first = expanded.constData();
const char * __last = __first + expanded.length();
const char * arg_it = __first;
const char *arg_end = skip_argument_variadics(actuals, __macro, arg_it, __last);
actuals.push_back(QByteArray(arg_it, arg_end - arg_it).trimmed());
arg_it = arg_end;
while (arg_it != __last) {
++arg_it; // skip ','
const char *arg_end = skip_argument_variadics(actuals, __macro, arg_it, __last);
actuals.push_back(QByteArray(arg_it, arg_end - arg_it).trimmed());
arg_it = arg_end;
}
}
}

View File

@@ -1,110 +0,0 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
/*
Copyright 2005 Roberto Raggi <roberto@kdevelop.org>
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef CPLUSPLUS_PP_MACRO_EXPANDER_H
#define CPLUSPLUS_PP_MACRO_EXPANDER_H
#include "Macro.h"
#include "pp-scanner.h"
#include <QVector>
#include <QByteArray>
namespace CPlusPlus {
class Environment;
class Client;
struct pp_frame;
class MacroExpander
{
Environment *env;
pp_frame *frame;
Client *client;
unsigned start_offset;
pp_skip_number skip_number;
pp_skip_identifier skip_identifier;
pp_skip_string_literal skip_string_literal;
pp_skip_char_literal skip_char_literal;
pp_skip_argument skip_argument;
pp_skip_comment_or_divop skip_comment_or_divop;
pp_skip_blanks skip_blanks;
pp_skip_whitespaces skip_whitespaces;
const QByteArray *resolve_formal(const QByteArray &name);
public:
explicit 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);
const char *operator()(const QByteArray &source,
QByteArray *result)
{ return operator()(source.constBegin(), source.constEnd(), result); }
const char *expand(const char *first, const char *last,
QByteArray *result);
const char *skip_argument_variadics(const QVector<QByteArray> &actuals,
Macro *macro,
const char *first, const char *last);
void pushActuals(QVector<QByteArray> & actuals, Macro *__macro, const QByteArray& expanded);
public: // attributes
int lines;
};
} // namespace CPlusPlus
#endif // CPLUSPLUS_PP_MACRO_EXPANDER_H

View File

@@ -56,7 +56,6 @@
#include "PreprocessorClient.h"
#include "PreprocessorEnvironment.h"
#include "pp-scanner.h"
#include "pp-macro-expander.h"
#include "pp-engine.h"
#endif // CPLUSPLUS_PREPROCESSOR_H

View File

@@ -103,7 +103,7 @@ public:
class Operation: public CppQuickFixOperation
{
public:
Operation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
Operation(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
int priority,
CompoundStatementAST *compoundStatement,
const QStringList &values)
@@ -156,7 +156,7 @@ static Enum *findEnum(const QList<LookupItem> &results,
return 0;
}
static Enum *conditionEnum(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
static Enum *conditionEnum(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
SwitchStatementAST *statement)
{
Block *block = statement->symbol;

View File

@@ -993,7 +993,7 @@ class ApplyDeclDefLinkOperation : public CppQuickFixOperation
{
public:
explicit ApplyDeclDefLinkOperation(
const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
const QSharedPointer<FunctionDeclDefLink> &link)
: CppQuickFixOperation(interface, 10)
, m_link(link)

View File

@@ -64,7 +64,7 @@ namespace {
class InsertDeclOperation: public CppQuickFixOperation
{
public:
InsertDeclOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
InsertDeclOperation(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
const QString &targetFileName, const Class *targetSymbol,
InsertionPointLocator::AccessSpec xsSpec,
const QString &decl)
@@ -229,7 +229,7 @@ namespace {
class InsertDefOperation: public CppQuickFixOperation
{
public:
InsertDefOperation(const QSharedPointer<const Internal::CppQuickFixAssistInterface> &interface,
InsertDefOperation(const QSharedPointer<const CppEditor::Internal::CppQuickFixAssistInterface> &interface,
Declaration *decl, const InsertionLocation &loc)
: CppQuickFixOperation(interface, 0)
, m_decl(decl)

View File

@@ -79,7 +79,7 @@
using namespace CPlusPlus;
using namespace CppEditor;
using namespace CppTools;
using namespace Internal;
using namespace CppTools::Internal;
using namespace TextEditor;
namespace CppTools {

View File

@@ -503,7 +503,7 @@ void CppPreprocessor::passedMacroDefinitionCheck(unsigned offset, const Macro &m
return;
m_currentDoc->addMacroUse(macro, offset, macro.name().length(), env.currentLine,
QVector<MacroArgumentReference>(), true);
QVector<MacroArgumentReference>());
}
void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArray &name)
@@ -517,7 +517,6 @@ void CppPreprocessor::failedMacroDefinitionCheck(unsigned offset, const QByteArr
void CppPreprocessor::startExpandingMacro(unsigned offset,
const Macro &macro,
const QByteArray &originalText,
bool inCondition,
const QVector<MacroArgumentReference> &actuals)
{
if (! m_currentDoc)
@@ -525,7 +524,7 @@ void CppPreprocessor::startExpandingMacro(unsigned offset,
//qDebug() << "start expanding:" << macro.name() << "text:" << originalText;
m_currentDoc->addMacroUse(macro, offset, originalText.length(), env.currentLine,
actuals, inCondition);
actuals);
}
void CppPreprocessor::stopExpandingMacro(unsigned, const Macro &)
@@ -600,7 +599,9 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
}
}
//qDebug() << "parse file:" << fileName << "contents:" << contents.size();
// qDebug() << "parse file:" << fileName
// << "contents:" << contents.size()
// ;
Document::Ptr doc = snapshot.document(fileName);
if (doc) {
@@ -620,6 +621,8 @@ void CppPreprocessor::sourceNeeded(QString &fileName, IncludeType type, unsigned
const QByteArray preprocessedCode = preprocess(fileName, contents);
// { QByteArray b(preprocessedCode); b.replace("\n", "<<<\n"); qDebug("Preprocessed code for \"%s\": [[%s]]", fileName.toUtf8().constData(), b.constData()); }
doc->setUtf8Source(preprocessedCode);
doc->keepSourceAndAST();
doc->tokenize();

View File

@@ -303,7 +303,6 @@ protected:
virtual void startExpandingMacro(unsigned offset,
const CPlusPlus::Macro &macro,
const QByteArray &originalText,
bool inCondition,
const QVector<CPlusPlus::MacroArgumentReference> &actuals);
virtual void stopExpandingMacro(unsigned offset, const CPlusPlus::Macro &macro);
virtual void startSkippingBlocks(unsigned offset);

View File

@@ -0,0 +1,13 @@
#define Q_DECL_EQ_DELETE
#define Q_DISABLE_COPY(Class) \
Class(const Class &) Q_DECL_EQ_DELETE;\
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
class Test {
private:
Q_DISABLE_COPY(Test)
public:
Test();
};

View File

@@ -0,0 +1,31 @@
# 6 "data/empty-macro.2.cpp"
class Test {
private:
Test
#gen true
# 3 "data/empty-macro.2.cpp"
(const
#gen false
# 8 "data/empty-macro.2.cpp"
Test
#gen true
# 3 "data/empty-macro.2.cpp"
&);
#gen false
# 8 "data/empty-macro.2.cpp"
Test
#gen true
# 4 "data/empty-macro.2.cpp"
&operator=(const
#gen false
# 8 "data/empty-macro.2.cpp"
Test
#gen true
# 4 "data/empty-macro.2.cpp"
&);
#gen false
# 10 "data/empty-macro.2.cpp"
public:
Test();
};

View File

@@ -0,0 +1,5 @@
#define EMPTY_MACRO
class EMPTY_MACRO Foo {
};

View File

@@ -0,0 +1,5 @@
class Foo {
};

View File

@@ -0,0 +1,5 @@
#define TEST test
TEST TEST;
void TEST();

View File

@@ -0,0 +1,14 @@
#gen true
# 1 "data/identifier-expansion.1.cpp"
test test
#gen false
# 3 "data/identifier-expansion.1.cpp"
;
void
#gen true
# 1 "data/identifier-expansion.1.cpp"
test
#gen false
# 5 "data/identifier-expansion.1.cpp"
();

View File

@@ -0,0 +1,6 @@
#define TEST test
#define ANOTHER_TEST TEST
ANOTHER_TEST TEST;
void ANOTHER_TEST();

View File

@@ -0,0 +1,14 @@
#gen true
# 1 "data/identifier-expansion.2.cpp"
test test
#gen false
# 4 "data/identifier-expansion.2.cpp"
;
void
#gen true
# 1 "data/identifier-expansion.2.cpp"
test
#gen false
# 6 "data/identifier-expansion.2.cpp"
();

View File

@@ -0,0 +1,14 @@
#define FOR_EACH_INSTR(V) \
V(ADD) \
V(SUB)
#define DECLARE_INSTR(op) #op,
#define DECLARE_OP_INSTR(op) op_##op,
enum op_code {
FOR_EACH_INSTR(DECLARE_OP_INSTR)
};
static const char *names[] = {
FOR_EACH_INSTR(DECLARE_INSTR)
};

View File

@@ -0,0 +1,23 @@
# 8 "data/identifier-expansion.3.cpp"
enum op_code {
#gen true
# 6 "data/identifier-expansion.3.cpp"
op_ADD, op_SUB,
#gen false
# 10 "data/identifier-expansion.3.cpp"
};
static const char *names[] = {
#gen true
# 2 "data/identifier-expansion.3.cpp"
"ADD"
,
# 3 "data/identifier-expansion.3.cpp"
"SUB"
,
#gen false
# 14 "data/identifier-expansion.3.cpp"
};

View File

@@ -0,0 +1,8 @@
#define foobar(a) a
#define food foobar
void baz()
{
int aaa;
food(aaa);
}

View File

@@ -0,0 +1,8 @@
void baz()
{
int aaa;
aaa;
}

View File

@@ -0,0 +1,8 @@
#define FOOBAR
#ifdef FOO
class FOOBAR Zoo {
};
#endif

View File

@@ -0,0 +1 @@
# 9 "data/identifier-expansion.5.cpp"

View File

@@ -0,0 +1,36 @@
#define USE(MY_USE) (defined MY_USE_##MY_USE && MY_USE_##MY_USE)
#define MY_USE_FEATURE1 1
#define MY_USE_FEATURE2 0
#if USE(FEATURE1)
void thisFunctionIsEnabled();
#endif
#if USE(FEATURE2)
void thisFunctionIsDisabled();
#endif
#if USE(FEATURE3)
void thisFunctionIsAlsoDisabled();
#endif
#define USE2(MY_USE) (defined MY_USE_##MY_USE)
#if USE2(FEATURE1)
void thisFunctionIsEnabled2();
#endif
#if USE2(FEATURE3)
void thisFunctionIsDisabled2();
#endif
#define USE3(MY_USE) (MY_USE_##MY_USE)
#if USE3(FEATURE1)
void thisFunctionIsEnabled3();
#endif
#if USE3(FEATURE2)
void thisFunctionIsDisabled3();
#endif

View File

@@ -0,0 +1,7 @@
# 7 "data/macro-test.cpp"
void thisFunctionIsEnabled();
# 21 "data/macro-test.cpp"
void thisFunctionIsEnabled2();
# 31 "data/macro-test.cpp"
void thisFunctionIsEnabled3();
# 37 "data/macro-test.cpp"

View File

@@ -0,0 +1,4 @@
#define X() Y
#define Y() X
A: X()()()

View File

@@ -0,0 +1,9 @@
A:
#gen true
# 1 "data/macro_expand.c"
Y
#gen false
# 5 "data/macro_expand.c"

View File

@@ -0,0 +1,18 @@
// This file is copied from Clang. Everything below this line is "theirs".
// This pounds on macro expansion for performance reasons. This is currently
// heavily constrained by darwin's malloc.
// Function-like macros.
#define A0(A, B) A B
#define A1(A, B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B) A0(A,B)
#define A2(A, B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B) A1(A,B)
#define A3(A, B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B) A2(A,B)
#define A4(A, B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B) A3(A,B)
#define A5(A, B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B) A4(A,B)
#define A6(A, B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B) A5(A,B)
#define A7(A, B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B) A6(A,B)
#define A8(A, B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B) A7(A,B)
A8(a, b)

View File

@@ -0,0 +1,66 @@
WRITE IN C (sung to The Beatles "Let it Be")
When I find my code in tons of trouble,
Friends and colleagues come to me,
Speaking words of wisdom:
"Write in C."
As the deadline fast approaches,
And bugs are all that I can see,
Somewhere, someone whispers"
"Write in C."
Write in C, write in C,
Write in C, write in C.
LISP is dead and buried,
Write in C.
I used to write a lot of FORTRAN,
for science it worked flawlessly.
Try using it for graphics!
Write in C.
If you've just spent nearly 30 hours
Debugging some assembly,
Soon you will be glad to
Write in C.
Write in C, write in C,
Write In C, yeah, write in C.
Only wimps use BASIC.
Write in C.
Write in C, write in C,
Write in C, oh, write in C.
Pascal won't quite cut it.
Write in C.
{
Guitar Solo
}
Write in C, write in C,
Write in C, yeah, write in C.
Don't even mention COBOL.
Write in C.
And when the screen is fuzzy,
And the edior is bugging me.
I'm sick of ones and zeroes.
Write in C.
A thousand people people swear that T.P.
Seven is the one for me.
I hate the word PROCEDURE,
Write in C.
Write in C, write in C,
Write in C, yeah, write in C.
PL1 is 80's,
Write in C.
Write in C, write in C,
Write in C, yeah, write in C.
The government loves ADA,
Write in C.

View File

@@ -0,0 +1,5 @@
#define a b
#define b a
b
a

View File

@@ -0,0 +1,6 @@
#gen true
# 1 "data/recursive.1.cpp"
b
a
#gen false
# 6 "data/recursive.1.cpp"

View File

@@ -0,0 +1,10 @@
#define Q_FOREACH(variable, container) foobar(variable, container)
#define foreach Q_FOREACH
int f() {
foreach (QString &s, QStringList()) {
doSomething();
}
return 1;
}

View File

@@ -0,0 +1,7 @@
# 5 "data/reserved.1.cpp"
int f() {
foreach (QString &s, QStringList()) {
doSomething();
}
return 1;
}

View File

@@ -1,3 +1,13 @@
include(../../qttest.pri)
include(../shared/shared.pri)
SOURCES += tst_preprocessor.cpp
OTHER_FILES = \
data/noPP.1.cpp data/noPP.1.errors.txt \
data/identifier-expansion.1.cpp data/identifier-expansion.1.out.cpp data/identifier-expansion.1.errors.txt \
data/identifier-expansion.2.cpp data/identifier-expansion.2.out.cpp data/identifier-expansion.2.errors.txt \
data/identifier-expansion.3.cpp data/identifier-expansion.3.out.cpp data/identifier-expansion.3.errors.txt \
data/identifier-expansion.4.cpp data/identifier-expansion.4.out.cpp data/identifier-expansion.4.errors.txt \
data/reserved.1.cpp data/reserved.1.out.cpp data/reserved.1.errors.txt \
data/macro_expand.c data/macro_expand.out.c data/macro_expand.errors.txt \
data/empty-macro.cpp data/empty-macro.out.cpp

View File

@@ -36,19 +36,191 @@
//TESTED_COMPONENT=src/libs/cplusplus
using namespace CPlusPlus;
#define DUMP_OUTPUT(x) {QByteArray b(x);qDebug("output: [[%s]]", b.replace("\n", "<<\n").constData());}
QByteArray loadSource(const QString &fileName)
{
QFile inf(fileName);
if (!inf.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug("Cannot open \"%s\"", fileName.toUtf8().constData());
return QByteArray();
}
QTextStream ins(&inf);
QString source = ins.readAll();
inf.close();
return source.toUtf8();
}
void saveData(const QByteArray &data, const QString &fileName)
{
QFile inf(fileName);
if (!inf.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug("Cannot open \"%s\"", fileName.toUtf8().constData());
return;
}
inf.write(data);
inf.close();
}
class MockClient: public Client
{
public:
struct Block {
Block(): start(0), end(0) {}
Block(unsigned start): start(start), end(0) {}
unsigned start;
unsigned end;
};
public:
MockClient(Environment *env, QByteArray *output)
: m_env(env)
, m_output(output)
, m_pp(this, env)
, m_includeDepth(0)
{}
virtual ~MockClient() {}
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*/,
const QVector<MacroArgumentReference> &/*actuals*/
= QVector<MacroArgumentReference>()) {}
virtual void stopExpandingMacro(unsigned /*offset*/,
const Macro &/*macro*/) {}
virtual void startSkippingBlocks(unsigned offset)
{ m_skippedBlocks.append(Block(offset)); }
virtual void stopSkippingBlocks(unsigned offset)
{ m_skippedBlocks.last().end = offset; }
virtual void sourceNeeded(QString &includedFileName, IncludeType mode,
unsigned /*line*/)
{
QString resolvedFileName;
if (mode == IncludeLocal)
resolvedFileName = resolveLocally(m_env->currentFile, includedFileName);
else
resolvedFileName = resolveGlobally(includedFileName);
// qDebug("resolved [[%s]] to [[%s]] from [[%s]] (%s)\n",
// includedFileName.toUtf8().constData(),
// resolvedFileName.toUtf8().constData(),
// currentFileName.toUtf8().constData(),
// (mode == IncludeLocal) ? "locally" : "globally");
if (resolvedFileName.isEmpty())
return;
++m_includeDepth;
// qDebug("%5d %s %s", m_includeDepth, QByteArray(m_includeDepth, '+').constData(), resolvedFileName.toUtf8().constData());
sourceNeeded(resolvedFileName);
--m_includeDepth;
}
QString resolveLocally(const QString &currentFileName,
const QString &includedFileName) const
{
QDir dir;
if (currentFileName.isEmpty())
dir = QDir::current();
else
dir = QFileInfo(currentFileName).dir();
const QFileInfo inc(dir, includedFileName);
if (inc.exists()) {
const QString resolved = inc.filePath();
return resolved.toUtf8().constData();
} else {
// std::cerr<<"Cannot find " << inc.fileName().toUtf8().constData()<<std::endl;
return QString();
}
}
QString resolveGlobally(const QString &currentFileName) const
{
foreach (const QDir &dir, m_includePaths) {
QFileInfo f(dir, currentFileName);
if (f.exists())
return f.filePath();
}
return QString();
}
void setIncludePaths(const QStringList &includePaths)
{
foreach (const QString &path, includePaths) {
QDir dir(path);
if (dir.exists())
m_includePaths.append(dir);
}
}
void sourceNeeded(const QString &fileName)
{
QByteArray src = loadSource(fileName);
QVERIFY(!src.isEmpty());
m_pp.preprocess(fileName, src, m_output, false, true, false);
}
QList<Block> skippedBlocks() const
{ return m_skippedBlocks; }
private:
Environment *m_env;
QByteArray *m_output;
Preprocessor m_pp;
QList<QDir> m_includePaths;
unsigned m_includeDepth;
QList<Block> m_skippedBlocks;
};
QDebug &operator<<(QDebug& d, const MockClient::Block &b) { d << '[' << b.start << ',' << b.end << ']'; return d; }
class tst_Preprocessor: public QObject
{
Q_OBJECT
Q_OBJECT
private Q_SLOTS:
protected:
QByteArray preprocess(const QString &fileName, QByteArray * /*errors*/) {
//### TODO: hook up errors
QByteArray output;
Environment env;
MockClient client(&env, &output);
client.sourceNeeded("data/" + fileName);
return output;
}
private /* not corrected yet */:
void macro_definition_lineno();
private slots:
void va_args();
void named_va_args();
void first_empty_macro_arg();
void param_expanding_as_multiple_params();
void macro_definition_lineno();
void invalid_param_count();
void unfinished_function_like_macro_call();
void nasty_macro_expansion();
void tstst();
void test_file_builtin();
void blockSkipping();
void comparisons_data();
void comparisons();
};
void tst_Preprocessor::va_args()
@@ -58,14 +230,18 @@ void tst_Preprocessor::va_args()
Preprocessor preprocess(client, &env);
QByteArray preprocessed = preprocess(QLatin1String("<stdin>"),
QByteArray("\n#define foo(...) int f(__VA_ARGS__);"
QByteArray("#define foo(...) int f(__VA_ARGS__);\n"
"\nfoo( )\n"
"\nfoo(int a)\n"
"\nfoo(int a,int b)\n"));
"\nfoo(int a,int b)\n"),
true,
false);
preprocessed = preprocessed.simplified();
// DUMP_OUTPUT(preprocessed);
QVERIFY(preprocessed.contains("int f();"));
QVERIFY(preprocessed.contains("int f(int a);"));
QVERIFY(preprocessed.contains("int f(int a,int b);"));
QVERIFY(preprocessed.contains("int f( int a );"));
QVERIFY(preprocessed.contains("int f( int a, int b );"));
}
void tst_Preprocessor::named_va_args()
@@ -78,11 +254,13 @@ void tst_Preprocessor::named_va_args()
QByteArray("\n#define foo(ARGS...) int f(ARGS);"
"\nfoo( )\n"
"\nfoo(int a)\n"
"\nfoo(int a,int b)\n"));
"\nfoo(int a,int b)\n"),
true, false);
preprocessed = preprocessed.simplified();
QVERIFY(preprocessed.contains("int f();"));
QVERIFY(preprocessed.contains("int f(int a);"));
QVERIFY(preprocessed.contains("int f(int a,int b);"));
QVERIFY(preprocessed.contains("int f( int a );"));
QVERIFY(preprocessed.contains("int f( int a, int b );"));
}
void tst_Preprocessor::first_empty_macro_arg()
@@ -95,24 +273,30 @@ void tst_Preprocessor::first_empty_macro_arg()
QByteArray("\n#define foo(a,b) a int b;"
"\nfoo(const,cVal)\n"
"\nfoo(,Val)\n"
"\nfoo( ,Val2)\n"));
"\nfoo( ,Val2)\n"),
true, false);
QVERIFY(preprocessed.contains("const int cVal;"));
QVERIFY(preprocessed.contains("int Val;"));
QVERIFY(preprocessed.contains("int Val2;"));
preprocessed = preprocessed.simplified();
// DUMP_OUTPUT(preprocessed);
QVERIFY(preprocessed.contains("const int cVal ;"));
QVERIFY(preprocessed.contains("int Val ;"));
QVERIFY(preprocessed.contains("int Val2 ;"));
}
void tst_Preprocessor::param_expanding_as_multiple_params()
void tst_Preprocessor::invalid_param_count()
{
Client *client = 0; // no client.
Environment env;
Preprocessor preprocess(client, &env);
// The following is illegal, but shouldn't crash the preprocessor.
// GCC says: 3:14: error: macro "foo" requires 2 arguments, but only 1 given
QByteArray preprocessed = preprocess(QLatin1String("<stdin>"),
QByteArray("\n#define foo(a,b) int f(a,b);"
"\n#define ARGS(t) t a,t b"
"\nfoo(ARGS(int))"));
QVERIFY(preprocessed.contains("int f(int a,int b);"));
"\nfoo(ARGS(int))"),
true, false);
// do not verify the output: it's illegal, so anything might be outputted.
}
void tst_Preprocessor::macro_definition_lineno()
@@ -158,8 +342,8 @@ void tst_Preprocessor::unfinished_function_like_macro_call()
QByteArray preprocessed = preprocess(QLatin1String("<stdin>"),
QByteArray("\n#define foo(a,b) a + b"
"\nfoo(1, 2\n"));
QCOMPARE(preprocessed.trimmed(), QByteArray("foo"));
QByteArray expected__("\n\n 1\n#gen true\n# 2 \"<stdin>\"\n+\n#gen false\n# 3 \"<stdin>\"\n 2\n");
QCOMPARE(preprocessed, expected__);
}
void tst_Preprocessor::nasty_macro_expansion()
@@ -228,17 +412,115 @@ void tst_Preprocessor::tstst()
"namespace std _GLIBCXX_VISIBILITY(default) {\n"
"}\n"
));
const QByteArray result =
"namespace std \n"
const QByteArray result____ ="\n\n"
"namespace std\n"
"#gen true\n"
"# 3 \"<stdin>\"\n"
" __attribute__ ((__visibility__ (\"default\")))\n"
"# 2 \"<stdin>\"\n"
"__attribute__ ((__visibility__ (\n"
"\"default\"\n"
"# 2 \"<stdin>\"\n"
")))\n"
"#gen false\n"
"# 3 \"<stdin>\"\n"
" {\n"
"}";
"}\n";
QVERIFY(preprocessed.contains(result));
QCOMPARE(preprocessed, result____);
}
void tst_Preprocessor::test_file_builtin()
{
Client *client = 0; // no client.
Environment env;
Preprocessor preprocess(client, &env);
QByteArray preprocessed = preprocess(
QLatin1String("some-file.c"),
QByteArray("const char *f = __FILE__\n"
));
const QByteArray result____ =
"const char *f =\n"
"#gen true\n"
"# 1 \"some-file.c\"\n"
"\"some-file.c\"\n"
"#gen false\n"
"# 2 \"some-file.c\"\n"
;
QCOMPARE(preprocessed, result____);
}
void tst_Preprocessor::comparisons_data()
{
QTest::addColumn<QString>("infile");
QTest::addColumn<QString>("outfile");
QTest::addColumn<QString>("errorfile");
QTest::newRow("do nothing") << "noPP.1.cpp" << "noPP.1.cpp" << "";
QTest::newRow("identifier-expansion 1") << "identifier-expansion.1.cpp" << "identifier-expansion.1.out.cpp" << "";
QTest::newRow("identifier-expansion 2") << "identifier-expansion.2.cpp" << "identifier-expansion.2.out.cpp" << "";
QTest::newRow("identifier-expansion 3") << "identifier-expansion.3.cpp" << "identifier-expansion.3.out.cpp" << "";
QTest::newRow("identifier-expansion 4") << "identifier-expansion.4.cpp" << "identifier-expansion.4.out.cpp" << "";
QTest::newRow("identifier-expansion 5") << "identifier-expansion.5.cpp" << "identifier-expansion.5.out.cpp" << "";
QTest::newRow("reserved 1") << "reserved.1.cpp" << "reserved.1.out.cpp" << "";
QTest::newRow("recursive 1") << "recursive.1.cpp" << "recursive.1.out.cpp" << "";
QTest::newRow("macro_pounder_fn") << "macro_pounder_fn.c" << "" << "";
QTest::newRow("macro_expand") << "macro_expand.c" << "macro_expand.out.c" << "";
QTest::newRow("macro-test") << "macro-test.cpp" << "macro-test.out.cpp" << "";
QTest::newRow("empty-macro") << "empty-macro.cpp" << "empty-macro.out.cpp" << "";
QTest::newRow("empty-macro 2") << "empty-macro.2.cpp" << "empty-macro.2.out.cpp" << "";
}
void tst_Preprocessor::comparisons()
{
QFETCH(QString, infile);
QFETCH(QString, outfile);
QFETCH(QString, errorfile);
QByteArray errors;
QByteArray preprocessed = preprocess(infile, &errors);
// DUMP_OUTPUT(preprocessed);
if (!outfile.isEmpty()) {
QByteArray output____ = loadSource("data/"+outfile); // these weird underscores are here to make the name as long as "preprocessed", so the QCOMPARE error messages are nicely aligned.
// QCOMPARE(preprocessed, output____);
QCOMPARE(QString::fromUtf8(preprocessed.constData()), QString::fromUtf8(output____.constData()));
}
if (!errorfile.isEmpty()) {
QByteArray errorFileContents = loadSource("data/"+errorfile);
QCOMPARE(QString::fromUtf8(errors.constData()), QString::fromUtf8(errorFileContents.constData()));
}
}
void tst_Preprocessor::blockSkipping()
{
QByteArray output;
Environment env;
MockClient client(&env, &output);
Preprocessor pp(&client, &env);
/*QByteArray preprocessed =*/ pp(
QLatin1String("<stdin>"),
QByteArray("#if 0\n"
"\n"
"int yes;\n"
"\n"
"#elif 0\n"
"\n"
"int no;\n"
"\n"
"#else // foobar\n"
"\n"
"void also_not;\n"
"\n"
"#endif\n"
));
QList<MockClient::Block> blocks = client.skippedBlocks();
QCOMPARE(blocks.size(), 1);
MockClient::Block b = blocks.at(0);
QCOMPARE(b.start, 6U);
QCOMPARE(b.end, 34U);
}
QTEST_APPLESS_MAIN(tst_Preprocessor)