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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qttools/src/linguist/lupdate/cpp.cpp