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

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

trunk: Merged in qt 4.6.2 sources.

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