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