tools/linguist/lupdate/cpp.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/linguist/lupdate/cpp.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2143 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "lupdate.h"
+
+#include <translator.h>
+
+#include <QtCore/QBitArray>
+#include <QtCore/QDebug>
+#include <QtCore/QFileInfo>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <ctype.h>              // for isXXX()
+
+QT_BEGIN_NAMESPACE
+
+/* qmake ignore Q_OBJECT */
+
+static QString MagicComment(QLatin1String("TRANSLATOR"));
+
+#define STRING(s) static QString str##s(QLatin1String(#s))
+
+//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this
+
+class HashString {
+public:
+    HashString() : m_hashed(false) {}
+    explicit HashString(const QString &str) : m_str(str), m_hashed(false) {}
+    void setValue(const QString &str) { m_str = str; m_hashed = false; }
+    const QString &value() const { return m_str; }
+    bool operator==(const HashString &other) const { return m_str == other.m_str; }
+private:
+    QString m_str;
+    mutable uint m_hash;
+    mutable bool m_hashed;
+    friend uint qHash(const HashString &str);
+};
+
+uint qHash(const HashString &str)
+{
+    if (!str.m_hashed) {
+        str.m_hashed = true;
+        str.m_hash = qHash(str.m_str);
+    }
+    return str.m_hash;
+}
+
+class HashStringList {
+public:
+    explicit HashStringList(const QList<HashString> &list) : m_list(list), m_hashed(false) {}
+    const QList<HashString> &value() const { return m_list; }
+    bool operator==(const HashStringList &other) const { return m_list == other.m_list; }
+private:
+    QList<HashString> m_list;
+    mutable uint m_hash;
+    mutable bool m_hashed;
+    friend uint qHash(const HashStringList &list);
+};
+
+uint qHash(const HashStringList &list)
+{
+    if (!list.m_hashed) {
+        list.m_hashed = true;
+        uint hash = 0;
+        foreach (const HashString &qs, list.m_list) {
+            hash ^= qHash(qs) ^ 0xa09df22f;
+            hash = (hash << 13) | (hash >> 19);
+        }
+        list.m_hash = hash;
+    }
+    return list.m_hash;
+}
+
+typedef QList<HashString> NamespaceList;
+
+struct Namespace {
+
+    Namespace() :
+            classDef(this),
+            hasTrFunctions(false), complained(false)
+    {}
+    ~Namespace()
+    {
+        qDeleteAll(children);
+    }
+
+    QHash<HashString, Namespace *> children;
+    QHash<HashString, NamespaceList> aliases;
+    QList<HashStringList> usings;
+
+    // Class declarations set no flags and create no namespaces, so they are ignored.
+    // Class definitions may appear multiple times - but only because we are trying to
+    // "compile" all sources irrespective of build configuration.
+    // Nested classes may be forward-declared inside a definition, and defined in another file.
+    // The latter will detach the class' child list, so clones need a backlink to the original
+    // definition (either one in case of multiple definitions).
+    Namespace *classDef;
+
+    QString trQualification;
+
+    bool hasTrFunctions;
+    bool complained; // ... that tr functions are missing.
+};
+
+static int nextFileId;
+
+class VisitRecorder {
+public:
+    VisitRecorder()
+    {
+        m_ba.resize(nextFileId);
+    }
+    bool tryVisit(int fileId)
+    {
+        if (m_ba.at(fileId))
+            return false;
+        m_ba[fileId] = true;
+        return true;
+    }
+private:
+    QBitArray m_ba;
+};
+
+struct ParseResults {
+    int fileId;
+    Namespace rootNamespace;
+    QSet<const ParseResults *> includes;
+};
+
+typedef QHash<QString, const ParseResults *> ParseResultHash;
+typedef QHash<QString, const Translator *> TranslatorHash;
+
+class CppFiles {
+
+public:
+    static const ParseResults *getResults(const QString &cleanFile);
+    static void setResults(const QString &cleanFile, const ParseResults *results);
+    static const Translator *getTranslator(const QString &cleanFile);
+    static void setTranslator(const QString &cleanFile, const Translator *results);
+    static bool isBlacklisted(const QString &cleanFile);
+    static void setBlacklisted(const QString &cleanFile);
+
+private:
+    static ParseResultHash &parsedFiles();
+    static TranslatorHash &translatedFiles();
+    static QSet<QString> &blacklistedFiles();
+};
+
+class CppParser {
+
+public:
+    CppParser(ParseResults *results = 0);
+    void setInput(const QString &in);
+    void setInput(QTextStream &ts, const QString &fileName);
+    void setTranslator(Translator *_tor) { tor = _tor; }
+    void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions);
+    void parseInternal(ConversionData &cd, QSet<QString> &inclusions);
+    const ParseResults *recordResults(bool isHeader);
+    void deleteResults() { delete results; }
+
+    struct SavedState {
+        NamespaceList namespaces;
+        QStack<int> namespaceDepths;
+        NamespaceList functionContext;
+        QString functionContextUnresolved;
+        QString pendingContext;
+    };
+
+private:
+    struct IfdefState {
+        IfdefState() {}
+        IfdefState(int _braceDepth, int _parenDepth) :
+            braceDepth(_braceDepth),
+            parenDepth(_parenDepth),
+            elseLine(-1)
+        {}
+
+        SavedState state;
+        int braceDepth, braceDepth1st;
+        int parenDepth, parenDepth1st;
+        int elseLine;
+    };
+
+    uint getChar();
+    uint getToken();
+    bool getMacroArgs();
+    bool match(uint t);
+    bool matchString(QString *s);
+    bool matchEncoding(bool *utf8);
+    bool matchStringOrNull(QString *s);
+    bool matchExpression();
+
+    QString transcode(const QString &str, bool utf8);
+    void recordMessage(
+        int line, const QString &context, const QString &text, const QString &comment,
+        const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+        bool utf8, bool plural);
+
+    void processInclude(const QString &file, ConversionData &cd,
+                        QSet<QString> &inclusions);
+
+    void saveState(SavedState *state);
+    void loadState(const SavedState *state);
+
+    static QString stringifyNamespace(const NamespaceList &namespaces);
+    static QStringList stringListifyNamespace(const NamespaceList &namespaces);
+    typedef bool (CppParser::*VisitNamespaceCallback)(const Namespace *ns, void *context) const;
+    bool visitNamespace(const NamespaceList &namespaces, int nsCount,
+                        VisitNamespaceCallback callback, void *context,
+                        VisitRecorder &vr, const ParseResults *rslt) const;
+    bool visitNamespace(const NamespaceList &namespaces, int nsCount,
+                        VisitNamespaceCallback callback, void *context) const;
+    static QStringList stringListifySegments(const QList<HashString> &namespaces);
+    bool qualifyOneCallbackOwn(const Namespace *ns, void *context) const;
+    bool qualifyOneCallbackUsing(const Namespace *ns, void *context) const;
+    bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
+                    NamespaceList *resolved) const;
+    bool fullyQualify(const NamespaceList &namespaces, const QList<HashString> &segments,
+                      bool isDeclaration,
+                      NamespaceList *resolved, QStringList *unresolved) const;
+    bool fullyQualify(const NamespaceList &namespaces, const QString &segments,
+                      bool isDeclaration,
+                      NamespaceList *resolved, QStringList *unresolved) const;
+    bool findNamespaceCallback(const Namespace *ns, void *context) const;
+    const Namespace *findNamespace(const NamespaceList &namespaces, int nsCount = -1) const;
+    void enterNamespace(NamespaceList *namespaces, const HashString &name);
+    void truncateNamespaces(NamespaceList *namespaces, int lenght);
+    Namespace *modifyNamespace(NamespaceList *namespaces, bool tryOrigin = true);
+
+    enum {
+        Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return,
+        Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, Tok_trid,
+        Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS,
+        Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon,
+        Tok_Equals,
+        Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon,
+        Tok_Null = 40, Tok_Integer,
+        Tok_QuotedInclude = 50, Tok_AngledInclude,
+        Tok_Other = 99
+    };
+
+    // Tokenizer state
+    QString yyFileName;
+    int yyCh;
+    bool yyAtNewline;
+    bool yyCodecIsUtf8;
+    bool yyForceUtf8;
+    QString yyWord;
+    qlonglong yyInteger;
+    QStack<IfdefState> yyIfdefStack;
+    int yyBraceDepth;
+    int yyParenDepth;
+    int yyLineNo;
+    int yyCurLineNo;
+    int yyBraceLineNo;
+    int yyParenLineNo;
+
+    // the string to read from and current position in the string
+    QTextCodec *yySourceCodec;
+    bool yySourceIsUnicode;
+    QString yyInStr;
+    const ushort *yyInPtr;
+
+    // Parser state
+    uint yyTok;
+
+    NamespaceList namespaces;
+    QStack<int> namespaceDepths;
+    NamespaceList functionContext;
+    QString functionContextUnresolved;
+    QString prospectiveContext;
+    QString pendingContext;
+    ParseResults *results;
+    Translator *tor;
+    bool directInclude;
+
+    SavedState savedState;
+    int yyMinBraceDepth;
+    bool inDefine;
+};
+
+CppParser::CppParser(ParseResults *_results)
+{
+    tor = 0;
+    if (_results) {
+        results = _results;
+        directInclude = true;
+    } else {
+        results = new ParseResults;
+        directInclude = false;
+    }
+    yyBraceDepth = 0;
+    yyParenDepth = 0;
+    yyCurLineNo = 1;
+    yyBraceLineNo = 1;
+    yyParenLineNo = 1;
+    yyAtNewline = true;
+    yyMinBraceDepth = 0;
+    inDefine = false;
+}
+
+void CppParser::setInput(const QString &in)
+{
+    yyInStr = in;
+    yyFileName = QString();
+    yySourceCodec = 0;
+    yySourceIsUnicode = true;
+    yyForceUtf8 = true;
+}
+
+void CppParser::setInput(QTextStream &ts, const QString &fileName)
+{
+    yyInStr = ts.readAll();
+    yyFileName = fileName;
+    yySourceCodec = ts.codec();
+    yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-");
+    yyForceUtf8 = false;
+}
+
+/*
+  The first part of this source file is the C++ tokenizer.  We skip
+  most of C++; the only tokens that interest us are defined here.
+  Thus, the code fragment
+
+      int main()
+      {
+          printf("Hello, world!\n");
+          return 0;
+      }
+
+  is broken down into the following tokens (Tok_ omitted):
+
+      Ident Ident LeftParen RightParen
+      LeftBrace
+          Ident LeftParen String RightParen Semicolon
+          return Semicolon
+      RightBrace.
+
+  The 0 doesn't produce any token.
+*/
+
+uint CppParser::getChar()
+{
+    const ushort *uc = yyInPtr;
+    forever {
+        ushort c = *uc;
+        if (!c) {
+            yyInPtr = uc;
+            return EOF;
+        }
+        ++uc;
+        if (c == '\\') {
+            ushort cc = *uc;
+            if (cc == '\n') {
+                ++yyCurLineNo;
+                ++uc;
+                continue;
+            }
+            if (cc == '\r') {
+                ++yyCurLineNo;
+                ++uc;
+                if (*uc == '\n')
+                    ++uc;
+                continue;
+            }
+        }
+        if (c == '\r') {
+            if (*uc == '\n')
+                ++uc;
+            c = '\n';
+            ++yyCurLineNo;
+            yyAtNewline = true;
+        } else if (c == '\n') {
+            ++yyCurLineNo;
+            yyAtNewline = true;
+        } else if (c != ' ' && c != '\t' && c != '#') {
+            yyAtNewline = false;
+        }
+        yyInPtr = uc;
+        return c;
+    }
+}
+
+// This ignores commas, parens and comments.
+// IOW, it understands only a single, simple argument.
+bool CppParser::getMacroArgs()
+{
+    // Failing this assertion would mean losing the preallocated buffer.
+    Q_ASSERT(yyWord.isDetached());
+    yyWord.resize(0);
+
+    while (isspace(yyCh))
+        yyCh = getChar();
+    if (yyCh != '(')
+        return false;
+    do {
+        yyCh = getChar();
+    } while (isspace(yyCh));
+    ushort *ptr = (ushort *)yyWord.unicode();
+    while (yyCh != ')') {
+        if (yyCh == EOF)
+            return false;
+        *ptr++ = yyCh;
+        yyCh = getChar();
+    }
+    yyCh = getChar();
+    for (; ptr != (ushort *)yyWord.unicode() && isspace(*(ptr - 1)); --ptr) ;
+    yyWord.resize(ptr - (ushort *)yyWord.unicode());
+    return true;
+}
+
+STRING(Q_OBJECT);
+STRING(Q_DECLARE_TR_FUNCTIONS);
+STRING(QT_TR_NOOP);
+STRING(QT_TRID_NOOP);
+STRING(QT_TRANSLATE_NOOP);
+STRING(QT_TRANSLATE_NOOP3);
+STRING(QT_TR_NOOP_UTF8);
+STRING(QT_TRANSLATE_NOOP_UTF8);
+STRING(QT_TRANSLATE_NOOP3_UTF8);
+STRING(class);
+// QTranslator::findMessage() has the same parameters as QApplication::translate()
+STRING(findMessage);
+STRING(friend);
+STRING(namespace);
+STRING(qtTrId);
+STRING(return);
+STRING(struct);
+STRING(TR);
+STRING(Tr);
+STRING(tr);
+STRING(trUtf8);
+STRING(translate);
+STRING(using);
+
+uint CppParser::getToken()
+{
+  restart:
+    // Failing this assertion would mean losing the preallocated buffer.
+    Q_ASSERT(yyWord.isDetached());
+    yyWord.resize(0);
+
+    while (yyCh != EOF) {
+        yyLineNo = yyCurLineNo;
+
+        if (yyCh == '#' && yyAtNewline) {
+            /*
+              Early versions of lupdate complained about
+              unbalanced braces in the following code:
+
+                  #ifdef ALPHA
+                      while (beta) {
+                  #else
+                      while (gamma) {
+                  #endif
+                          delta;
+                      }
+
+              The code contains, indeed, two opening braces for
+              one closing brace; yet there's no reason to panic.
+
+              The solution is to remember yyBraceDepth as it was
+              when #if, #ifdef or #ifndef was met, and to set
+              yyBraceDepth to that value when meeting #elif or
+              #else.
+            */
+            do {
+                yyCh = getChar();
+            } while (isspace(yyCh) && yyCh != '\n');
+
+            switch (yyCh) {
+            case 'd': // define
+                // Skip over the name of the define to avoid it being interpreted as c++ code
+                do { // Rest of "define"
+                    yyCh = getChar();
+                    if (yyCh == EOF)
+                        return Tok_Eof;
+                    if (yyCh == '\n')
+                        goto restart;
+                } while (!isspace(yyCh));
+                do { // Space beween "define" and macro name
+                    yyCh = getChar();
+                    if (yyCh == EOF)
+                        return Tok_Eof;
+                    if (yyCh == '\n')
+                        goto restart;
+                } while (isspace(yyCh));
+                do { // Macro name
+                    if (yyCh == '(') {
+                        // Argument list. Follows the name without a space, and no
+                        // paren nesting is possible.
+                        do {
+                            yyCh = getChar();
+                            if (yyCh == EOF)
+                                return Tok_Eof;
+                            if (yyCh == '\n')
+                                goto restart;
+                        } while (yyCh != ')');
+                        break;
+                    }
+                    yyCh = getChar();
+                    if (yyCh == EOF)
+                        return Tok_Eof;
+                    if (yyCh == '\n')
+                        goto restart;
+                } while (!isspace(yyCh));
+                do { // Shortcut the immediate newline case if no comments follow.
+                    yyCh = getChar();
+                    if (yyCh == EOF)
+                        return Tok_Eof;
+                    if (yyCh == '\n')
+                        goto restart;
+                } while (isspace(yyCh));
+
+                saveState(&savedState);
+                yyMinBraceDepth = yyBraceDepth;
+                inDefine = true;
+                goto restart;
+            case 'i':
+                yyCh = getChar();
+                if (yyCh == 'f') {
+                    // if, ifdef, ifndef
+                    yyIfdefStack.push(IfdefState(yyBraceDepth, yyParenDepth));
+                    yyCh = getChar();
+                } else if (yyCh == 'n') {
+                    // include
+                    do {
+                        yyCh = getChar();
+                    } while (yyCh != EOF && !isspace(yyCh));
+                    do {
+                        yyCh = getChar();
+                    } while (isspace(yyCh));
+                    int tChar;
+                    if (yyCh == '"')
+                        tChar = '"';
+                    else if (yyCh == '<')
+                        tChar = '>';
+                    else
+                        break;
+                    ushort *ptr = (ushort *)yyWord.unicode();
+                    forever {
+                        yyCh = getChar();
+                        if (yyCh == EOF || yyCh == '\n')
+                            break;
+                        if (yyCh == tChar) {
+                            yyCh = getChar();
+                            break;
+                        }
+                        *ptr++ = yyCh;
+                    }
+                    yyWord.resize(ptr - (ushort *)yyWord.unicode());
+                    return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude;
+                }
+                break;
+            case 'e':
+                yyCh = getChar();
+                if (yyCh == 'l') {
+                    // elif, else
+                    if (!yyIfdefStack.isEmpty()) {
+                        IfdefState &is = yyIfdefStack.top();
+                        if (is.elseLine != -1) {
+                            if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st)
+                                qWarning("%s:%d: Parenthesis/brace mismatch between "
+                                         "#if and #else branches; using #if branch\n",
+                                         qPrintable(yyFileName), is.elseLine);
+                        } else {
+                            is.braceDepth1st = yyBraceDepth;
+                            is.parenDepth1st = yyParenDepth;
+                            saveState(&is.state);
+                        }
+                        is.elseLine = yyLineNo;
+                        yyBraceDepth = is.braceDepth;
+                        yyParenDepth = is.parenDepth;
+                    }
+                    yyCh = getChar();
+                } else if (yyCh == 'n') {
+                    // endif
+                    if (!yyIfdefStack.isEmpty()) {
+                        IfdefState is = yyIfdefStack.pop();
+                        if (is.elseLine != -1) {
+                            if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st)
+                                qWarning("%s:%d: Parenthesis/brace mismatch between "
+                                         "#if and #else branches; using #if branch\n",
+                                         qPrintable(yyFileName), is.elseLine);
+                            yyBraceDepth = is.braceDepth1st;
+                            yyParenDepth = is.parenDepth1st;
+                            loadState(&is.state);
+                        }
+                    }
+                    yyCh = getChar();
+                }
+                break;
+            }
+            // Optimization: skip over rest of preprocessor directive
+            do {
+                if (yyCh == '/') {
+                    yyCh = getChar();
+                    if (yyCh == '/') {
+                        do {
+                            yyCh = getChar();
+                        } while (yyCh != EOF && yyCh != '\n');
+                        break;
+                    } else if (yyCh == '*') {
+                        bool metAster = false;
+
+                        forever {
+                            yyCh = getChar();
+                            if (yyCh == EOF) {
+                                qWarning("%s:%d: Unterminated C++ comment\n",
+                                         qPrintable(yyFileName), yyLineNo);
+                                break;
+                            }
+
+                            if (yyCh == '*') {
+                                metAster = true;
+                            } else if (metAster && yyCh == '/') {
+                                yyCh = getChar();
+                                break;
+                            } else {
+                                metAster = false;
+                            }
+                        }
+                    }
+                } else {
+                    yyCh = getChar();
+                }
+            } while (yyCh != '\n' && yyCh != EOF);
+            yyCh = getChar();
+        } else if ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z') || yyCh == '_') {
+            ushort *ptr = (ushort *)yyWord.unicode();
+            do {
+                *ptr++ = yyCh;
+                yyCh = getChar();
+            } while ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z')
+                     || (yyCh >= '0' && yyCh <= '9') || yyCh == '_');
+            yyWord.resize(ptr - (ushort *)yyWord.unicode());
+
+            //qDebug() << "IDENT: " << yyWord;
+
+            switch (yyWord.unicode()[0].unicode()) {
+            case 'Q':
+                if (yyWord == strQ_OBJECT)
+                    return Tok_Q_OBJECT;
+                if (yyWord == strQ_DECLARE_TR_FUNCTIONS)
+                    return Tok_Q_DECLARE_TR_FUNCTIONS;
+                if (yyWord == strQT_TR_NOOP)
+                    return Tok_tr;
+                if (yyWord == strQT_TRID_NOOP)
+                    return Tok_trid;
+                if (yyWord == strQT_TRANSLATE_NOOP)
+                    return Tok_translate;
+                if (yyWord == strQT_TRANSLATE_NOOP3)
+                    return Tok_translate;
+                if (yyWord == strQT_TR_NOOP_UTF8)
+                    return Tok_trUtf8;
+                if (yyWord == strQT_TRANSLATE_NOOP_UTF8)
+                    return Tok_translateUtf8;
+                if (yyWord == strQT_TRANSLATE_NOOP3_UTF8)
+                    return Tok_translateUtf8;
+                break;
+            case 'T':
+                // TR() for when all else fails
+                if (yyWord == strTR || yyWord == strTr)
+                    return Tok_tr;
+                break;
+            case 'c':
+                if (yyWord == strclass)
+                    return Tok_class;
+                break;
+            case 'f':
+                /*
+                  QTranslator::findMessage() has the same parameters as
+                  QApplication::translate().
+                */
+                if (yyWord == strfindMessage)
+                    return Tok_translate;
+                if (yyWord == strfriend)
+                    return Tok_friend;
+                break;
+            case 'n':
+                if (yyWord == strnamespace)
+                    return Tok_namespace;
+                break;
+            case 'q':
+                if (yyWord == strqtTrId)
+                    return Tok_trid;
+                break;
+            case 'r':
+                if (yyWord == strreturn)
+                    return Tok_return;
+                break;
+            case 's':
+                if (yyWord == strstruct)
+                    return Tok_class;
+                break;
+            case 't':
+                if (yyWord == strtr)
+                    return Tok_tr;
+                if (yyWord == strtrUtf8)
+                    return Tok_trUtf8;
+                if (yyWord == strtranslate)
+                    return Tok_translate;
+                break;
+            case 'u':
+                if (yyWord == strusing)
+                    return Tok_using;
+                break;
+            }
+            return Tok_Ident;
+        } else {
+            switch (yyCh) {
+            case '\n':
+                if (inDefine) {
+                    loadState(&savedState);
+                    prospectiveContext.clear();
+                    yyBraceDepth = yyMinBraceDepth;
+                    yyMinBraceDepth = 0;
+                    inDefine = false;
+                }
+                yyCh = getChar();
+                break;
+            case '/':
+                yyCh = getChar();
+                if (yyCh == '/') {
+                    ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
+                    do {
+                        yyCh = getChar();
+                        if (yyCh == EOF)
+                            break;
+                        *ptr++ = yyCh;
+                    } while (yyCh != '\n');
+                    yyWord.resize(ptr - (ushort *)yyWord.unicode());
+                } else if (yyCh == '*') {
+                    bool metAster = false;
+                    ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
+
+                    forever {
+                        yyCh = getChar();
+                        if (yyCh == EOF) {
+                            qWarning("%s:%d: Unterminated C++ comment\n",
+                                     qPrintable(yyFileName), yyLineNo);
+                            return Tok_Comment;
+                        }
+                        *ptr++ = yyCh;
+
+                        if (yyCh == '*')
+                            metAster = true;
+                        else if (metAster && yyCh == '/')
+                            break;
+                        else
+                            metAster = false;
+                    }
+                    yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2);
+
+                    yyCh = getChar();
+                }
+                return Tok_Comment;
+            case '"': {
+                ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
+                yyCh = getChar();
+                while (yyCh != EOF && yyCh != '\n' && yyCh != '"') {
+                    if (yyCh == '\\') {
+                        yyCh = getChar();
+                        if (yyCh == EOF || yyCh == '\n')
+                            break;
+                        *ptr++ = '\\';
+                    }
+                    *ptr++ = yyCh;
+                    yyCh = getChar();
+                }
+                yyWord.resize(ptr - (ushort *)yyWord.unicode());
+
+                if (yyCh != '"')
+                    qWarning("%s:%d: Unterminated C++ string\n",
+                              qPrintable(yyFileName), yyLineNo);
+                else
+                    yyCh = getChar();
+                return Tok_String;
+            }
+            case '-':
+                yyCh = getChar();
+                if (yyCh == '>') {
+                    yyCh = getChar();
+                    return Tok_Arrow;
+                }
+                break;
+            case ':':
+                yyCh = getChar();
+                if (yyCh == ':') {
+                    yyCh = getChar();
+                    return Tok_ColonColon;
+                }
+                return Tok_Colon;
+            // Incomplete: '<' might be part of '<=' or of template syntax.
+            // The main intent of not completely ignoring it is to break
+            // parsing of things like   std::cout << QObject::tr()  as
+            // context std::cout::QObject (see Task 161106)
+            case '=':
+                yyCh = getChar();
+                return Tok_Equals;
+            case '>':
+            case '<':
+                yyCh = getChar();
+                return Tok_Other;
+            case '\'':
+                yyCh = getChar();
+                if (yyCh == '\\')
+                    yyCh = getChar();
+
+                forever {
+                    if (yyCh == EOF || yyCh == '\n') {
+                        qWarning("%s:%d: Unterminated C++ character\n",
+                                  qPrintable(yyFileName), yyLineNo);
+                        break;
+                    }
+                    yyCh = getChar();
+                    if (yyCh == '\'') {
+                        yyCh = getChar();
+                        break;
+                    }
+                }
+                break;
+            case '{':
+                if (yyBraceDepth == 0)
+                    yyBraceLineNo = yyCurLineNo;
+                yyBraceDepth++;
+                yyCh = getChar();
+                return Tok_LeftBrace;
+            case '}':
+                if (yyBraceDepth == yyMinBraceDepth) {
+                    if (!inDefine)
+                        qWarning("%s:%d: Excess closing brace in C++ code"
+                                  " (or abuse of the C++ preprocessor)\n",
+                                  qPrintable(yyFileName), yyCurLineNo);
+                    // Avoid things getting messed up even more
+                    yyCh = getChar();
+                    return Tok_Semicolon;
+                }
+                yyBraceDepth--;
+                yyCh = getChar();
+                return Tok_RightBrace;
+            case '(':
+                if (yyParenDepth == 0)
+                    yyParenLineNo = yyCurLineNo;
+                yyParenDepth++;
+                yyCh = getChar();
+                return Tok_LeftParen;
+            case ')':
+                if (yyParenDepth == 0)
+                    qWarning("%s:%d: Excess closing parenthesis in C++ code"
+                             " (or abuse of the C++ preprocessor)\n",
+                             qPrintable(yyFileName), yyCurLineNo);
+                else
+                    yyParenDepth--;
+                yyCh = getChar();
+                return Tok_RightParen;
+            case ',':
+                yyCh = getChar();
+                return Tok_Comma;
+            case ';':
+                yyCh = getChar();
+                return Tok_Semicolon;
+            case '0':
+                yyCh = getChar();
+                if (yyCh == 'x') {
+                    do {
+                        yyCh = getChar();
+                    } while ((yyCh >= '0' && yyCh <= '9')
+                             || (yyCh >= 'a' && yyCh <= 'f') || (yyCh >= 'A' && yyCh <= 'F'));
+                    return Tok_Integer;
+                }
+                if (yyCh < '0' || yyCh > '9')
+                    return Tok_Null;
+                // Fallthrough
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                do {
+                    yyCh = getChar();
+                } while (yyCh >= '0' && yyCh <= '9');
+                return Tok_Integer;
+            default:
+                yyCh = getChar();
+                break;
+            }
+        }
+    }
+    return Tok_Eof;
+}
+
+/*
+  The second part of this source file are namespace/class related
+  utilities for the third part.
+*/
+
+void CppParser::saveState(SavedState *state)
+{
+    state->namespaces = namespaces;
+    state->namespaceDepths = namespaceDepths;
+    state->functionContext = functionContext;
+    state->functionContextUnresolved = functionContextUnresolved;
+    state->pendingContext = pendingContext;
+}
+
+void CppParser::loadState(const SavedState *state)
+{
+    namespaces = state->namespaces;
+    namespaceDepths = state->namespaceDepths;
+    functionContext = state->functionContext;
+    functionContextUnresolved = state->functionContextUnresolved;
+    pendingContext = state->pendingContext;
+}
+
+Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool tryOrigin)
+{
+    Namespace *pns, *ns = &results->rootNamespace;
+    for (int i = 1; i < namespaces->count(); ++i) {
+        pns = ns;
+        if (!(ns = pns->children.value(namespaces->at(i)))) {
+            do {
+                ns = new Namespace;
+                if (tryOrigin)
+                    if (const Namespace *ons = findNamespace(*namespaces, i + 1))
+                        ns->classDef = ons->classDef;
+                pns->children.insert(namespaces->at(i), ns);
+                pns = ns;
+            } while (++i < namespaces->count());
+            break;
+        }
+    }
+    return ns;
+}
+
+QString CppParser::stringifyNamespace(const NamespaceList &namespaces)
+{
+    QString ret;
+    for (int i = 1; i < namespaces.count(); ++i) {
+        if (i > 1)
+            ret += QLatin1String("::");
+        ret += namespaces.at(i).value();
+    }
+    return ret;
+}
+
+QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces)
+{
+    QStringList ret;
+    for (int i = 1; i < namespaces.count(); ++i)
+        ret << namespaces.at(i).value();
+    return ret;
+}
+
+bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount,
+                               VisitNamespaceCallback callback, void *context,
+                               VisitRecorder &vr, const ParseResults *rslt) const
+{
+    const Namespace *ns = &rslt->rootNamespace;
+    for (int i = 1; i < nsCount; ++i)
+        if (!(ns = ns->children.value(namespaces.at(i))))
+            goto supers;
+    if ((this->*callback)(ns, context))
+        return true;
+supers:
+    foreach (const ParseResults *sup, rslt->includes)
+        if (vr.tryVisit(sup->fileId)
+            && visitNamespace(namespaces, nsCount, callback, context, vr, sup))
+            return true;
+    return false;
+}
+
+bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount,
+                               VisitNamespaceCallback callback, void *context) const
+{
+    VisitRecorder vr;
+    return visitNamespace(namespaces, nsCount, callback, context, vr, results);
+}
+
+QStringList CppParser::stringListifySegments(const QList<HashString> &segments)
+{
+    QStringList ret;
+    for (int i = 0; i < segments.count(); ++i)
+        ret << segments.at(i).value();
+    return ret;
+}
+
+struct QualifyOneData {
+    QualifyOneData(const NamespaceList &ns, int nsc, const HashString &seg, NamespaceList *rslvd)
+        : namespaces(ns), nsCount(nsc), segment(seg), resolved(rslvd)
+    {}
+
+    const NamespaceList &namespaces;
+    int nsCount;
+    const HashString &segment;
+    NamespaceList *resolved;
+    QSet<HashStringList> visitedUsings;
+};
+
+bool CppParser::qualifyOneCallbackOwn(const Namespace *ns, void *context) const
+{
+    QualifyOneData *data = (QualifyOneData *)context;
+    if (ns->children.contains(data->segment)) {
+        *data->resolved = data->namespaces.mid(0, data->nsCount);
+        *data->resolved << data->segment;
+        return true;
+    }
+    QHash<HashString, NamespaceList>::ConstIterator nsai = ns->aliases.constFind(data->segment);
+    if (nsai != ns->aliases.constEnd()) {
+        *data->resolved = *nsai;
+        return true;
+    }
+    return false;
+}
+
+bool CppParser::qualifyOneCallbackUsing(const Namespace *ns, void *context) const
+{
+    QualifyOneData *data = (QualifyOneData *)context;
+    foreach (const HashStringList &use, ns->usings)
+        if (!data->visitedUsings.contains(use)) {
+            data->visitedUsings.insert(use);
+            if (qualifyOne(use.value(), use.value().count(), data->segment, data->resolved))
+                return true;
+        }
+    return false;
+}
+
+bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
+                           NamespaceList *resolved) const
+{
+    QualifyOneData data(namespaces, nsCnt, segment, resolved);
+
+    if (visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackOwn, &data))
+        return true;
+
+    return visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackUsing, &data);
+}
+
+bool CppParser::fullyQualify(const NamespaceList &namespaces, const QList<HashString> &segments,
+                             bool isDeclaration,
+                             NamespaceList *resolved, QStringList *unresolved) const
+{
+    int nsIdx;
+    int initSegIdx;
+
+    if (segments.first().value().isEmpty()) {
+        // fully qualified
+        if (segments.count() == 1) {
+            resolved->clear();
+            *resolved << HashString(QString());
+            return true;
+        }
+        initSegIdx = 1;
+        nsIdx = 0;
+    } else {
+        initSegIdx = 0;
+        nsIdx = namespaces.count() - 1;
+    }
+
+    do {
+        if (qualifyOne(namespaces, nsIdx + 1, segments[initSegIdx], resolved)) {
+            int segIdx = initSegIdx;
+            while (++segIdx < segments.count()) {
+                if (!qualifyOne(*resolved, resolved->count(), segments[segIdx], resolved)) {
+                    if (unresolved)
+                        *unresolved = stringListifySegments(segments.mid(segIdx));
+                    return false;
+                }
+            }
+            return true;
+        }
+    } while (!isDeclaration && --nsIdx >= 0);
+    resolved->clear();
+    *resolved << HashString(QString());
+    if (unresolved)
+        *unresolved = stringListifySegments(segments.mid(initSegIdx));
+    return false;
+}
+
+bool CppParser::fullyQualify(const NamespaceList &namespaces, const QString &quali,
+                             bool isDeclaration,
+                             NamespaceList *resolved, QStringList *unresolved) const
+{
+    static QString strColons(QLatin1String("::"));
+
+    QList<HashString> segments;
+    foreach (const QString &str, quali.split(strColons)) // XXX slow, but needs to be fast(?)
+        segments << HashString(str);
+    return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved);
+}
+
+bool CppParser::findNamespaceCallback(const Namespace *ns, void *context) const
+{
+    *((const Namespace **)context) = ns;
+    return true;
+}
+
+const Namespace *CppParser::findNamespace(const NamespaceList &namespaces, int nsCount) const
+{
+    const Namespace *ns = 0;
+    if (nsCount == -1)
+        nsCount = namespaces.count();
+    visitNamespace(namespaces, nsCount, &CppParser::findNamespaceCallback, &ns);
+    return ns;
+}
+
+void CppParser::enterNamespace(NamespaceList *namespaces, const HashString &name)
+{
+    *namespaces << name;
+    if (!findNamespace(*namespaces))
+        modifyNamespace(namespaces, false);
+}
+
+void CppParser::truncateNamespaces(NamespaceList *namespaces, int length)
+{
+    if (namespaces->count() > length)
+        namespaces->erase(namespaces->begin() + length, namespaces->end());
+}
+
+/*
+  Functions for processing include files.
+*/
+
+ParseResultHash &CppFiles::parsedFiles()
+{
+    static ParseResultHash parsed;
+
+    return parsed;
+}
+
+TranslatorHash &CppFiles::translatedFiles()
+{
+    static TranslatorHash tors;
+
+    return tors;
+}
+
+QSet<QString> &CppFiles::blacklistedFiles()
+{
+    static QSet<QString> blacklisted;
+
+    return blacklisted;
+}
+
+const ParseResults *CppFiles::getResults(const QString &cleanFile)
+{
+    return parsedFiles().value(cleanFile);
+}
+
+void CppFiles::setResults(const QString &cleanFile, const ParseResults *results)
+{
+    parsedFiles().insert(cleanFile, results);
+}
+
+const Translator *CppFiles::getTranslator(const QString &cleanFile)
+{
+    return translatedFiles().value(cleanFile);
+}
+
+void CppFiles::setTranslator(const QString &cleanFile, const Translator *tor)
+{
+    translatedFiles().insert(cleanFile, tor);
+}
+
+bool CppFiles::isBlacklisted(const QString &cleanFile)
+{
+    return blacklistedFiles().contains(cleanFile);
+}
+
+void CppFiles::setBlacklisted(const QString &cleanFile)
+{
+    blacklistedFiles().insert(cleanFile);
+}
+
+static bool isHeader(const QString &name)
+{
+    QString fileExt = QFileInfo(name).suffix();
+    return fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive);
+}
+
+void CppParser::processInclude(const QString &file, ConversionData &cd,
+                               QSet<QString> &inclusions)
+{
+    QString cleanFile = QDir::cleanPath(file);
+
+    if (inclusions.contains(cleanFile)) {
+        qWarning("%s:%d: circular inclusion of %s\n",
+                 qPrintable(yyFileName), yyLineNo, qPrintable(cleanFile));
+        return;
+    }
+
+    // If the #include is in any kind of namespace, has been blacklisted previously,
+    // or is not a header file (stdc++ extensionless or *.h*), then really include
+    // it. Otherwise it is safe to process it stand-alone and re-use the parsed
+    // namespace data for inclusion into other files.
+    bool isIndirect = false;
+    if (namespaces.count() == 1 && functionContext.count() == 1
+        && functionContextUnresolved.isEmpty() && pendingContext.isEmpty()
+        && !CppFiles::isBlacklisted(cleanFile)
+        && isHeader(cleanFile)) {
+
+        if (const ParseResults *res = CppFiles::getResults(cleanFile)) {
+            results->includes.insert(res);
+            return;
+        }
+
+        isIndirect = true;
+    }
+
+    QFile f(cleanFile);
+    if (!f.open(QIODevice::ReadOnly)) {
+        qWarning("%s:%d: Cannot open %s: %s\n",
+                 qPrintable(yyFileName), yyLineNo,
+                 qPrintable(cleanFile), qPrintable(f.errorString()));
+        return;
+    }
+
+    QTextStream ts(&f);
+    ts.setCodec(yySourceCodec);
+    ts.setAutoDetectUnicode(true);
+
+    inclusions.insert(cleanFile);
+    if (isIndirect) {
+        CppParser parser;
+        foreach (const QString &projectRoot, cd.m_projectRoots)
+            if (cleanFile.startsWith(projectRoot)) {
+                parser.setTranslator(new Translator);
+                break;
+            }
+        parser.setInput(ts, cleanFile);
+        parser.parse(cd.m_defaultContext, cd, inclusions);
+        results->includes.insert(parser.recordResults(true));
+    } else {
+        CppParser parser(results);
+        parser.namespaces = namespaces;
+        parser.functionContext = functionContext;
+        parser.functionContextUnresolved = functionContextUnresolved;
+        parser.pendingContext = pendingContext;
+        parser.setInput(ts, cleanFile);
+        parser.parseInternal(cd, inclusions);
+        // Avoid that messages obtained by direct scanning are used
+        CppFiles::setBlacklisted(cleanFile);
+    }
+    inclusions.remove(cleanFile);
+}
+
+/*
+  The third part of this source file is the parser. It accomplishes
+  a very easy task: It finds all strings inside a tr() or translate()
+  call, and possibly finds out the context of the call. It supports
+  three cases: (1) the context is specified, as in
+  FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello");
+  (2) the call appears within an inlined function; (3) the call
+  appears within a function defined outside the class definition.
+*/
+
+bool CppParser::match(uint t)
+{
+    bool matches = (yyTok == t);
+    if (matches)
+        yyTok = getToken();
+    return matches;
+}
+
+bool CppParser::matchString(QString *s)
+{
+    bool matches = false;
+    s->clear();
+    forever {
+        while (yyTok == Tok_Comment)
+            yyTok = getToken();
+        if (yyTok != Tok_String)
+            return matches;
+        matches = true;
+        *s += yyWord;
+        s->detach();
+        yyTok = getToken();
+    }
+}
+
+STRING(QApplication);
+STRING(QCoreApplication);
+STRING(UnicodeUTF8);
+STRING(DefaultCodec);
+STRING(CodecForTr);
+
+bool CppParser::matchEncoding(bool *utf8)
+{
+    if (yyTok != Tok_Ident)
+        return false;
+    if (yyWord == strQApplication || yyWord == strQCoreApplication) {
+        yyTok = getToken();
+        if (yyTok == Tok_ColonColon)
+            yyTok = getToken();
+    }
+    if (yyWord == strUnicodeUTF8) {
+        *utf8 = true;
+        yyTok = getToken();
+        return true;
+    }
+    if (yyWord == strDefaultCodec || yyWord == strCodecForTr) {
+        *utf8 = false;
+        yyTok = getToken();
+        return true;
+    }
+    return false;
+}
+
+bool CppParser::matchStringOrNull(QString *s)
+{
+    return matchString(s) || match(Tok_Null);
+}
+
+/*
+ * match any expression that can return a number, which can be
+ * 1. Literal number (e.g. '11')
+ * 2. simple identifier (e.g. 'm_count')
+ * 3. simple function call (e.g. 'size()' )
+ * 4. function call on an object (e.g. 'list.size()')
+ * 5. function call on an object (e.g. 'list->size()')
+ *
+ * Other cases:
+ * size(2,4)
+ * list().size()
+ * list(a,b).size(2,4)
+ * etc...
+ */
+bool CppParser::matchExpression()
+{
+    if (match(Tok_Null) || match(Tok_Integer))
+        return true;
+
+    int parenlevel = 0;
+    while (match(Tok_Ident) || parenlevel > 0) {
+        if (yyTok == Tok_RightParen) {
+            if (parenlevel == 0) break;
+            --parenlevel;
+            yyTok = getToken();
+        } else if (yyTok == Tok_LeftParen) {
+            yyTok = getToken();
+            if (yyTok == Tok_RightParen) {
+                yyTok = getToken();
+            } else {
+                ++parenlevel;
+            }
+        } else if (yyTok == Tok_Ident) {
+            continue;
+        } else if (yyTok == Tok_Arrow) {
+            yyTok = getToken();
+        } else if (parenlevel == 0) {
+            return false;
+        }
+    }
+    return true;
+}
+
+QString CppParser::transcode(const QString &str, bool utf8)
+{
+    static const char tab[] = "abfnrtv";
+    static const char backTab[] = "\a\b\f\n\r\t\v";
+    const QString in = (!utf8 || yySourceIsUnicode)
+        ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data());
+    QString out;
+
+    out.reserve(in.length());
+    for (int i = 0; i < in.length();) {
+        ushort c = in[i++].unicode();
+        if (c == '\\') {
+            if (i >= in.length())
+                break;
+            c = in[i++].unicode();
+
+            if (c == '\n')
+                continue;
+
+            if (c == 'x') {
+                QByteArray hex;
+                while (i < in.length() && isxdigit((c = in[i].unicode()))) {
+                    hex += c;
+                    i++;
+                }
+                out += hex.toUInt(0, 16);
+            } else if (c >= '0' && c < '8') {
+                QByteArray oct;
+                int n = 0;
+                oct += c;
+                while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') {
+                    i++;
+                    n++;
+                    oct += c;
+                }
+                out += oct.toUInt(0, 8);
+            } else {
+                const char *p = strchr(tab, c);
+                out += QChar(QLatin1Char(!p ? c : backTab[p - tab]));
+            }
+        } else {
+            out += c;
+        }
+    }
+    return out;
+}
+
+void CppParser::recordMessage(
+    int line, const QString &context, const QString &text, const QString &comment,
+    const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+    bool utf8, bool plural)
+{
+    TranslatorMessage msg(
+        transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(),
+        yyFileName, line, QStringList(),
+        TranslatorMessage::Unfinished, plural);
+    msg.setExtraComment(transcode(extracomment.simplified(), utf8));
+    msg.setId(msgid);
+    msg.setExtras(extra);
+    if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit())
+        msg.setUtf8(true);
+    tor->append(msg);
+}
+
+void CppParser::parse(const QString &initialContext, ConversionData &cd,
+                      QSet<QString> &inclusions)
+{
+    if (tor)
+        yyCodecIsUtf8 = (tor->codecName() == "UTF-8");
+
+    namespaces << HashString();
+    functionContext = namespaces;
+    functionContextUnresolved = initialContext;
+
+    parseInternal(cd, inclusions);
+}
+
+void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
+{
+    static QString strColons(QLatin1String("::"));
+
+    QString context;
+    QString text;
+    QString comment;
+    QString extracomment;
+    QString msgid;
+    QString sourcetext;
+    TranslatorMessage::ExtraData extra;
+    QString prefix;
+#ifdef DIAGNOSE_RETRANSLATABILITY
+    QString functionName;
+#endif
+    int line;
+    bool utf8;
+    bool yyTokColonSeen = false; // Start of c'tor's initializer list
+
+    yyWord.reserve(yyInStr.size()); // Rather insane. That's because we do no length checking.
+    yyInPtr = (const ushort *)yyInStr.unicode();
+    yyCh = getChar();
+    yyTok = getToken();
+    while (yyTok != Tok_Eof) {
+        //qDebug() << "TOKEN: " << yyTok;
+        switch (yyTok) {
+        case Tok_QuotedInclude: {
+            text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord);
+            text.detach();
+            if (QFileInfo(text).isFile()) {
+                processInclude(text, cd, inclusions);
+                yyTok = getToken();
+                break;
+            }
+        }
+        /* fall through */
+        case Tok_AngledInclude: {
+            QStringList cSources = cd.m_allCSources.values(yyWord);
+            if (!cSources.isEmpty()) {
+                foreach (const QString &cSource, cSources)
+                    processInclude(cSource, cd, inclusions);
+                goto incOk;
+            }
+            foreach (const QString &incPath, cd.m_includePath) {
+                text = QDir(incPath).absoluteFilePath(yyWord);
+                text.detach();
+                if (QFileInfo(text).isFile()) {
+                    processInclude(text, cd, inclusions);
+                    goto incOk;
+                }
+            }
+          incOk:
+            yyTok = getToken();
+            break;
+        }
+        case Tok_friend:
+            yyTok = getToken();
+            // These are forward declarations, so ignore them.
+            if (yyTok == Tok_class)
+                yyTok = getToken();
+            break;
+        case Tok_class:
+            yyTokColonSeen = false;
+            /*
+              Partial support for inlined functions.
+            */
+            yyTok = getToken();
+            if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) {
+                QList<HashString> quali;
+                HashString fct;
+                do {
+                    /*
+                      This code should execute only once, but we play
+                      safe with impure definitions such as
+                      'class Q_EXPORT QMessageBox', in which case
+                      'QMessageBox' is the class name, not 'Q_EXPORT'.
+                    */
+                    text = yyWord;
+                    text.detach();
+                    fct.setValue(text);
+                    yyTok = getToken();
+                } while (yyTok == Tok_Ident);
+                while (yyTok == Tok_ColonColon) {
+                    yyTok = getToken();
+                    if (yyTok != Tok_Ident)
+                        break; // Oops ...
+                    quali << fct;
+                    text = yyWord;
+                    text.detach();
+                    fct.setValue(text);
+                    yyTok = getToken();
+                }
+                while (yyTok == Tok_Comment)
+                    yyTok = getToken();
+                if (yyTok == Tok_Colon) {
+                    // Skip any token until '{' since we might do things wrong if we find
+                    // a '::' token here.
+                    do {
+                        yyTok = getToken();
+                    } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof);
+                } else {
+                    if (yyTok != Tok_LeftBrace) {
+                        // Obviously a forward declaration. We skip those, as they
+                        // don't create actually usable namespaces.
+                        break;
+                    }
+                }
+
+                if (!quali.isEmpty()) {
+                    // Forward-declared class definitions can be namespaced.
+                    NamespaceList nsl;
+                    if (!fullyQualify(namespaces, quali, true, &nsl, 0)) {
+                        qWarning("%s:%d: Ignoring definition of undeclared qualified class\n",
+                                 qPrintable(yyFileName), yyLineNo);
+                        break;
+                    }
+                    namespaceDepths.push(namespaces.count());
+                    namespaces = nsl;
+                } else {
+                    namespaceDepths.push(namespaces.count());
+                }
+                enterNamespace(&namespaces, fct);
+
+                functionContext = namespaces;
+                functionContextUnresolved.clear(); // Pointless
+                prospectiveContext.clear();
+                pendingContext.clear();
+            }
+            break;
+        case Tok_namespace:
+            yyTokColonSeen = false;
+            yyTok = getToken();
+            if (yyTok == Tok_Ident) {
+                text = yyWord;
+                text.detach();
+                HashString ns = HashString(text);
+                yyTok = getToken();
+                if (yyTok == Tok_LeftBrace) {
+                    namespaceDepths.push(namespaces.count());
+                    enterNamespace(&namespaces, ns);
+                    yyTok = getToken();
+                } else if (yyTok == Tok_Equals) {
+                    // e.g. namespace Is = OuterSpace::InnerSpace;
+                    QList<HashString> fullName;
+                    yyTok = getToken();
+                    if (yyTok == Tok_ColonColon)
+                        fullName.append(HashString(QString()));
+                    while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+                        if (yyTok == Tok_Ident) {
+                            text = yyWord;
+                            text.detach();
+                            fullName.append(HashString(text));
+                        }
+                        yyTok = getToken();
+                    }
+                    if (fullName.isEmpty())
+                        break;
+                    NamespaceList nsl;
+                    if (fullyQualify(namespaces, fullName, false, &nsl, 0))
+                        modifyNamespace(&namespaces, false)->aliases[ns] = nsl;
+                }
+            } else if (yyTok == Tok_LeftBrace) {
+                // Anonymous namespace
+                namespaceDepths.push(namespaces.count());
+                yyTok = getToken();
+            }
+            break;
+        case Tok_using:
+            yyTok = getToken();
+            // XXX this should affect only the current scope, not the entire current namespace
+            if (yyTok == Tok_namespace) {
+                QList<HashString> fullName;
+                yyTok = getToken();
+                if (yyTok == Tok_ColonColon)
+                    fullName.append(HashString(QString()));
+                while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+                    if (yyTok == Tok_Ident) {
+                        text = yyWord;
+                        text.detach();
+                        fullName.append(HashString(text));
+                    }
+                    yyTok = getToken();
+                }
+                NamespaceList nsl;
+                if (fullyQualify(namespaces, fullName, false, &nsl, 0))
+                    modifyNamespace(&namespaces, false)->usings << HashStringList(nsl);
+            } else {
+                QList<HashString> fullName;
+                if (yyTok == Tok_ColonColon)
+                    fullName.append(HashString(QString()));
+                while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+                    if (yyTok == Tok_Ident) {
+                        text = yyWord;
+                        text.detach();
+                        fullName.append(HashString(text));
+                    }
+                    yyTok = getToken();
+                }
+                if (fullName.isEmpty())
+                    break;
+                NamespaceList nsl;
+                if (fullyQualify(namespaces, fullName, false, &nsl, 0))
+                    modifyNamespace(&namespaces, true)->aliases[nsl.last()] = nsl;
+            }
+            break;
+        case Tok_tr:
+        case Tok_trUtf8:
+            if (!tor)
+                goto case_default;
+            if (!sourcetext.isEmpty())
+                qWarning("%s:%d: //%% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n",
+                         qPrintable(yyFileName), yyLineNo);
+            utf8 = (yyTok == Tok_trUtf8);
+            line = yyLineNo;
+            yyTok = getToken();
+            if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) {
+                comment.clear();
+                bool plural = false;
+
+                if (match(Tok_RightParen)) {
+                    // no comment
+                } else if (match(Tok_Comma) && matchStringOrNull(&comment)) {   //comment
+                    if (match(Tok_RightParen)) {
+                        // ok,
+                    } else if (match(Tok_Comma)) {
+                        plural = true;
+                    }
+                }
+                if (!pendingContext.isEmpty()) {
+                    QStringList unresolved;
+                    if (!fullyQualify(namespaces, pendingContext, true, &functionContext, &unresolved)) {
+                        functionContextUnresolved = unresolved.join(strColons);
+                        qWarning("%s:%d: Qualifying with unknown namespace/class %s::%s\n",
+                                 qPrintable(yyFileName), yyLineNo,
+                                 qPrintable(stringifyNamespace(functionContext)),
+                                 qPrintable(unresolved.first()));
+                    }
+                    pendingContext.clear();
+                }
+                if (prefix.isEmpty()) {
+                    if (functionContextUnresolved.isEmpty()) {
+                        int idx = functionContext.length();
+                        if (idx < 2) {
+                            qWarning("%s:%d: tr() cannot be called without context\n",
+                                     qPrintable(yyFileName), yyLineNo);
+                            break;
+                        }
+                        Namespace *fctx;
+                        while (!(fctx = findNamespace(functionContext, idx)->classDef)->hasTrFunctions) {
+                            if (idx == 1) {
+                                context = stringifyNamespace(functionContext);
+                                fctx = findNamespace(functionContext)->classDef;
+                                if (!fctx->complained) {
+                                    qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n",
+                                             qPrintable(yyFileName), yyLineNo,
+                                             qPrintable(context));
+                                    fctx->complained = true;
+                                }
+                                goto gotctx;
+                            }
+                            --idx;
+                        }
+                        if (fctx->trQualification.isEmpty()) {
+                            context.clear();
+                            for (int i = 1;;) {
+                                context += functionContext.at(i).value();
+                                if (++i == idx)
+                                    break;
+                                context += strColons;
+                            }
+                            fctx->trQualification = context;
+                        } else {
+                            context = fctx->trQualification;
+                        }
+                    } else {
+                        context = (stringListifyNamespace(functionContext)
+                                   << functionContextUnresolved).join(strColons);
+                    }
+                } else {
+#ifdef DIAGNOSE_RETRANSLATABILITY
+                    int last = prefix.lastIndexOf(strColons);
+                    QString className = prefix.mid(last == -1 ? 0 : last + 2);
+                    if (!className.isEmpty() && className == functionName) {
+                        qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ",
+                                  qPrintable(yyFileName), yyLineNo,
+                                  className.constData(), functionName.constData());
+                    }
+#endif
+                    prefix.chop(2);
+                    NamespaceList nsl;
+                    QStringList unresolved;
+                    if (fullyQualify(functionContext, prefix, false, &nsl, &unresolved)) {
+                        Namespace *fctx = findNamespace(nsl)->classDef;
+                        if (fctx->trQualification.isEmpty()) {
+                            context = stringifyNamespace(nsl);
+                            fctx->trQualification = context;
+                        } else {
+                            context = fctx->trQualification;
+                        }
+                        if (!fctx->hasTrFunctions && !fctx->complained) {
+                            qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n",
+                                     qPrintable(yyFileName), yyLineNo,
+                                     qPrintable(context));
+                            fctx->complained = true;
+                        }
+                    } else {
+                        context = (stringListifyNamespace(nsl) + unresolved).join(strColons);
+                    }
+                    prefix.clear();
+                }
+
+              gotctx:
+                recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural);
+            }
+            extracomment.clear();
+            msgid.clear();
+            extra.clear();
+            break;
+        case Tok_translateUtf8:
+        case Tok_translate:
+            if (!tor)
+                goto case_default;
+            if (!sourcetext.isEmpty())
+                qWarning("%s:%d: //%% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n",
+                         qPrintable(yyFileName), yyLineNo);
+            utf8 = (yyTok == Tok_translateUtf8);
+            line = yyLineNo;
+            yyTok = getToken();
+            if (match(Tok_LeftParen)
+                && matchString(&context)
+                && match(Tok_Comma)
+                && matchString(&text) && !text.isEmpty())
+            {
+                comment.clear();
+                bool plural = false;
+                if (!match(Tok_RightParen)) {
+                    // look for comment
+                    if (match(Tok_Comma) && matchStringOrNull(&comment)) {
+                        if (!match(Tok_RightParen)) {
+                            // look for encoding
+                            if (match(Tok_Comma)) {
+                                if (matchEncoding(&utf8)) {
+                                    if (!match(Tok_RightParen)) {
+                                        // look for the plural quantifier,
+                                        // this can be a number, an identifier or
+                                        // a function call,
+                                        // so for simplicity we mark it as plural if
+                                        // we know we have a comma instead of an
+                                        // right parentheses.
+                                        plural = match(Tok_Comma);
+                                    }
+                                } else {
+                                    // This can be a QTranslator::translate("context",
+                                    // "source", "comment", n) plural translation
+                                    if (matchExpression() && match(Tok_RightParen)) {
+                                        plural = true;
+                                    } else {
+                                        break;
+                                    }
+                                }
+                            } else {
+                                break;
+                            }
+                        }
+                    } else {
+                        break;
+                    }
+                }
+                recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural);
+            }
+            extracomment.clear();
+            msgid.clear();
+            extra.clear();
+            break;
+        case Tok_trid:
+            if (!tor)
+                goto case_default;
+            if (sourcetext.isEmpty()) {
+                yyTok = getToken();
+            } else {
+                if (!msgid.isEmpty())
+                    qWarning("%s:%d: //= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n",
+                             qPrintable(yyFileName), yyLineNo);
+                //utf8 = false; // Maybe use //%% or something like that
+                line = yyLineNo;
+                yyTok = getToken();
+                if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) {
+                    bool plural = match(Tok_Comma);
+                    recordMessage(line, QString(), sourcetext, QString(), extracomment,
+                                  msgid, extra, false, plural);
+                }
+                sourcetext.clear();
+            }
+            extracomment.clear();
+            msgid.clear();
+            extra.clear();
+            break;
+        case Tok_Q_DECLARE_TR_FUNCTIONS:
+            if (getMacroArgs()) {
+                Namespace *ns = modifyNamespace(&namespaces, true);
+                ns->hasTrFunctions = true;
+                ns->trQualification = yyWord;
+                ns->trQualification.detach();
+            }
+            yyTok = getToken();
+            break;
+        case Tok_Q_OBJECT:
+            modifyNamespace(&namespaces, true)->hasTrFunctions = true;
+            yyTok = getToken();
+            break;
+        case Tok_Ident:
+            prefix += yyWord;
+            prefix.detach();
+            yyTok = getToken();
+            if (yyTok != Tok_ColonColon) {
+                prefix.clear();
+                if (yyTok == Tok_Ident && !yyParenDepth)
+                    prospectiveContext.clear();
+            }
+            break;
+        case Tok_Comment:
+            if (!tor)
+                goto case_default;
+            if (yyWord.startsWith(QLatin1Char(':'))) {
+                yyWord.remove(0, 1);
+                extracomment += yyWord;
+                extracomment.detach();
+            } else if (yyWord.startsWith(QLatin1Char('='))) {
+                yyWord.remove(0, 1);
+                msgid = yyWord.simplified();
+                msgid.detach();
+            } else if (yyWord.startsWith(QLatin1Char('~'))) {
+                yyWord.remove(0, 1);
+                text = yyWord.trimmed();
+                int k = text.indexOf(QLatin1Char(' '));
+                if (k > -1)
+                    extra.insert(text.left(k), text.mid(k + 1).trimmed());
+                text.clear();
+            } else if (yyWord.startsWith(QLatin1Char('%'))) {
+                sourcetext.reserve(sourcetext.length() + yyWord.length());
+                ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length();
+                int p = 1, c;
+                forever {
+                    if (p >= yyWord.length())
+                        break;
+                    c = yyWord.unicode()[p++].unicode();
+                    if (isspace(c))
+                        continue;
+                    if (c != '"') {
+                        qWarning("%s:%d: Unexpected character in meta string\n",
+                                 qPrintable(yyFileName), yyLineNo);
+                        break;
+                    }
+                    forever {
+                        if (p >= yyWord.length()) {
+                          whoops:
+                            qWarning("%s:%d: Unterminated meta string\n",
+                                     qPrintable(yyFileName), yyLineNo);
+                            break;
+                        }
+                        c = yyWord.unicode()[p++].unicode();
+                        if (c == '"')
+                            break;
+                        if (c == '\\') {
+                            if (p >= yyWord.length())
+                                goto whoops;
+                            c = yyWord.unicode()[p++].unicode();
+                            if (c == '\n')
+                                goto whoops;
+                            *ptr++ = '\\';
+                        }
+                        *ptr++ = c;
+                    }
+                }
+                sourcetext.resize(ptr - (ushort *)sourcetext.data());
+            } else {
+                const ushort *uc = (const ushort *)yyWord.unicode(); // Is zero-terminated
+                int idx = 0;
+                ushort c;
+                while ((c = uc[idx]) == ' ' || c == '\t' || c == '\n')
+                    ++idx;
+                if (!memcmp(uc + idx, MagicComment.unicode(), MagicComment.length() * 2)) {
+                    idx += MagicComment.length();
+                    comment = QString::fromRawData(yyWord.unicode() + idx,
+                                                   yyWord.length() - idx).simplified();
+                    int k = comment.indexOf(QLatin1Char(' '));
+                    if (k == -1) {
+                        context = comment;
+                    } else {
+                        context = comment.left(k);
+                        comment.remove(0, k + 1);
+                        recordMessage(yyLineNo, context, QString(), comment, extracomment,
+                                      QString(), TranslatorMessage::ExtraData(), false, false);
+                        extracomment.clear();
+                        tor->setExtras(extra);
+                        extra.clear();
+                    }
+                }
+            }
+            yyTok = getToken();
+            break;
+        case Tok_Arrow:
+            yyTok = getToken();
+            if (yyTok == Tok_tr || yyTok == Tok_trUtf8)
+                qWarning("%s:%d: Cannot invoke tr() like this\n",
+                          qPrintable(yyFileName), yyLineNo);
+            break;
+        case Tok_ColonColon:
+            if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen)
+                prospectiveContext = prefix;
+            prefix += strColons;
+            yyTok = getToken();
+#ifdef DIAGNOSE_RETRANSLATABILITY
+            if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) {
+                functionName = yyWord;
+                functionName.detach();
+            }
+#endif
+            break;
+        case Tok_RightBrace:
+            if (yyBraceDepth + 1 == namespaceDepths.count()) // class or namespace
+                truncateNamespaces(&namespaces, namespaceDepths.pop());
+            if (yyBraceDepth == namespaceDepths.count()) {
+                // function, class or namespace
+                if (!yyBraceDepth && !directInclude) {
+                    truncateNamespaces(&functionContext, 1);
+                    functionContextUnresolved = cd.m_defaultContext;
+                } else {
+                    functionContext = namespaces;
+                    functionContextUnresolved.clear();
+                }
+                pendingContext.clear();
+            }
+            // fallthrough
+        case Tok_Semicolon:
+            prospectiveContext.clear();
+            prefix.clear();
+            extracomment.clear();
+            msgid.clear();
+            extra.clear();
+            yyTokColonSeen = false;
+            yyTok = getToken();
+            break;
+        case Tok_Colon:
+            if (!prospectiveContext.isEmpty()
+                && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0)
+                pendingContext = prospectiveContext;
+            yyTokColonSeen = true;
+            yyTok = getToken();
+            break;
+        case Tok_LeftBrace:
+            if (!prospectiveContext.isEmpty()
+                && yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0)
+                pendingContext = prospectiveContext;
+            // fallthrough
+        case Tok_LeftParen:
+        case Tok_RightParen:
+            yyTokColonSeen = false;
+            yyTok = getToken();
+            break;
+        default:
+            if (!yyParenDepth)
+                prospectiveContext.clear();
+        case_default:
+            yyTok = getToken();
+            break;
+        }
+    }
+
+    if (yyBraceDepth != 0)
+        qWarning("%s:%d: Unbalanced opening brace in C++ code"
+                  " (or abuse of the C++ preprocessor)\n",
+                  qPrintable(yyFileName), yyBraceLineNo);
+    else if (yyParenDepth != 0)
+        qWarning("%s:%d: Unbalanced opening parenthesis in C++ code"
+                 " (or abuse of the C++ preprocessor)\n",
+                 qPrintable(yyFileName), yyParenLineNo);
+}
+
+const ParseResults *CppParser::recordResults(bool isHeader)
+{
+    if (tor) {
+        if (tor->messageCount()) {
+            CppFiles::setTranslator(yyFileName, tor);
+        } else {
+            delete tor;
+            tor = 0;
+        }
+    }
+    if (isHeader) {
+        const ParseResults *pr;
+        if (!tor && results->includes.count() == 1
+            && results->rootNamespace.children.isEmpty()
+            && results->rootNamespace.aliases.isEmpty()
+            && results->rootNamespace.usings.isEmpty()) {
+            // This is a forwarding header. Slash it.
+            pr = *results->includes.begin();
+            delete results;
+        } else {
+            results->fileId = nextFileId++;
+            pr = results;
+        }
+        CppFiles::setResults(yyFileName, pr);
+        return pr;
+    } else {
+        delete results;
+        return 0;
+    }
+}
+
+/*
+  Fetches tr() calls in C++ code in UI files (inside "<function>"
+  tag). This mechanism is obsolete.
+*/
+void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context)
+{
+    CppParser parser;
+    parser.setInput(in);
+    ConversionData cd;
+    QSet<QString> inclusions;
+    parser.setTranslator(&translator);
+    parser.parse(context, cd, inclusions);
+    parser.deleteResults();
+}
+
+void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd)
+{
+    QByteArray codecName = cd.m_codecForSource.isEmpty()
+                            ? translator.codecName() : cd.m_codecForSource;
+    QTextCodec *codec = QTextCodec::codecForName(codecName);
+
+    foreach (const QString &filename, filenames) {
+        if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename))
+            continue;
+
+        QFile file(filename);
+        if (!file.open(QIODevice::ReadOnly)) {
+            cd.appendError(QString::fromLatin1("Cannot open %1: %2")
+                .arg(filename, file.errorString()));
+            continue;
+        }
+
+        CppParser parser;
+        QTextStream ts(&file);
+        ts.setCodec(codec);
+        ts.setAutoDetectUnicode(true);
+        if (ts.codec()->name() == "UTF-16")
+            translator.setCodecName("System");
+        parser.setInput(ts, filename);
+        Translator *tor = new Translator;
+        tor->setCodecName(translator.codecName());
+        parser.setTranslator(tor);
+        QSet<QString> inclusions;
+        parser.parse(cd.m_defaultContext, cd, inclusions);
+        parser.recordResults(isHeader(filename));
+    }
+
+    foreach (const QString &filename, filenames)
+        if (!CppFiles::isBlacklisted(filename))
+            if (const Translator *tor = CppFiles::getTranslator(filename))
+                foreach (const TranslatorMessage &msg, tor->messages())
+                    translator.extend(msg);
+}
+
+QT_END_NAMESPACE