source: trunk/tools/linguist/lupdate/cpp.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 80.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the Qt Linguist of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "lupdate.h"
43
44#include <translator.h>
45
46#include <QtCore/QBitArray>
47#include <QtCore/QDebug>
48#include <QtCore/QFileInfo>
49#include <QtCore/QStack>
50#include <QtCore/QString>
51#include <QtCore/QTextCodec>
52#include <QtCore/QTextStream>
53#include <QtCore/QCoreApplication>
54
55#include <iostream>
56
57#include <ctype.h> // for isXXX()
58
59QT_BEGIN_NAMESPACE
60
61class LU {
62 Q_DECLARE_TR_FUNCTIONS(LUpdate)
63};
64
65/* qmake ignore Q_OBJECT */
66
67static QString MagicComment(QLatin1String("TRANSLATOR"));
68
69#define STRING(s) static QString str##s(QLatin1String(#s))
70
71//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this
72
73class HashString {
74public:
75 HashString() : m_hash(0x80000000) {}
76 explicit HashString(const QString &str) : m_str(str), m_hash(0x80000000) {}
77 void setValue(const QString &str) { m_str = str; m_hash = 0x80000000; }
78 const QString &value() const { return m_str; }
79 bool operator==(const HashString &other) const { return m_str == other.m_str; }
80private:
81 QString m_str;
82 // qHash() of a QString is only 28 bits wide, so we can use
83 // the highest bit(s) as the "hash valid" flag.
84 mutable uint m_hash;
85 friend uint qHash(const HashString &str);
86};
87
88uint qHash(const HashString &str)
89{
90 if (str.m_hash & 0x80000000)
91 str.m_hash = qHash(str.m_str);
92 return str.m_hash;
93}
94
95class HashStringList {
96public:
97 explicit HashStringList(const QList<HashString> &list) : m_list(list), m_hash(0x80000000) {}
98 const QList<HashString> &value() const { return m_list; }
99 bool operator==(const HashStringList &other) const { return m_list == other.m_list; }
100private:
101 QList<HashString> m_list;
102 mutable uint m_hash;
103 friend uint qHash(const HashStringList &list);
104};
105
106uint qHash(const HashStringList &list)
107{
108 if (list.m_hash & 0x80000000) {
109 uint hash = 0;
110 foreach (const HashString &qs, list.m_list) {
111 hash ^= qHash(qs) ^ 0x0ad9f526;
112 hash = ((hash << 13) & 0x0fffffff) | (hash >> 15);
113 }
114 list.m_hash = hash;
115 }
116 return list.m_hash;
117}
118
119typedef QList<HashString> NamespaceList;
120
121struct Namespace {
122
123 Namespace() :
124 classDef(this),
125 hasTrFunctions(false), complained(false)
126 {}
127 ~Namespace()
128 {
129 qDeleteAll(children);
130 }
131
132 QHash<HashString, Namespace *> children;
133 QHash<HashString, NamespaceList> aliases;
134 QList<HashStringList> usings;
135
136 // Class declarations set no flags and create no namespaces, so they are ignored.
137 // Class definitions may appear multiple times - but only because we are trying to
138 // "compile" all sources irrespective of build configuration.
139 // Nested classes may be forward-declared inside a definition, and defined in another file.
140 // The latter will detach the class' child list, so clones need a backlink to the original
141 // definition (either one in case of multiple definitions).
142 // Namespaces can have tr() functions as well, so we need to track parent definitions for
143 // them as well. The complication is that we may have to deal with a forrest instead of
144 // a tree - in that case the parent will be arbitrary. However, it seem likely that
145 // Q_DECLARE_TR_FUNCTIONS would be used either in "class-like" namespaces with a central
146 // header or only locally in a file.
147 Namespace *classDef;
148
149 QString trQualification;
150
151 bool hasTrFunctions;
152 bool complained; // ... that tr functions are missing.
153};
154
155static int nextFileId;
156
157class VisitRecorder {
158public:
159 VisitRecorder()
160 {
161 m_ba.resize(nextFileId);
162 }
163 bool tryVisit(int fileId)
164 {
165 if (m_ba.at(fileId))
166 return false;
167 m_ba[fileId] = true;
168 return true;
169 }
170private:
171 QBitArray m_ba;
172};
173
174struct ParseResults {
175 int fileId;
176 Namespace rootNamespace;
177 QSet<const ParseResults *> includes;
178};
179
180typedef QHash<QString, const ParseResults *> ParseResultHash;
181typedef QHash<QString, const Translator *> TranslatorHash;
182
183class CppFiles {
184
185public:
186 static const ParseResults *getResults(const QString &cleanFile);
187 static void setResults(const QString &cleanFile, const ParseResults *results);
188 static const Translator *getTranslator(const QString &cleanFile);
189 static void setTranslator(const QString &cleanFile, const Translator *results);
190 static bool isBlacklisted(const QString &cleanFile);
191 static void setBlacklisted(const QString &cleanFile);
192
193private:
194 static ParseResultHash &parsedFiles();
195 static TranslatorHash &translatedFiles();
196 static QSet<QString> &blacklistedFiles();
197};
198
199class CppParser {
200
201public:
202 CppParser(ParseResults *results = 0);
203 void setInput(const QString &in);
204 void setInput(QTextStream &ts, const QString &fileName);
205 void setTranslator(Translator *_tor) { tor = _tor; }
206 void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions);
207 void parseInternal(ConversionData &cd, QSet<QString> &inclusions);
208 const ParseResults *recordResults(bool isHeader);
209 void deleteResults() { delete results; }
210
211 struct SavedState {
212 NamespaceList namespaces;
213 QStack<int> namespaceDepths;
214 NamespaceList functionContext;
215 QString functionContextUnresolved;
216 QString pendingContext;
217 };
218
219private:
220 struct IfdefState {
221 IfdefState() {}
222 IfdefState(int _bracketDepth, int _braceDepth, int _parenDepth) :
223 bracketDepth(_bracketDepth),
224 braceDepth(_braceDepth),
225 parenDepth(_parenDepth),
226 elseLine(-1)
227 {}
228
229 SavedState state;
230 int bracketDepth, bracketDepth1st;
231 int braceDepth, braceDepth1st;
232 int parenDepth, parenDepth1st;
233 int elseLine;
234 };
235
236 std::ostream &yyMsg(int line = 0);
237
238 uint getChar();
239 uint getToken();
240 bool getMacroArgs();
241 bool match(uint t);
242 bool matchString(QString *s);
243 bool matchEncoding(bool *utf8);
244 bool matchStringOrNull(QString *s);
245 bool matchExpression();
246
247 QString transcode(const QString &str, bool utf8);
248 void recordMessage(
249 int line, const QString &context, const QString &text, const QString &comment,
250 const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
251 bool utf8, bool plural);
252
253 void processInclude(const QString &file, ConversionData &cd,
254 QSet<QString> &inclusions);
255
256 void saveState(SavedState *state);
257 void loadState(const SavedState *state);
258
259 static QString stringifyNamespace(const NamespaceList &namespaces);
260 static QStringList stringListifyNamespace(const NamespaceList &namespaces);
261 typedef bool (CppParser::*VisitNamespaceCallback)(const Namespace *ns, void *context) const;
262 bool visitNamespace(const NamespaceList &namespaces, int nsCount,
263 VisitNamespaceCallback callback, void *context,
264 VisitRecorder &vr, const ParseResults *rslt) const;
265 bool visitNamespace(const NamespaceList &namespaces, int nsCount,
266 VisitNamespaceCallback callback, void *context) const;
267 static QStringList stringListifySegments(const QList<HashString> &namespaces);
268 bool qualifyOneCallbackOwn(const Namespace *ns, void *context) const;
269 bool qualifyOneCallbackUsing(const Namespace *ns, void *context) const;
270 bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
271 NamespaceList *resolved, QSet<HashStringList> *visitedUsings) const;
272 bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
273 NamespaceList *resolved) const;
274 bool fullyQualify(const NamespaceList &namespaces, int nsCnt,
275 const QList<HashString> &segments, bool isDeclaration,
276 NamespaceList *resolved, QStringList *unresolved) const;
277 bool fullyQualify(const NamespaceList &namespaces,
278 const QList<HashString> &segments, bool isDeclaration,
279 NamespaceList *resolved, QStringList *unresolved) const;
280 bool fullyQualify(const NamespaceList &namespaces,
281 const QString &segments, bool isDeclaration,
282 NamespaceList *resolved, QStringList *unresolved) const;
283 bool findNamespaceCallback(const Namespace *ns, void *context) const;
284 const Namespace *findNamespace(const NamespaceList &namespaces, int nsCount = -1) const;
285 void enterNamespace(NamespaceList *namespaces, const HashString &name);
286 void truncateNamespaces(NamespaceList *namespaces, int lenght);
287 Namespace *modifyNamespace(NamespaceList *namespaces, bool haveLast = true);
288
289 enum {
290 Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return,
291 Tok_tr, Tok_trUtf8, Tok_translate, Tok_translateUtf8, Tok_trid,
292 Tok_Q_OBJECT, Tok_Q_DECLARE_TR_FUNCTIONS,
293 Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon,
294 Tok_Equals, Tok_LeftBracket, Tok_RightBracket,
295 Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon,
296 Tok_Null, Tok_Integer,
297 Tok_QuotedInclude, Tok_AngledInclude,
298 Tok_Other
299 };
300
301 // Tokenizer state
302 QString yyFileName;
303 int yyCh;
304 bool yyAtNewline;
305 bool yyCodecIsUtf8;
306 bool yyForceUtf8;
307 QString yyWord;
308 qlonglong yyInteger;
309 QStack<IfdefState> yyIfdefStack;
310 int yyBracketDepth;
311 int yyBraceDepth;
312 int yyParenDepth;
313 int yyLineNo;
314 int yyCurLineNo;
315 int yyBracketLineNo;
316 int yyBraceLineNo;
317 int yyParenLineNo;
318
319 // the string to read from and current position in the string
320 QTextCodec *yySourceCodec;
321 QString yyInStr;
322 const ushort *yyInPtr;
323
324 // Parser state
325 uint yyTok;
326
327 NamespaceList namespaces;
328 QStack<int> namespaceDepths;
329 NamespaceList functionContext;
330 QString functionContextUnresolved;
331 QString prospectiveContext;
332 QString pendingContext;
333 ParseResults *results;
334 Translator *tor;
335 bool directInclude;
336
337 SavedState savedState;
338 int yyMinBraceDepth;
339 bool inDefine;
340};
341
342CppParser::CppParser(ParseResults *_results)
343{
344 tor = 0;
345 if (_results) {
346 results = _results;
347 directInclude = true;
348 } else {
349 results = new ParseResults;
350 directInclude = false;
351 }
352 yyBracketDepth = 0;
353 yyBraceDepth = 0;
354 yyParenDepth = 0;
355 yyCurLineNo = 1;
356 yyBracketLineNo = 1;
357 yyBraceLineNo = 1;
358 yyParenLineNo = 1;
359 yyAtNewline = true;
360 yyMinBraceDepth = 0;
361 inDefine = false;
362}
363
364
365std::ostream &CppParser::yyMsg(int line)
366{
367 return std::cerr << qPrintable(yyFileName) << ':' << (line ? line : yyLineNo) << ": ";
368}
369
370void CppParser::setInput(const QString &in)
371{
372 yyInStr = in;
373 yyFileName = QString();
374 yySourceCodec = 0;
375 yyForceUtf8 = true;
376}
377
378void CppParser::setInput(QTextStream &ts, const QString &fileName)
379{
380 yyInStr = ts.readAll();
381 yyFileName = fileName;
382 yySourceCodec = ts.codec();
383 yyForceUtf8 = false;
384}
385
386/*
387 The first part of this source file is the C++ tokenizer. We skip
388 most of C++; the only tokens that interest us are defined here.
389 Thus, the code fragment
390
391 int main()
392 {
393 printf("Hello, world!\n");
394 return 0;
395 }
396
397 is broken down into the following tokens (Tok_ omitted):
398
399 Ident Ident LeftParen RightParen
400 LeftBrace
401 Ident LeftParen String RightParen Semicolon
402 return Semicolon
403 RightBrace.
404
405 The 0 doesn't produce any token.
406*/
407
408uint CppParser::getChar()
409{
410 const ushort *uc = yyInPtr;
411 forever {
412 ushort c = *uc;
413 if (!c) {
414 yyInPtr = uc;
415 return EOF;
416 }
417 ++uc;
418 if (c == '\\') {
419 ushort cc = *uc;
420 if (cc == '\n') {
421 ++yyCurLineNo;
422 ++uc;
423 continue;
424 }
425 if (cc == '\r') {
426 ++yyCurLineNo;
427 ++uc;
428 if (*uc == '\n')
429 ++uc;
430 continue;
431 }
432 }
433 if (c == '\r') {
434 if (*uc == '\n')
435 ++uc;
436 c = '\n';
437 ++yyCurLineNo;
438 yyAtNewline = true;
439 } else if (c == '\n') {
440 ++yyCurLineNo;
441 yyAtNewline = true;
442 } else if (c != ' ' && c != '\t' && c != '#') {
443 yyAtNewline = false;
444 }
445 yyInPtr = uc;
446 return c;
447 }
448}
449
450// This ignores commas, parens and comments.
451// IOW, it understands only a single, simple argument.
452bool CppParser::getMacroArgs()
453{
454 // Failing this assertion would mean losing the preallocated buffer.
455 Q_ASSERT(yyWord.isDetached());
456 yyWord.resize(0);
457
458 while (isspace(yyCh))
459 yyCh = getChar();
460 if (yyCh != '(')
461 return false;
462 do {
463 yyCh = getChar();
464 } while (isspace(yyCh));
465 ushort *ptr = (ushort *)yyWord.unicode();
466 while (yyCh != ')') {
467 if (yyCh == EOF)
468 return false;
469 *ptr++ = yyCh;
470 yyCh = getChar();
471 }
472 yyCh = getChar();
473 for (; ptr != (ushort *)yyWord.unicode() && isspace(*(ptr - 1)); --ptr) ;
474 yyWord.resize(ptr - (ushort *)yyWord.unicode());
475 return true;
476}
477
478STRING(Q_OBJECT);
479STRING(Q_DECLARE_TR_FUNCTIONS);
480STRING(QT_TR_NOOP);
481STRING(QT_TRID_NOOP);
482STRING(QT_TRANSLATE_NOOP);
483STRING(QT_TRANSLATE_NOOP3);
484STRING(QT_TR_NOOP_UTF8);
485STRING(QT_TRANSLATE_NOOP_UTF8);
486STRING(QT_TRANSLATE_NOOP3_UTF8);
487STRING(class);
488// QTranslator::findMessage() has the same parameters as QApplication::translate()
489STRING(findMessage);
490STRING(friend);
491STRING(namespace);
492STRING(operator);
493STRING(qtTrId);
494STRING(return);
495STRING(struct);
496STRING(TR);
497STRING(Tr);
498STRING(tr);
499STRING(trUtf8);
500STRING(translate);
501STRING(using);
502
503uint CppParser::getToken()
504{
505 restart:
506 // Failing this assertion would mean losing the preallocated buffer.
507 Q_ASSERT(yyWord.isDetached());
508 yyWord.resize(0);
509
510 while (yyCh != EOF) {
511 yyLineNo = yyCurLineNo;
512
513 if (yyCh == '#' && yyAtNewline) {
514 /*
515 Early versions of lupdate complained about
516 unbalanced braces in the following code:
517
518 #ifdef ALPHA
519 while (beta) {
520 #else
521 while (gamma) {
522 #endif
523 delta;
524 }
525
526 The code contains, indeed, two opening braces for
527 one closing brace; yet there's no reason to panic.
528
529 The solution is to remember yyBraceDepth as it was
530 when #if, #ifdef or #ifndef was met, and to set
531 yyBraceDepth to that value when meeting #elif or
532 #else.
533 */
534 do {
535 yyCh = getChar();
536 } while (isspace(yyCh) && yyCh != '\n');
537
538 switch (yyCh) {
539 case 'd': // define
540 // Skip over the name of the define to avoid it being interpreted as c++ code
541 do { // Rest of "define"
542 yyCh = getChar();
543 if (yyCh == EOF)
544 return Tok_Eof;
545 if (yyCh == '\n')
546 goto restart;
547 } while (!isspace(yyCh));
548 do { // Space beween "define" and macro name
549 yyCh = getChar();
550 if (yyCh == EOF)
551 return Tok_Eof;
552 if (yyCh == '\n')
553 goto restart;
554 } while (isspace(yyCh));
555 do { // Macro name
556 if (yyCh == '(') {
557 // Argument list. Follows the name without a space, and no
558 // paren nesting is possible.
559 do {
560 yyCh = getChar();
561 if (yyCh == EOF)
562 return Tok_Eof;
563 if (yyCh == '\n')
564 goto restart;
565 } while (yyCh != ')');
566 break;
567 }
568 yyCh = getChar();
569 if (yyCh == EOF)
570 return Tok_Eof;
571 if (yyCh == '\n')
572 goto restart;
573 } while (!isspace(yyCh));
574 do { // Shortcut the immediate newline case if no comments follow.
575 yyCh = getChar();
576 if (yyCh == EOF)
577 return Tok_Eof;
578 if (yyCh == '\n')
579 goto restart;
580 } while (isspace(yyCh));
581
582 saveState(&savedState);
583 yyMinBraceDepth = yyBraceDepth;
584 inDefine = true;
585 goto restart;
586 case 'i':
587 yyCh = getChar();
588 if (yyCh == 'f') {
589 // if, ifdef, ifndef
590 yyIfdefStack.push(IfdefState(yyBracketDepth, yyBraceDepth, yyParenDepth));
591 yyCh = getChar();
592 } else if (yyCh == 'n') {
593 // include
594 do {
595 yyCh = getChar();
596 } while (yyCh != EOF && !isspace(yyCh));
597 do {
598 yyCh = getChar();
599 } while (isspace(yyCh));
600 int tChar;
601 if (yyCh == '"')
602 tChar = '"';
603 else if (yyCh == '<')
604 tChar = '>';
605 else
606 break;
607 ushort *ptr = (ushort *)yyWord.unicode();
608 forever {
609 yyCh = getChar();
610 if (yyCh == EOF || yyCh == '\n')
611 break;
612 if (yyCh == tChar) {
613 yyCh = getChar();
614 break;
615 }
616 *ptr++ = yyCh;
617 }
618 yyWord.resize(ptr - (ushort *)yyWord.unicode());
619 return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude;
620 }
621 break;
622 case 'e':
623 yyCh = getChar();
624 if (yyCh == 'l') {
625 // elif, else
626 if (!yyIfdefStack.isEmpty()) {
627 IfdefState &is = yyIfdefStack.top();
628 if (is.elseLine != -1) {
629 if (yyBracketDepth != is.bracketDepth1st
630 || yyBraceDepth != is.braceDepth1st
631 || yyParenDepth != is.parenDepth1st)
632 yyMsg(is.elseLine)
633 << qPrintable(LU::tr("Parenthesis/bracket/brace mismatch between "
634 "#if and #else branches; using #if branch\n"));
635 } else {
636 is.bracketDepth1st = yyBracketDepth;
637 is.braceDepth1st = yyBraceDepth;
638 is.parenDepth1st = yyParenDepth;
639 saveState(&is.state);
640 }
641 is.elseLine = yyLineNo;
642 yyBracketDepth = is.bracketDepth;
643 yyBraceDepth = is.braceDepth;
644 yyParenDepth = is.parenDepth;
645 }
646 yyCh = getChar();
647 } else if (yyCh == 'n') {
648 // endif
649 if (!yyIfdefStack.isEmpty()) {
650 IfdefState is = yyIfdefStack.pop();
651 if (is.elseLine != -1) {
652 if (yyBracketDepth != is.bracketDepth1st
653 || yyBraceDepth != is.braceDepth1st
654 || yyParenDepth != is.parenDepth1st)
655 yyMsg(is.elseLine)
656 << qPrintable(LU::tr("Parenthesis/brace mismatch between "
657 "#if and #else branches; using #if branch\n"));
658 yyBracketDepth = is.bracketDepth1st;
659 yyBraceDepth = is.braceDepth1st;
660 yyParenDepth = is.parenDepth1st;
661 loadState(&is.state);
662 }
663 }
664 yyCh = getChar();
665 }
666 break;
667 }
668 // Optimization: skip over rest of preprocessor directive
669 do {
670 if (yyCh == '/') {
671 yyCh = getChar();
672 if (yyCh == '/') {
673 do {
674 yyCh = getChar();
675 } while (yyCh != EOF && yyCh != '\n');
676 break;
677 } else if (yyCh == '*') {
678 bool metAster = false;
679
680 forever {
681 yyCh = getChar();
682 if (yyCh == EOF) {
683 yyMsg() << qPrintable(LU::tr("Unterminated C++ comment\n"));
684 break;
685 }
686
687 if (yyCh == '*') {
688 metAster = true;
689 } else if (metAster && yyCh == '/') {
690 yyCh = getChar();
691 break;
692 } else {
693 metAster = false;
694 }
695 }
696 }
697 } else {
698 yyCh = getChar();
699 }
700 } while (yyCh != '\n' && yyCh != EOF);
701 yyCh = getChar();
702 } else if ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z') || yyCh == '_') {
703 ushort *ptr = (ushort *)yyWord.unicode();
704 do {
705 *ptr++ = yyCh;
706 yyCh = getChar();
707 } while ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z')
708 || (yyCh >= '0' && yyCh <= '9') || yyCh == '_');
709 yyWord.resize(ptr - (ushort *)yyWord.unicode());
710
711 //qDebug() << "IDENT: " << yyWord;
712
713 switch (yyWord.unicode()[0].unicode()) {
714 case 'Q':
715 if (yyWord == strQ_OBJECT)
716 return Tok_Q_OBJECT;
717 if (yyWord == strQ_DECLARE_TR_FUNCTIONS)
718 return Tok_Q_DECLARE_TR_FUNCTIONS;
719 if (yyWord == strQT_TR_NOOP)
720 return Tok_tr;
721 if (yyWord == strQT_TRID_NOOP)
722 return Tok_trid;
723 if (yyWord == strQT_TRANSLATE_NOOP)
724 return Tok_translate;
725 if (yyWord == strQT_TRANSLATE_NOOP3)
726 return Tok_translate;
727 if (yyWord == strQT_TR_NOOP_UTF8)
728 return Tok_trUtf8;
729 if (yyWord == strQT_TRANSLATE_NOOP_UTF8)
730 return Tok_translateUtf8;
731 if (yyWord == strQT_TRANSLATE_NOOP3_UTF8)
732 return Tok_translateUtf8;
733 break;
734 case 'T':
735 // TR() for when all else fails
736 if (yyWord == strTR || yyWord == strTr)
737 return Tok_tr;
738 break;
739 case 'c':
740 if (yyWord == strclass)
741 return Tok_class;
742 break;
743 case 'f':
744 /*
745 QTranslator::findMessage() has the same parameters as
746 QApplication::translate().
747 */
748 if (yyWord == strfindMessage)
749 return Tok_translate;
750 if (yyWord == strfriend)
751 return Tok_friend;
752 break;
753 case 'n':
754 if (yyWord == strnamespace)
755 return Tok_namespace;
756 break;
757 case 'o':
758 if (yyWord == stroperator) {
759 // Operator overload declaration/definition.
760 // We need to prevent those characters from confusing the followup
761 // parsing. Actually using them does not add value, so just eat them.
762 while (isspace(yyCh))
763 yyCh = getChar();
764 while (yyCh == '+' || yyCh == '-' || yyCh == '*' || yyCh == '/' || yyCh == '%'
765 || yyCh == '=' || yyCh == '<' || yyCh == '>' || yyCh == '!'
766 || yyCh == '&' || yyCh == '|' || yyCh == '~' || yyCh == '^'
767 || yyCh == '[' || yyCh == ']')
768 yyCh = getChar();
769 }
770 break;
771 case 'q':
772 if (yyWord == strqtTrId)
773 return Tok_trid;
774 break;
775 case 'r':
776 if (yyWord == strreturn)
777 return Tok_return;
778 break;
779 case 's':
780 if (yyWord == strstruct)
781 return Tok_class;
782 break;
783 case 't':
784 if (yyWord == strtr)
785 return Tok_tr;
786 if (yyWord == strtrUtf8)
787 return Tok_trUtf8;
788 if (yyWord == strtranslate)
789 return Tok_translate;
790 break;
791 case 'u':
792 if (yyWord == strusing)
793 return Tok_using;
794 break;
795 }
796 return Tok_Ident;
797 } else {
798 switch (yyCh) {
799 case '\n':
800 if (inDefine) {
801 loadState(&savedState);
802 prospectiveContext.clear();
803 yyBraceDepth = yyMinBraceDepth;
804 yyMinBraceDepth = 0;
805 inDefine = false;
806 }
807 yyCh = getChar();
808 break;
809 case '/':
810 yyCh = getChar();
811 if (yyCh == '/') {
812 ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
813 do {
814 yyCh = getChar();
815 if (yyCh == EOF)
816 break;
817 *ptr++ = yyCh;
818 } while (yyCh != '\n');
819 yyWord.resize(ptr - (ushort *)yyWord.unicode());
820 } else if (yyCh == '*') {
821 bool metAster = false;
822 ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
823
824 forever {
825 yyCh = getChar();
826 if (yyCh == EOF) {
827 yyMsg() << qPrintable(LU::tr("Unterminated C++ comment\n"));
828 break;
829 }
830 *ptr++ = yyCh;
831
832 if (yyCh == '*')
833 metAster = true;
834 else if (metAster && yyCh == '/')
835 break;
836 else
837 metAster = false;
838 }
839 yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2);
840
841 yyCh = getChar();
842 }
843 return Tok_Comment;
844 case '"': {
845 ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
846 yyCh = getChar();
847 while (yyCh != EOF && yyCh != '\n' && yyCh != '"') {
848 if (yyCh == '\\') {
849 yyCh = getChar();
850 if (yyCh == EOF || yyCh == '\n')
851 break;
852 *ptr++ = '\\';
853 }
854 *ptr++ = yyCh;
855 yyCh = getChar();
856 }
857 yyWord.resize(ptr - (ushort *)yyWord.unicode());
858
859 if (yyCh != '"')
860 yyMsg() << qPrintable(LU::tr("Unterminated C++ string\n"));
861 else
862 yyCh = getChar();
863 return Tok_String;
864 }
865 case '-':
866 yyCh = getChar();
867 if (yyCh == '>') {
868 yyCh = getChar();
869 return Tok_Arrow;
870 }
871 break;
872 case ':':
873 yyCh = getChar();
874 if (yyCh == ':') {
875 yyCh = getChar();
876 return Tok_ColonColon;
877 }
878 return Tok_Colon;
879 // Incomplete: '<' might be part of '<=' or of template syntax.
880 // The main intent of not completely ignoring it is to break
881 // parsing of things like std::cout << QObject::tr() as
882 // context std::cout::QObject (see Task 161106)
883 case '=':
884 yyCh = getChar();
885 return Tok_Equals;
886 case '>':
887 case '<':
888 yyCh = getChar();
889 return Tok_Other;
890 case '\'':
891 yyCh = getChar();
892 if (yyCh == '\\')
893 yyCh = getChar();
894
895 forever {
896 if (yyCh == EOF || yyCh == '\n') {
897 yyMsg() << "Unterminated C++ character\n";
898 break;
899 }
900 yyCh = getChar();
901 if (yyCh == '\'') {
902 yyCh = getChar();
903 break;
904 }
905 }
906 break;
907 case '{':
908 if (yyBraceDepth == 0)
909 yyBraceLineNo = yyCurLineNo;
910 yyBraceDepth++;
911 yyCh = getChar();
912 return Tok_LeftBrace;
913 case '}':
914 if (yyBraceDepth == yyMinBraceDepth) {
915 if (!inDefine)
916 yyMsg(yyCurLineNo)
917 << qPrintable(LU::tr("Excess closing brace in C++ code"
918 " (or abuse of the C++ preprocessor)\n"));
919 // Avoid things getting messed up even more
920 yyCh = getChar();
921 return Tok_Semicolon;
922 }
923 yyBraceDepth--;
924 yyCh = getChar();
925 return Tok_RightBrace;
926 case '(':
927 if (yyParenDepth == 0)
928 yyParenLineNo = yyCurLineNo;
929 yyParenDepth++;
930 yyCh = getChar();
931 return Tok_LeftParen;
932 case ')':
933 if (yyParenDepth == 0)
934 yyMsg(yyCurLineNo)
935 << qPrintable(LU::tr("Excess closing parenthesis in C++ code"
936 " (or abuse of the C++ preprocessor)\n"));
937 else
938 yyParenDepth--;
939 yyCh = getChar();
940 return Tok_RightParen;
941 case '[':
942 if (yyBracketDepth == 0)
943 yyBracketLineNo = yyCurLineNo;
944 yyBracketDepth++;
945 yyCh = getChar();
946 return Tok_LeftBracket;
947 case ']':
948 if (yyBracketDepth == 0)
949 yyMsg(yyCurLineNo)
950 << qPrintable(LU::tr("Excess closing bracket in C++ code"
951 " (or abuse of the C++ preprocessor)\n"));
952 else
953 yyBracketDepth--;
954 yyCh = getChar();
955 return Tok_RightBracket;
956 case ',':
957 yyCh = getChar();
958 return Tok_Comma;
959 case ';':
960 yyCh = getChar();
961 return Tok_Semicolon;
962 case '0':
963 yyCh = getChar();
964 if (yyCh == 'x') {
965 do {
966 yyCh = getChar();
967 } while ((yyCh >= '0' && yyCh <= '9')
968 || (yyCh >= 'a' && yyCh <= 'f') || (yyCh >= 'A' && yyCh <= 'F'));
969 return Tok_Integer;
970 }
971 if (yyCh < '0' || yyCh > '9')
972 return Tok_Null;
973 // Fallthrough
974 case '1':
975 case '2':
976 case '3':
977 case '4':
978 case '5':
979 case '6':
980 case '7':
981 case '8':
982 case '9':
983 do {
984 yyCh = getChar();
985 } while (yyCh >= '0' && yyCh <= '9');
986 return Tok_Integer;
987 default:
988 yyCh = getChar();
989 break;
990 }
991 }
992 }
993 return Tok_Eof;
994}
995
996/*
997 The second part of this source file are namespace/class related
998 utilities for the third part.
999*/
1000
1001void CppParser::saveState(SavedState *state)
1002{
1003 state->namespaces = namespaces;
1004 state->namespaceDepths = namespaceDepths;
1005 state->functionContext = functionContext;
1006 state->functionContextUnresolved = functionContextUnresolved;
1007 state->pendingContext = pendingContext;
1008}
1009
1010void CppParser::loadState(const SavedState *state)
1011{
1012 namespaces = state->namespaces;
1013 namespaceDepths = state->namespaceDepths;
1014 functionContext = state->functionContext;
1015 functionContextUnresolved = state->functionContextUnresolved;
1016 pendingContext = state->pendingContext;
1017}
1018
1019Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool haveLast)
1020{
1021 Namespace *pns, *ns = &results->rootNamespace;
1022 for (int i = 1; i < namespaces->count(); ++i) {
1023 pns = ns;
1024 if (!(ns = pns->children.value(namespaces->at(i)))) {
1025 do {
1026 ns = new Namespace;
1027 if (haveLast || i < namespaces->count() - 1)
1028 if (const Namespace *ons = findNamespace(*namespaces, i + 1))
1029 ns->classDef = ons->classDef;
1030 pns->children.insert(namespaces->at(i), ns);
1031 pns = ns;
1032 } while (++i < namespaces->count());
1033 break;
1034 }
1035 }
1036 return ns;
1037}
1038
1039QString CppParser::stringifyNamespace(const NamespaceList &namespaces)
1040{
1041 QString ret;
1042 for (int i = 1; i < namespaces.count(); ++i) {
1043 if (i > 1)
1044 ret += QLatin1String("::");
1045 ret += namespaces.at(i).value();
1046 }
1047 return ret;
1048}
1049
1050QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces)
1051{
1052 QStringList ret;
1053 for (int i = 1; i < namespaces.count(); ++i)
1054 ret << namespaces.at(i).value();
1055 return ret;
1056}
1057
1058bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount,
1059 VisitNamespaceCallback callback, void *context,
1060 VisitRecorder &vr, const ParseResults *rslt) const
1061{
1062 const Namespace *ns = &rslt->rootNamespace;
1063 for (int i = 1; i < nsCount; ++i)
1064 if (!(ns = ns->children.value(namespaces.at(i))))
1065 goto supers;
1066 if ((this->*callback)(ns, context))
1067 return true;
1068supers:
1069 foreach (const ParseResults *sup, rslt->includes)
1070 if (vr.tryVisit(sup->fileId)
1071 && visitNamespace(namespaces, nsCount, callback, context, vr, sup))
1072 return true;
1073 return false;
1074}
1075
1076bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount,
1077 VisitNamespaceCallback callback, void *context) const
1078{
1079 VisitRecorder vr;
1080 return visitNamespace(namespaces, nsCount, callback, context, vr, results);
1081}
1082
1083QStringList CppParser::stringListifySegments(const QList<HashString> &segments)
1084{
1085 QStringList ret;
1086 for (int i = 0; i < segments.count(); ++i)
1087 ret << segments.at(i).value();
1088 return ret;
1089}
1090
1091struct QualifyOneData {
1092 QualifyOneData(const NamespaceList &ns, int nsc, const HashString &seg, NamespaceList *rslvd,
1093 QSet<HashStringList> *visited)
1094 : namespaces(ns), nsCount(nsc), segment(seg), resolved(rslvd), visitedUsings(visited)
1095 {}
1096
1097 const NamespaceList &namespaces;
1098 int nsCount;
1099 const HashString &segment;
1100 NamespaceList *resolved;
1101 QSet<HashStringList> *visitedUsings;
1102};
1103
1104bool CppParser::qualifyOneCallbackOwn(const Namespace *ns, void *context) const
1105{
1106 QualifyOneData *data = (QualifyOneData *)context;
1107 if (ns->children.contains(data->segment)) {
1108 *data->resolved = data->namespaces.mid(0, data->nsCount);
1109 *data->resolved << data->segment;
1110 return true;
1111 }
1112 QHash<HashString, NamespaceList>::ConstIterator nsai = ns->aliases.constFind(data->segment);
1113 if (nsai != ns->aliases.constEnd()) {
1114 const NamespaceList &nsl = *nsai;
1115 if (nsl.last().value().isEmpty()) { // Delayed alias resolution
1116 NamespaceList &nslIn = *const_cast<NamespaceList *>(&nsl);
1117 nslIn.removeLast();
1118 NamespaceList nslOut;
1119 if (!fullyQualify(data->namespaces, data->nsCount, nslIn, false, &nslOut, 0)) {
1120 const_cast<Namespace *>(ns)->aliases.remove(data->segment);
1121 return false;
1122 }
1123 nslIn = nslOut;
1124 }
1125 *data->resolved = nsl;
1126 return true;
1127 }
1128 return false;
1129}
1130
1131bool CppParser::qualifyOneCallbackUsing(const Namespace *ns, void *context) const
1132{
1133 QualifyOneData *data = (QualifyOneData *)context;
1134 foreach (const HashStringList &use, ns->usings)
1135 if (!data->visitedUsings->contains(use)) {
1136 data->visitedUsings->insert(use);
1137 if (qualifyOne(use.value(), use.value().count(), data->segment, data->resolved,
1138 data->visitedUsings))
1139 return true;
1140 }
1141 return false;
1142}
1143
1144bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
1145 NamespaceList *resolved, QSet<HashStringList> *visitedUsings) const
1146{
1147 QualifyOneData data(namespaces, nsCnt, segment, resolved, visitedUsings);
1148
1149 if (visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackOwn, &data))
1150 return true;
1151
1152 return visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackUsing, &data);
1153}
1154
1155bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
1156 NamespaceList *resolved) const
1157{
1158 QSet<HashStringList> visitedUsings;
1159
1160 return qualifyOne(namespaces, nsCnt, segment, resolved, &visitedUsings);
1161}
1162
1163bool CppParser::fullyQualify(const NamespaceList &namespaces, int nsCnt,
1164 const QList<HashString> &segments, bool isDeclaration,
1165 NamespaceList *resolved, QStringList *unresolved) const
1166{
1167 int nsIdx;
1168 int initSegIdx;
1169
1170 if (segments.first().value().isEmpty()) {
1171 // fully qualified
1172 if (segments.count() == 1) {
1173 resolved->clear();
1174 *resolved << HashString(QString());
1175 return true;
1176 }
1177 initSegIdx = 1;
1178 nsIdx = 0;
1179 } else {
1180 initSegIdx = 0;
1181 nsIdx = nsCnt - 1;
1182 }
1183
1184 do {
1185 if (qualifyOne(namespaces, nsIdx + 1, segments[initSegIdx], resolved)) {
1186 int segIdx = initSegIdx;
1187 while (++segIdx < segments.count()) {
1188 if (!qualifyOne(*resolved, resolved->count(), segments[segIdx], resolved)) {
1189 if (unresolved)
1190 *unresolved = stringListifySegments(segments.mid(segIdx));
1191 return false;
1192 }
1193 }
1194 return true;
1195 }
1196 } while (!isDeclaration && --nsIdx >= 0);
1197 resolved->clear();
1198 *resolved << HashString(QString());
1199 if (unresolved)
1200 *unresolved = stringListifySegments(segments.mid(initSegIdx));
1201 return false;
1202}
1203
1204bool CppParser::fullyQualify(const NamespaceList &namespaces,
1205 const QList<HashString> &segments, bool isDeclaration,
1206 NamespaceList *resolved, QStringList *unresolved) const
1207{
1208 return fullyQualify(namespaces, namespaces.count(),
1209 segments, isDeclaration, resolved, unresolved);
1210}
1211
1212bool CppParser::fullyQualify(const NamespaceList &namespaces,
1213 const QString &quali, bool isDeclaration,
1214 NamespaceList *resolved, QStringList *unresolved) const
1215{
1216 static QString strColons(QLatin1String("::"));
1217
1218 QList<HashString> segments;
1219 foreach (const QString &str, quali.split(strColons)) // XXX slow, but needs to be fast(?)
1220 segments << HashString(str);
1221 return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved);
1222}
1223
1224bool CppParser::findNamespaceCallback(const Namespace *ns, void *context) const
1225{
1226 *((const Namespace **)context) = ns;
1227 return true;
1228}
1229
1230const Namespace *CppParser::findNamespace(const NamespaceList &namespaces, int nsCount) const
1231{
1232 const Namespace *ns = 0;
1233 if (nsCount == -1)
1234 nsCount = namespaces.count();
1235 visitNamespace(namespaces, nsCount, &CppParser::findNamespaceCallback, &ns);
1236 return ns;
1237}
1238
1239void CppParser::enterNamespace(NamespaceList *namespaces, const HashString &name)
1240{
1241 *namespaces << name;
1242 if (!findNamespace(*namespaces))
1243 modifyNamespace(namespaces, false);
1244}
1245
1246void CppParser::truncateNamespaces(NamespaceList *namespaces, int length)
1247{
1248 if (namespaces->count() > length)
1249 namespaces->erase(namespaces->begin() + length, namespaces->end());
1250}
1251
1252/*
1253 Functions for processing include files.
1254*/
1255
1256ParseResultHash &CppFiles::parsedFiles()
1257{
1258 static ParseResultHash parsed;
1259
1260 return parsed;
1261}
1262
1263TranslatorHash &CppFiles::translatedFiles()
1264{
1265 static TranslatorHash tors;
1266
1267 return tors;
1268}
1269
1270QSet<QString> &CppFiles::blacklistedFiles()
1271{
1272 static QSet<QString> blacklisted;
1273
1274 return blacklisted;
1275}
1276
1277const ParseResults *CppFiles::getResults(const QString &cleanFile)
1278{
1279 return parsedFiles().value(cleanFile);
1280}
1281
1282void CppFiles::setResults(const QString &cleanFile, const ParseResults *results)
1283{
1284 parsedFiles().insert(cleanFile, results);
1285}
1286
1287const Translator *CppFiles::getTranslator(const QString &cleanFile)
1288{
1289 return translatedFiles().value(cleanFile);
1290}
1291
1292void CppFiles::setTranslator(const QString &cleanFile, const Translator *tor)
1293{
1294 translatedFiles().insert(cleanFile, tor);
1295}
1296
1297bool CppFiles::isBlacklisted(const QString &cleanFile)
1298{
1299 return blacklistedFiles().contains(cleanFile);
1300}
1301
1302void CppFiles::setBlacklisted(const QString &cleanFile)
1303{
1304 blacklistedFiles().insert(cleanFile);
1305}
1306
1307static bool isHeader(const QString &name)
1308{
1309 QString fileExt = QFileInfo(name).suffix();
1310 return fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive);
1311}
1312
1313void CppParser::processInclude(const QString &file, ConversionData &cd,
1314 QSet<QString> &inclusions)
1315{
1316 QString cleanFile = QDir::cleanPath(file);
1317
1318 if (inclusions.contains(cleanFile)) {
1319 yyMsg() << qPrintable(LU::tr("circular inclusion of %1\n").arg(cleanFile));
1320 return;
1321 }
1322
1323 // If the #include is in any kind of namespace, has been blacklisted previously,
1324 // or is not a header file (stdc++ extensionless or *.h*), then really include
1325 // it. Otherwise it is safe to process it stand-alone and re-use the parsed
1326 // namespace data for inclusion into other files.
1327 bool isIndirect = false;
1328 if (namespaces.count() == 1 && functionContext.count() == 1
1329 && functionContextUnresolved.isEmpty() && pendingContext.isEmpty()
1330 && !CppFiles::isBlacklisted(cleanFile)
1331 && isHeader(cleanFile)) {
1332
1333 if (const ParseResults *res = CppFiles::getResults(cleanFile)) {
1334 results->includes.insert(res);
1335 return;
1336 }
1337
1338 isIndirect = true;
1339 }
1340
1341 QFile f(cleanFile);
1342 if (!f.open(QIODevice::ReadOnly)) {
1343 yyMsg() << qPrintable(LU::tr("Cannot open %1: %2\n").arg(cleanFile, f.errorString()));
1344 return;
1345 }
1346
1347 QTextStream ts(&f);
1348 ts.setCodec(yySourceCodec);
1349 ts.setAutoDetectUnicode(true);
1350
1351 inclusions.insert(cleanFile);
1352 if (isIndirect) {
1353 CppParser parser;
1354 foreach (const QString &projectRoot, cd.m_projectRoots)
1355 if (cleanFile.startsWith(projectRoot)) {
1356 parser.setTranslator(new Translator);
1357 break;
1358 }
1359 parser.setInput(ts, cleanFile);
1360 parser.parse(cd.m_defaultContext, cd, inclusions);
1361 results->includes.insert(parser.recordResults(true));
1362 } else {
1363 CppParser parser(results);
1364 parser.namespaces = namespaces;
1365 parser.functionContext = functionContext;
1366 parser.functionContextUnresolved = functionContextUnresolved;
1367 parser.pendingContext = pendingContext;
1368 parser.setInput(ts, cleanFile);
1369 parser.parseInternal(cd, inclusions);
1370 // Avoid that messages obtained by direct scanning are used
1371 CppFiles::setBlacklisted(cleanFile);
1372 }
1373 inclusions.remove(cleanFile);
1374}
1375
1376/*
1377 The third part of this source file is the parser. It accomplishes
1378 a very easy task: It finds all strings inside a tr() or translate()
1379 call, and possibly finds out the context of the call. It supports
1380 three cases: (1) the context is specified, as in
1381 FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello");
1382 (2) the call appears within an inlined function; (3) the call
1383 appears within a function defined outside the class definition.
1384*/
1385
1386bool CppParser::match(uint t)
1387{
1388 bool matches = (yyTok == t);
1389 if (matches)
1390 yyTok = getToken();
1391 return matches;
1392}
1393
1394bool CppParser::matchString(QString *s)
1395{
1396 bool matches = false;
1397 s->clear();
1398 forever {
1399 while (yyTok == Tok_Comment)
1400 yyTok = getToken();
1401 if (yyTok != Tok_String)
1402 return matches;
1403 matches = true;
1404 *s += yyWord;
1405 s->detach();
1406 yyTok = getToken();
1407 }
1408}
1409
1410STRING(QApplication);
1411STRING(QCoreApplication);
1412STRING(UnicodeUTF8);
1413STRING(DefaultCodec);
1414STRING(CodecForTr);
1415
1416bool CppParser::matchEncoding(bool *utf8)
1417{
1418 if (yyTok != Tok_Ident)
1419 return false;
1420 if (yyWord == strQApplication || yyWord == strQCoreApplication) {
1421 yyTok = getToken();
1422 if (yyTok == Tok_ColonColon)
1423 yyTok = getToken();
1424 }
1425 if (yyWord == strUnicodeUTF8) {
1426 *utf8 = true;
1427 yyTok = getToken();
1428 return true;
1429 }
1430 if (yyWord == strDefaultCodec || yyWord == strCodecForTr) {
1431 *utf8 = false;
1432 yyTok = getToken();
1433 return true;
1434 }
1435 return false;
1436}
1437
1438bool CppParser::matchStringOrNull(QString *s)
1439{
1440 return matchString(s) || match(Tok_Null);
1441}
1442
1443/*
1444 * match any expression that can return a number, which can be
1445 * 1. Literal number (e.g. '11')
1446 * 2. simple identifier (e.g. 'm_count')
1447 * 3. simple function call (e.g. 'size()' )
1448 * 4. function call on an object (e.g. 'list.size()')
1449 * 5. function call on an object (e.g. 'list->size()')
1450 *
1451 * Other cases:
1452 * size(2,4)
1453 * list().size()
1454 * list(a,b).size(2,4)
1455 * etc...
1456 */
1457bool CppParser::matchExpression()
1458{
1459 if (match(Tok_Null) || match(Tok_Integer))
1460 return true;
1461
1462 int parenlevel = 0;
1463 while (match(Tok_Ident) || parenlevel > 0) {
1464 if (yyTok == Tok_RightParen) {
1465 if (parenlevel == 0) break;
1466 --parenlevel;
1467 yyTok = getToken();
1468 } else if (yyTok == Tok_LeftParen) {
1469 yyTok = getToken();
1470 if (yyTok == Tok_RightParen) {
1471 yyTok = getToken();
1472 } else {
1473 ++parenlevel;
1474 }
1475 } else if (yyTok == Tok_Ident) {
1476 continue;
1477 } else if (yyTok == Tok_Arrow) {
1478 yyTok = getToken();
1479 } else if (parenlevel == 0) {
1480 return false;
1481 }
1482 }
1483 return true;
1484}
1485
1486QString CppParser::transcode(const QString &str, bool utf8)
1487{
1488 static const char tab[] = "abfnrtv";
1489 static const char backTab[] = "\a\b\f\n\r\t\v";
1490 // This function has to convert back to bytes, as C's \0* sequences work at that level.
1491 const QByteArray in = yyForceUtf8 ? str.toUtf8() : tor->codec()->fromUnicode(str);
1492 QByteArray out;
1493
1494 out.reserve(in.length());
1495 for (int i = 0; i < in.length();) {
1496 uchar c = in[i++];
1497 if (c == '\\') {
1498 if (i >= in.length())
1499 break;
1500 c = in[i++];
1501
1502 if (c == '\n')
1503 continue;
1504
1505 if (c == 'x') {
1506 QByteArray hex;
1507 while (i < in.length() && isxdigit((c = in[i]))) {
1508 hex += c;
1509 i++;
1510 }
1511 out += hex.toUInt(0, 16);
1512 } else if (c >= '0' && c < '8') {
1513 QByteArray oct;
1514 int n = 0;
1515 oct += c;
1516 while (n < 2 && i < in.length() && (c = in[i]) >= '0' && c < '8') {
1517 i++;
1518 n++;
1519 oct += c;
1520 }
1521 out += oct.toUInt(0, 8);
1522 } else {
1523 const char *p = strchr(tab, c);
1524 out += !p ? c : backTab[p - tab];
1525 }
1526 } else {
1527 out += c;
1528 }
1529 }
1530 return (utf8 || yyForceUtf8) ? QString::fromUtf8(out.constData(), out.length())
1531 : tor->codec()->toUnicode(out);
1532}
1533
1534void CppParser::recordMessage(
1535 int line, const QString &context, const QString &text, const QString &comment,
1536 const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
1537 bool utf8, bool plural)
1538{
1539 TranslatorMessage msg(
1540 transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(),
1541 yyFileName, line, QStringList(),
1542 TranslatorMessage::Unfinished, plural);
1543 msg.setExtraComment(transcode(extracomment.simplified(), utf8));
1544 msg.setId(msgid);
1545 msg.setExtras(extra);
1546 if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit())
1547 msg.setUtf8(true);
1548 tor->append(msg);
1549}
1550
1551void CppParser::parse(const QString &initialContext, ConversionData &cd,
1552 QSet<QString> &inclusions)
1553{
1554 if (tor)
1555 yyCodecIsUtf8 = (tor->codecName() == "UTF-8");
1556
1557 namespaces << HashString();
1558 functionContext = namespaces;
1559 functionContextUnresolved = initialContext;
1560
1561 parseInternal(cd, inclusions);
1562}
1563
1564void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
1565{
1566 static QString strColons(QLatin1String("::"));
1567
1568 QString context;
1569 QString text;
1570 QString comment;
1571 QString extracomment;
1572 QString msgid;
1573 QString sourcetext;
1574 TranslatorMessage::ExtraData extra;
1575 QString prefix;
1576#ifdef DIAGNOSE_RETRANSLATABILITY
1577 QString functionName;
1578#endif
1579 int line;
1580 bool utf8;
1581 bool yyTokColonSeen = false; // Start of c'tor's initializer list
1582
1583 yyWord.reserve(yyInStr.size()); // Rather insane. That's because we do no length checking.
1584 yyInPtr = (const ushort *)yyInStr.unicode();
1585 yyCh = getChar();
1586 yyTok = getToken();
1587 while (yyTok != Tok_Eof) {
1588 // these are array indexing operations. we ignore them entirely
1589 // so they don't confuse our scoping of static initializers.
1590 // we enter the loop by either reading a left bracket or by an
1591 // #else popping the state.
1592 while (yyBracketDepth)
1593 yyTok = getToken();
1594 //qDebug() << "TOKEN: " << yyTok;
1595 switch (yyTok) {
1596 case Tok_QuotedInclude: {
1597 text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord);
1598 text.detach();
1599 if (QFileInfo(text).isFile()) {
1600 processInclude(text, cd, inclusions);
1601 yyTok = getToken();
1602 break;
1603 }
1604 }
1605 /* fall through */
1606 case Tok_AngledInclude: {
1607 QStringList cSources = cd.m_allCSources.values(yyWord);
1608 if (!cSources.isEmpty()) {
1609 foreach (const QString &cSource, cSources)
1610 processInclude(cSource, cd, inclusions);
1611 goto incOk;
1612 }
1613 foreach (const QString &incPath, cd.m_includePath) {
1614 text = QDir(incPath).absoluteFilePath(yyWord);
1615 text.detach();
1616 if (QFileInfo(text).isFile()) {
1617 processInclude(text, cd, inclusions);
1618 goto incOk;
1619 }
1620 }
1621 incOk:
1622 yyTok = getToken();
1623 break;
1624 }
1625 case Tok_friend:
1626 yyTok = getToken();
1627 // These are forward declarations, so ignore them.
1628 if (yyTok == Tok_class)
1629 yyTok = getToken();
1630 break;
1631 case Tok_class:
1632 yyTokColonSeen = false;
1633 /*
1634 Partial support for inlined functions.
1635 */
1636 yyTok = getToken();
1637 if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) {
1638 QList<HashString> quali;
1639 HashString fct;
1640 do {
1641 /*
1642 This code should execute only once, but we play
1643 safe with impure definitions such as
1644 'class Q_EXPORT QMessageBox', in which case
1645 'QMessageBox' is the class name, not 'Q_EXPORT'.
1646 */
1647 text = yyWord;
1648 text.detach();
1649 fct.setValue(text);
1650 yyTok = getToken();
1651 } while (yyTok == Tok_Ident);
1652 while (yyTok == Tok_ColonColon) {
1653 yyTok = getToken();
1654 if (yyTok != Tok_Ident)
1655 break; // Oops ...
1656 quali << fct;
1657 text = yyWord;
1658 text.detach();
1659 fct.setValue(text);
1660 yyTok = getToken();
1661 }
1662 while (yyTok == Tok_Comment)
1663 yyTok = getToken();
1664 if (yyTok == Tok_Colon) {
1665 // Skip any token until '{' since we might do things wrong if we find
1666 // a '::' token here.
1667 do {
1668 yyTok = getToken();
1669 } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof);
1670 } else {
1671 if (yyTok != Tok_LeftBrace) {
1672 // Obviously a forward declaration. We skip those, as they
1673 // don't create actually usable namespaces.
1674 break;
1675 }
1676 }
1677
1678 if (!quali.isEmpty()) {
1679 // Forward-declared class definitions can be namespaced.
1680 NamespaceList nsl;
1681 if (!fullyQualify(namespaces, quali, true, &nsl, 0)) {
1682 yyMsg() << "Ignoring definition of undeclared qualified class\n";
1683 break;
1684 }
1685 namespaceDepths.push(namespaces.count());
1686 namespaces = nsl;
1687 } else {
1688 namespaceDepths.push(namespaces.count());
1689 }
1690 enterNamespace(&namespaces, fct);
1691
1692 functionContext = namespaces;
1693 functionContextUnresolved.clear(); // Pointless
1694 prospectiveContext.clear();
1695 pendingContext.clear();
1696
1697 yyTok = getToken();
1698 }
1699 break;
1700 case Tok_namespace:
1701 yyTokColonSeen = false;
1702 yyTok = getToken();
1703 if (yyTok == Tok_Ident) {
1704 text = yyWord;
1705 text.detach();
1706 HashString ns = HashString(text);
1707 yyTok = getToken();
1708 if (yyTok == Tok_LeftBrace) {
1709 namespaceDepths.push(namespaces.count());
1710 enterNamespace(&namespaces, ns);
1711
1712 functionContext = namespaces;
1713 functionContextUnresolved.clear();
1714 prospectiveContext.clear();
1715 pendingContext.clear();
1716 yyTok = getToken();
1717 } else if (yyTok == Tok_Equals) {
1718 // e.g. namespace Is = OuterSpace::InnerSpace;
1719 QList<HashString> fullName;
1720 yyTok = getToken();
1721 if (yyTok == Tok_ColonColon)
1722 fullName.append(HashString(QString()));
1723 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1724 if (yyTok == Tok_Ident) {
1725 text = yyWord;
1726 text.detach();
1727 fullName.append(HashString(text));
1728 }
1729 yyTok = getToken();
1730 }
1731 if (fullName.isEmpty())
1732 break;
1733 fullName.append(HashString(QString())); // Mark as unresolved
1734 modifyNamespace(&namespaces)->aliases[ns] = fullName;
1735 }
1736 } else if (yyTok == Tok_LeftBrace) {
1737 // Anonymous namespace
1738 namespaceDepths.push(namespaces.count());
1739 yyTok = getToken();
1740 }
1741 break;
1742 case Tok_using:
1743 yyTok = getToken();
1744 // XXX this should affect only the current scope, not the entire current namespace
1745 if (yyTok == Tok_namespace) {
1746 QList<HashString> fullName;
1747 yyTok = getToken();
1748 if (yyTok == Tok_ColonColon)
1749 fullName.append(HashString(QString()));
1750 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1751 if (yyTok == Tok_Ident) {
1752 text = yyWord;
1753 text.detach();
1754 fullName.append(HashString(text));
1755 }
1756 yyTok = getToken();
1757 }
1758 NamespaceList nsl;
1759 if (fullyQualify(namespaces, fullName, false, &nsl, 0))
1760 modifyNamespace(&namespaces)->usings << HashStringList(nsl);
1761 } else {
1762 QList<HashString> fullName;
1763 if (yyTok == Tok_ColonColon)
1764 fullName.append(HashString(QString()));
1765 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1766 if (yyTok == Tok_Ident) {
1767 text = yyWord;
1768 text.detach();
1769 fullName.append(HashString(text));
1770 }
1771 yyTok = getToken();
1772 }
1773 if (fullName.isEmpty())
1774 break;
1775 // using-declarations cannot rename classes, so the last element of
1776 // fullName is already the resolved name we actually want.
1777 // As we do no resolution here, we'll collect useless usings of data
1778 // members and methods as well. This is no big deal.
1779 HashString &ns = fullName.last();
1780 fullName.append(HashString(QString())); // Mark as unresolved
1781 modifyNamespace(&namespaces)->aliases[ns] = fullName;
1782 }
1783 break;
1784 case Tok_tr:
1785 case Tok_trUtf8:
1786 if (!tor)
1787 goto case_default;
1788 if (!sourcetext.isEmpty())
1789 yyMsg() << qPrintable(LU::tr("//% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n"));
1790 utf8 = (yyTok == Tok_trUtf8);
1791 line = yyLineNo;
1792 yyTok = getToken();
1793 if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) {
1794 comment.clear();
1795 bool plural = false;
1796
1797 if (match(Tok_RightParen)) {
1798 // no comment
1799 } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment
1800 if (match(Tok_RightParen)) {
1801 // ok,
1802 } else if (match(Tok_Comma)) {
1803 plural = true;
1804 }
1805 }
1806 if (!pendingContext.isEmpty() && !prefix.startsWith(strColons)) {
1807 QStringList unresolved;
1808 if (!fullyQualify(namespaces, pendingContext, true, &functionContext, &unresolved)) {
1809 functionContextUnresolved = unresolved.join(strColons);
1810 yyMsg() << qPrintable(LU::tr("Qualifying with unknown namespace/class %1::%2\n")
1811 .arg(stringifyNamespace(functionContext)).arg(unresolved.first()));
1812 }
1813 pendingContext.clear();
1814 }
1815 if (prefix.isEmpty()) {
1816 if (functionContextUnresolved.isEmpty()) {
1817 int idx = functionContext.length();
1818 if (idx < 2) {
1819 yyMsg() << qPrintable(LU::tr("tr() cannot be called without context\n"));
1820 break;
1821 }
1822 Namespace *fctx;
1823 while (!(fctx = findNamespace(functionContext, idx)->classDef)->hasTrFunctions) {
1824 if (idx == 1) {
1825 context = stringifyNamespace(functionContext);
1826 fctx = findNamespace(functionContext)->classDef;
1827 if (!fctx->complained) {
1828 yyMsg() << qPrintable(LU::tr("Class '%1' lacks Q_OBJECT macro\n")
1829 .arg(context));
1830 fctx->complained = true;
1831 }
1832 goto gotctx;
1833 }
1834 --idx;
1835 }
1836 if (fctx->trQualification.isEmpty()) {
1837 context.clear();
1838 for (int i = 1;;) {
1839 context += functionContext.at(i).value();
1840 if (++i == idx)
1841 break;
1842 context += strColons;
1843 }
1844 fctx->trQualification = context;
1845 } else {
1846 context = fctx->trQualification;
1847 }
1848 } else {
1849 context = (stringListifyNamespace(functionContext)
1850 << functionContextUnresolved).join(strColons);
1851 }
1852 } else {
1853#ifdef DIAGNOSE_RETRANSLATABILITY
1854 int last = prefix.lastIndexOf(strColons);
1855 QString className = prefix.mid(last == -1 ? 0 : last + 2);
1856 if (!className.isEmpty() && className == functionName) {
1857 yyMsg() << qPrintable(LU::tr("It is not recommended to call tr() from within a constructor '%1::%2'\n")
1858 .arg(className).arg(functionName));
1859 }
1860#endif
1861 prefix.chop(2);
1862 NamespaceList nsl;
1863 QStringList unresolved;
1864 if (fullyQualify(functionContext, prefix, false, &nsl, &unresolved)) {
1865 Namespace *fctx = findNamespace(nsl)->classDef;
1866 if (fctx->trQualification.isEmpty()) {
1867 context = stringifyNamespace(nsl);
1868 fctx->trQualification = context;
1869 } else {
1870 context = fctx->trQualification;
1871 }
1872 if (!fctx->hasTrFunctions && !fctx->complained) {
1873 yyMsg() << qPrintable(LU::tr("Class '%1' lacks Q_OBJECT macro\n").arg(context));
1874 fctx->complained = true;
1875 }
1876 } else {
1877 context = (stringListifyNamespace(nsl) + unresolved).join(strColons);
1878 }
1879 prefix.clear();
1880 }
1881
1882 gotctx:
1883 recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural);
1884 }
1885 sourcetext.clear(); // Will have warned about that already
1886 extracomment.clear();
1887 msgid.clear();
1888 extra.clear();
1889 break;
1890 case Tok_translateUtf8:
1891 case Tok_translate:
1892 if (!tor)
1893 goto case_default;
1894 if (!sourcetext.isEmpty())
1895 yyMsg() << qPrintable(LU::tr("//% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n"));
1896 utf8 = (yyTok == Tok_translateUtf8);
1897 line = yyLineNo;
1898 yyTok = getToken();
1899 if (match(Tok_LeftParen)
1900 && matchString(&context)
1901 && match(Tok_Comma)
1902 && matchString(&text) && !text.isEmpty())
1903 {
1904 comment.clear();
1905 bool plural = false;
1906 if (!match(Tok_RightParen)) {
1907 // look for comment
1908 if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1909 if (!match(Tok_RightParen)) {
1910 // look for encoding
1911 if (match(Tok_Comma)) {
1912 if (matchEncoding(&utf8)) {
1913 if (!match(Tok_RightParen)) {
1914 // look for the plural quantifier,
1915 // this can be a number, an identifier or
1916 // a function call,
1917 // so for simplicity we mark it as plural if
1918 // we know we have a comma instead of an
1919 // right parentheses.
1920 plural = match(Tok_Comma);
1921 }
1922 } else {
1923 // This can be a QTranslator::translate("context",
1924 // "source", "comment", n) plural translation
1925 if (matchExpression() && match(Tok_RightParen)) {
1926 plural = true;
1927 } else {
1928 break;
1929 }
1930 }
1931 } else {
1932 break;
1933 }
1934 }
1935 } else {
1936 break;
1937 }
1938 }
1939 recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural);
1940 }
1941 sourcetext.clear(); // Will have warned about that already
1942 extracomment.clear();
1943 msgid.clear();
1944 extra.clear();
1945 break;
1946 case Tok_trid:
1947 if (!tor)
1948 goto case_default;
1949 if (!msgid.isEmpty())
1950 yyMsg() << qPrintable(LU::tr("//= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n"));
1951 //utf8 = false; // Maybe use //%% or something like that
1952 line = yyLineNo;
1953 yyTok = getToken();
1954 if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) {
1955 bool plural = match(Tok_Comma);
1956 recordMessage(line, QString(), sourcetext, QString(), extracomment,
1957 msgid, extra, false, plural);
1958 }
1959 sourcetext.clear();
1960 extracomment.clear();
1961 msgid.clear();
1962 extra.clear();
1963 break;
1964 case Tok_Q_DECLARE_TR_FUNCTIONS:
1965 if (getMacroArgs()) {
1966 Namespace *ns = modifyNamespace(&namespaces);
1967 ns->hasTrFunctions = true;
1968 ns->trQualification = yyWord;
1969 ns->trQualification.detach();
1970 }
1971 yyTok = getToken();
1972 break;
1973 case Tok_Q_OBJECT:
1974 modifyNamespace(&namespaces)->hasTrFunctions = true;
1975 yyTok = getToken();
1976 break;
1977 case Tok_Ident:
1978 prefix += yyWord;
1979 prefix.detach();
1980 yyTok = getToken();
1981 if (yyTok != Tok_ColonColon) {
1982 prefix.clear();
1983 if (yyTok == Tok_Ident && !yyParenDepth)
1984 prospectiveContext.clear();
1985 }
1986 break;
1987 case Tok_Comment: {
1988 if (!tor)
1989 goto case_default;
1990 const QChar *ptr = yyWord.unicode();
1991 if (*ptr == QLatin1Char(':') && ptr[1].isSpace()) {
1992 yyWord.remove(0, 2);
1993 extracomment += yyWord;
1994 extracomment.detach();
1995 } else if (*ptr == QLatin1Char('=') && ptr[1].isSpace()) {
1996 yyWord.remove(0, 2);
1997 msgid = yyWord.simplified();
1998 msgid.detach();
1999 } else if (*ptr == QLatin1Char('~') && ptr[1].isSpace()) {
2000 yyWord.remove(0, 2);
2001 text = yyWord.trimmed();
2002 int k = text.indexOf(QLatin1Char(' '));
2003 if (k > -1)
2004 extra.insert(text.left(k), text.mid(k + 1).trimmed());
2005 text.clear();
2006 } else if (*ptr == QLatin1Char('%') && ptr[1].isSpace()) {
2007 sourcetext.reserve(sourcetext.length() + yyWord.length() - 2);
2008 ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length();
2009 int p = 2, c;
2010 forever {
2011 if (p >= yyWord.length())
2012 break;
2013 c = yyWord.unicode()[p++].unicode();
2014 if (isspace(c))
2015 continue;
2016 if (c != '"') {
2017 yyMsg() << qPrintable(LU::tr("Unexpected character in meta string\n"));
2018 break;
2019 }
2020 forever {
2021 if (p >= yyWord.length()) {
2022 whoops:
2023 yyMsg() << qPrintable(LU::tr("Unterminated meta string\n"));
2024 break;
2025 }
2026 c = yyWord.unicode()[p++].unicode();
2027 if (c == '"')
2028 break;
2029 if (c == '\\') {
2030 if (p >= yyWord.length())
2031 goto whoops;
2032 c = yyWord.unicode()[p++].unicode();
2033 if (c == '\n')
2034 goto whoops;
2035 *ptr++ = '\\';
2036 }
2037 *ptr++ = c;
2038 }
2039 }
2040 sourcetext.resize(ptr - (ushort *)sourcetext.data());
2041 } else {
2042 const ushort *uc = (const ushort *)yyWord.unicode(); // Is zero-terminated
2043 int idx = 0;
2044 ushort c;
2045 while ((c = uc[idx]) == ' ' || c == '\t' || c == '\n')
2046 ++idx;
2047 if (!memcmp(uc + idx, MagicComment.unicode(), MagicComment.length() * 2)) {
2048 idx += MagicComment.length();
2049 comment = QString::fromRawData(yyWord.unicode() + idx,
2050 yyWord.length() - idx).simplified();
2051 int k = comment.indexOf(QLatin1Char(' '));
2052 if (k == -1) {
2053 context = comment;
2054 } else {
2055 context = comment.left(k);
2056 comment.remove(0, k + 1);
2057 TranslatorMessage msg(
2058 transcode(context, false), QString(),
2059 transcode(comment, false), QString(),
2060 yyFileName, yyLineNo, QStringList(),
2061 TranslatorMessage::Finished, false);
2062 msg.setExtraComment(transcode(extracomment.simplified(), false));
2063 extracomment.clear();
2064 tor->append(msg);
2065 tor->setExtras(extra);
2066 extra.clear();
2067 }
2068 }
2069 }
2070 yyTok = getToken();
2071 break;
2072 }
2073 case Tok_Arrow:
2074 yyTok = getToken();
2075 if (yyTok == Tok_tr || yyTok == Tok_trUtf8)
2076 yyMsg() << qPrintable(LU::tr("Cannot invoke tr() like this\n"));
2077 break;
2078 case Tok_ColonColon:
2079 if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen)
2080 prospectiveContext = prefix;
2081 prefix += strColons;
2082 yyTok = getToken();
2083#ifdef DIAGNOSE_RETRANSLATABILITY
2084 if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) {
2085 functionName = yyWord;
2086 functionName.detach();
2087 }
2088#endif
2089 break;
2090 case Tok_RightBrace:
2091 if (yyBraceDepth + 1 == namespaceDepths.count()) // class or namespace
2092 truncateNamespaces(&namespaces, namespaceDepths.pop());
2093 if (yyBraceDepth == namespaceDepths.count()) {
2094 // function, class or namespace
2095 if (!yyBraceDepth && !directInclude) {
2096 truncateNamespaces(&functionContext, 1);
2097 functionContextUnresolved = cd.m_defaultContext;
2098 } else {
2099 functionContext = namespaces;
2100 functionContextUnresolved.clear();
2101 }
2102 pendingContext.clear();
2103 }
2104 // fallthrough
2105 case Tok_Semicolon:
2106 prospectiveContext.clear();
2107 prefix.clear();
2108 if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) {
2109 yyMsg() << qPrintable(LU::tr("Discarding unconsumed meta data\n"));
2110 sourcetext.clear();
2111 extracomment.clear();
2112 msgid.clear();
2113 extra.clear();
2114 }
2115 yyTokColonSeen = false;
2116 yyTok = getToken();
2117 break;
2118 case Tok_Colon:
2119 if (!prospectiveContext.isEmpty()
2120 && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0)
2121 pendingContext = prospectiveContext;
2122 yyTokColonSeen = true;
2123 yyTok = getToken();
2124 break;
2125 case Tok_LeftBrace:
2126 if (!prospectiveContext.isEmpty()
2127 && yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0)
2128 pendingContext = prospectiveContext;
2129 // fallthrough
2130 case Tok_LeftParen:
2131 case Tok_RightParen:
2132 yyTokColonSeen = false;
2133 yyTok = getToken();
2134 break;
2135 default:
2136 if (!yyParenDepth)
2137 prospectiveContext.clear();
2138 // fallthrough
2139 case Tok_Equals: // for static initializers; other cases make no difference
2140 case Tok_RightBracket: // ignoring indexing; same reason
2141 case_default:
2142 yyTok = getToken();
2143 break;
2144 }
2145 }
2146
2147 if (yyBraceDepth != 0)
2148 yyMsg(yyBraceLineNo)
2149 << qPrintable(LU::tr("Unbalanced opening brace in C++ code"
2150 " (or abuse of the C++ preprocessor)\n"));
2151 else if (yyParenDepth != 0)
2152 yyMsg(yyParenLineNo)
2153 << qPrintable(LU::tr("Unbalanced opening parenthesis in C++ code"
2154 " (or abuse of the C++ preprocessor)\n"));
2155 else if (yyBracketDepth != 0)
2156 yyMsg(yyBracketLineNo)
2157 << qPrintable(LU::tr("Unbalanced opening bracket in C++ code"
2158 " (or abuse of the C++ preprocessor)\n"));
2159}
2160
2161const ParseResults *CppParser::recordResults(bool isHeader)
2162{
2163 if (tor) {
2164 if (tor->messageCount()) {
2165 CppFiles::setTranslator(yyFileName, tor);
2166 } else {
2167 delete tor;
2168 tor = 0;
2169 }
2170 }
2171 if (isHeader) {
2172 const ParseResults *pr;
2173 if (!tor && results->includes.count() == 1
2174 && results->rootNamespace.children.isEmpty()
2175 && results->rootNamespace.aliases.isEmpty()
2176 && results->rootNamespace.usings.isEmpty()) {
2177 // This is a forwarding header. Slash it.
2178 pr = *results->includes.begin();
2179 delete results;
2180 } else {
2181 results->fileId = nextFileId++;
2182 pr = results;
2183 }
2184 CppFiles::setResults(yyFileName, pr);
2185 return pr;
2186 } else {
2187 delete results;
2188 return 0;
2189 }
2190}
2191
2192/*
2193 Fetches tr() calls in C++ code in UI files (inside "<function>"
2194 tag). This mechanism is obsolete.
2195*/
2196void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context)
2197{
2198 CppParser parser;
2199 parser.setInput(in);
2200 ConversionData cd;
2201 QSet<QString> inclusions;
2202 parser.setTranslator(&translator);
2203 parser.parse(context, cd, inclusions);
2204 parser.deleteResults();
2205}
2206
2207void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd)
2208{
2209 QByteArray codecName = cd.m_codecForSource.isEmpty()
2210 ? translator.codecName() : cd.m_codecForSource;
2211 QTextCodec *codec = QTextCodec::codecForName(codecName);
2212
2213 foreach (const QString &filename, filenames) {
2214 if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename))
2215 continue;
2216
2217 QFile file(filename);
2218 if (!file.open(QIODevice::ReadOnly)) {
2219 cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
2220 continue;
2221 }
2222
2223 CppParser parser;
2224 QTextStream ts(&file);
2225 ts.setCodec(codec);
2226 ts.setAutoDetectUnicode(true);
2227 parser.setInput(ts, filename);
2228 if (cd.m_outputCodec.isEmpty() && ts.codec()->name() == "UTF-16")
2229 translator.setCodecName("System");
2230 Translator *tor = new Translator;
2231 tor->setCodecName(translator.codecName());
2232 parser.setTranslator(tor);
2233 QSet<QString> inclusions;
2234 parser.parse(cd.m_defaultContext, cd, inclusions);
2235 parser.recordResults(isHeader(filename));
2236 }
2237
2238 foreach (const QString &filename, filenames)
2239 if (!CppFiles::isBlacklisted(filename))
2240 if (const Translator *tor = CppFiles::getTranslator(filename))
2241 foreach (const TranslatorMessage &msg, tor->messages())
2242 translator.extend(msg);
2243}
2244
2245QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.