9#include <QtCore/QBitArray>
10#include <QtCore/QTextStream>
11#include <QtCore/QRegularExpression>
21using namespace Qt::StringLiterals;
25 if (str.m_hash & 0x80000000)
26 str.m_hash = qHash(str.m_str) & 0x7fffffff;
32 return debug << s.value();
37 if (list.m_hash & 0x80000000) {
39 for (
const HashString &qs : list.m_list) {
40 hash ^= qHash(qs) ^ 0x6ad9f526;
41 hash = ((hash << 13) & 0x7fffffff) | (hash >> 18);
50 return debug << lst.m_list;
59 m_ba.resize(nextFileId);
77 void setInput(QTextStream &ts,
const QString &fileName);
82 QSet<QString> &inclusions);
89 IfdefState(
int _bracketDepth,
int _braceDepth,
int _parenDepth) :
90 bracketDepth(_bracketDepth),
91 braceDepth(_braceDepth),
92 parenDepth(_parenDepth),
97 int bracketDepth, bracketDepth1st;
98 int braceDepth, braceDepth1st;
99 int parenDepth, parenDepth1st;
125 Tok_LeftAngleBracket,
126 Tok_RightAngleBracket,
141 std::ostream &yyMsg(
int line = 0);
144 TokenType lookAheadToSemicolonOrLeftBrace();
145 TokenType getToken();
147 void processComment();
149 bool match(TokenType t);
150 bool matchString(QString *s);
151 bool matchEncoding();
152 bool matchStringOrNull(QString *s);
153 bool skipExpression();
155 void recordMessage(
int line,
const QString &context,
const QString &text,
156 const QString &comment,
const QString &extracomment,
const QString &msgid,
160 void handleTr(QString &prefix,
bool plural);
161 void handleTranslate(
bool plural);
162 void handleTrId(
bool plural);
163 void handleDeclareTrFunctions();
166 const QStringList &includeStack, QSet<QString> &inclusions);
171 static QString stringifyNamespace(
int start,
const NamespaceList &namespaces);
172 static QString stringifyNamespace(
const NamespaceList &namespaces)
173 {
return stringifyNamespace(1, namespaces); }
174 static QString joinNamespaces(
const QString &one,
const QString &two);
175 typedef bool (CppParser::*VisitNamespaceCallback)(
const Namespace *ns,
void *context)
const;
176 bool visitNamespace(
const NamespaceList &namespaces,
int nsCount,
177 VisitNamespaceCallback callback,
void *context,
179 bool visitNamespace(
const NamespaceList &namespaces,
int nsCount,
180 VisitNamespaceCallback callback,
void *context)
const;
181 bool qualifyOneCallbackOwn(
const Namespace *ns,
void *context)
const;
182 bool qualifyOneCallbackUsing(
const Namespace *ns,
void *context)
const;
183 bool qualifyOne(
const NamespaceList &namespaces,
int nsCnt,
const HashString &segment,
185 QSet<HashStringList> *visitedUsings,
bool *foundViaUsing =
nullptr)
const;
186 bool qualifyOne(
const NamespaceList &namespaces,
int nsCnt,
const HashString &segment,
188 bool fullyQualify(
const NamespaceList &namespaces,
int nsCnt,
195 const QString &segments,
bool isDeclaration,
197 bool findNamespaceCallback(
const Namespace *ns,
void *context)
const;
199 void enterNamespace(
NamespaceList *namespaces,
const HashString &name);
200 void truncateNamespaces(
NamespaceList *namespaces,
int lenght);
207 bool yyTrailingSpace;
209 qsizetype yyWordInitialCapacity = 0;
210 QStack<IfdefState> yyIfdefStack;
223 const ushort *yyInPtr;
230 MetaStrings m_metaStrings;
232 QString prospectiveContext;
247 directInclude =
true;
250 directInclude =
false;
267 return std::cerr << qPrintable(yyFileName) <<
':' << (line ? line : yyLineNo) <<
": ";
273 yyFileName = QString();
274 yySourceEncoding = QStringConverter::Utf8;
279 yyInStr = ts.readAll();
280 yyFileName = fileName;
281 yySourceEncoding = ts.encoding();
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
308 const ushort *uc = yyInPtr;
337 }
else if (c ==
'\n') {
340 }
else if (c !=
' ' && c !=
'\t' && c !=
'#') {
352 const ushort *uc = yyInPtr + 1;
358 return Tok_Semicolon;
360 return Tok_LeftBrace;
375 if (s.endsWith(u'R')) {
377 return s.isEmpty() || isStringLiteralPrefix(s);
408 Q_ASSERT(yyWord.capacity() == yyWordInitialCapacity);
410 while (yyCh != EOF) {
411 yyLineNo = yyCurLineNo;
413 if (yyCh ==
'#' && yyAtNewline) {
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
436 }
while (isspace(yyCh) && yyCh !=
'\n');
447 }
while (!isspace(yyCh));
454 }
while (isspace(yyCh));
465 }
while (yyCh !=
')');
473 }
while (!isspace(yyCh));
480 }
while (isspace(yyCh));
482 saveState(&savedState);
483 yyMinBraceDepth = yyBraceDepth;
490 yyIfdefStack.push(IfdefState(yyBracketDepth, yyBraceDepth, yyParenDepth));
492 }
else if (yyCh ==
'n') {
496 }
while (yyCh != EOF && !isspace(yyCh) && yyCh !=
'"' && yyCh !=
'<' );
497 while (isspace(yyCh))
502 else if (yyCh ==
'<')
506 ushort *ptr = (ushort *)yyWord.unicode();
509 if (yyCh == EOF || yyCh ==
'\n')
517 yyWord.resize(ptr - (ushort *)yyWord.unicode());
518 return (tChar ==
'"') ? Tok_QuotedInclude : Tok_AngledInclude;
525 if (!yyIfdefStack.isEmpty()) {
526 IfdefState &is = yyIfdefStack.top();
527 if (is.elseLine != -1) {
528 if (yyBracketDepth != is.bracketDepth1st
529 || yyBraceDepth != is.braceDepth1st
530 || yyParenDepth != is.parenDepth1st)
532 <<
"Parenthesis/bracket/brace mismatch between "
533 "#if and #else branches; using #if branch\n";
535 is.bracketDepth1st = yyBracketDepth;
536 is.braceDepth1st = yyBraceDepth;
537 is.parenDepth1st = yyParenDepth;
538 saveState(&is.state);
540 is.elseLine = yyLineNo;
541 yyBracketDepth = is.bracketDepth;
542 yyBraceDepth = is.braceDepth;
543 yyParenDepth = is.parenDepth;
546 }
else if (yyCh ==
'n') {
548 if (!yyIfdefStack.isEmpty()) {
549 IfdefState is = yyIfdefStack.pop();
550 if (is.elseLine != -1) {
551 if (yyBracketDepth != is.bracketDepth1st
552 || yyBraceDepth != is.braceDepth1st
553 || yyParenDepth != is.parenDepth1st)
555 <<
"Parenthesis/brace mismatch between "
556 "#if and #else branches; using #if branch\n";
557 yyBracketDepth = is.bracketDepth1st;
558 yyBraceDepth = is.braceDepth1st;
559 yyParenDepth = is.parenDepth1st;
574 }
while (yyCh != EOF && yyCh !=
'\n');
576 }
else if (yyCh ==
'*') {
577 bool metAster =
false;
582 yyMsg() <<
"Unterminated C++ comment\n";
588 }
else if (metAster && yyCh ==
'/') {
599 }
while (yyCh !=
'\n' && yyCh != EOF);
601 }
else if ((yyCh >=
'A' && yyCh <=
'Z') || (yyCh >=
'a' && yyCh <=
'z') || yyCh ==
'_') {
602 ushort *ptr = (ushort *)yyWord.unicode();
606 }
while ((yyCh >=
'A' && yyCh <=
'Z') || (yyCh >=
'a' && yyCh <=
'z')
607 || (yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'_');
608 yyWord.resize(ptr - (ushort *)yyWord.unicode());
609 yyTrailingSpace = isspace(yyCh);
613 if (yyCh ==
'"' && isStringLiteralPrefix(yyWord)) {
618 switch (yyWord.unicode()[0].unicode()) {
620 if (yyWord == strNULL)
624 if (yyWord == strQ_NULLPTR)
626 if (yyWord == strQ_OBJECT)
628 if (yyWord == strQ_SLOTS || yyWord == strQ_SIGNALS)
632 if (yyWord == strclass)
636 if (yyWord == strdecltype)
640 if (yyWord == strenum)
644 if (yyWord == strfriend)
648 if (yyWord == strnamespace)
649 return Tok_namespace;
650 if (yyWord == strnullptr)
654 if (yyWord == stroperator) {
658 while (isspace(yyCh))
660 while (yyCh ==
'+' || yyCh ==
'-' || yyCh ==
'*' || yyCh ==
'/' || yyCh ==
'%'
661 || yyCh ==
'=' || yyCh ==
'<' || yyCh ==
'>' || yyCh ==
'!'
662 || yyCh ==
'&' || yyCh ==
'|' || yyCh ==
'~' || yyCh ==
'^'
663 || yyCh ==
'[' || yyCh ==
']')
668 if (yyWord == strpublic || yyWord == strprotected || yyWord == strprivate)
672 if (yyWord == strreturn)
676 if (yyWord == strstruct)
678 if (yyWord == strslots || yyWord == strsignals)
682 if (yyWord == strusing)
688 if (yyCh ==
'"' && isRawStringLiteralPrefix(yyWord)) {
689 ptr =
reinterpret_cast<ushort *>(
const_cast<QChar *>(yyWord.unicode()));
692 for (yyCh = getChar(); yyCh != EOF && yyCh !=
'('; yyCh = getChar())
693 delimiter += QLatin1Char(yyCh);
697 ushort *ptr_past_end =
nullptr;
698 while (yyCh != EOF && !is_end) {
700 if (ptr_past_end !=
nullptr) {
701 if (delimiter.size() == ptr - ptr_past_end
702 && memcmp(delimiter.unicode(), ptr_past_end, (ptr - ptr_past_end) *
sizeof (ushort)) == 0
709 ptr_past_end =
nullptr;
715 if (delimiter.isEmpty()) {
721 ptr_past_end =
nullptr;
728 yyWord.resize(ptr_past_end - 1 -
reinterpret_cast<
const ushort *>(yyWord.unicode()));
730 yyWord.resize(ptr -
reinterpret_cast<
const ushort *>(yyWord.unicode()));
732 yyMsg() <<
"Unterminated/mismatched C++ Raw string\n";
735 return Tok_RawString;
743 loadState(savedState);
744 prospectiveContext.clear();
745 yyBraceDepth = yyMinBraceDepth;
757 ushort *ptr = (ushort *)yyWord.unicode();
763 }
while (yyCh !=
'\n');
764 yyWord.resize(ptr - (ushort *)yyWord.unicode());
766 }
else if (yyCh ==
'*') {
767 bool metAster =
false;
768 ushort *ptr = (ushort *)yyWord.unicode();
773 yyMsg() <<
"Unterminated C++ comment\n";
780 else if (metAster && yyCh ==
'/')
785 yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2);
792 ushort *ptr = (ushort *)yyWord.unicode();
794 while (yyCh != EOF && yyCh !=
'\n' && yyCh !=
'"') {
797 if (yyCh == EOF || yyCh ==
'\n')
804 yyWord.resize(ptr - (ushort *)yyWord.unicode());
807 yyMsg() <<
"Unterminated C++ string\n";
823 return Tok_ColonColon;
835 return Tok_RightAngleBracket;
840 return Tok_LeftShift;
842 return Tok_LeftAngleBracket;
849 if (yyCh == EOF || yyCh ==
'\n') {
850 yyMsg() <<
"Unterminated C++ character\n";
861 if (yyBraceDepth == 0)
862 yyBraceLineNo = yyCurLineNo;
865 return Tok_LeftBrace;
867 if (yyBraceDepth == yyMinBraceDepth) {
870 <<
"Excess closing brace in C++ code"
871 " (or abuse of the C++ preprocessor)\n";
874 return Tok_Semicolon;
878 return Tok_RightBrace;
880 if (yyParenDepth == 0)
881 yyParenLineNo = yyCurLineNo;
884 return Tok_LeftParen;
886 if (yyParenDepth == 0)
888 <<
"Excess closing parenthesis in C++ code"
889 " (or abuse of the C++ preprocessor)\n";
893 return Tok_RightParen;
900 bool inString =
false;
902 bool escaped =
false;
905 while (depth > 0 && yyCh != EOF) {
908 }
else if (yyCh ==
'\\') {
910 }
else if (yyCh ==
'"' && !inChar) {
911 inString = !inString;
912 }
else if (yyCh ==
'\'' && !inString) {
914 }
else if (!inString && !inChar) {
917 else if (yyCh ==
']')
923 if (yyCh == EOF || yyCh !=
']') {
924 yyMsg(yyCurLineNo) <<
"Unterminated C++ attribute (missing ']]')\n";
925 return Tok_LeftBracket;
929 return Tok_Attribute;
931 if (yyBracketDepth == 0)
932 yyBracketLineNo = yyCurLineNo;
934 return Tok_LeftBracket;
936 if (yyBracketDepth == 0)
938 <<
"Excess closing bracket in C++ code"
939 " (or abuse of the C++ preprocessor)\n";
943 return Tok_RightBracket;
949 return Tok_Semicolon;
952 return Tok_QuestionMark;
955 if (yyCh ==
'x' || yyCh ==
'X') {
958 }
while ((yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'\''
959 || (yyCh >=
'a' && yyCh <=
'f') || (yyCh >=
'A' && yyCh <=
'F'));
962 if (yyCh <
'0' || yyCh >
'9')
976 }
while ((yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'\'');
988
989
990
999 *
static_cast<CppParserState *>(
this) = state;
1004 Namespace *pns, *ns = &results->rootNamespace;
1005 for (
int i = 1; i < namespaces->size(); ++i) {
1007 if (!(ns = pns->children.value(namespaces->at(i)))) {
1010 if (haveLast || i < namespaces->size() - 1)
1011 if (
const Namespace *ons = findNamespace(*namespaces, i + 1))
1013 pns->children.insert(namespaces->at(i), ns);
1016 }
while (++i < namespaces->size());
1027 for (
int j = start; j < namespaces.size(); ++j)
1028 l += namespaces.at(j).value().size();
1029 ret.reserve(l + qMax(0, (namespaces.size() - start - 1)) * 2);
1030 for (
int i = start; i < namespaces.size(); ++i) {
1033 ret += namespaces.at(i).value();
1038QString
CppParser::joinNamespaces(
const QString &one,
const QString &two)
1040 return two.isEmpty() ? one : one.isEmpty() ? two : one + QStringLiteral(
"::") + two;
1044 VisitNamespaceCallback callback,
void *context,
1047 const Namespace *ns = &rslt->rootNamespace;
1048 for (
int i = 1; i < nsCount; ++i)
1049 if (!(ns = ns->children.value(namespaces.at(i))))
1051 if ((
this->*callback)(ns, context))
1054 for (
const ParseResults *sup : rslt->includes)
1055 if (vr.tryVisit(sup->fileId)
1056 && visitNamespace(namespaces, nsCount, callback, context, vr, sup))
1062 VisitNamespaceCallback callback,
void *context)
const
1065 return visitNamespace(namespaces, nsCount, callback, context, vr, results);
1070 QSet<HashStringList> *visited,
Namespace const **resolvedNs)
1090 if (
auto rns = ns->children.constFind(data
->segment); rns != ns->children.cend()) {
1096 auto nsai = ns->aliases.constFind(data
->segment);
1097 if (nsai != ns->aliases.constEnd()) {
1099 if (nsl.last().value().isEmpty()) {
1119 for (
const HashStringList &use : ns->usings)
1120 if (!data->visitedUsings->contains(use)) {
1121 data->visitedUsings->insert(use);
1122 if (qualifyOne(use.value(), use.value().size(), data->segment, data->resolved,
1123 data->resolvedNamespace, data->visitedUsings))
1131 QSet<HashStringList> *visitedUsings,
bool *foundViaUsing)
const
1133 QualifyOneData data(namespaces, nsCnt, segment, resolved, visitedUsings, resolvedNamespace);
1135 if (visitNamespace(namespaces, nsCnt, &
CppParser::qualifyOneCallbackOwn, &data)) {
1137 *foundViaUsing =
false;
1141 if (visitNamespace(namespaces, nsCnt, &
CppParser::qualifyOneCallbackUsing, &data)) {
1143 *foundViaUsing =
true;
1153 QSet<HashStringList> visitedUsings;
1155 return qualifyOne(namespaces, nsCnt, segment, resolved, resolvedNamespace, &visitedUsings);
1165 if (segments.first().value().isEmpty()) {
1167 if (segments.size() == 1) {
1169 *resolved << HashString(QString());
1184 auto matchSeg = segments.crbegin();
1185 auto matchNs = namespaces.crbegin();
1187 if (matchSeg->value() != matchNs->value())
1190 int matchingSuffixLength = 0;
1191 while (matchSeg != segments.crend()
1192 && matchNs != namespaces.crend()
1193 && matchSeg->value() == matchNs->value()) {
1197 matchingSuffixLength++;
1210 bool viaUsing =
false;
1216 int segIdx = initSegIdx;
1217 int resolveSize = nsIdx + 1;
1218 *resolved = namespaces;
1220 QSet<HashStringList> visitedUsings;
1221 if (!qualifyOne(*resolved, resolveSize, segments[segIdx], resolved, &resolvedNs,
1222 &visitedUsings, &viaUsing))
1226 resolveSize = resolved->size();
1227 }
while (segIdx < segments.size());
1229 if (segIdx == segments.size()) {
1233 else if (!trCandidate)
1234 trCandidate.emplace(*resolved);
1235 }
else if (resolvedNs) {
1237 noTrCandidate.emplace(*resolved);
1238 else if (!noTrCandidate)
1239 noTrCandidate.emplace(*resolved);
1241 }
else if (!unresolvedCandidate) {
1242 unresolvedCandidate.emplace(segments.mid(segIdx));
1247 }
while ((!isDeclaration || matchingSuffixLength-- > 0) && --nsIdx >= 0);
1260 *resolved = *trCandidate;
1262 }
else if (noTrCandidate) {
1263 *resolved = *noTrCandidate;
1265 }
else if (unresolvedCandidate && unresolved) {
1266 *unresolved = *unresolvedCandidate;
1277 return fullyQualify(namespaces, namespaces.size(),
1278 segments, isDeclaration, resolved, unresolved);
1282 const QString &quali,
bool isDeclaration,
1286 for (
const QString &str : quali.split(
"::"_L1))
1287 segments << HashString(str);
1288 return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved);
1301 nsCount = namespaces.size();
1302 visitNamespace(namespaces, nsCount, &
CppParser::findNamespaceCallback, &ns);
1308 *namespaces << name;
1310 if (!(ns = findNamespace(*namespaces)))
1311 ns = modifyNamespace(namespaces,
false);
1313 const Namespace *cns = &results->rootNamespace;
1314 for (
int i = 1; i < namespaces->size(); ++i) {
1315 ns->usings << cns->usings;
1316 if (!(cns = cns->children.value(namespaces->at(i))))
1323 if (namespaces->size() > length)
1324 namespaces->erase(namespaces->begin() + length, namespaces->end());
1329
1330
1334 seed = qHash(s.namespaces, seed);
1335 seed = qHash(s.namespaceDepths, seed);
1336 seed = qHash(s.functionContext, seed);
1337 seed = qHash(s.functionContextUnresolved, seed);
1338 seed = qHash(s.pendingContext, seed);
1344 seed = qHash(key.cleanFile, seed);
1345 seed = qHash(key.parserState, seed);
1363QSet<QString> &
CppFiles::blacklistedFiles()
1365 static QSet<QString> blacklisted;
1372 IncludeCycle *
const cycle = includeCycles().value(key);
1375 return cycle->results;
1386 includeCycles().insert(key, cycle);
1389 cycle->fileNames.insert(key.cleanFile);
1390 cycle->results.insert(results);
1395 return translatedFiles().value(cleanFile);
1400 translatedFiles().insert(cleanFile, tor);
1405 return blacklistedFiles().contains(cleanFile);
1410 blacklistedFiles().insert(cleanFile);
1416 cycle->fileNames = fileNames;
1418 QSet<IncludeCycle *> intersectingCycles;
1419 for (
const QString &fileName : fileNames) {
1420 const ResultsCacheKey key = { fileName, parserState };
1421 IncludeCycle *intersectingCycle = includeCycles().value(key);
1423 if (intersectingCycle && !intersectingCycles.contains(intersectingCycle)) {
1424 intersectingCycles.insert(intersectingCycle);
1426 cycle->fileNames.unite(intersectingCycle->fileNames);
1427 cycle->results.unite(intersectingCycle->results);
1430 qDeleteAll(intersectingCycles);
1432 for (
const QString &fileName : std::as_const(cycle->fileNames))
1433 includeCycles().insert({ fileName, parserState }, cycle);
1438 QString fileExt = QFileInfo(name).suffix();
1439 return fileExt.isEmpty() || fileExt.startsWith(u'h', Qt::CaseInsensitive);
1443 QSet<QString> &inclusions)
1445 QString cleanFile = QDir::cleanPath(file);
1447 for (
const QRegularExpression &rx : std::as_const(cd.m_excludes)) {
1448 if (rx.match(cleanFile).hasMatch())
1452 const int index = includeStack.indexOf(cleanFile);
1454 CppFiles::addIncludeCycle(QSet<QString>(includeStack.cbegin() + index, includeStack.cend()),
1463 bool isIndirect =
false;
1464 if (!CppFiles::isBlacklisted(cleanFile)
1465 && isHeader(cleanFile)) {
1467 QSet<
const ParseResults *> res = CppFiles::getResults(ResultsCacheKey(cleanFile, *
this));
1468 if (!res.isEmpty()) {
1469 results->includes.unite(res);
1477 if (!f.open(QIODevice::ReadOnly)) {
1478 yyMsg() << qPrintable(
1479 QStringLiteral(
"Cannot open %1: %2\n").arg(cleanFile, f.errorString()));
1484 ts.setEncoding(yySourceEncoding);
1485 ts.setAutoDetectUnicode(
true);
1487 inclusions.insert(cleanFile);
1490 for (
const QString &projectRoot : std::as_const(cd.m_projectRoots))
1491 if (cleanFile.startsWith(projectRoot)) {
1492 parser.setTranslator(
new Translator);
1495 parser.setInput(ts, cleanFile);
1496 QStringList stack = includeStack;
1498 parser.parse(cd, stack, inclusions);
1504 parser.functionContextUnresolved = functionContextUnresolved;
1505 parser.setInput(ts, cleanFile);
1507 QStringList stack = includeStack;
1509 parser.parseInternal(cd, stack, inclusions);
1511 CppFiles::setBlacklisted(cleanFile);
1513 inclusions.remove(cleanFile);
1515 prospectiveContext.clear();
1516 pendingContext.clear();
1520
1521
1522
1523
1524
1525
1526
1527
1531 bool matches = (yyTok == t);
1539 bool matches =
false;
1542 if (yyTok != Tok_String && yyTok != Tok_RawString)
1545 if (yyTok == Tok_String)
1546 *s += ParserTool::transcode(yyWord);
1563 if (yyTok != Tok_Ident)
1565 if (yyWord == strQApplication || yyWord == strQCoreApplication) {
1567 if (yyTok == Tok_ColonColon)
1570 if (yyWord == strUnicodeUTF8) {
1574 if (yyWord == strLatin1 || yyWord == strDefaultCodec || yyWord == strCodecForTr)
1575 yyMsg() <<
"Unsupported encoding Latin1/DefaultCodec/CodecForTr\n";
1579bool CppParser::matchStringOrNull(QString *s)
1581 return matchString(s) || match(Tok_Null);
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1597 if (match(Tok_Null) || match(Tok_Integer))
1601 while (parenlevel >= 0) {
1603 if (yyTok == Tok_RightParen)
1605 else if (yyTok == Tok_LeftParen)
1607 else if (yyTok == Tok_Cancel || yyTok == Tok_Eof)
1613void CppParser::recordMessage(
int line,
const QString &context,
const QString &text,
1614 const QString &comment,
const QString &extracomment,
1615 const QString &msgid,
const QString &label,
1619 ParserTool::transcode(context), text, ParserTool::transcode(comment), QString(),
1620 yyFileName, line, QStringList(),
1621 TranslatorMessage::Unfinished, plural);
1622 msg.setExtraComment(ParserTool::transcode(extracomment.simplified()));
1625 if (!msgid.isEmpty())
1626 msg.setLabel(label);
1630void CppParser::handleTr(QString &prefix,
bool plural)
1632 if (!m_metaStrings.sourcetext().isEmpty())
1633 yyMsg() <<
"//% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n";
1634 if (!m_metaStrings.label().isEmpty() && m_metaStrings.msgid().isEmpty())
1635 yyMsg() <<
"labels cannot be used with text-based translation. Ignoring\n";
1637 int line = yyLineNo;
1640 if (matchString(&text)) {
1642 if (yyTok == Tok_RightParen) {
1644 }
else if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1645 if (yyTok == Tok_RightParen) {
1647 }
else if (match(Tok_Comma)) {
1651 if (!pendingContext.isEmpty() && !prefix.startsWith(
"::"_L1)) {
1653 if (!fullyQualify(namespaces, pendingContext,
true, &functionContext, &unresolved)) {
1654 functionContextUnresolved = stringifyNamespace(0, unresolved);
1655 yyMsg() << qPrintable(
1656 QStringLiteral(
"Qualifying with unknown namespace/class %1::%2\n")
1657 .arg(stringifyNamespace(functionContext)).arg(unresolved.first().value()));
1659 pendingContext.clear();
1661 if (prefix.isEmpty()) {
1662 if (functionContextUnresolved.isEmpty()) {
1665 yyMsg() <<
"tr() cannot be called without context\n";
1671 context = stringifyNamespace(functionContext);
1674 yyMsg() << qPrintable(
1675 QStringLiteral(
"Class '%1' lacks Q_OBJECT macro\n").arg(context));
1682 if (fctx->trQualification.isEmpty()) {
1685 context += functionContext.at(i).value();
1690 fctx->trQualification = context;
1692 context = fctx->trQualification;
1695 context = joinNamespaces(stringifyNamespace(functionContext), functionContextUnresolved);
1701 if (fullyQualify(
functionContext, prefix,
false, &nsl, &unresolved)) {
1703 if (fctx->trQualification.isEmpty()) {
1704 context = stringifyNamespace(nsl);
1705 fctx->trQualification = context;
1707 context = fctx->trQualification;
1710 yyMsg() << qPrintable(QStringLiteral(
"Class '%1' lacks Q_OBJECT macro\n")
1715 context = joinNamespaces(stringifyNamespace(nsl), stringifyNamespace(0, unresolved));
1721 recordMessage(line, context, text, comment, m_metaStrings.extracomment(),
1722 m_metaStrings.msgid(), m_metaStrings.label(), m_metaStrings.extra(),
1725 m_metaStrings.clear();
1726 metaExpected =
false;
1729void CppParser::handleTranslate(
bool plural)
1731 if (!m_metaStrings.sourcetext().isEmpty())
1732 yyMsg() <<
"//% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n";
1733 if (!m_metaStrings.label().isEmpty() && m_metaStrings.msgid().isEmpty())
1734 yyMsg() <<
"labels cannot be used with text-based translation. Ignoring\n";
1735 int line = yyLineNo;
1738 if (matchString(&context)
1740 && matchString(&text) && !text.isEmpty())
1743 if (yyTok != Tok_RightParen) {
1745 if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1746 if (yyTok != Tok_RightParen) {
1748 if (match(Tok_Comma)) {
1749 if (matchEncoding()) {
1750 if (yyTok != Tok_RightParen) {
1757 plural |= match(Tok_Comma);
1762 if (skipExpression() && yyTok == Tok_RightParen) {
1776 recordMessage(line, context, text, comment, m_metaStrings.extracomment(),
1777 m_metaStrings.msgid(), m_metaStrings.label(), m_metaStrings.extra(), plural);
1779 m_metaStrings.clear();
1780 metaExpected =
false;
1785 if (!m_metaStrings.msgid().isEmpty())
1786 yyMsg() <<
"//= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n";
1787 int line = yyLineNo;
1790 if (matchString(&msgid) && !msgid.isEmpty()) {
1791 plural |= match(Tok_Comma);
1792 recordMessage(line, QString(), ParserTool::transcode(m_metaStrings.sourcetext()), QString(),
1793 m_metaStrings.extracomment(), msgid, m_metaStrings.label(),
1794 m_metaStrings.extra(), plural);
1796 m_metaStrings.clear();
1797 metaExpected =
false;
1800void CppParser::handleDeclareTrFunctions()
1805 if (yyTok != Tok_Ident)
1810 if (yyTok == Tok_RightParen)
1812 if (yyTok != Tok_ColonColon)
1816 Namespace *ns = modifyNamespace(&namespaces);
1817 ns->hasTrFunctions =
true;
1818 ns->trQualification = name;
1819 ns->trQualification.detach();
1823 QSet<QString> &inclusions)
1827 functionContextUnresolved.clear();
1829 parseInternal(cd, includeStack, inclusions);
1834 bool forcePlural =
false;
1835 switch (trFunctionAliasManager.trFunctionByName(yyWord)) {
1837 handleDeclareTrFunctions();
1842 case TrFunctionAliasManager::Function_tr:
1843 case TrFunctionAliasManager::Function_trUtf8:
1844 case TrFunctionAliasManager::Function_QT_TR_NOOP:
1845 case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
1847 handleTr(prefix, forcePlural);
1860 handleTranslate(forcePlural);
1869 handleTrId(forcePlural);
1878 QSet<QString> &inclusions)
1880 static constexpr auto strColons(
"::"_L1);
1883 bool yyTokColonSeen =
false;
1884 bool yyTokIdentSeen =
false;
1885 bool maybeInTrailingReturnType =
false;
1887 const QList<TokenType> allowedTokensInFnTemplate{
1888 Tok_Ident, Tok_decltype, Tok_ColonColon, Tok_Comma,
1889 Tok_Integer, Tok_LeftAngleBracket, Tok_RightAngleBracket
1891 metaExpected =
true;
1893 prospectiveContext.clear();
1894 pendingContext.clear();
1896 yyWord.reserve(yyInStr.size());
1897 yyWordInitialCapacity = yyWord.capacity();
1898 yyInPtr = (
const ushort *)yyInStr.unicode();
1901 while (yyTok != Tok_Eof) {
1906 if (yyBracketDepth && yyBraceDepth == namespaceDepths.size()) {
1912 case Tok_QuotedInclude: {
1913 QString text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord);
1915 if (QFileInfo(text).isFile()) {
1916 processInclude(text, cd, includeStack, inclusions);
1922 case Tok_AngledInclude: {
1923 const QStringList cSources = cd.m_allCSources.values(yyWord);
1924 if (!cSources.isEmpty()) {
1925 for (
const QString &cSource : cSources)
1926 processInclude(cSource, cd, includeStack, inclusions);
1929 for (
const QString &incPath : std::as_const(cd.m_includePath)) {
1930 QString text = QDir(incPath).absoluteFilePath(yyWord);
1932 if (QFileInfo(text).isFile()) {
1933 processInclude(text, cd, includeStack, inclusions);
1944 if (yyTok == Tok_class)
1949
1950
1954 if (yyTok == Tok_Equals) {
1957 }
else if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
1963 QString text = yyWord;
1968 if (yyTok == Tok_ColonColon) {
1971 }
else if (yyTok == Tok_Ident) {
1972 if (yyWord == strfinal) {
1982 }
else if (yyTok == Tok_Attribute) {
1989 if (yyTok == Tok_Colon || yyTok == Tok_LeftAngleBracket) {
1995 if (yyTok == Tok_Eof)
1997 if (yyTok == Tok_Cancel)
1999 if (yyTok == Tok_class)
2001 if (yyTok == Tok_Ident) {
2003 if (yyTok == Tok_LeftParen)
2004 parseTranslate(prefix);
2006 goto tokenInTemplate;
2008 }
while (yyTok != Tok_LeftBrace && yyTok != Tok_Semicolon);
2009 if (yyTok == Tok_Semicolon)
2012 if (yyTok != Tok_LeftBrace) {
2019 if (!quali.isEmpty()) {
2022 if (!fullyQualify(namespaces, quali,
true, &nsl, 0)) {
2023 yyMsg() <<
"Ignoring definition of undeclared qualified class\n";
2026 namespaceDepths.push(namespaces.size());
2029 namespaceDepths.push(namespaces.size());
2031 enterNamespace(&namespaces, fct);
2034 functionContextUnresolved.clear();
2035 prospectiveContext.clear();
2036 pendingContext.clear();
2038 metaExpected =
true;
2045 while (yyTok == Tok_Attribute)
2047 if (yyTok == Tok_Ident) {
2048 QString text = yyWord;
2050 HashString ns = HashString(text);
2054 if (yyTok != Tok_ColonColon)
2057 if (yyTok != Tok_Ident)
2059 nestedNamespaces.append(ns);
2062 ns = HashString(text);
2064 if (yyTok == Tok_LeftBrace) {
2065 namespaceDepths.push(namespaces.size());
2066 for (
const auto &nns : nestedNamespaces)
2067 enterNamespace(&namespaces, nns);
2068 enterNamespace(&namespaces, ns);
2070 functionContext = namespaces;
2071 functionContextUnresolved.clear();
2072 prospectiveContext.clear();
2073 pendingContext.clear();
2074 metaExpected =
true;
2076 }
else if (yyTok == Tok_Equals) {
2079 NamespaceList fullName;
2081 if (yyTok == Tok_ColonColon)
2082 fullName.append(HashString(QString()));
2083 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
2084 if (yyTok == Tok_Ident) {
2087 fullName.append(HashString(text));
2091 if (fullName.isEmpty())
2093 fullName.append(HashString(QString()));
2094 modifyNamespace(&namespaces)->aliases[ns] = fullName;
2096 }
else if (yyTok == Tok_LeftBrace) {
2098 namespaceDepths.push(namespaces.size());
2099 metaExpected =
true;
2106 if (yyTok == Tok_namespace) {
2109 if (yyTok == Tok_ColonColon)
2110 fullName.append(HashString(QString()));
2111 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
2112 if (yyTok == Tok_Ident) {
2113 QString text = yyWord;
2115 fullName.append(HashString(text));
2120 if (fullyQualify(
namespaces, fullName,
false, &nsl, 0))
2121 modifyNamespace(&
namespaces)->usings << HashStringList(nsl);
2124 if (yyTok == Tok_ColonColon)
2125 fullName.append(HashString(QString()));
2126 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
2127 if (yyTok == Tok_Ident) {
2128 QString text = yyWord;
2130 fullName.append(HashString(text));
2134 if (fullName.isEmpty())
2140 fullName.append(HashString(QString()));
2141 const HashString &ns = *(fullName.constEnd() - 2);
2142 modifyNamespace(&
namespaces)->aliases[ns] = fullName;
2150 if (yyTokColonSeen &&
2151 yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
2153 yyTokIdentSeen =
true;
2157 bool methodSpecialization =
false;
2158 if (yyTok == Tok_LeftAngleBracket) {
2163 while (count && allowedTokensInFnTemplate.contains(yyTok)) {
2164 if (yyTok == Tok_LeftAngleBracket)
2166 else if (yyTok == Tok_RightAngleBracket)
2170 methodSpecialization = count == 0;
2172 if (yyTok == Tok_LeftParen) {
2173 if (parseTranslate(prefix)) {
2180 if (yyTok == Tok_ColonColon && !maybeInTrailingReturnType && !yyTrailingSpace) {
2181 prefix += methodSpecialization ? className : yyWord;
2185 if (!prefix.isEmpty() && yyTok != Tok_LeftParen && yyTok != Tok_LeftBracket
2186 && yyTok != Tok_Equals) {
2189 prospectiveContext.clear();
2193 metaExpected =
false;
2197 if (yyParenDepth == 0 && yyBraceDepth == namespaceDepths.size())
2198 maybeInTrailingReturnType =
true;
2200 if (yyTok == Tok_Ident) {
2202 if (yyTok == Tok_LeftParen) {
2203 switch (trFunctionAliasManager.trFunctionByName(yyWord)) {
2206 yyMsg() <<
"Cannot invoke tr() like this\n";
2212 case Tok_ColonColon:
2213 if (yyTokIdentSeen || maybeInTrailingReturnType) {
2218 if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0 && !yyTokColonSeen)
2219 prospectiveContext = prefix;
2220 if (!prefix.isEmpty())
2221 prefix += strColons;
2224 case Tok_RightBrace:
2225 if (!yyTokColonSeen) {
2226 if (yyBraceDepth + 1 == namespaceDepths.size()) {
2228 truncateNamespaces(&namespaces, namespaceDepths.pop());
2230 if (yyBraceDepth == namespaceDepths.size()) {
2232 if (!yyBraceDepth && !directInclude)
2236 functionContextUnresolved.clear();
2237 pendingContext.clear();
2242 maybeInTrailingReturnType =
false;
2243 prospectiveContext.clear();
2245 if (m_metaStrings.hasData()) {
2246 yyMsg() <<
"Discarding unconsumed meta data\n";
2247 m_metaStrings.clear();
2249 metaExpected =
true;
2256 }
while (yyTok == Tok_Access);
2257 metaExpected =
true;
2258 if (yyTok == Tok_Colon)
2263 if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
2264 if (!prospectiveContext.isEmpty()) {
2265 pendingContext = prospectiveContext;
2266 prospectiveContext.clear();
2269 if (yyTok == Tok_Colon) {
2270 if (lookAheadToSemicolonOrLeftBrace() != Tok_Semicolon)
2271 yyTokColonSeen =
true;
2274 metaExpected =
true;
2278 if (yyBraceDepth == namespaceDepths.size() + 1 && yyParenDepth == 0) {
2279 if (!prospectiveContext.isEmpty()) {
2280 pendingContext = prospectiveContext;
2281 prospectiveContext.clear();
2283 if (!yyTokIdentSeen) {
2285 yyTokColonSeen =
false;
2288 maybeInTrailingReturnType =
false;
2289 yyTokIdentSeen =
false;
2290 metaExpected =
true;
2294 if (!yyTokColonSeen && yyBraceDepth == namespaceDepths.size() && yyParenDepth == 1
2295 && !prospectiveContext.isEmpty()) {
2296 pendingContext = prospectiveContext;
2297 prospectiveContext.clear();
2299 yyTokIdentSeen =
false;
2300 metaExpected =
true;
2304 case Tok_QuestionMark:
2305 metaExpected =
true;
2308 case Tok_RightParen:
2309 if (yyParenDepth == 0) {
2310 if (!yyTokColonSeen && !pendingContext.isEmpty()
2311 && yyBraceDepth == namespaceDepths.size()) {
2313 prospectiveContext = pendingContext;
2314 pendingContext.clear();
2316 metaExpected =
true;
2318 metaExpected =
false;
2325 auto initialParenDepth = yyParenDepth;
2331 while (yyParenDepth != initialParenDepth && yyTok != Tok_Eof)
2338 if (yyTok == Tok_class)
2344 if (yyTok == Tok_Colon)
2348 if (!yyParenDepth && !maybeInTrailingReturnType)
2349 prospectiveContext.clear();
2351 case Tok_RightBracket:
2359 if (yyBraceDepth != 0)
2360 yyMsg(yyBraceLineNo)
2361 <<
"Unbalanced opening brace in C++ code (or abuse of the C++ preprocessor)\n";
2362 else if (yyParenDepth != 0)
2363 yyMsg(yyParenLineNo)
2364 <<
"Unbalanced opening parenthesis in C++ code"
2365 " (or abuse of the C++ preprocessor)\n";
2366 else if (yyBracketDepth != 0)
2367 yyMsg(yyBracketLineNo)
2368 <<
"Unbalanced opening bracket in C++ code"
2369 " (or abuse of the C++ preprocessor)\n";
2374 if (!tor || !metaExpected)
2377 if (!m_metaStrings.parse(yyWord)) {
2378 yyMsg() << m_metaStrings.popError().toStdString();
2382 if (m_metaStrings.magicComment()) {
2383 auto [context, comment] = *m_metaStrings.magicComment();
2385 ParserTool::transcode(comment), QString(), yyFileName, yyLineNo,
2386 QStringList(), TranslatorMessage::Finished,
false);
2387 msg.setExtraComment(ParserTool::transcode(m_metaStrings.extracomment().simplified()));
2389 tor->setExtras(m_metaStrings.extra());
2390 m_metaStrings.clear();
2398 CppFiles::setTranslator(yyFileName, tor);
2406 if (!tor && results->includes.size() == 1
2407 && results->rootNamespace.children.isEmpty()
2408 && results->rootNamespace.aliases.isEmpty()
2409 && results->rootNamespace.usings.isEmpty()) {
2411 pr = *results->includes.cbegin();
2417 CppFiles::setResults(ResultsCacheKey(yyFileName, *
this), pr);
2427 QStringConverter::Encoding e = cd.m_sourceIsUtf16 ? QStringConverter::Utf16 : QStringConverter::Utf8;
2429 for (
const QString &filename : filenames) {
2430 if (!CppFiles::getResults(ResultsCacheKey(filename)).isEmpty() || CppFiles::isBlacklisted(filename))
2433 QFile file(filename);
2434 if (!file.open(QIODevice::ReadOnly)) {
2435 cd.appendError(QStringLiteral(
"Cannot open %1: %2").arg(filename,
2436 file.errorString()));
2441 QTextStream ts(&file);
2443 ts.setAutoDetectUnicode(
true);
2444 parser.setInput(ts, filename);
2445 Translator *tor =
new Translator;
2446 parser.setTranslator(tor);
2447 QSet<QString> inclusions;
2448 parser.parse(cd, QStringList(), inclusions);
2449 parser.recordResults(isHeader(filename));
2452 for (
const QString &filename : filenames) {
2453 if (!CppFiles::isBlacklisted(filename)) {
2454 if (
const Translator *tor = CppFiles::getTranslator(filename)) {
2455 for (
const TranslatorMessage &msg : tor->messages())
2456 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
Namespace const ** resolvedNamespace
const NamespaceList & namespaces
QSet< HashStringList > * visitedUsings
QualifyOneData(const NamespaceList &ns, int nsc, const HashString &seg, NamespaceList *rslvd, QSet< HashStringList > *visited, Namespace const **resolvedNs)