source: branches/vendor/trolltech/qt/current/tools/linguist/shared/cpp.cpp @ 2

Last change on this file since 2 was 2, checked in by Dmitry A. Kuminov, 13 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 36.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file.  Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file.  Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "translator.h"
43
44#include <QtCore/QDebug>
45#include <QtCore/QStack>
46#include <QtCore/QString>
47#include <QtCore/QTextCodec>
48#include <QtCore/QTextStream>
49
50#include <ctype.h>              // for isXXX()
51
52QT_BEGIN_NAMESPACE
53
54/* qmake ignore Q_OBJECT */
55
56static const char MagicComment[] = "TRANSLATOR ";
57
58static QSet<QString> needs_Q_OBJECT;
59static QSet<QString> lacks_Q_OBJECT;
60
61static const int yyIdentMaxLen = 128;
62static const int yyCommentMaxLen = 65536;
63static const int yyStringMaxLen = 65536;
64
65#define STRINGIFY_INTERNAL(x) #x
66#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
67#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
68
69//#define DIAGNOSE_RETRANSLATABILITY
70/*
71  The first part of this source file is the C++ tokenizer.  We skip
72  most of C++; the only tokens that interest us are defined here.
73  Thus, the code fragment
74
75      int main()
76      {
77          printf("Hello, world!\n");
78          return 0;
79      }
80
81  is broken down into the following tokens (Tok_ omitted):
82
83      Ident Ident LeftParen RightParen
84      LeftBrace
85          Ident LeftParen String RightParen Semicolon
86          return Semicolon
87      RightBrace.
88
89  The 0 doesn't produce any token.
90*/
91
92enum {
93    Tok_Eof, Tok_class, Tok_namespace, Tok_return,
94    Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8,
95    Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS,
96    Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon,
97    Tok_Equals,
98    Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon,
99    Tok_Integer = 40,
100    Tok_Other
101};
102
103/*
104  The tokenizer maintains the following global variables. The names
105  should be self-explanatory.
106*/
107static QString yyFileName;
108static int yyCh;
109static bool yyCodecIsUtf8;
110static bool yyForceUtf8;
111static QString yyIdent;
112static QString yyComment;
113static QString yyString;
114static qlonglong yyInteger;
115static QStack<int> yySavedBraceDepth;
116static QStack<int> yySavedParenDepth;
117static int yyBraceDepth;
118static int yyParenDepth;
119static int yyLineNo;
120static int yyCurLineNo;
121static int yyBraceLineNo;
122static int yyParenLineNo;
123static bool yyTokColonSeen = false;
124
125// the string to read from and current position in the string
126static QTextCodec *yySourceCodec;
127static bool yySourceIsUnicode;
128static QString yyInStr;
129static int yyInPos;
130
131static uint getChar()
132{
133    forever {
134        if (yyInPos >= yyInStr.size())
135            return EOF;
136        uint c = yyInStr[yyInPos++].unicode();
137        if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') {
138            ++yyCurLineNo;
139            ++yyInPos;
140            continue;
141        }
142        if (c == '\n')
143            ++yyCurLineNo;
144        return c;
145    }
146}
147
148static uint getToken()
149{
150    yyIdent.clear();
151    yyComment.clear();
152    yyString.clear();
153
154    while (yyCh != EOF) {
155        yyLineNo = yyCurLineNo;
156
157        if (isalpha(yyCh) || yyCh == '_') {
158            do {
159                yyIdent += yyCh;
160                yyCh = getChar();
161            } while (isalnum(yyCh) || yyCh == '_');
162
163            //qDebug() << "IDENT: " << yyIdent;
164
165            switch (yyIdent.at(0).unicode()) {
166            case 'Q':
167                if (yyIdent == QLatin1String("Q_OBJECT"))
168                    return Tok_Q_OBJECT;
169                if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS"))
170                    return Tok_Q_DECLARE_TR_FUNCTIONS;
171                if (yyIdent == QLatin1String("QT_TR_NOOP"))
172                    return Tok_tr;
173                if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP"))
174                    return Tok_translate;
175                if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3"))
176                    return Tok_translate;
177                if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8"))
178                    return Tok_trUtf8;
179                if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8"))
180                    return Tok_translateUtf8;
181                if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8"))
182                    return Tok_translateUtf8;
183                break;
184            case 'T':
185                // TR() for when all else fails
186                if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) {
187                    return Tok_tr;
188                }
189                break;
190            case 'c':
191                if (yyIdent == QLatin1String("class"))
192                    return Tok_class;
193                break;
194            case 'f':
195                /*
196                  QTranslator::findMessage() has the same parameters as
197                  QApplication::translate().
198                */
199                if (yyIdent == QLatin1String("findMessage"))
200                    return Tok_translate;
201                break;
202            case 'n':
203                if (yyIdent == QLatin1String("namespace"))
204                    return Tok_namespace;
205                break;
206            case 'r':
207                if (yyIdent == QLatin1String("return"))
208                    return Tok_return;
209                break;
210            case 's':
211                if (yyIdent == QLatin1String("struct"))
212                    return Tok_class;
213                break;
214            case 't':
215                if (yyIdent == QLatin1String("tr")) {
216                    return Tok_tr;
217                }
218                if (yyIdent == QLatin1String("trUtf8")) {
219                    return Tok_trUtf8;
220                }
221                if (yyIdent == QLatin1String("translate")) {
222                    return Tok_translate;
223                }
224            }
225            return Tok_Ident;
226        } else {
227            switch (yyCh) {
228            case '#':
229                /*
230                  Early versions of lupdate complained about
231                  unbalanced braces in the following code:
232
233                      #ifdef ALPHA
234                          while (beta) {
235                      #else
236                          while (gamma) {
237                      #endif
238                              delta;
239                          }
240
241                  The code contains, indeed, two opening braces for
242                  one closing brace; yet there's no reason to panic.
243
244                  The solution is to remember yyBraceDepth as it was
245                  when #if, #ifdef or #ifndef was met, and to set
246                  yyBraceDepth to that value when meeting #elif or
247                  #else.
248                */
249                do {
250                    yyCh = getChar();
251                } while (isspace(yyCh) && yyCh != '\n');
252
253                switch (yyCh) {
254                case 'i':
255                    yyCh = getChar();
256                    if (yyCh == 'f') {
257                        // if, ifdef, ifndef
258                        yySavedBraceDepth.push(yyBraceDepth);
259                        yySavedParenDepth.push(yyParenDepth);
260                    }
261                    break;
262                case 'e':
263                    yyCh = getChar();
264                    if (yyCh == 'l') {
265                        // elif, else
266                        if (!yySavedBraceDepth.isEmpty()) {
267                            yyBraceDepth = yySavedBraceDepth.top();
268                            yyParenDepth = yySavedParenDepth.top();
269                        }
270                    } else if (yyCh == 'n') {
271                        // endif
272                        if (!yySavedBraceDepth.isEmpty()) {
273                            yySavedBraceDepth.pop();
274                            yySavedParenDepth.pop();
275                        }
276                    }
277                }
278                while (isalnum(yyCh) || yyCh == '_')
279                    yyCh = getChar();
280                break;
281            case '/':
282                yyCh = getChar();
283                if (yyCh == '/') {
284                    do {
285                        yyCh = getChar();
286                        if (yyCh == EOF)
287                            break;
288                        yyComment.append(yyCh);
289                    } while (yyCh != '\n');
290                } else if (yyCh == '*') {
291                    bool metAster = false;
292                    bool metAsterSlash = false;
293
294                    while (!metAsterSlash) {
295                        yyCh = getChar();
296                        if (yyCh == EOF) {
297                            qWarning("%s: Unterminated C++ comment starting at"
298                                     " line %d\n",
299                                     qPrintable(yyFileName), yyLineNo);
300                            return Tok_Comment;
301                        }
302                        yyComment.append(yyCh);
303
304                        if (yyCh == '*')
305                            metAster = true;
306                        else if (metAster && yyCh == '/')
307                            metAsterSlash = true;
308                        else
309                            metAster = false;
310                    }
311                    yyCh = getChar();
312                    yyComment.chop(2);
313                }
314                return Tok_Comment;
315            case '"':
316                yyCh = getChar();
317                while (yyCh != EOF && yyCh != '\n' && yyCh != '"') {
318                    if (yyCh == '\\') {
319                        yyCh = getChar();
320                        if (yyString.size() < yyStringMaxLen) {
321                            yyString.append(QLatin1Char('\\'));
322                            yyString.append(yyCh);
323                        }
324                    } else {
325                        if (yyString.size() < yyStringMaxLen)
326                            yyString.append(yyCh);
327                    }
328                    yyCh = getChar();
329                }
330
331                if (yyCh != '"')
332                    qWarning("%s:%d: Unterminated C++ string",
333                              qPrintable(yyFileName), yyLineNo);
334
335                if (yyCh == EOF)
336                    return Tok_Eof;
337                yyCh = getChar();
338                return Tok_String;
339            case '-':
340                yyCh = getChar();
341                if (yyCh == '>') {
342                    yyCh = getChar();
343                    return Tok_Arrow;
344                }
345                break;
346            case ':':
347                yyCh = getChar();
348                if (yyCh == ':') {
349                    yyCh = getChar();
350                    return Tok_ColonColon;
351                }
352                return Tok_Colon;
353            // Incomplete: '<' might be part of '<=' or of template syntax.
354            // The main intent of not completely ignoring it is to break
355            // parsing of things like   std::cout << QObject::tr()  as
356            // context std::cout::QObject (see Task 161106)
357            case '=':
358                yyCh = getChar();
359                return Tok_Equals;
360            case '>':
361            case '<':
362                yyCh = getChar();
363                return Tok_Other;
364            case '\'':
365                yyCh = getChar();
366                if (yyCh == '\\')
367                    yyCh = getChar();
368
369                do {
370                    yyCh = getChar();
371                } while (yyCh != EOF && yyCh != '\'');
372                yyCh = getChar();
373                break;
374            case '{':
375                if (yyBraceDepth == 0)
376                    yyBraceLineNo = yyCurLineNo;
377                yyBraceDepth++;
378                yyCh = getChar();
379                return Tok_LeftBrace;
380            case '}':
381                if (yyBraceDepth == 0)
382                    yyBraceLineNo = yyCurLineNo;
383                yyBraceDepth--;
384                yyCh = getChar();
385                return Tok_RightBrace;
386            case '(':
387                if (yyParenDepth == 0)
388                    yyParenLineNo = yyCurLineNo;
389                yyParenDepth++;
390                yyCh = getChar();
391                return Tok_LeftParen;
392            case ')':
393                if (yyParenDepth == 0)
394                    yyParenLineNo = yyCurLineNo;
395                yyParenDepth--;
396                yyCh = getChar();
397                return Tok_RightParen;
398            case ',':
399                yyCh = getChar();
400                return Tok_Comma;
401            case ';':
402                yyCh = getChar();
403                return Tok_Semicolon;
404            case '0':
405            case '1':
406            case '2':
407            case '3':
408            case '4':
409            case '5':
410            case '6':
411            case '7':
412            case '8':
413            case '9':
414                {
415                    QByteArray ba;
416                    ba += yyCh;
417                    yyCh = getChar();
418                    bool hex = yyCh == 'x';
419                    if (hex) {
420                        ba += yyCh;
421                        yyCh = getChar();
422                    }
423                    while (hex ? isxdigit(yyCh) : isdigit(yyCh)) {
424                        ba += yyCh;
425                        yyCh = getChar();
426                    }
427                    bool ok;
428                    yyInteger = ba.toLongLong(&ok);
429                    if (ok)
430                        return Tok_Integer;
431                    break;
432                }
433            default:
434                yyCh = getChar();
435                break;
436            }
437        }
438    }
439    return Tok_Eof;
440}
441
442/*
443  The second part of this source file is the parser. It accomplishes
444  a very easy task: It finds all strings inside a tr() or translate()
445  call, and possibly finds out the context of the call. It supports
446  three cases: (1) the context is specified, as in
447  FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello");
448  (2) the call appears within an inlined function; (3) the call
449  appears within a function defined outside the class definition.
450*/
451
452static uint yyTok;
453
454static bool match(uint t)
455{
456    bool matches = (yyTok == t);
457    if (matches)
458        yyTok = getToken();
459    return matches;
460}
461
462static bool matchString(QString *s)
463{
464    bool matches = (yyTok == Tok_String);
465    s->clear();
466    while (yyTok == Tok_String) {
467        *s += yyString;
468        yyTok = getToken();
469    }
470    return matches;
471}
472
473static bool matchEncoding(bool *utf8)
474{
475    STRING(QApplication);
476    STRING(QCoreApplication);
477    STRING(UnicodeUTF8);
478    STRING(DefaultCodec);
479    STRING(CodecForTr);
480
481    if (yyTok != Tok_Ident)
482        return false;
483    if (yyIdent == strQApplication || yyIdent == strQCoreApplication) {
484        yyTok = getToken();
485        if (yyTok == Tok_ColonColon)
486            yyTok = getToken();
487    }
488    if (yyIdent == strUnicodeUTF8) {
489        *utf8 = true;
490        yyTok = getToken();
491        return true;
492    }
493    if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) {
494        *utf8 = false;
495        yyTok = getToken();
496    return true;
497    }
498    return false;
499}
500
501static bool matchInteger(qlonglong *number)
502{
503    bool matches = (yyTok == Tok_Integer);
504    if (matches) {
505        yyTok = getToken();
506        *number = yyInteger;
507    }
508    return matches;
509}
510
511static bool matchStringOrNull(QString *s)
512{
513    bool matches = matchString(s);
514    qlonglong num = 0;
515    if (!matches)
516        matches = matchInteger(&num);
517    return matches && num == 0;
518}
519
520/*
521 * match any expression that can return a number, which can be
522 * 1. Literal number (e.g. '11')
523 * 2. simple identifier (e.g. 'm_count')
524 * 3. simple function call (e.g. 'size()' )
525 * 4. function call on an object (e.g. 'list.size()')
526 * 5. function call on an object (e.g. 'list->size()')
527 *
528 * Other cases:
529 * size(2,4)
530 * list().size()
531 * list(a,b).size(2,4)
532 * etc...
533 */
534static bool matchExpression()
535{
536    if (match(Tok_Integer))
537        return true;
538
539    int parenlevel = 0;
540    while (match(Tok_Ident) || parenlevel > 0) {
541        if (yyTok == Tok_RightParen) {
542            if (parenlevel == 0) break;
543            --parenlevel;
544            yyTok = getToken();
545        } else if (yyTok == Tok_LeftParen) {
546            yyTok = getToken();
547            if (yyTok == Tok_RightParen) {
548                yyTok = getToken();
549            } else {
550                ++parenlevel;
551            }
552        } else if (yyTok == Tok_Ident) {
553            continue;
554        } else if (yyTok == Tok_Arrow) {
555            yyTok = getToken();
556        } else if (parenlevel == 0) {
557            return false;
558        }
559    }
560    return true;
561}
562
563static QStringList resolveNamespaces(
564    const QStringList &namespaces, const QHash<QString, QStringList> &namespaceAliases)
565{
566    static QString strColons(QLatin1String("::"));
567
568    QStringList ns;
569    foreach (const QString &cns, namespaces) {
570        ns << cns;
571        ns = namespaceAliases.value(ns.join(strColons), ns);
572    }
573    return ns;
574}
575
576static QStringList getFullyQualifiedNamespaceName(
577    const QSet<QString> &allNamespaces, const QStringList &namespaces,
578    const QHash<QString, QStringList> &namespaceAliases,
579    const QStringList &segments)
580{
581    static QString strColons(QLatin1String("::"));
582
583    if (segments.first().isEmpty()) {
584        // fully qualified
585        QStringList segs = segments;
586        segs.removeFirst();
587        return resolveNamespaces(segs, namespaceAliases);
588    } else {
589        for (int n = namespaces.count(); --n >= -1; ) {
590            QStringList ns;
591            for (int i = 0; i <= n; ++i)  // Note: n == -1 possible
592                ns << namespaces[i];
593            foreach (const QString &cns, segments) {
594                ns << cns;
595                ns = namespaceAliases.value(ns.join(strColons), ns);
596            }
597            if (allNamespaces.contains(ns.join(strColons)))
598                return ns;
599        }
600
601        // Fallback when the namespace was declared in a header, etc.
602        QStringList ns = namespaces;
603        ns += segments;
604        return ns;
605    }
606}
607
608static QString getFullyQualifiedClassName(
609    const QSet<QString> &allClasses, const QStringList &namespaces,
610    const QHash<QString, QStringList> &namespaceAliases,
611    const QString &ident, bool hasPrefix)
612{
613    static QString strColons(QLatin1String("::"));
614
615    QString context = ident;
616    QStringList segments = context.split(strColons);
617    if (segments.first().isEmpty()) {
618        // fully qualified
619        segments.removeFirst();
620        context = resolveNamespaces(segments, namespaceAliases).join(strColons);
621    } else {
622        for (int n = namespaces.count(); --n >= -1; ) {
623            QStringList ns;
624            for (int i = 0; i <= n; ++i)  // Note: n == -1 possible
625                ns.append(namespaces[i]);
626            foreach (const QString &cns, segments) {
627                ns.append(cns);
628                ns = namespaceAliases.value(ns.join(strColons), ns);
629            }
630            QString nctx = ns.join(strColons);
631            if (allClasses.contains(nctx)) {
632                context = nctx;
633                goto gotit;
634            }
635        }
636
637        if (!hasPrefix && namespaces.count())
638            context = namespaces.join(strColons) + strColons + context;
639    }
640gotit:
641    //qDebug() << "CLASSES:" << allClasses << "NAMEPACES:" << namespaces
642    //    << "IDENT:" << ident << "CONTEXT:" << context;
643    return context;
644}
645
646
647static QString transcode(const QString &str, bool utf8)
648{
649    static const char tab[] = "abfnrtv";
650    static const char backTab[] = "\a\b\f\n\r\t\v";
651    const QString in = (!utf8 || yySourceIsUnicode)
652        ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data());
653    QString out;
654
655    out.reserve(in.length());
656    for (int i = 0; i < in.length();) {
657        ushort c = in[i++].unicode();
658        if (c == '\\') {
659            if (i >= in.length())
660                break;
661            c = in[i++].unicode();
662
663            if (c == '\n')
664                continue;
665
666            if (c == 'x') {
667                QByteArray hex;
668                while (i < in.length() && isxdigit((c = in[i].unicode()))) {
669                    hex += c;
670                    i++;
671                }
672                out += hex.toUInt(0, 16);
673            } else if (c >= '0' && c < '8') {
674                QByteArray oct;
675                int n = 0;
676                oct += c;
677                while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') {
678                    i++;
679                    n++;
680                    oct += c;
681                }
682                out += oct.toUInt(0, 8);
683            } else {
684                const char *p = strchr(tab, c);
685                out += QChar(QLatin1Char(!p ? c : backTab[p - tab]));
686            }
687        } else {
688            out += c;
689        }
690    }
691    return out;
692}
693
694static void recordMessage(
695    Translator *tor, int line, const QString &context, const QString &text, const QString &comment,
696    const QString &extracomment,  bool utf8, bool plural)
697{
698    TranslatorMessage msg(
699        transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(),
700        yyFileName, line, QStringList(),
701        TranslatorMessage::Unfinished, plural);
702    msg.setExtraComment(transcode(extracomment.simplified(), utf8));
703    if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit())
704        msg.setUtf8(true);
705    tor->extend(msg);
706}
707
708static void parse(Translator *tor, const QString &initialContext, const QString &defaultContext)
709{
710    static QString strColons(QLatin1String("::"));
711
712    QMap<QString, QString> qualifiedContexts;
713    QSet<QString> allClasses;
714    QSet<QString> allNamespaces;
715    QHash<QString, QStringList> namespaceAliases;
716    QStringList namespaces;
717    QString context;
718    QString text;
719    QString comment;
720    QString extracomment;
721    QString functionContext = initialContext;
722    QString prefix;
723#ifdef DIAGNOSE_RETRANSLATABILITY
724    QString functionName;
725#endif
726    int line;
727    bool utf8 = false;
728    bool missing_Q_OBJECT = false;
729
730    yyTok = getToken();
731    while (yyTok != Tok_Eof) {
732        //qDebug() << "TOKEN: " << yyTok;
733        switch (yyTok) {
734        case Tok_class:
735            yyTokColonSeen = false;
736            /*
737              Partial support for inlined functions.
738            */
739            yyTok = getToken();
740            if (yyBraceDepth == namespaces.count() && yyParenDepth == 0) {
741                QStringList fct;
742                do {
743                    /*
744                      This code should execute only once, but we play
745                      safe with impure definitions such as
746                      'class Q_EXPORT QMessageBox', in which case
747                      'QMessageBox' is the class name, not 'Q_EXPORT'.
748                    */
749                    fct = QStringList(yyIdent);
750                    yyTok = getToken();
751                } while (yyTok == Tok_Ident);
752                while (yyTok == Tok_ColonColon) {
753                    yyTok = getToken();
754                    if (yyTok != Tok_Ident)
755                        break; // Oops ...
756                    fct += yyIdent;
757                    yyTok = getToken();
758                }
759                functionContext = resolveNamespaces(namespaces + fct, namespaceAliases).join(strColons);
760                allClasses.insert(functionContext);
761
762                if (yyTok == Tok_Colon) {
763                    missing_Q_OBJECT = true;
764                    // Skip any token until '{' since lupdate might do things wrong if it finds
765                    // a '::' token here.
766                    do {
767                        yyTok = getToken();
768                    } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof);
769                } else {
770                    //functionContext = defaultContext;
771                }
772            }
773            break;
774        case Tok_namespace:
775            yyTokColonSeen = false;
776            yyTok = getToken();
777            if (yyTok == Tok_Ident) {
778                QString ns = yyIdent;
779                yyTok = getToken();
780                if (yyTok == Tok_LeftBrace) {
781                    if (yyBraceDepth == namespaces.count() + 1) {
782                        namespaces.append(ns);
783                        allNamespaces.insert(namespaces.join(strColons));
784                    }
785                } else if (yyTok == Tok_Equals) {
786                    // e.g. namespace Is = OuterSpace::InnerSpace;
787                    QStringList alias = namespaces;
788                    alias.append(ns);
789                    QStringList fullName;
790                    yyTok = getToken();
791                    if (yyTok == Tok_ColonColon)
792                        fullName.append(QString());
793                    while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
794                        if (yyTok == Tok_Ident)
795                            fullName.append(yyIdent);
796                        yyTok = getToken();
797                    }
798                    namespaceAliases[alias.join(strColons)] =
799                        getFullyQualifiedNamespaceName(allNamespaces, namespaces, namespaceAliases, fullName);
800                }
801            }
802            break;
803        case Tok_tr:
804        case Tok_trUtf8:
805            utf8 = (yyTok == Tok_trUtf8);
806            line = yyLineNo;
807            yyTok = getToken();
808            if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) {
809                comment.clear();
810                bool plural = false;
811
812                if (match(Tok_RightParen)) {
813                    // no comment
814                } else if (match(Tok_Comma) && matchStringOrNull(&comment)) {   //comment
815                    if (match(Tok_RightParen)) {
816                        // ok,
817                    } else if (match(Tok_Comma)) {
818                        plural = true;
819                    }
820                }
821                if (prefix.isEmpty()) {
822                    context = functionContext;
823                } else {
824#ifdef DIAGNOSE_RETRANSLATABILITY
825                    int last = prefix.lastIndexOf(strColons);
826                    QString className = prefix.mid(last == -1 ? 0 : last + 2);
827                    if (!className.isEmpty() && className == functionName) {
828                        qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ",
829                                  qPrintable(yyFileName), yyLineNo,
830                                  className.constData(), functionName.constData());
831                    }
832#endif
833                    prefix.chop(2);
834                    context = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, true);
835                }
836                prefix.clear();
837                if (qualifiedContexts.contains(context))
838                    context = qualifiedContexts[context];
839
840                if (!text.isEmpty())
841                    recordMessage(tor, line, context, text, comment, extracomment, utf8, plural);
842
843                if (lacks_Q_OBJECT.contains(context)) {
844                    qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro",
845                             qPrintable(yyFileName), yyLineNo,
846                             qPrintable(context));
847                    lacks_Q_OBJECT.remove(context);
848                } else {
849                    needs_Q_OBJECT.insert(context);
850                }
851            }
852            extracomment.clear();
853            break;
854        case Tok_translateUtf8:
855        case Tok_translate:
856            utf8 = (yyTok == Tok_translateUtf8);
857            line = yyLineNo;
858            yyTok = getToken();
859            if (match(Tok_LeftParen)
860                && matchString(&context)
861                && match(Tok_Comma)
862                && matchString(&text))
863            {
864                comment.clear();
865                bool plural = false;
866                if (!match(Tok_RightParen)) {
867                    // look for comment
868                    if (match(Tok_Comma) && matchStringOrNull(&comment)) {
869                        if (!match(Tok_RightParen)) {
870                            // look for encoding
871                            if (match(Tok_Comma)) {
872                                if (matchEncoding(&utf8)) {
873                                    if (!match(Tok_RightParen)) {
874                                        // look for the plural quantifier,
875                                        // this can be a number, an identifier or
876                                        // a function call,
877                                        // so for simplicity we mark it as plural if
878                                        // we know we have a comma instead of an
879                                        // right parentheses.
880                                        plural = match(Tok_Comma);
881                                    }
882                                } else {
883                                    // This can be a QTranslator::translate("context",
884                                    // "source", "comment", n) plural translation
885                                    if (matchExpression() && match(Tok_RightParen)) {
886                                        plural = true;
887                                    } else {
888                                        break;
889                                    }
890                                }
891                            } else {
892                                break;
893                            }
894                        }
895                    } else {
896                        break;
897                    }
898                }
899                if (!text.isEmpty())
900                    recordMessage(tor, line, context, text, comment, extracomment, utf8, plural);
901            }
902            extracomment.clear();
903            break;
904        case Tok_Q_DECLARE_TR_FUNCTIONS:
905        case Tok_Q_OBJECT:
906            missing_Q_OBJECT = false;
907            yyTok = getToken();
908            break;
909        case Tok_Ident:
910            prefix += yyIdent;
911            yyTok = getToken();
912            if (yyTok != Tok_ColonColon)
913                prefix.clear();
914            break;
915        case Tok_Comment:
916            if (yyComment.startsWith(QLatin1Char(':'))) {
917                yyComment.remove(0, 1);
918                extracomment.append(yyComment);
919            } else {
920                comment = yyComment.simplified();
921                if (comment.startsWith(QLatin1String(MagicComment))) {
922                    comment.remove(0, sizeof(MagicComment) - 1);
923                    int k = comment.indexOf(QLatin1Char(' '));
924                    if (k == -1) {
925                        context = comment;
926                    } else {
927                        context = comment.left(k);
928                        comment.remove(0, k + 1);
929                        recordMessage(tor, yyLineNo, context, QString(), comment, extracomment, false, false);
930                    }
931
932                    /*
933                    Provide a backdoor for people using "using
934                    namespace". See the manual for details.
935                    */
936                    k = 0;
937                    while ((k = context.indexOf(strColons, k)) != -1) {
938                        qualifiedContexts.insert(context.mid(k + 2), context);
939                        k++;
940                    }
941                }
942            }
943            yyTok = getToken();
944            break;
945        case Tok_Arrow:
946            yyTok = getToken();
947            if (yyTok == Tok_tr || yyTok == Tok_trUtf8)
948                qWarning("%s:%d: Cannot invoke tr() like this",
949                          qPrintable(yyFileName), yyLineNo);
950            break;
951        case Tok_ColonColon:
952            if (yyBraceDepth == namespaces.count() && yyParenDepth == 0 && !yyTokColonSeen)
953                functionContext = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, false);
954            prefix += strColons;
955            yyTok = getToken();
956#ifdef DIAGNOSE_RETRANSLATABILITY
957            if (yyTok == Tok_Ident && yyBraceDepth == namespaces.count() && yyParenDepth == 0)
958                functionName = yyIdent;
959#endif
960            break;
961        case Tok_RightBrace:
962        case Tok_Semicolon:
963            prefix.clear();
964            extracomment.clear();
965            yyTokColonSeen = false;
966            if (yyBraceDepth >= 0 && yyBraceDepth + 1 == namespaces.count())
967                namespaces.removeLast();
968            if (yyBraceDepth == namespaces.count()) {
969                if (missing_Q_OBJECT) {
970                    if (needs_Q_OBJECT.contains(functionContext)) {
971                        qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro",
972                                 qPrintable(yyFileName), yyLineNo,
973                                 qPrintable(functionContext));
974                    } else {
975                        lacks_Q_OBJECT.insert(functionContext);
976                    }
977                }
978                functionContext = defaultContext;
979                missing_Q_OBJECT = false;
980            }
981            yyTok = getToken();
982            break;
983        case Tok_Colon:
984            yyTokColonSeen = true;
985            yyTok = getToken();
986            break;
987        case Tok_LeftParen:
988        case Tok_RightParen:
989        case Tok_LeftBrace:
990            yyTokColonSeen = false;
991            yyTok = getToken();
992            break;
993        default:
994            yyTok = getToken();
995            break;
996        }
997    }
998
999    if (yyBraceDepth != 0)
1000        qWarning("%s:%d: Unbalanced braces in C++ code (or abuse of the C++"
1001                  " preprocessor)\n",
1002                  qPrintable(yyFileName), yyBraceLineNo);
1003    else if (yyParenDepth != 0)
1004        qWarning("%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++"
1005                 " preprocessor)\n",
1006                 qPrintable(yyFileName), yyParenLineNo);
1007}
1008
1009/*
1010  Fetches tr() calls in C++ code in UI files (inside "<function>"
1011  tag). This mechanism is obsolete.
1012*/
1013void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context)
1014{
1015    yyInStr = in;
1016    yyInPos = 0;
1017    yyFileName = QString();
1018    yyCodecIsUtf8 = (translator.codecName() == "UTF-8");
1019    yyForceUtf8 = true;
1020    yySourceIsUnicode = true;
1021    yySavedBraceDepth.clear();
1022    yySavedParenDepth.clear();
1023    yyBraceDepth = 0;
1024    yyParenDepth = 0;
1025    yyCurLineNo = 1;
1026    yyBraceLineNo = 1;
1027    yyParenLineNo = 1;
1028    yyCh = getChar();
1029
1030    parse(&translator, context, QString());
1031}
1032
1033
1034bool loadCPP(Translator &translator, QIODevice &dev, ConversionData &cd)
1035{
1036    QString defaultContext = cd.m_defaultContext;
1037
1038    yyCodecIsUtf8 = (translator.codecName() == "UTF-8");
1039    yyForceUtf8 = false;
1040    QTextStream ts(&dev);
1041    QByteArray codecName = cd.m_codecForSource.isEmpty()
1042        ? translator.codecName() : cd.m_codecForSource;
1043    ts.setCodec(QTextCodec::codecForName(codecName));
1044    ts.setAutoDetectUnicode(true);
1045    yySourceCodec = ts.codec();
1046    if (yySourceCodec->name() == "UTF-16")
1047        translator.setCodecName("System");
1048    yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-");
1049    yyInStr = ts.readAll();
1050    yyInPos = 0;
1051    yyFileName = cd.m_sourceFileName;
1052    yySavedBraceDepth.clear();
1053    yySavedParenDepth.clear();
1054    yyBraceDepth = 0;
1055    yyParenDepth = 0;
1056    yyCurLineNo = 1;
1057    yyBraceLineNo = 1;
1058    yyParenLineNo = 1;
1059    yyCh = getChar();
1060
1061    parse(&translator, defaultContext, defaultContext);
1062
1063    return true;
1064}
1065
1066int initCPP()
1067{
1068    Translator::FileFormat format;
1069    format.extension = QLatin1String("cpp");
1070    format.fileType = Translator::FileFormat::SourceCode;
1071    format.priority = 0;
1072    format.description = QObject::tr("C++ source files");
1073    format.loader = &loadCPP;
1074    format.saver = 0;
1075    Translator::registerFileFormat(format);
1076    return 1;
1077}
1078
1079Q_CONSTRUCTOR_FUNCTION(initCPP)
1080
1081QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.