lupdate: allow to configure names of tr()-like functions
authorMarc Mutz <marc.mutz@kdab.com>
Mon, 26 Nov 2012 14:06:17 +0000 (15:06 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 9 Apr 2013 15:39:12 +0000 (17:39 +0200)
This makes it possible to use lupdate in projects that would like to
wrap the tr()-like functions with their own versions, e.g. to add
error checking or other instrumentation code.

While we're at it, also simplify the c++ parsing with partly
table-driven code.

Change-Id: I72af68afa851a46342e75dcdd3dd58f4c14276b8
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
src/linguist/lupdate/cpp.cpp
src/linguist/lupdate/lupdate.1
src/linguist/lupdate/lupdate.h
src/linguist/lupdate/main.cpp
src/linguist/lupdate/qdeclarative.cpp
tests/auto/linguist/lupdate/testdata/good/tr_function_alias/lupdatecmd [new file with mode: 0644]
tests/auto/linguist/lupdate/testdata/good/tr_function_alias/main.cpp [new file with mode: 0644]
tests/auto/linguist/lupdate/testdata/good/tr_function_alias/project.ts.result [new file with mode: 0644]

index ec02be1..99d0d29 100644 (file)
@@ -292,7 +292,7 @@ private:
     void truncateNamespaces(NamespaceList *namespaces, int lenght);
     Namespace *modifyNamespace(NamespaceList *namespaces, bool haveLast = true);
 
-    enum {
+    enum TokenType {
         Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return,
         Tok_tr, Tok_trUtf8, Tok_translate, Tok_translateUtf8, Tok_trid,
         Tok_Q_OBJECT, Tok_Q_DECLARE_TR_FUNCTIONS,
@@ -478,28 +478,12 @@ bool CppParser::getMacroArgs()
 }
 
 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(operator);
-STRING(qtTrId);
 STRING(return);
 STRING(struct);
-STRING(TR);
-STRING(Tr);
-STRING(tr);
-STRING(trUtf8);
-STRING(translate);
 STRING(using);
 
 uint CppParser::getToken()
@@ -716,39 +700,12 @@ uint CppParser::getToken()
             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;
@@ -770,10 +727,6 @@ uint CppParser::getToken()
                         yyCh = getChar();
                 }
                 break;
-            case 'q':
-                if (yyWord == strqtTrId)
-                    return Tok_trid;
-                break;
             case 'r':
                 if (yyWord == strreturn)
                     return Tok_return;
@@ -782,19 +735,38 @@ uint CppParser::getToken()
                 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;
             }
+
+            static const TokenType trFunctionTokens[] = {
+                Tok_Q_DECLARE_TR_FUNCTIONS, // Q_DECLARE_TR_FUNCTIONS
+                Tok_tr,                     // QT_TR_NOOP
+                Tok_trid,                   // QT_TRID_NOOP
+                Tok_translate,              // QT_TRANSLATE_NOOP
+                Tok_translate,              // QT_TRANSLATE_NOOP3
+                Tok_trUtf8,                 // QT_TR_NOOP_UTF8
+                Tok_translateUtf8,          // QT_TRANSLATE_NOOP_UTF8
+                Tok_translateUtf8,          // QT_TRANSLATE_NOOP3_UTF8
+                Tok_translate,              // findMessage
+                Tok_trid,                   // qtTrId
+                Tok_tr,                     // TR
+                Tok_tr,                     // Tr
+                Tok_tr,                     // tr
+                Tok_trUtf8,                 // trUtf8
+                Tok_translate,              // translate
+                Tok_Ident,                  // qsTr (QML only)
+                Tok_Ident,                  // qsTrId (QML only)
+                Tok_Ident,                  // qsTranslate (QML only)
+            };
+            Q_STATIC_ASSERT((sizeof trFunctionTokens / sizeof *trFunctionTokens == TrFunctionAliasManager::NumTrFunctions));
+
+            const int trFunction = trFunctionAliasManager.trFunctionByName(yyWord);
+            if (trFunction >= 0)
+                return trFunctionTokens[trFunction];
+
             return Tok_Ident;
         } else {
             switch (yyCh) {
index 344c5b3..e90b163 100644 (file)
@@ -107,6 +107,12 @@ POSIX if not specified and the file does not name it yet.
 Specify/override the language of the translation.
 The target language is guessed from the file name if this option
 is not specified and the file contents name no language yet.
+.TP
+.I "-tr-function-alias <function>{+=,=}<alias>[,<function>{+=,=}<alias>]..."
+With +=, recognize <alias> as an alternative spelling of <function>.
+With  =, recognize <alias>> as the only spelling of <function>.
+See lupdate -h for a list of available function names.
+.TP
 .I "-version"
 Display the version of
 .B lupdate
index 05a3ce5..a464884 100644 (file)
 
 #include "qglobal.h"
 
+#include <QByteArray>
 #include <QList>
+#include <QString>
+#include <QHash>
 
 QT_BEGIN_NAMESPACE
 
 class ConversionData;
-class QString;
 class QStringList;
 class Translator;
 class TranslatorMessage;
@@ -85,6 +87,68 @@ bool loadQScript(Translator &translator, const QString &filename, ConversionData
 bool loadQml(Translator &translator, const QString &filename, ConversionData &cd);
 #endif
 
+#define LUPDATE_FOR_EACH_TR_FUNCTION(UNARY_MACRO) \
+    /* from cpp.cpp */ \
+    UNARY_MACRO(Q_DECLARE_TR_FUNCTIONS) \
+    UNARY_MACRO(QT_TR_NOOP) \
+    UNARY_MACRO(QT_TRID_NOOP) \
+    UNARY_MACRO(QT_TRANSLATE_NOOP) \
+    UNARY_MACRO(QT_TRANSLATE_NOOP3) \
+    UNARY_MACRO(QT_TR_NOOP_UTF8) \
+    UNARY_MACRO(QT_TRANSLATE_NOOP_UTF8) \
+    UNARY_MACRO(QT_TRANSLATE_NOOP3_UTF8) \
+    UNARY_MACRO(findMessage) /* QTranslator::findMessage() has the same parameters as QApplication::translate() */ \
+    UNARY_MACRO(qtTrId) \
+    UNARY_MACRO(TR) \
+    UNARY_MACRO(Tr) \
+    UNARY_MACRO(tr) \
+    UNARY_MACRO(trUtf8) \
+    UNARY_MACRO(translate) \
+    /* from qdeclarative.cpp: */ \
+    UNARY_MACRO(qsTr) \
+    UNARY_MACRO(qsTrId) \
+    UNARY_MACRO(qsTranslate) \
+    /*end*/
+
+
+class TrFunctionAliasManager {
+public:
+    TrFunctionAliasManager();
+    ~TrFunctionAliasManager();
+
+    enum TrFunction {
+        // need to uglify names b/c most of the names are themselves macros:
+#define MAKE_ENTRY(F) Function_##F,
+        LUPDATE_FOR_EACH_TR_FUNCTION(MAKE_ENTRY)
+#undef MAKE_ENTRY
+        NumTrFunctions
+    };
+
+    enum Operation { AddAlias, SetAlias };
+
+    int trFunctionByName(const QByteArray &trFunctionByName) const;
+    int trFunctionByName(const QString &trFunctionName) const
+    { return trFunctionByName(trFunctionName.toLatin1()); }
+
+    void modifyAlias(int trFunction, const QByteArray &alias, Operation op);
+
+    bool isAliasFor(const QByteArray &identifier, TrFunction trFunction) const
+    { return m_trFunctionAliases[trFunction].contains(identifier); }
+    bool isAliasFor(const QString &identifier, TrFunction trFunction) const
+    { return isAliasFor(identifier.toLatin1(), trFunction); }
+
+    QStringList availableFunctionsWithAliases() const;
+
+private:
+    void ensureTrFunctionHashUpdated() const;
+
+private:
+    QList<QByteArray> m_trFunctionAliases[NumTrFunctions];
+    mutable QHash<QByteArray,TrFunction> m_nameToTrFunctionMap;
+};
+
 QT_END_NAMESPACE
 
+extern QT_PREPEND_NAMESPACE(TrFunctionAliasManager) trFunctionAliasManager;
+
 #endif
index cc0d42a..25486a0 100644 (file)
@@ -1,6 +1,7 @@
 /****************************************************************************
 **
 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Linguist of the Qt Toolkit.
 
 #include <iostream>
 
+// Can't have an array of QStaticByteArrayData<N> for different N, so
+// use QByteArray, which requires constructor calls. Doesn't matter
+// much, since this is in an app, not a lib:
+static const QByteArray defaultTrFunctionNames[] = {
+// MSVC can't handle the lambda in this array if QByteArrayLiteral expands
+// to a lambda. In that case, use a QByteArray instead.
+#if defined(Q_CC_MSVC) && defined(Q_COMPILER_LAMBDA)
+#define BYTEARRAYLITERAL(F) QByteArray(#F),
+#else
+#define BYTEARRAYLITERAL(F) QByteArrayLiteral(#F),
+#endif
+    LUPDATE_FOR_EACH_TR_FUNCTION(BYTEARRAYLITERAL)
+#undef BYTEARRAYLITERAL
+};
+Q_STATIC_ASSERT((TrFunctionAliasManager::NumTrFunctions == sizeof defaultTrFunctionNames / sizeof *defaultTrFunctionNames));
+
+static int trFunctionByDefaultName(const QByteArray &trFunctionName)
+{
+    for (int i = 0; i < TrFunctionAliasManager::NumTrFunctions; ++i)
+        if (trFunctionName == defaultTrFunctionNames[i])
+            return i;
+    return -1;
+}
+
+static int trFunctionByDefaultName(const QString &trFunctionName)
+{
+    return trFunctionByDefaultName(trFunctionName.toLatin1());
+}
+
+TrFunctionAliasManager::TrFunctionAliasManager()
+    : m_trFunctionAliases()
+{
+    for (int i = 0; i < NumTrFunctions; ++i)
+        m_trFunctionAliases[i].push_back(defaultTrFunctionNames[i]);
+}
+
+TrFunctionAliasManager::~TrFunctionAliasManager() {}
+
+int TrFunctionAliasManager::trFunctionByName(const QByteArray &trFunctionName) const
+{
+    ensureTrFunctionHashUpdated();
+    // this function needs to be fast
+    const QHash<QByteArray, TrFunction>::const_iterator it
+        = m_nameToTrFunctionMap.find(trFunctionName);
+    return it == m_nameToTrFunctionMap.end() ? -1 : *it;
+}
+
+void TrFunctionAliasManager::modifyAlias(int trFunction, const QByteArray &alias, Operation op)
+{
+    QList<QByteArray> &list = m_trFunctionAliases[trFunction];
+    if (op == SetAlias)
+        list.clear();
+    list.push_back(alias);
+    m_nameToTrFunctionMap.clear();
+}
+
+void TrFunctionAliasManager::ensureTrFunctionHashUpdated() const
+{
+    if (!m_nameToTrFunctionMap.empty())
+        return;
+
+    QHash<QByteArray, TrFunction> nameToTrFunctionMap;
+    for (int i = 0; i < NumTrFunctions; ++i)
+        foreach (const QByteArray &alias, m_trFunctionAliases[i])
+            nameToTrFunctionMap[alias] = TrFunction(i);
+    // commit:
+    m_nameToTrFunctionMap.swap(nameToTrFunctionMap);
+}
+
+static QStringList availableFunctions()
+{
+    QStringList result;
+    result.reserve(TrFunctionAliasManager::NumTrFunctions);
+    for (int i = 0; i < TrFunctionAliasManager::NumTrFunctions; ++i)
+        result.push_back(QString::fromLatin1(defaultTrFunctionNames[i]));
+    return result;
+}
+
+static QStringList byteArrayToStringList(const QList<QByteArray> &in)
+{
+    QStringList result;
+    result.reserve(in.size());
+    foreach (const QByteArray &function, in)
+        result.push_back(QString::fromLatin1(function));
+    return result;
+}
+
+QStringList TrFunctionAliasManager::availableFunctionsWithAliases() const
+{
+    QStringList result;
+    result.reserve(NumTrFunctions);
+    for (int i = 0; i < NumTrFunctions; ++i)
+        result.push_back(QString::fromLatin1(defaultTrFunctionNames[i]) +
+                         QLatin1String(" (=") +
+                         byteArrayToStringList(m_trFunctionAliases[i]).join(QLatin1Char('=')) +
+                         QLatin1Char(')'));
+    return result;
+}
+
+TrFunctionAliasManager trFunctionAliasManager;
+
 static QString m_defaultExtensions;
 
 static void printOut(const QString & out)
@@ -133,6 +235,11 @@ static void printUsage()
         "    -target-language <language>[_<region>]\n"
         "           Specify the language of the translations for new files.\n"
         "           Guessed from the file name if not specified.\n"
+        "    -tr-function-alias <function>{+=,=}<alias>[,<function>{+=,=}<alias>]...\n"
+        "           With +=, recognize <alias> as an alternative spelling of <function>.\n"
+        "           With  =, recognize <alias> as the only spelling of <function>.\n"
+        "           Available <function>s (with their currently defined aliases) are:\n"
+        "             %2\n"
         "    -ts <ts-file>...\n"
         "           Specify the output file(s). This will override the TRANSLATIONS.\n"
         "    -version\n"
@@ -140,7 +247,39 @@ static void printUsage()
         "    @lst-file\n"
         "           Read additional file names (one per line) or includepaths (one per\n"
         "           line, and prefixed with -I) from lst-file.\n"
-    ).arg(m_defaultExtensions));
+    ).arg(m_defaultExtensions,
+          trFunctionAliasManager.availableFunctionsWithAliases()
+                                .join(QStringLiteral("\n             "))));
+}
+
+static bool handleTrFunctionAliases(const QString &arg)
+{
+    foreach (const QString &pair, arg.split(QLatin1Char(','), QString::SkipEmptyParts)) {
+        const int equalSign = pair.indexOf(QLatin1Char('='));
+        if (equalSign < 0) {
+            printErr(LU::tr("tr-function mapping '%1' in -tr-function-alias is missing the '='.\n").arg(pair));
+            return false;
+        }
+        const bool plusEqual = equalSign > 0 && pair[equalSign-1] == QLatin1Char('+');
+        const int trFunctionEnd = plusEqual ? equalSign-1 : equalSign;
+        const QString trFunctionName = pair.left(trFunctionEnd).trimmed();
+        const QString alias = pair.mid(equalSign+1).trimmed();
+        const int trFunction = trFunctionByDefaultName(trFunctionName);
+        if (trFunction < 0) {
+            printErr(LU::tr("Unknown tr-function '%1' in -tr-function-alias option.\n"
+                            "Available tr-functions are: %2")
+                     .arg(trFunctionName, availableFunctions().join(QLatin1Char(','))));
+            return false;
+        }
+        if (alias.isEmpty()) {
+            printErr(LU::tr("Empty alias for tr-function '%1' in -tr-function-alias option.\n")
+                     .arg(trFunctionName));
+            return false;
+        }
+        trFunctionAliasManager.modifyAlias(trFunction, alias.toLatin1(),
+                                           plusEqual ? TrFunctionAliasManager::AddAlias : TrFunctionAliasManager::SetAlias);
+    }
+    return true;
 }
 
 static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames,
@@ -639,6 +778,15 @@ int main(int argc, char **argv)
             }
             extensions = args[i];
             continue;
+        } else if (arg == QLatin1String("-tr-function-alias")) {
+            ++i;
+            if (i == argc) {
+                printErr(LU::tr("The -tr-function-alias option should be followed by a list of function=alias mappings.\n"));
+                return 1;
+            }
+            if (!handleTrFunctionAliases(args[i]))
+                return 1;
+            continue;
         } else if (arg == QLatin1String("-pro")) {
             ++i;
             if (i == argc) {
index befeea2..3a18b97 100644 (file)
@@ -107,8 +107,9 @@ protected:
 
             const QString name = idExpr->name.toString();
             const int identLineNo = idExpr->identifierToken.startLine;
-            if (name == QLatin1String("qsTr") ||
-                name == QLatin1String("QT_TR_NOOP")) {
+            switch (trFunctionAliasManager.trFunctionByName(name)) {
+            case TrFunctionAliasManager::Function_qsTr:
+            case TrFunctionAliasManager::Function_QT_TR_NOOP: {
                 if (!node->arguments) {
                     yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
                     return;
@@ -142,8 +143,9 @@ protected:
                 msg.setExtras(extra);
                 m_translator->extend(msg);
                 consumeComment();
-            } else if (name == QLatin1String("qsTranslate") ||
-                       name == QLatin1String("QT_TRANSLATE_NOOP")) {
+                break; }
+            case TrFunctionAliasManager::Function_qsTranslate:
+            case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: {
                 if (! (node->arguments && node->arguments->next)) {
                     yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least two arguments.\n").arg(name));
                     return;
@@ -186,8 +188,9 @@ protected:
                 msg.setExtras(extra);
                 m_translator->extend(msg);
                 consumeComment();
-            } else if (name == QLatin1String("qsTrId") ||
-                       name == QLatin1String("QT_TRID_NOOP")) {
+                break; }
+            case TrFunctionAliasManager::Function_qsTrId:
+            case TrFunctionAliasManager::Function_QT_TRID_NOOP: {
                 if (!node->arguments) {
                     yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
                     return;
@@ -215,6 +218,7 @@ protected:
                 msg.setExtras(extra);
                 m_translator->extend(msg);
                 consumeComment();
+                break; }
             }
         }
     }
diff --git a/tests/auto/linguist/lupdate/testdata/good/tr_function_alias/lupdatecmd b/tests/auto/linguist/lupdate/testdata/good/tr_function_alias/lupdatecmd
new file mode 100644 (file)
index 0000000..7996a44
--- /dev/null
@@ -0,0 +1 @@
+lupdate main.cpp -tr-function-alias QT_TRANSLATE_NOOP+=QT_TRANSLATE_NOOP_ALIAS,QT_TRANSLATE_NOOP_UTF8+=QT_TRANSLATE_NOOP_UTF8_ALIAS,QT_TRANSLATE_NOOP3+=QT_TRANSLATE_NOOP3_ALIAS,QT_TRANSLATE_NOOP3_UTF8+=QT_TRANSLATE_NOOP3_UTF8_ALIAS,QT_TRID_NOOP+=QT_TRID_NOOP_ALIAS,Q_DECLARE_TR_FUNCTIONS+=Q_DECLARE_TR_FUNCTIONS_ALIAS,qtTrId+=qtTrId_alias,QT_TR_NOOP+=QT_TR_NOOP_ALIAS,QT_TR_NOOP_UTF8+=QT_TR_NOOP_UTF8_ALIAS,tr+=tr_alias,trUtf8+=trUtf8_alias,translate+=translate_alias -ts project.ts
diff --git a/tests/auto/linguist/lupdate/testdata/good/tr_function_alias/main.cpp b/tests/auto/linguist/lupdate/testdata/good/tr_function_alias/main.cpp
new file mode 100644 (file)
index 0000000..8d7d53a
--- /dev/null
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MYOBJECT_H
+#define MYOBJECT_H
+
+#include <QtCore/QObject>
+
+QT_TRANSLATE_NOOP("scope", "string")
+QT_TRANSLATE_NOOP_ALIAS("scope", "string_alias")
+QT_TRANSLATE_NOOP_UTF8("scope", "utf8_string")
+QT_TRANSLATE_NOOP_UTF8_ALIAS("scope", "utf8_string_alias")
+QT_TRANSLATE_NOOP3("scope", "string_with_comment", "comment")
+QT_TRANSLATE_NOOP3_ALIAS("scope", "string_with_comment_alias", "comment")
+QT_TRANSLATE_NOOP3_UTF8("scope", "utf8_string_with_comment", "comment")
+QT_TRANSLATE_NOOP3_UTF8_ALIAS("scope", "utf8_string_with_comment_alias", "comment")
+QT_TRID_NOOP("this_a_id")
+QT_TRID_NOOP_ALIAS("this_a_id_alias")
+QString test = qtTrId("yet_another_id");
+QString test_alias = qtTrId_alias("yet_another_id_alias");
+
+class Bogus : QObject {
+    Q_OBJECT
+
+    static const char * const s_strings[];
+};
+
+const char * const Bogus::s_strings[] = {
+    QT_TR_NOOP("this should be in Bogus"),
+    QT_TR_NOOP_ALIAS("this should be in Bogus Alias"),
+    QT_TR_NOOP_UTF8("this should be utf8 in Bogus")
+    QT_TR_NOOP_UTF8_ALIAS("this should be utf8 in Bogus Alias")
+};
+
+class MyObject : public QObject
+{
+    Q_OBJECT
+    explicit MyObject(QObject *parent = 0)
+    {
+        tr("Boo", "nsF::D");
+        tr_alias("Boo_alias", "nsB::C");
+        trUtf8("utf8_Boo", "nsF::D");
+        trUtf8_alias("utf8_Boo_alias", "nsF::D");
+        translate("QTranslator", "Simple");
+        translate_alias("QTranslator", "Simple with comment alias", "with comment")
+    }
+};
+
+struct NonQObject
+{
+    Q_DECLARE_TR_FUNCTIONS_ALIAS(NonQObject)
+
+    NonQObject()
+    {
+        tr("NonQObject_Boo", "nsF::NonQObject_D");
+        tr_alias("NonQObject_Boo_alias", "nsB::NonQObject_C");
+        trUtf8("utf_NonQObject_Boo", "nsF::D");
+        trUtf8_alias("utf8_NonQObject_Boo_alias", "nsF::D");
+        translate("NonQObject_QTranslator", "Simple");
+        translate_alias("NonQObject_QTranslator", "Simple with comment alias", "with comment")
+    }
+};
+
+#endif
diff --git a/tests/auto/linguist/lupdate/testdata/good/tr_function_alias/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/tr_function_alias/project.ts.result
new file mode 100644 (file)
index 0000000..e05d01c
--- /dev/null
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0">
+<context>
+    <name></name>
+    <message id="this_a_id">
+        <location filename="main.cpp" line="55"/>
+        <source></source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message id="this_a_id_alias">
+        <location filename="main.cpp" line="56"/>
+        <source></source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message id="yet_another_id">
+        <location filename="main.cpp" line="57"/>
+        <source></source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message id="yet_another_id_alias">
+        <location filename="main.cpp" line="58"/>
+        <source></source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Bogus</name>
+    <message>
+        <location filename="main.cpp" line="67"/>
+        <source>this should be in Bogus</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="68"/>
+        <source>this should be in Bogus Alias</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="69"/>
+        <source>this should be utf8 in Bogus</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="70"/>
+        <source>this should be utf8 in Bogus Alias</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MyObject</name>
+    <message>
+        <location filename="main.cpp" line="78"/>
+        <source>Boo</source>
+        <comment>nsF::D</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="79"/>
+        <source>Boo_alias</source>
+        <comment>nsB::C</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="80"/>
+        <source>utf8_Boo</source>
+        <comment>nsF::D</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="81"/>
+        <source>utf8_Boo_alias</source>
+        <comment>nsF::D</comment>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>NonQObject</name>
+    <message>
+        <location filename="main.cpp" line="93"/>
+        <source>NonQObject_Boo</source>
+        <comment>nsF::NonQObject_D</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="94"/>
+        <source>NonQObject_Boo_alias</source>
+        <comment>nsB::NonQObject_C</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="95"/>
+        <source>utf_NonQObject_Boo</source>
+        <comment>nsF::D</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="96"/>
+        <source>utf8_NonQObject_Boo_alias</source>
+        <comment>nsF::D</comment>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>NonQObject_QTranslator</name>
+    <message>
+        <location filename="main.cpp" line="97"/>
+        <source>Simple</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="98"/>
+        <source>Simple with comment alias</source>
+        <comment>with comment</comment>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>QTranslator</name>
+    <message>
+        <location filename="main.cpp" line="82"/>
+        <source>Simple</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="83"/>
+        <source>Simple with comment alias</source>
+        <comment>with comment</comment>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>scope</name>
+    <message>
+        <location filename="main.cpp" line="47"/>
+        <source>string</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="48"/>
+        <source>string_alias</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="49"/>
+        <source>utf8_string</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="50"/>
+        <source>utf8_string_alias</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="51"/>
+        <source>string_with_comment</source>
+        <comment>comment</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="52"/>
+        <source>string_with_comment_alias</source>
+        <comment>comment</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="53"/>
+        <source>utf8_string_with_comment</source>
+        <comment>comment</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.cpp" line="54"/>
+        <source>utf8_string_with_comment_alias</source>
+        <comment>comment</comment>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>