9#include <QtCore/QBitArray>
10#include <QtCore/QTextStream>
11#include <QtCore/QRegularExpression>
20using namespace Qt::StringLiterals;
24 if (str.m_hash & 0x80000000)
25 str.m_hash = qHash(str.m_str) & 0x7fffffff;
31 return debug << s.value();
36 if (list.m_hash & 0x80000000) {
38 for (
const HashString &qs : list.m_list) {
39 hash ^= qHash(qs) ^ 0x6ad9f526;
40 hash = ((hash << 13) & 0x7fffffff) | (hash >> 18);
49 return debug << lst.m_list;
58 m_ba.resize(nextFileId);
76 void setInput(QTextStream &ts,
const QString &fileName);
81 QSet<QString> &inclusions);
88 IfdefState(
int _bracketDepth,
int _braceDepth,
int _parenDepth) :
89 bracketDepth(_bracketDepth),
90 braceDepth(_braceDepth),
91 parenDepth(_parenDepth),
96 int bracketDepth, bracketDepth1st;
97 int braceDepth, braceDepth1st;
98 int parenDepth, parenDepth1st;
123 Tok_LeftAngleBracket,
124 Tok_RightAngleBracket,
139 std::ostream &yyMsg(
int line = 0);
142 TokenType lookAheadToSemicolonOrLeftBrace();
143 TokenType getToken();
145 void processComment();
147 bool match(TokenType t);
148 bool matchString(QString *s);
149 bool matchEncoding();
150 bool matchStringOrNull(QString *s);
151 bool matchExpression();
153 void recordMessage(
int line,
const QString &context,
const QString &text,
154 const QString &comment,
const QString &extracomment,
const QString &msgid,
158 void handleTr(QString &prefix,
bool plural);
159 void handleTranslate(
bool plural);
160 void handleTrId(
bool plural);
161 void handleDeclareTrFunctions();
164 const QStringList &includeStack, QSet<QString> &inclusions);
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,
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,
185 bool fullyQualify(
const NamespaceList &namespaces,
int nsCnt,
192 const QString &segments,
bool isDeclaration,
194 bool findNamespaceCallback(
const Namespace *ns,
void *context)
const;
196 void enterNamespace(
NamespaceList *namespaces,
const HashString &name);
197 void truncateNamespaces(
NamespaceList *namespaces,
int lenght);
204 bool yyTrailingSpace;
206 qsizetype yyWordInitialCapacity = 0;
207 QStack<IfdefState> yyIfdefStack;
220 const ushort *yyInPtr;
227 MetaStrings m_metaStrings;
229 QString prospectiveContext;
244 directInclude =
true;
247 directInclude =
false;
264 return std::cerr << qPrintable(yyFileName) <<
':' << (line ? line : yyLineNo) <<
": ";
270 yyFileName = QString();
271 yySourceEncoding = QStringConverter::Utf8;
276 yyInStr = ts.readAll();
277 yyFileName = fileName;
278 yySourceEncoding = ts.encoding();
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
305 const ushort *uc = yyInPtr;
334 }
else if (c ==
'\n') {
337 }
else if (c !=
' ' && c !=
'\t' && c !=
'#') {
349 const ushort *uc = yyInPtr + 1;
355 return Tok_Semicolon;
357 return Tok_LeftBrace;
372 if (s.endsWith(u'R')) {
374 return s.isEmpty() || isStringLiteralPrefix(s);
405 Q_ASSERT(yyWord.capacity() == yyWordInitialCapacity);
407 while (yyCh != EOF) {
408 yyLineNo = yyCurLineNo;
410 if (yyCh ==
'#' && yyAtNewline) {
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
433 }
while (isspace(yyCh) && yyCh !=
'\n');
444 }
while (!isspace(yyCh));
451 }
while (isspace(yyCh));
462 }
while (yyCh !=
')');
470 }
while (!isspace(yyCh));
477 }
while (isspace(yyCh));
479 saveState(&savedState);
480 yyMinBraceDepth = yyBraceDepth;
487 yyIfdefStack.push(IfdefState(yyBracketDepth, yyBraceDepth, yyParenDepth));
489 }
else if (yyCh ==
'n') {
493 }
while (yyCh != EOF && !isspace(yyCh) && yyCh !=
'"' && yyCh !=
'<' );
494 while (isspace(yyCh))
499 else if (yyCh ==
'<')
503 ushort *ptr = (ushort *)yyWord.unicode();
506 if (yyCh == EOF || yyCh ==
'\n')
514 yyWord.resize(ptr - (ushort *)yyWord.unicode());
515 return (tChar ==
'"') ? Tok_QuotedInclude : Tok_AngledInclude;
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)
529 <<
"Parenthesis/bracket/brace mismatch between "
530 "#if and #else branches; using #if branch\n";
532 is.bracketDepth1st = yyBracketDepth;
533 is.braceDepth1st = yyBraceDepth;
534 is.parenDepth1st = yyParenDepth;
535 saveState(&is.state);
537 is.elseLine = yyLineNo;
538 yyBracketDepth = is.bracketDepth;
539 yyBraceDepth = is.braceDepth;
540 yyParenDepth = is.parenDepth;
543 }
else if (yyCh ==
'n') {
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)
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;
571 }
while (yyCh != EOF && yyCh !=
'\n');
573 }
else if (yyCh ==
'*') {
574 bool metAster =
false;
579 yyMsg() <<
"Unterminated C++ comment\n";
585 }
else if (metAster && yyCh ==
'/') {
596 }
while (yyCh !=
'\n' && yyCh != EOF);
598 }
else if ((yyCh >=
'A' && yyCh <=
'Z') || (yyCh >=
'a' && yyCh <=
'z') || yyCh ==
'_') {
599 ushort *ptr = (ushort *)yyWord.unicode();
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);
610 if (yyCh ==
'"' && isStringLiteralPrefix(yyWord)) {
615 switch (yyWord.unicode()[0].unicode()) {
617 if (yyWord == strNULL)
621 if (yyWord == strQ_NULLPTR)
623 if (yyWord == strQ_OBJECT)
625 if (yyWord == strQ_SLOTS || yyWord == strQ_SIGNALS)
629 if (yyWord == strclass)
633 if (yyWord == strdecltype)
637 if (yyWord == strenum)
641 if (yyWord == strfriend)
645 if (yyWord == strnamespace)
646 return Tok_namespace;
647 if (yyWord == strnullptr)
651 if (yyWord == stroperator) {
655 while (isspace(yyCh))
657 while (yyCh ==
'+' || yyCh ==
'-' || yyCh ==
'*' || yyCh ==
'/' || yyCh ==
'%'
658 || yyCh ==
'=' || yyCh ==
'<' || yyCh ==
'>' || yyCh ==
'!'
659 || yyCh ==
'&' || yyCh ==
'|' || yyCh ==
'~' || yyCh ==
'^'
660 || yyCh ==
'[' || yyCh ==
']')
665 if (yyWord == strpublic || yyWord == strprotected || yyWord == strprivate)
669 if (yyWord == strreturn)
673 if (yyWord == strstruct)
675 if (yyWord == strslots || yyWord == strsignals)
679 if (yyWord == strusing)
685 if (yyCh ==
'"' && isRawStringLiteralPrefix(yyWord)) {
686 ptr =
reinterpret_cast<ushort *>(
const_cast<QChar *>(yyWord.unicode()));
689 for (yyCh = getChar(); yyCh != EOF && yyCh !=
'('; yyCh = getChar())
690 delimiter += QLatin1Char(yyCh);
694 ushort *ptr_past_end =
nullptr;
695 while (yyCh != EOF && !is_end) {
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
706 ptr_past_end =
nullptr;
712 if (delimiter.isEmpty()) {
718 ptr_past_end =
nullptr;
725 yyWord.resize(ptr_past_end - 1 -
reinterpret_cast<
const ushort *>(yyWord.unicode()));
727 yyWord.resize(ptr -
reinterpret_cast<
const ushort *>(yyWord.unicode()));
729 yyMsg() <<
"Unterminated/mismatched C++ Raw string\n";
732 return Tok_RawString;
740 loadState(savedState);
741 prospectiveContext.clear();
742 yyBraceDepth = yyMinBraceDepth;
754 ushort *ptr = (ushort *)yyWord.unicode();
760 }
while (yyCh !=
'\n');
761 yyWord.resize(ptr - (ushort *)yyWord.unicode());
763 }
else if (yyCh ==
'*') {
764 bool metAster =
false;
765 ushort *ptr = (ushort *)yyWord.unicode();
770 yyMsg() <<
"Unterminated C++ comment\n";
777 else if (metAster && yyCh ==
'/')
782 yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2);
789 ushort *ptr = (ushort *)yyWord.unicode();
791 while (yyCh != EOF && yyCh !=
'\n' && yyCh !=
'"') {
794 if (yyCh == EOF || yyCh ==
'\n')
801 yyWord.resize(ptr - (ushort *)yyWord.unicode());
804 yyMsg() <<
"Unterminated C++ string\n";
820 return Tok_ColonColon;
832 return Tok_RightAngleBracket;
837 return Tok_LeftShift;
839 return Tok_LeftAngleBracket;
846 if (yyCh == EOF || yyCh ==
'\n') {
847 yyMsg() <<
"Unterminated C++ character\n";
858 if (yyBraceDepth == 0)
859 yyBraceLineNo = yyCurLineNo;
862 return Tok_LeftBrace;
864 if (yyBraceDepth == yyMinBraceDepth) {
867 <<
"Excess closing brace in C++ code"
868 " (or abuse of the C++ preprocessor)\n";
871 return Tok_Semicolon;
875 return Tok_RightBrace;
877 if (yyParenDepth == 0)
878 yyParenLineNo = yyCurLineNo;
881 return Tok_LeftParen;
883 if (yyParenDepth == 0)
885 <<
"Excess closing parenthesis in C++ code"
886 " (or abuse of the C++ preprocessor)\n";
890 return Tok_RightParen;
892 if (yyBracketDepth == 0)
893 yyBracketLineNo = yyCurLineNo;
896 return Tok_LeftBracket;
898 if (yyBracketDepth == 0)
900 <<
"Excess closing bracket in C++ code"
901 " (or abuse of the C++ preprocessor)\n";
905 return Tok_RightBracket;
911 return Tok_Semicolon;
914 return Tok_QuestionMark;
917 if (yyCh ==
'x' || yyCh ==
'X') {
920 }
while ((yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'\''
921 || (yyCh >=
'a' && yyCh <=
'f') || (yyCh >=
'A' && yyCh <=
'F'));
924 if (yyCh <
'0' || yyCh >
'9')
938 }
while ((yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'\'');
950
951
952
961 *
static_cast<CppParserState *>(
this) = state;
966 Namespace *pns, *ns = &results->rootNamespace;
967 for (
int i = 1; i < namespaces->size(); ++i) {
969 if (!(ns = pns->children.value(namespaces->at(i)))) {
972 if (haveLast || i < namespaces->size() - 1)
973 if (
const Namespace *ons = findNamespace(*namespaces, i + 1))
975 pns->children.insert(namespaces->at(i), ns);
978 }
while (++i < namespaces->size());
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) {
995 ret += namespaces.at(i).value();
1000QString
CppParser::joinNamespaces(
const QString &one,
const QString &two)
1002 return two.isEmpty() ? one : one.isEmpty() ? two : one + QStringLiteral(
"::") + two;
1006 VisitNamespaceCallback callback,
void *context,
1009 const Namespace *ns = &rslt->rootNamespace;
1010 for (
int i = 1; i < nsCount; ++i)
1011 if (!(ns = ns->children.value(namespaces.at(i))))
1013 if ((
this->*callback)(ns, context))
1016 for (
const ParseResults *sup : rslt->includes)
1017 if (vr.tryVisit(sup->fileId)
1018 && visitNamespace(namespaces, nsCount, callback, context, vr, sup))
1024 VisitNamespaceCallback callback,
void *context)
const
1027 return visitNamespace(namespaces, nsCount, callback, context, vr, results);
1032 QSet<HashStringList> *visited)
1046 if (ns->children.contains(data
->segment)) {
1051 auto nsai = ns->aliases.constFind(data
->segment);
1052 if (nsai != ns->aliases.constEnd()) {
1054 if (nsl.last().value().isEmpty()) {
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))
1084 NamespaceList *resolved, QSet<HashStringList> *visitedUsings)
const
1086 QualifyOneData data(namespaces, nsCnt, segment, resolved, visitedUsings);
1088 if (visitNamespace(namespaces, nsCnt, &
CppParser::qualifyOneCallbackOwn, &data))
1091 return visitNamespace(namespaces, nsCnt, &
CppParser::qualifyOneCallbackUsing, &data);
1097 QSet<HashStringList> visitedUsings;
1099 return qualifyOne(namespaces, nsCnt, segment, resolved, &visitedUsings);
1109 if (segments.first().value().isEmpty()) {
1111 if (segments.size() == 1) {
1113 *resolved << HashString(QString());
1123 auto matchSeg = segments.crbegin();
1124 auto matchNs = namespaces.crbegin();
1126 if (matchSeg->value() != matchNs->value())
1129 while (matchSeg != segments.crend()
1130 && matchNs != namespaces.crend()
1131 && matchSeg->value() == matchNs->value()) {
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)) {
1144 *unresolved = segments.mid(segIdx);
1150 }
while (!isDeclaration && --nsIdx >= 0);
1152 *resolved << HashString(QString());
1154 *unresolved = segments.mid(initSegIdx);
1162 return fullyQualify(namespaces, namespaces.size(),
1163 segments, isDeclaration, resolved, unresolved);
1167 const QString &quali,
bool isDeclaration,
1171 for (
const QString &str : quali.split(
"::"_L1))
1172 segments << HashString(str);
1173 return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved);
1186 nsCount = namespaces.size();
1187 visitNamespace(namespaces, nsCount, &
CppParser::findNamespaceCallback, &ns);
1193 *namespaces << name;
1195 if (!(ns = findNamespace(*namespaces)))
1196 ns = modifyNamespace(namespaces,
false);
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))))
1208 if (namespaces->size() > length)
1209 namespaces->erase(namespaces->begin() + length, namespaces->end());
1214
1215
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);
1229 seed = qHash(key.cleanFile, seed);
1230 seed = qHash(key.parserState, seed);
1248QSet<QString> &
CppFiles::blacklistedFiles()
1250 static QSet<QString> blacklisted;
1257 IncludeCycle *
const cycle = includeCycles().value(key);
1260 return cycle->results;
1271 includeCycles().insert(key, cycle);
1274 cycle->fileNames.insert(key.cleanFile);
1275 cycle->results.insert(results);
1280 return translatedFiles().value(cleanFile);
1285 translatedFiles().insert(cleanFile, tor);
1290 return blacklistedFiles().contains(cleanFile);
1295 blacklistedFiles().insert(cleanFile);
1301 cycle->fileNames = fileNames;
1303 QSet<IncludeCycle *> intersectingCycles;
1304 for (
const QString &fileName : fileNames) {
1305 const ResultsCacheKey key = { fileName, parserState };
1306 IncludeCycle *intersectingCycle = includeCycles().value(key);
1308 if (intersectingCycle && !intersectingCycles.contains(intersectingCycle)) {
1309 intersectingCycles.insert(intersectingCycle);
1311 cycle->fileNames.unite(intersectingCycle->fileNames);
1312 cycle->results.unite(intersectingCycle->results);
1315 qDeleteAll(intersectingCycles);
1317 for (
const QString &fileName : std::as_const(cycle->fileNames))
1318 includeCycles().insert({ fileName, parserState }, cycle);
1323 QString fileExt = QFileInfo(name).suffix();
1324 return fileExt.isEmpty() || fileExt.startsWith(u'h', Qt::CaseInsensitive);
1328 QSet<QString> &inclusions)
1330 QString cleanFile = QDir::cleanPath(file);
1332 for (
const QRegularExpression &rx : std::as_const(cd.m_excludes)) {
1333 if (rx.match(cleanFile).hasMatch())
1337 const int index = includeStack.indexOf(cleanFile);
1339 CppFiles::addIncludeCycle(QSet<QString>(includeStack.cbegin() + index, includeStack.cend()),
1348 bool isIndirect =
false;
1349 if (!CppFiles::isBlacklisted(cleanFile)
1350 && isHeader(cleanFile)) {
1352 QSet<
const ParseResults *> res = CppFiles::getResults(ResultsCacheKey(cleanFile, *
this));
1353 if (!res.isEmpty()) {
1354 results->includes.unite(res);
1362 if (!f.open(QIODevice::ReadOnly)) {
1363 yyMsg() << qPrintable(
1364 QStringLiteral(
"Cannot open %1: %2\n").arg(cleanFile, f.errorString()));
1369 ts.setEncoding(yySourceEncoding);
1370 ts.setAutoDetectUnicode(
true);
1372 inclusions.insert(cleanFile);
1375 for (
const QString &projectRoot : std::as_const(cd.m_projectRoots))
1376 if (cleanFile.startsWith(projectRoot)) {
1377 parser.setTranslator(
new Translator);
1380 parser.setInput(ts, cleanFile);
1381 QStringList stack = includeStack;
1383 parser.parse(cd, stack, inclusions);
1389 parser.functionContextUnresolved = functionContextUnresolved;
1390 parser.setInput(ts, cleanFile);
1392 QStringList stack = includeStack;
1394 parser.parseInternal(cd, stack, inclusions);
1396 CppFiles::setBlacklisted(cleanFile);
1398 inclusions.remove(cleanFile);
1400 prospectiveContext.clear();
1401 pendingContext.clear();
1405
1406
1407
1408
1409
1410
1411
1412
1416 bool matches = (yyTok == t);
1424 bool matches =
false;
1427 if (yyTok != Tok_String && yyTok != Tok_RawString)
1430 if (yyTok == Tok_String)
1431 *s += ParserTool::transcode(yyWord);
1448 if (yyTok != Tok_Ident)
1450 if (yyWord == strQApplication || yyWord == strQCoreApplication) {
1452 if (yyTok == Tok_ColonColon)
1455 if (yyWord == strUnicodeUTF8) {
1459 if (yyWord == strLatin1 || yyWord == strDefaultCodec || yyWord == strCodecForTr)
1460 yyMsg() <<
"Unsupported encoding Latin1/DefaultCodec/CodecForTr\n";
1464bool CppParser::matchStringOrNull(QString *s)
1466 return matchString(s) || match(Tok_Null);
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1485 if (match(Tok_Null) || match(Tok_Integer))
1489 int angleBracketLevel = 0;
1490 while (match(Tok_Ident) || parenlevel > 0) {
1491 if (yyTok == Tok_RightParen) {
1492 if (parenlevel == 0)
break;
1495 }
else if (yyTok == Tok_LeftParen) {
1497 if (yyTok == Tok_RightParen) {
1502 }
else if (yyTok == Tok_LeftAngleBracket) {
1503 angleBracketLevel++;
1505 }
else if (yyTok == Tok_RightAngleBracket) {
1506 angleBracketLevel--;
1508 if (yyTok == Tok_LeftParen) {
1512 }
else if (yyTok == Tok_Ident) {
1514 }
else if (yyTok == Tok_Arrow) {
1516 }
else if ((parenlevel == 0 && angleBracketLevel == 0) || yyTok == Tok_Cancel) {
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,
1529 ParserTool::transcode(context), text, ParserTool::transcode(comment), QString(),
1530 yyFileName, line, QStringList(),
1531 TranslatorMessage::Unfinished, plural);
1532 msg.setExtraComment(ParserTool::transcode(extracomment.simplified()));
1535 if (!msgid.isEmpty())
1536 msg.setLabel(label);
1540void CppParser::handleTr(QString &prefix,
bool plural)
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";
1547 int line = yyLineNo;
1550 if (matchString(&text)) {
1552 if (yyTok == Tok_RightParen) {
1554 }
else if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1555 if (yyTok == Tok_RightParen) {
1557 }
else if (match(Tok_Comma)) {
1561 if (!pendingContext.isEmpty() && !prefix.startsWith(
"::"_L1)) {
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()));
1569 pendingContext.clear();
1571 if (prefix.isEmpty()) {
1572 if (functionContextUnresolved.isEmpty()) {
1575 yyMsg() <<
"tr() cannot be called without context\n";
1581 context = stringifyNamespace(functionContext);
1584 yyMsg() << qPrintable(
1585 QStringLiteral(
"Class '%1' lacks Q_OBJECT macro\n").arg(context));
1592 if (fctx->trQualification.isEmpty()) {
1595 context += functionContext.at(i).value();
1600 fctx->trQualification = context;
1602 context = fctx->trQualification;
1605 context = joinNamespaces(stringifyNamespace(functionContext), functionContextUnresolved);
1611 if (fullyQualify(
functionContext, prefix,
false, &nsl, &unresolved)) {
1613 if (fctx->trQualification.isEmpty()) {
1614 context = stringifyNamespace(nsl);
1615 fctx->trQualification = context;
1617 context = fctx->trQualification;
1620 yyMsg() << qPrintable(QStringLiteral(
"Class '%1' lacks Q_OBJECT macro\n")
1625 context = joinNamespaces(stringifyNamespace(nsl), stringifyNamespace(0, unresolved));
1631 recordMessage(line, context, text, comment, m_metaStrings.extracomment(),
1632 m_metaStrings.msgid(), m_metaStrings.label(), m_metaStrings.extra(),
1635 m_metaStrings.clear();
1636 metaExpected =
false;
1639void CppParser::handleTranslate(
bool plural)
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;
1648 if (matchString(&context)
1650 && matchString(&text) && !text.isEmpty())
1653 if (yyTok != Tok_RightParen) {
1655 if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1656 if (yyTok != Tok_RightParen) {
1658 if (match(Tok_Comma)) {
1659 if (matchEncoding()) {
1660 if (yyTok != Tok_RightParen) {
1667 plural |= match(Tok_Comma);
1672 if (matchExpression() && yyTok == Tok_RightParen) {
1686 recordMessage(line, context, text, comment, m_metaStrings.extracomment(),
1687 m_metaStrings.msgid(), m_metaStrings.label(), m_metaStrings.extra(), plural);
1689 m_metaStrings.clear();
1690 metaExpected =
false;
1695 if (!m_metaStrings.msgid().isEmpty())
1696 yyMsg() <<
"//= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n";
1697 int line = yyLineNo;
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);
1706 m_metaStrings.clear();
1707 metaExpected =
false;
1710void CppParser::handleDeclareTrFunctions()
1715 if (yyTok != Tok_Ident)
1720 if (yyTok == Tok_RightParen)
1722 if (yyTok != Tok_ColonColon)
1726 Namespace *ns = modifyNamespace(&namespaces);
1727 ns->hasTrFunctions =
true;
1728 ns->trQualification = name;
1729 ns->trQualification.detach();
1733 QSet<QString> &inclusions)
1737 functionContextUnresolved.clear();
1739 parseInternal(cd, includeStack, inclusions);
1744 bool forcePlural =
false;
1745 switch (trFunctionAliasManager.trFunctionByName(yyWord)) {
1747 handleDeclareTrFunctions();
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:
1757 handleTr(prefix, forcePlural);
1770 handleTranslate(forcePlural);
1778 handleTrId(forcePlural);
1787 QSet<QString> &inclusions)
1789 static constexpr auto strColons(
"::"_L1);
1792 bool yyTokColonSeen =
false;
1793 bool yyTokIdentSeen =
false;
1794 bool maybeInTrailingReturnType =
false;
1796 const QList<TokenType> allowedTokensInFnTemplate{
1797 Tok_Ident, Tok_decltype, Tok_ColonColon, Tok_Comma,
1798 Tok_Integer, Tok_LeftAngleBracket, Tok_RightAngleBracket
1800 metaExpected =
true;
1802 prospectiveContext.clear();
1803 pendingContext.clear();
1805 yyWord.reserve(yyInStr.size());
1806 yyWordInitialCapacity = yyWord.capacity();
1807 yyInPtr = (
const ushort *)yyInStr.unicode();
1810 while (yyTok != Tok_Eof) {
1815 if (yyBracketDepth && yyBraceDepth == namespaceDepths.size()) {
1821 case Tok_QuotedInclude: {
1822 QString text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord);
1824 if (QFileInfo(text).isFile()) {
1825 processInclude(text, cd, includeStack, inclusions);
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);
1838 for (
const QString &incPath : std::as_const(cd.m_includePath)) {
1839 QString text = QDir(incPath).absoluteFilePath(yyWord);
1841 if (QFileInfo(text).isFile()) {
1842 processInclude(text, cd, includeStack, inclusions);
1853 if (yyTok == Tok_class)
1858
1859
1863 if (yyTok == Tok_Equals) {
1866 }
else if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
1872 QString text = yyWord;
1877 if (yyTok == Tok_ColonColon) {
1880 }
else if (yyTok == Tok_Ident) {
1881 if (yyWord == strfinal) {
1896 if (yyTok == Tok_Colon || yyTok == Tok_LeftAngleBracket) {
1902 if (yyTok == Tok_Eof)
1904 if (yyTok == Tok_Cancel)
1906 if (yyTok == Tok_class)
1908 if (yyTok == Tok_Ident) {
1910 if (yyTok == Tok_LeftParen)
1911 parseTranslate(prefix);
1913 goto tokenInTemplate;
1915 }
while (yyTok != Tok_LeftBrace && yyTok != Tok_Semicolon);
1916 if (yyTok == Tok_Semicolon)
1919 if (yyTok != Tok_LeftBrace) {
1926 if (!quali.isEmpty()) {
1929 if (!fullyQualify(namespaces, quali,
true, &nsl, 0)) {
1930 yyMsg() <<
"Ignoring definition of undeclared qualified class\n";
1933 namespaceDepths.push(namespaces.size());
1936 namespaceDepths.push(namespaces.size());
1938 enterNamespace(&namespaces, fct);
1941 functionContextUnresolved.clear();
1942 prospectiveContext.clear();
1943 pendingContext.clear();
1945 metaExpected =
true;
1951 if (yyTok == Tok_Ident) {
1952 QString text = yyWord;
1954 HashString ns = HashString(text);
1958 if (yyTok != Tok_ColonColon)
1961 if (yyTok != Tok_Ident)
1963 nestedNamespaces.append(ns);
1966 ns = HashString(text);
1968 if (yyTok == Tok_LeftBrace) {
1969 namespaceDepths.push(namespaces.size());
1970 for (
const auto &nns : nestedNamespaces)
1971 enterNamespace(&namespaces, nns);
1972 enterNamespace(&namespaces, ns);
1974 functionContext = namespaces;
1975 functionContextUnresolved.clear();
1976 prospectiveContext.clear();
1977 pendingContext.clear();
1978 metaExpected =
true;
1980 }
else if (yyTok == Tok_Equals) {
1983 NamespaceList fullName;
1985 if (yyTok == Tok_ColonColon)
1986 fullName.append(HashString(QString()));
1987 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1988 if (yyTok == Tok_Ident) {
1991 fullName.append(HashString(text));
1995 if (fullName.isEmpty())
1997 fullName.append(HashString(QString()));
1998 modifyNamespace(&namespaces)->aliases[ns] = fullName;
2000 }
else if (yyTok == Tok_LeftBrace) {
2002 namespaceDepths.push(namespaces.size());
2003 metaExpected =
true;
2010 if (yyTok == Tok_namespace) {
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;
2019 fullName.append(HashString(text));
2024 if (fullyQualify(
namespaces, fullName,
false, &nsl, 0))
2025 modifyNamespace(&
namespaces)->usings << HashStringList(nsl);
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;
2034 fullName.append(HashString(text));
2038 if (fullName.isEmpty())
2044 fullName.append(HashString(QString()));
2045 const HashString &ns = *(fullName.constEnd() - 2);
2046 modifyNamespace(&
namespaces)->aliases[ns] = fullName;
2054 if (yyTokColonSeen &&
2055 yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
2057 yyTokIdentSeen =
true;
2061 bool methodSpecialization =
false;
2062 if (yyTok == Tok_LeftAngleBracket) {
2067 while (count && allowedTokensInFnTemplate.contains(yyTok)) {
2068 if (yyTok == Tok_LeftAngleBracket)
2070 else if (yyTok == Tok_RightAngleBracket)
2074 methodSpecialization = count == 0;
2076 if (yyTok == Tok_LeftParen) {
2077 if (parseTranslate(prefix)) {
2084 if (yyTok == Tok_ColonColon && !maybeInTrailingReturnType && !yyTrailingSpace) {
2085 prefix += methodSpecialization ? className : yyWord;
2090 metaExpected =
false;
2094 if (yyParenDepth == 0 && yyBraceDepth == namespaceDepths.size())
2095 maybeInTrailingReturnType =
true;
2097 if (yyTok == Tok_Ident) {
2099 if (yyTok == Tok_LeftParen) {
2100 switch (trFunctionAliasManager.trFunctionByName(yyWord)) {
2103 yyMsg() <<
"Cannot invoke tr() like this\n";
2109 case Tok_ColonColon:
2110 if (yyTokIdentSeen || maybeInTrailingReturnType) {
2115 if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0 && !yyTokColonSeen)
2116 prospectiveContext = prefix;
2117 if (!prefix.isEmpty())
2118 prefix += strColons;
2121 case Tok_RightBrace:
2122 if (!yyTokColonSeen) {
2123 if (yyBraceDepth + 1 == namespaceDepths.size()) {
2125 truncateNamespaces(&namespaces, namespaceDepths.pop());
2127 if (yyBraceDepth == namespaceDepths.size()) {
2129 if (!yyBraceDepth && !directInclude)
2133 functionContextUnresolved.clear();
2134 pendingContext.clear();
2139 maybeInTrailingReturnType =
false;
2140 prospectiveContext.clear();
2142 if (m_metaStrings.hasData()) {
2143 yyMsg() <<
"Discarding unconsumed meta data\n";
2144 m_metaStrings.clear();
2146 metaExpected =
true;
2153 }
while (yyTok == Tok_Access);
2154 metaExpected =
true;
2155 if (yyTok == Tok_Colon)
2160 if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
2161 if (!prospectiveContext.isEmpty()) {
2162 pendingContext = prospectiveContext;
2163 prospectiveContext.clear();
2166 if (yyTok == Tok_Colon) {
2167 if (lookAheadToSemicolonOrLeftBrace() != Tok_Semicolon)
2168 yyTokColonSeen =
true;
2171 metaExpected =
true;
2175 if (yyBraceDepth == namespaceDepths.size() + 1 && yyParenDepth == 0) {
2176 if (!prospectiveContext.isEmpty()) {
2177 pendingContext = prospectiveContext;
2178 prospectiveContext.clear();
2180 if (!yyTokIdentSeen) {
2182 yyTokColonSeen =
false;
2185 maybeInTrailingReturnType =
false;
2186 yyTokIdentSeen =
false;
2187 metaExpected =
true;
2191 if (!yyTokColonSeen && yyBraceDepth == namespaceDepths.size() && yyParenDepth == 1
2192 && !prospectiveContext.isEmpty()) {
2193 pendingContext = prospectiveContext;
2194 prospectiveContext.clear();
2196 yyTokIdentSeen =
false;
2197 metaExpected =
true;
2201 case Tok_QuestionMark:
2202 metaExpected =
true;
2205 case Tok_RightParen:
2206 if (yyParenDepth == 0) {
2207 if (!yyTokColonSeen && !pendingContext.isEmpty()
2208 && yyBraceDepth == namespaceDepths.size()) {
2210 prospectiveContext = pendingContext;
2211 pendingContext.clear();
2213 metaExpected =
true;
2215 metaExpected =
false;
2222 auto initialParenDepth = yyParenDepth;
2228 while (yyParenDepth != initialParenDepth && yyTok != Tok_Eof)
2235 if (yyTok == Tok_class)
2241 if (yyTok == Tok_Colon)
2245 if (!yyParenDepth && !maybeInTrailingReturnType)
2246 prospectiveContext.clear();
2248 case Tok_RightBracket:
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";
2271 if (!tor || !metaExpected)
2274 if (!m_metaStrings.parse(yyWord)) {
2275 yyMsg() << m_metaStrings.popError().toStdString();
2279 if (m_metaStrings.magicComment()) {
2280 auto [context, comment] = *m_metaStrings.magicComment();
2282 ParserTool::transcode(comment), QString(), yyFileName, yyLineNo,
2283 QStringList(), TranslatorMessage::Finished,
false);
2284 msg.setExtraComment(ParserTool::transcode(m_metaStrings.extracomment().simplified()));
2286 tor->setExtras(m_metaStrings.extra());
2287 m_metaStrings.clear();
2295 CppFiles::setTranslator(yyFileName, tor);
2303 if (!tor && results->includes.size() == 1
2304 && results->rootNamespace.children.isEmpty()
2305 && results->rootNamespace.aliases.isEmpty()
2306 && results->rootNamespace.usings.isEmpty()) {
2308 pr = *results->includes.cbegin();
2314 CppFiles::setResults(ResultsCacheKey(yyFileName, *
this), pr);
2324 QStringConverter::Encoding e = cd.m_sourceIsUtf16 ? QStringConverter::Utf16 : QStringConverter::Utf8;
2326 for (
const QString &filename : filenames) {
2327 if (!CppFiles::getResults(ResultsCacheKey(filename)).isEmpty() || CppFiles::isBlacklisted(filename))
2330 QFile file(filename);
2331 if (!file.open(QIODevice::ReadOnly)) {
2332 cd.appendError(QStringLiteral(
"Cannot open %1: %2").arg(filename,
2333 file.errorString()));
2338 QTextStream ts(&file);
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));
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);
static void setResults(const ResultsCacheKey &key, const ParseResults *results)
bool parseTranslate(QString &prefix)
void setInput(QTextStream &ts, const QString &fileName)
void setTranslator(Translator *_tor)
void setInput(const QString &in)
void parseInternal(ConversionData &cd, const QStringList &includeStack, QSet< QString > &inclusions)
CppParser(ParseResults *results=0)
void parse(ConversionData &cd, const QStringList &includeStack, QSet< QString > &inclusions)
const ParseResults * recordResults(bool isHeader)
QHash< QString, QString > ExtraData
void setExtras(const ExtraData &extras)
void append(const TranslatorMessage &msg)
bool tryVisit(int fileId)
static bool isStringLiteralPrefix(const QStringView s)
static const QString strclass
static const QString strreturn
QDebug operator<<(QDebug debug, const HashStringList &lst)
static const QString strQCoreApplication
static const QString strQ_SIGNALS
static const QString strnamespace
static const QString strfinal
static const QString strenum
size_t qHash(const ResultsCacheKey &key, size_t seed)
static const QString strnullptr
static const QString strsignals
size_t qHash(const HashStringList &list)
static const QString strCodecForTr
static const QString strpublic
static const QString strQ_SLOTS
static const QString strUnicodeUTF8
static const QString strprotected
static const QString strLatin1
static bool isHeader(const QString &name)
static const QString strNULL
static const QString strstruct
static bool isRawStringLiteralPrefix(QStringView s)
static const QString stroperator
static const QString strusing
static const QString strQ_OBJECT
static const QString strQ_NULLPTR
size_t qHash(const HashString &str)
static const QString strQApplication
size_t qHash(const CppParserState &s, size_t seed)
static const QString strDefaultCodec
static const QString strprivate
static const QString strfriend
void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd)
QDebug operator<<(QDebug debug, const HashString &s)
static const QString strslots
static const QString strdecltype
QHash< QString, const Translator * > TranslatorHash
QList< HashString > NamespaceList
QHash< ResultsCacheKey, IncludeCycle * > IncludeCycleHash
NamespaceList functionContext
const HashString & segment
QualifyOneData(const NamespaceList &ns, int nsc, const HashString &seg, NamespaceList *rslvd, QSet< HashStringList > *visited)
const NamespaceList & namespaces
QSet< HashStringList > * visitedUsings