source: trunk/tools/linguist/shared/cpp.cpp@ 385

Last change on this file since 385 was 2, checked in by Dmitry A. Kuminov, 16 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.