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