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