summaryrefslogtreecommitdiffstats
path: root/tools/linguist/shared/cpp.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/linguist/shared/cpp.cpp
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'tools/linguist/shared/cpp.cpp')
-rw-r--r--tools/linguist/shared/cpp.cpp1074
1 files changed, 1074 insertions, 0 deletions
diff --git a/tools/linguist/shared/cpp.cpp b/tools/linguist/shared/cpp.cpp
new file mode 100644
index 0000000..28616cc
--- /dev/null
+++ b/tools/linguist/shared/cpp.cpp
@@ -0,0 +1,1074 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translator.h"
+
+#include <QtCore/QDebug>
+#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 const char MagicComment[] = "TRANSLATOR ";
+
+static QSet<QString> needs_Q_OBJECT;
+static QSet<QString> lacks_Q_OBJECT;
+
+static const int yyIdentMaxLen = 128;
+static const int yyCommentMaxLen = 65536;
+static const int yyStringMaxLen = 65536;
+
+#define STRINGIFY_INTERNAL(x) #x
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
+
+//#define DIAGNOSE_RETRANSLATABILITY
+/*
+ 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.
+*/
+
+enum {
+ Tok_Eof, Tok_class, Tok_namespace, Tok_return,
+ Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8,
+ 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_Integer = 40,
+ Tok_Other
+};
+
+/*
+ The tokenizer maintains the following global variables. The names
+ should be self-explanatory.
+*/
+static QString yyFileName;
+static int yyCh;
+static bool yyCodecIsUtf8;
+static bool yyForceUtf8;
+static QString yyIdent;
+static QString yyComment;
+static QString yyString;
+static qlonglong yyInteger;
+static QStack<int> yySavedBraceDepth;
+static QStack<int> yySavedParenDepth;
+static int yyBraceDepth;
+static int yyParenDepth;
+static int yyLineNo;
+static int yyCurLineNo;
+static int yyBraceLineNo;
+static int yyParenLineNo;
+static bool yyTokColonSeen = false;
+
+// the string to read from and current position in the string
+static QTextCodec *yySourceCodec;
+static bool yySourceIsUnicode;
+static QString yyInStr;
+static int yyInPos;
+
+static uint getChar()
+{
+ if (yyInPos >= yyInStr.size())
+ return EOF;
+ QChar c = yyInStr[yyInPos++];
+ if (c.unicode() == '\n')
+ ++yyCurLineNo;
+ return c.unicode();
+}
+
+static uint getToken()
+{
+ yyIdent.clear();
+ yyComment.clear();
+ yyString.clear();
+
+ while (yyCh != EOF) {
+ yyLineNo = yyCurLineNo;
+
+ if (isalpha(yyCh) || yyCh == '_') {
+ do {
+ yyIdent += yyCh;
+ yyCh = getChar();
+ } while (isalnum(yyCh) || yyCh == '_');
+
+ //qDebug() << "IDENT: " << yyIdent;
+
+ switch (yyIdent.at(0).unicode()) {
+ case 'Q':
+ if (yyIdent == QLatin1String("Q_OBJECT"))
+ return Tok_Q_OBJECT;
+ if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS"))
+ return Tok_Q_DECLARE_TR_FUNCTIONS;
+ if (yyIdent == QLatin1String("QT_TR_NOOP"))
+ return Tok_tr;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP"))
+ return Tok_translate;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3"))
+ return Tok_translate;
+ if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8"))
+ return Tok_trUtf8;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8"))
+ return Tok_translateUtf8;
+ if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8"))
+ return Tok_translateUtf8;
+ break;
+ case 'T':
+ // TR() for when all else fails
+ if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) {
+ return Tok_tr;
+ }
+ break;
+ case 'c':
+ if (yyIdent == QLatin1String("class"))
+ return Tok_class;
+ break;
+ case 'f':
+ /*
+ QTranslator::findMessage() has the same parameters as
+ QApplication::translate().
+ */
+ if (yyIdent == QLatin1String("findMessage"))
+ return Tok_translate;
+ break;
+ case 'n':
+ if (yyIdent == QLatin1String("namespace"))
+ return Tok_namespace;
+ break;
+ case 'r':
+ if (yyIdent == QLatin1String("return"))
+ return Tok_return;
+ break;
+ case 's':
+ if (yyIdent == QLatin1String("struct"))
+ return Tok_class;
+ break;
+ case 't':
+ if (yyIdent == QLatin1String("tr")) {
+ return Tok_tr;
+ }
+ if (yyIdent == QLatin1String("trUtf8")) {
+ return Tok_trUtf8;
+ }
+ if (yyIdent == QLatin1String("translate")) {
+ return Tok_translate;
+ }
+ }
+ return Tok_Ident;
+ } else {
+ switch (yyCh) {
+ case '#':
+ /*
+ 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 'i':
+ yyCh = getChar();
+ if (yyCh == 'f') {
+ // if, ifdef, ifndef
+ yySavedBraceDepth.push(yyBraceDepth);
+ yySavedParenDepth.push(yyParenDepth);
+ }
+ break;
+ case 'e':
+ yyCh = getChar();
+ if (yyCh == 'l') {
+ // elif, else
+ if (!yySavedBraceDepth.isEmpty()) {
+ yyBraceDepth = yySavedBraceDepth.top();
+ yyParenDepth = yySavedParenDepth.top();
+ }
+ } else if (yyCh == 'n') {
+ // endif
+ if (!yySavedBraceDepth.isEmpty()) {
+ yySavedBraceDepth.pop();
+ yySavedParenDepth.pop();
+ }
+ }
+ }
+ while (isalnum(yyCh) || yyCh == '_')
+ yyCh = getChar();
+ break;
+ case '/':
+ yyCh = getChar();
+ if (yyCh == '/') {
+ do {
+ yyCh = getChar();
+ if (yyCh == EOF)
+ break;
+ yyComment.append(yyCh);
+ } while (yyCh != '\n');
+ } else if (yyCh == '*') {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ while (!metAsterSlash) {
+ yyCh = getChar();
+ if (yyCh == EOF) {
+ qWarning("%s: Unterminated C++ comment starting at"
+ " line %d\n",
+ qPrintable(yyFileName), yyLineNo);
+ return Tok_Comment;
+ }
+ yyComment.append(yyCh);
+
+ if (yyCh == '*')
+ metAster = true;
+ else if (metAster && yyCh == '/')
+ metAsterSlash = true;
+ else
+ metAster = false;
+ }
+ yyCh = getChar();
+ yyComment.chop(2);
+ }
+ return Tok_Comment;
+ case '"':
+ yyCh = getChar();
+ while (yyCh != EOF && yyCh != '\n' && yyCh != '"') {
+ if (yyCh == '\\') {
+ yyCh = getChar();
+ if (yyString.size() < yyStringMaxLen) {
+ yyString.append(QLatin1Char('\\'));
+ yyString.append(yyCh);
+ }
+ } else {
+ if (yyString.size() < yyStringMaxLen)
+ yyString.append(yyCh);
+ }
+ yyCh = getChar();
+ }
+
+ if (yyCh != '"')
+ qWarning("%s:%d: Unterminated C++ string",
+ qPrintable(yyFileName), yyLineNo);
+
+ if (yyCh == EOF)
+ return Tok_Eof;
+ 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();
+
+ do {
+ yyCh = getChar();
+ } while (yyCh != EOF && yyCh != '\'');
+ yyCh = getChar();
+ break;
+ case '{':
+ if (yyBraceDepth == 0)
+ yyBraceLineNo = yyCurLineNo;
+ yyBraceDepth++;
+ yyCh = getChar();
+ return Tok_LeftBrace;
+ case '}':
+ if (yyBraceDepth == 0)
+ yyBraceLineNo = yyCurLineNo;
+ yyBraceDepth--;
+ yyCh = getChar();
+ return Tok_RightBrace;
+ case '(':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth--;
+ yyCh = getChar();
+ return Tok_RightParen;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case ';':
+ yyCh = getChar();
+ return Tok_Semicolon;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ QByteArray ba;
+ ba += yyCh;
+ yyCh = getChar();
+ bool hex = yyCh == 'x';
+ if (hex) {
+ ba += yyCh;
+ yyCh = getChar();
+ }
+ while (hex ? isxdigit(yyCh) : isdigit(yyCh)) {
+ ba += yyCh;
+ yyCh = getChar();
+ }
+ bool ok;
+ yyInteger = ba.toLongLong(&ok);
+ if (ok)
+ return Tok_Integer;
+ break;
+ }
+ default:
+ yyCh = getChar();
+ break;
+ }
+ }
+ }
+ return Tok_Eof;
+}
+
+/*
+ The second 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.
+*/
+
+static uint yyTok;
+
+static bool match(uint t)
+{
+ bool matches = (yyTok == t);
+ if (matches)
+ yyTok = getToken();
+ return matches;
+}
+
+static bool matchString(QString *s)
+{
+ bool matches = (yyTok == Tok_String);
+ s->clear();
+ while (yyTok == Tok_String) {
+ *s += yyString;
+ yyTok = getToken();
+ }
+ return matches;
+}
+
+static bool matchEncoding(bool *utf8)
+{
+ STRING(QApplication);
+ STRING(QCoreApplication);
+ STRING(UnicodeUTF8);
+ STRING(DefaultCodec);
+ STRING(CodecForTr);
+
+ if (yyTok != Tok_Ident)
+ return false;
+ if (yyIdent == strQApplication || yyIdent == strQCoreApplication) {
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ yyTok = getToken();
+ }
+ if (yyIdent == strUnicodeUTF8) {
+ *utf8 = true;
+ yyTok = getToken();
+ return true;
+ }
+ if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) {
+ *utf8 = false;
+ yyTok = getToken();
+ return true;
+ }
+ return false;
+}
+
+static bool matchInteger(qlonglong *number)
+{
+ bool matches = (yyTok == Tok_Integer);
+ if (matches) {
+ yyTok = getToken();
+ *number = yyInteger;
+ }
+ return matches;
+}
+
+static bool matchStringOrNull(QString *s)
+{
+ bool matches = matchString(s);
+ qlonglong num = 0;
+ if (!matches)
+ matches = matchInteger(&num);
+ return matches && num == 0;
+}
+
+/*
+ * 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...
+ */
+static bool matchExpression()
+{
+ if (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;
+}
+
+static QStringList resolveNamespaces(
+ const QStringList &namespaces, const QHash<QString, QStringList> &namespaceAliases)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QStringList ns;
+ foreach (const QString &cns, namespaces) {
+ ns << cns;
+ ns = namespaceAliases.value(ns.join(strColons), ns);
+ }
+ return ns;
+}
+
+static QStringList getFullyQualifiedNamespaceName(
+ const QSet<QString> &allNamespaces, const QStringList &namespaces,
+ const QHash<QString, QStringList> &namespaceAliases,
+ const QStringList &segments)
+{
+ static QString strColons(QLatin1String("::"));
+
+ if (segments.first().isEmpty()) {
+ // fully qualified
+ QStringList segs = segments;
+ segs.removeFirst();
+ return resolveNamespaces(segs, namespaceAliases);
+ } else {
+ for (int n = namespaces.count(); --n >= -1; ) {
+ QStringList ns;
+ for (int i = 0; i <= n; ++i) // Note: n == -1 possible
+ ns << namespaces[i];
+ foreach (const QString &cns, segments) {
+ ns << cns;
+ ns = namespaceAliases.value(ns.join(strColons), ns);
+ }
+ if (allNamespaces.contains(ns.join(strColons)))
+ return ns;
+ }
+
+ // Fallback when the namespace was declared in a header, etc.
+ QStringList ns = namespaces;
+ ns += segments;
+ return ns;
+ }
+}
+
+static QString getFullyQualifiedClassName(
+ const QSet<QString> &allClasses, const QStringList &namespaces,
+ const QHash<QString, QStringList> &namespaceAliases,
+ const QString &ident, bool hasPrefix)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QString context = ident;
+ QStringList segments = context.split(strColons);
+ if (segments.first().isEmpty()) {
+ // fully qualified
+ segments.removeFirst();
+ context = resolveNamespaces(segments, namespaceAliases).join(strColons);
+ } else {
+ for (int n = namespaces.count(); --n >= -1; ) {
+ QStringList ns;
+ for (int i = 0; i <= n; ++i) // Note: n == -1 possible
+ ns.append(namespaces[i]);
+ foreach (const QString &cns, segments) {
+ ns.append(cns);
+ ns = namespaceAliases.value(ns.join(strColons), ns);
+ }
+ QString nctx = ns.join(strColons);
+ if (allClasses.contains(nctx)) {
+ context = nctx;
+ goto gotit;
+ }
+ }
+
+ if (!hasPrefix && namespaces.count())
+ context = namespaces.join(strColons) + strColons + context;
+ }
+gotit:
+ //qDebug() << "CLASSES:" << allClasses << "NAMEPACES:" << namespaces
+ // << "IDENT:" << ident << "CONTEXT:" << context;
+ return context;
+}
+
+
+static QString 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;
+}
+
+static void recordMessage(
+ Translator *tor, int line, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, 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));
+ if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit())
+ msg.setUtf8(true);
+ tor->extend(msg);
+}
+
+static void parse(Translator *tor, const QString &initialContext, const QString &defaultContext)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QMap<QString, QString> qualifiedContexts;
+ QSet<QString> allClasses;
+ QSet<QString> allNamespaces;
+ QHash<QString, QStringList> namespaceAliases;
+ QStringList namespaces;
+ QString context;
+ QString text;
+ QString comment;
+ QString extracomment;
+ QString functionContext = initialContext;
+ QString prefix;
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ QString functionName;
+#endif
+ int line;
+ bool utf8 = false;
+ bool missing_Q_OBJECT = false;
+
+ yyTok = getToken();
+ while (yyTok != Tok_Eof) {
+ //qDebug() << "TOKEN: " << yyTok;
+ switch (yyTok) {
+ case Tok_class:
+ yyTokColonSeen = false;
+ /*
+ Partial support for inlined functions.
+ */
+ yyTok = getToken();
+ if (yyBraceDepth == namespaces.count() && yyParenDepth == 0) {
+ QStringList 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'.
+ */
+ fct = QStringList(yyIdent);
+ yyTok = getToken();
+ } while (yyTok == Tok_Ident);
+ while (yyTok == Tok_ColonColon) {
+ yyTok = getToken();
+ if (yyTok != Tok_Ident)
+ break; // Oops ...
+ fct += yyIdent;
+ yyTok = getToken();
+ }
+ functionContext = resolveNamespaces(namespaces + fct, namespaceAliases).join(strColons);
+ allClasses.insert(functionContext);
+
+ if (yyTok == Tok_Colon) {
+ missing_Q_OBJECT = true;
+ // Skip any token until '{' since lupdate might do things wrong if it finds
+ // a '::' token here.
+ do {
+ yyTok = getToken();
+ } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof);
+ } else {
+ //functionContext = defaultContext;
+ }
+ }
+ break;
+ case Tok_namespace:
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ if (yyTok == Tok_Ident) {
+ QString ns = yyIdent;
+ yyTok = getToken();
+ if (yyTok == Tok_LeftBrace) {
+ if (yyBraceDepth == namespaces.count() + 1) {
+ namespaces.append(ns);
+ allNamespaces.insert(namespaces.join(strColons));
+ }
+ } else if (yyTok == Tok_Equals) {
+ // e.g. namespace Is = OuterSpace::InnerSpace;
+ QStringList alias = namespaces;
+ alias.append(ns);
+ QStringList fullName;
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ fullName.append(QString());
+ while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+ if (yyTok == Tok_Ident)
+ fullName.append(yyIdent);
+ yyTok = getToken();
+ }
+ namespaceAliases[alias.join(strColons)] =
+ getFullyQualifiedNamespaceName(allNamespaces, namespaces, namespaceAliases, fullName);
+ }
+ }
+ break;
+ case Tok_tr:
+ case Tok_trUtf8:
+ 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 (prefix.isEmpty()) {
+ context = functionContext;
+ } 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);
+ context = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, true);
+ }
+ prefix.clear();
+ if (qualifiedContexts.contains(context))
+ context = qualifiedContexts[context];
+
+ if (!text.isEmpty())
+ recordMessage(tor, line, context, text, comment, extracomment, utf8, plural);
+
+ if (lacks_Q_OBJECT.contains(context)) {
+ qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro",
+ qPrintable(yyFileName), yyLineNo,
+ qPrintable(context));
+ lacks_Q_OBJECT.remove(context);
+ } else {
+ needs_Q_OBJECT.insert(context);
+ }
+ }
+ extracomment.clear();
+ break;
+ case Tok_translateUtf8:
+ case Tok_translate:
+ utf8 = (yyTok == Tok_translateUtf8);
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen)
+ && matchString(&context)
+ && match(Tok_Comma)
+ && matchString(&text))
+ {
+ 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;
+ }
+ }
+ if (!text.isEmpty())
+ recordMessage(tor, line, context, text, comment, extracomment, utf8, plural);
+ }
+ extracomment.clear();
+ break;
+ case Tok_Q_DECLARE_TR_FUNCTIONS:
+ case Tok_Q_OBJECT:
+ missing_Q_OBJECT = false;
+ yyTok = getToken();
+ break;
+ case Tok_Ident:
+ prefix += yyIdent;
+ yyTok = getToken();
+ if (yyTok != Tok_ColonColon)
+ prefix.clear();
+ break;
+ case Tok_Comment:
+ if (yyComment.startsWith(QLatin1Char(':'))) {
+ yyComment.remove(0, 1);
+ extracomment.append(yyComment);
+ } else {
+ comment = yyComment.simplified();
+ if (comment.startsWith(QLatin1String(MagicComment))) {
+ comment.remove(0, sizeof(MagicComment) - 1);
+ int k = comment.indexOf(QLatin1Char(' '));
+ if (k == -1) {
+ context = comment;
+ } else {
+ context = comment.left(k);
+ comment.remove(0, k + 1);
+ recordMessage(tor, yyLineNo, context, QString(), comment, extracomment, false, false);
+ }
+
+ /*
+ Provide a backdoor for people using "using
+ namespace". See the manual for details.
+ */
+ k = 0;
+ while ((k = context.indexOf(strColons, k)) != -1) {
+ qualifiedContexts.insert(context.mid(k + 2), context);
+ k++;
+ }
+ }
+ }
+ yyTok = getToken();
+ break;
+ case Tok_Arrow:
+ yyTok = getToken();
+ if (yyTok == Tok_tr || yyTok == Tok_trUtf8)
+ qWarning("%s:%d: Cannot invoke tr() like this",
+ qPrintable(yyFileName), yyLineNo);
+ break;
+ case Tok_ColonColon:
+ if (yyBraceDepth == namespaces.count() && yyParenDepth == 0 && !yyTokColonSeen)
+ functionContext = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, false);
+ prefix += strColons;
+ yyTok = getToken();
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ if (yyTok == Tok_Ident && yyBraceDepth == namespaces.count() && yyParenDepth == 0)
+ functionName = yyIdent;
+#endif
+ break;
+ case Tok_RightBrace:
+ case Tok_Semicolon:
+ prefix.clear();
+ extracomment.clear();
+ yyTokColonSeen = false;
+ if (yyBraceDepth >= 0 && yyBraceDepth + 1 == namespaces.count())
+ namespaces.removeLast();
+ if (yyBraceDepth == namespaces.count()) {
+ if (missing_Q_OBJECT) {
+ if (needs_Q_OBJECT.contains(functionContext)) {
+ qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro",
+ qPrintable(yyFileName), yyLineNo,
+ qPrintable(functionContext));
+ } else {
+ lacks_Q_OBJECT.insert(functionContext);
+ }
+ }
+ functionContext = defaultContext;
+ missing_Q_OBJECT = false;
+ }
+ yyTok = getToken();
+ break;
+ case Tok_Colon:
+ yyTokColonSeen = true;
+ yyTok = getToken();
+ break;
+ case Tok_LeftParen:
+ case Tok_RightParen:
+ case Tok_LeftBrace:
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ break;
+ default:
+ yyTok = getToken();
+ break;
+ }
+ }
+
+ if (yyBraceDepth != 0)
+ qWarning("%s:%d: Unbalanced braces in C++ code (or abuse of the C++"
+ " preprocessor)\n",
+ qPrintable(yyFileName), yyBraceLineNo);
+ else if (yyParenDepth != 0)
+ qWarning("%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++"
+ " preprocessor)\n",
+ qPrintable(yyFileName), yyParenLineNo);
+}
+
+/*
+ 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)
+{
+ yyInStr = in;
+ yyInPos = 0;
+ yyFileName = QString();
+ yyCodecIsUtf8 = (translator.codecName() == "UTF-8");
+ yyForceUtf8 = true;
+ yySourceIsUnicode = true;
+ yySavedBraceDepth.clear();
+ yySavedParenDepth.clear();
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyCurLineNo = 1;
+ yyBraceLineNo = 1;
+ yyParenLineNo = 1;
+ yyCh = getChar();
+
+ parse(&translator, context, QString());
+}
+
+
+bool loadCPP(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QString defaultContext = cd.m_defaultContext;
+
+ yyCodecIsUtf8 = (translator.codecName() == "UTF-8");
+ yyForceUtf8 = false;
+ QTextStream ts(&dev);
+ QByteArray codecName = cd.m_codecForSource.isEmpty()
+ ? translator.codecName() : cd.m_codecForSource;
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+ yySourceCodec = ts.codec();
+ if (yySourceCodec->name() == "UTF-16")
+ translator.setCodecName("System");
+ yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-");
+ yyInStr = ts.readAll();
+ yyInPos = 0;
+ yyFileName = cd.m_sourceFileName;
+ yySavedBraceDepth.clear();
+ yySavedParenDepth.clear();
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyCurLineNo = 1;
+ yyBraceLineNo = 1;
+ yyParenLineNo = 1;
+ yyCh = getChar();
+
+ parse(&translator, defaultContext, defaultContext);
+
+ return true;
+}
+
+int initCPP()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("cpp");
+ format.fileType = Translator::FileFormat::SourceCode;
+ format.priority = 0;
+ format.description = QObject::tr("C++ source files");
+ format.loader = &loadCPP;
+ format.saver = 0;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initCPP)
+
+QT_END_NAMESPACE