7#include <QtCore/QBitArray>
8#include <QtCore/QTextStream>
9#include <QtCore/QRegularExpression>
16using namespace Qt::StringLiterals;
22 if (str.m_hash & 0x80000000)
23 str.m_hash = qHash(str.m_str) & 0x7fffffff;
29 return debug << s.value();
36 for (
const HashString &qs : list.m_list) {
37 hash ^= qHash(qs) ^ 0x6ad9f526;
38 hash = ((hash << 13) & 0x7fffffff) | (hash >> 18);
47 return debug << lst.m_list;
56 m_ba.resize(nextFileId);
74 void setInput(QTextStream &ts,
const QString &fileName);
84 IfdefState(
int _bracketDepth,
int _braceDepth,
int _parenDepth) :
85 bracketDepth(_bracketDepth),
86 braceDepth(_braceDepth),
87 parenDepth(_parenDepth),
92 int bracketDepth, bracketDepth1st;
93 int braceDepth, braceDepth1st;
94 int parenDepth, parenDepth1st;
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
108 std::ostream &yyMsg(
int line = 0);
111 TokenType lookAheadToSemicolonOrLeftBrace();
112 TokenType getToken();
114 void processComment();
116 bool match(TokenType t);
117 bool matchString(QString *s);
118 bool matchEncoding();
119 bool matchStringOrNull(QString *s);
120 bool matchExpression();
123 int line,
const QString &context,
const QString &text,
const QString &comment,
127 void handleTr(QString &prefix,
bool plural);
128 void handleTranslate(
bool plural);
129 void handleTrId(
bool plural);
130 void handleDeclareTrFunctions();
133 const QStringList &includeStack, QSet<QString> &inclusions);
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,
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,
154 bool fullyQualify(
const NamespaceList &namespaces,
int nsCnt,
161 const QString &segments,
bool isDeclaration,
163 bool findNamespaceCallback(
const Namespace *ns,
void *context)
const;
165 void enterNamespace(
NamespaceList *namespaces,
const HashString &name);
166 void truncateNamespaces(
NamespaceList *namespaces,
int lenght);
174 qsizetype yyWordInitialCapacity = 0;
175 QStack<IfdefState> yyIfdefStack;
188 const ushort *yyInPtr;
197 QString extracomment;
202 QString prospectiveContext;
217 directInclude =
true;
220 directInclude =
false;
237 return std::cerr << qPrintable(yyFileName) <<
':' << (line ? line : yyLineNo) <<
": ";
243 yyFileName = QString();
244 yySourceEncoding = QStringConverter::Utf8;
249 yyInStr = ts.readAll();
250 yyFileName = fileName;
251 yySourceEncoding = ts.encoding();
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
278 const ushort *uc = yyInPtr;
307 }
else if (c ==
'\n') {
310 }
else if (c !=
' ' && c !=
'\t' && c !=
'#') {
322 const ushort *uc = yyInPtr + 1;
328 return Tok_Semicolon;
330 return Tok_LeftBrace;
345 if (s.endsWith(u'R')) {
347 return s.isEmpty() || isStringLiteralPrefix(s);
378 Q_ASSERT(yyWord.capacity() == yyWordInitialCapacity);
380 while (yyCh != EOF) {
381 yyLineNo = yyCurLineNo;
383 if (yyCh ==
'#' && yyAtNewline) {
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
406 }
while (isspace(yyCh) && yyCh !=
'\n');
417 }
while (!isspace(yyCh));
424 }
while (isspace(yyCh));
435 }
while (yyCh !=
')');
443 }
while (!isspace(yyCh));
450 }
while (isspace(yyCh));
452 saveState(&savedState);
453 yyMinBraceDepth = yyBraceDepth;
460 yyIfdefStack.push(IfdefState(yyBracketDepth, yyBraceDepth, yyParenDepth));
462 }
else if (yyCh ==
'n') {
466 }
while (yyCh != EOF && !isspace(yyCh) && yyCh !=
'"' && yyCh !=
'<' );
467 while (isspace(yyCh))
472 else if (yyCh ==
'<')
476 ushort *ptr = (ushort *)yyWord.unicode();
479 if (yyCh == EOF || yyCh ==
'\n')
487 yyWord.resize(ptr - (ushort *)yyWord.unicode());
488 return (tChar ==
'"') ? Tok_QuotedInclude : Tok_AngledInclude;
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)
502 <<
"Parenthesis/bracket/brace mismatch between "
503 "#if and #else branches; using #if branch\n";
505 is.bracketDepth1st = yyBracketDepth;
506 is.braceDepth1st = yyBraceDepth;
507 is.parenDepth1st = yyParenDepth;
508 saveState(&is.state);
510 is.elseLine = yyLineNo;
511 yyBracketDepth = is.bracketDepth;
512 yyBraceDepth = is.braceDepth;
513 yyParenDepth = is.parenDepth;
516 }
else if (yyCh ==
'n') {
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)
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;
544 }
while (yyCh != EOF && yyCh !=
'\n');
546 }
else if (yyCh ==
'*') {
547 bool metAster =
false;
552 yyMsg() <<
"Unterminated C++ comment\n";
558 }
else if (metAster && yyCh ==
'/') {
569 }
while (yyCh !=
'\n' && yyCh != EOF);
571 }
else if ((yyCh >=
'A' && yyCh <=
'Z') || (yyCh >=
'a' && yyCh <=
'z') || yyCh ==
'_') {
572 ushort *ptr = (ushort *)yyWord.unicode();
576 }
while ((yyCh >=
'A' && yyCh <=
'Z') || (yyCh >=
'a' && yyCh <=
'z')
577 || (yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'_');
578 yyWord.resize(ptr - (ushort *)yyWord.unicode());
582 if (yyCh ==
'"' && isStringLiteralPrefix(yyWord)) {
587 switch (yyWord.unicode()[0].unicode()) {
589 if (yyWord == strNULL)
593 if (yyWord == strQ_NULLPTR)
595 if (yyWord == strQ_OBJECT)
597 if (yyWord == strQ_SLOTS || yyWord == strQ_SIGNALS)
601 if (yyWord == strclass)
605 if (yyWord == strdecltype)
609 if (yyWord == strenum)
613 if (yyWord == strfriend)
617 if (yyWord == strnamespace)
618 return Tok_namespace;
619 if (yyWord == strnullptr)
623 if (yyWord == stroperator) {
627 while (isspace(yyCh))
629 while (yyCh ==
'+' || yyCh ==
'-' || yyCh ==
'*' || yyCh ==
'/' || yyCh ==
'%'
630 || yyCh ==
'=' || yyCh ==
'<' || yyCh ==
'>' || yyCh ==
'!'
631 || yyCh ==
'&' || yyCh ==
'|' || yyCh ==
'~' || yyCh ==
'^'
632 || yyCh ==
'[' || yyCh ==
']')
637 if (yyWord == strpublic || yyWord == strprotected || yyWord == strprivate)
641 if (yyWord == strreturn)
645 if (yyWord == strstruct)
647 if (yyWord == strslots || yyWord == strsignals)
651 if (yyWord == strusing)
657 if (yyCh ==
'"' && isRawStringLiteralPrefix(yyWord)) {
658 ptr =
reinterpret_cast<ushort *>(
const_cast<QChar *>(yyWord.unicode()));
661 for (yyCh = getChar(); yyCh != EOF && yyCh !=
'('; yyCh = getChar())
662 delimiter += QLatin1Char(yyCh);
666 ushort *ptr_past_end =
nullptr;
667 while (yyCh != EOF && !is_end) {
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
678 ptr_past_end =
nullptr;
684 if (delimiter.isEmpty()) {
690 ptr_past_end =
nullptr;
697 yyWord.resize(ptr_past_end - 1 -
reinterpret_cast<
const ushort *>(yyWord.unicode()));
699 yyWord.resize(ptr -
reinterpret_cast<
const ushort *>(yyWord.unicode()));
701 yyMsg() <<
"Unterminated/mismatched C++ Raw string\n";
704 return Tok_RawString;
712 loadState(savedState);
713 prospectiveContext.clear();
714 yyBraceDepth = yyMinBraceDepth;
726 ushort *ptr = (ushort *)yyWord.unicode();
732 }
while (yyCh !=
'\n');
733 yyWord.resize(ptr - (ushort *)yyWord.unicode());
735 }
else if (yyCh ==
'*') {
736 bool metAster =
false;
737 ushort *ptr = (ushort *)yyWord.unicode();
742 yyMsg() <<
"Unterminated C++ comment\n";
749 else if (metAster && yyCh ==
'/')
754 yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2);
761 ushort *ptr = (ushort *)yyWord.unicode();
763 while (yyCh != EOF && yyCh !=
'\n' && yyCh !=
'"') {
766 if (yyCh == EOF || yyCh ==
'\n')
773 yyWord.resize(ptr - (ushort *)yyWord.unicode());
776 yyMsg() <<
"Unterminated C++ string\n";
792 return Tok_ColonColon;
805 return Tok_AngleBracket;
812 if (yyCh == EOF || yyCh ==
'\n') {
813 yyMsg() <<
"Unterminated C++ character\n";
824 if (yyBraceDepth == 0)
825 yyBraceLineNo = yyCurLineNo;
828 return Tok_LeftBrace;
830 if (yyBraceDepth == yyMinBraceDepth) {
833 <<
"Excess closing brace in C++ code"
834 " (or abuse of the C++ preprocessor)\n";
837 return Tok_Semicolon;
841 return Tok_RightBrace;
843 if (yyParenDepth == 0)
844 yyParenLineNo = yyCurLineNo;
847 return Tok_LeftParen;
849 if (yyParenDepth == 0)
851 <<
"Excess closing parenthesis in C++ code"
852 " (or abuse of the C++ preprocessor)\n";
856 return Tok_RightParen;
858 if (yyBracketDepth == 0)
859 yyBracketLineNo = yyCurLineNo;
862 return Tok_LeftBracket;
864 if (yyBracketDepth == 0)
866 <<
"Excess closing bracket in C++ code"
867 " (or abuse of the C++ preprocessor)\n";
871 return Tok_RightBracket;
877 return Tok_Semicolon;
880 return Tok_QuestionMark;
883 if (yyCh ==
'x' || yyCh ==
'X') {
886 }
while ((yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'\''
887 || (yyCh >=
'a' && yyCh <=
'f') || (yyCh >=
'A' && yyCh <=
'F'));
890 if (yyCh <
'0' || yyCh >
'9')
904 }
while ((yyCh >=
'0' && yyCh <=
'9') || yyCh ==
'\'');
916
917
918
927 *
static_cast<CppParserState *>(
this) = state;
932 Namespace *pns, *ns = &results->rootNamespace;
933 for (
int i = 1; i < namespaces->size(); ++i) {
935 if (!(ns = pns->children.value(namespaces->at(i)))) {
938 if (haveLast || i < namespaces->size() - 1)
939 if (
const Namespace *ons = findNamespace(*namespaces, i + 1))
941 pns->children.insert(namespaces->at(i), ns);
943 }
while (++i < namespaces->size());
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) {
959 ret += QLatin1String(
"::");
960 ret += namespaces.at(i).value();
965QString
CppParser::joinNamespaces(
const QString &one,
const QString &two)
967 return two.isEmpty() ? one : one.isEmpty() ? two : one + QStringLiteral(
"::") + two;
971 VisitNamespaceCallback callback,
void *context,
974 const Namespace *ns = &rslt->rootNamespace;
975 for (
int i = 1; i < nsCount; ++i)
976 if (!(ns = ns->children.value(namespaces.at(i))))
978 if ((
this->*callback)(ns, context))
981 for (
const ParseResults *sup : rslt->includes)
982 if (vr.tryVisit(sup->fileId)
983 && visitNamespace(namespaces, nsCount, callback, context, vr, sup))
989 VisitNamespaceCallback callback,
void *context)
const
992 return visitNamespace(namespaces, nsCount, callback, context, vr, results);
997 QSet<HashStringList> *visited)
1011 if (ns->children.contains(data
->segment)) {
1016 auto nsai = ns->aliases.constFind(data
->segment);
1017 if (nsai != ns->aliases.constEnd()) {
1019 if (nsl.last().value().isEmpty()) {
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))
1049 NamespaceList *resolved, QSet<HashStringList> *visitedUsings)
const
1051 QualifyOneData data(namespaces, nsCnt, segment, resolved, visitedUsings);
1053 if (visitNamespace(namespaces, nsCnt, &
CppParser::qualifyOneCallbackOwn, &data))
1056 return visitNamespace(namespaces, nsCnt, &
CppParser::qualifyOneCallbackUsing, &data);
1062 QSet<HashStringList> visitedUsings;
1064 return qualifyOne(namespaces, nsCnt, segment, resolved, &visitedUsings);
1074 if (segments.first().value().isEmpty()) {
1076 if (segments.size() == 1) {
1078 *resolved << HashString(QString());
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)) {
1094 *unresolved = segments.mid(segIdx);
1100 }
while (!isDeclaration && --nsIdx >= 0);
1102 *resolved << HashString(QString());
1104 *unresolved = segments.mid(initSegIdx);
1112 return fullyQualify(namespaces, namespaces.size(),
1113 segments, isDeclaration, resolved, unresolved);
1117 const QString &quali,
bool isDeclaration,
1120 static QString strColons(QLatin1String(
"::"));
1123 for (
const QString &str : quali.split(strColons))
1124 segments << HashString(str);
1125 return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved);
1138 nsCount = namespaces.size();
1139 visitNamespace(namespaces, nsCount, &
CppParser::findNamespaceCallback, &ns);
1145 *namespaces << name;
1146 if (!findNamespace(*namespaces))
1147 modifyNamespace(namespaces,
false);
1152 if (namespaces->size() > length)
1153 namespaces->erase(namespaces->begin() + length, namespaces->end());
1158
1159
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);
1173 seed = qHash(key.cleanFile, seed);
1174 seed = qHash(key.parserState, seed);
1192QSet<QString> &
CppFiles::blacklistedFiles()
1194 static QSet<QString> blacklisted;
1201 IncludeCycle *
const cycle = includeCycles().value(key);
1204 return cycle->results;
1215 includeCycles().insert(key, cycle);
1218 cycle->fileNames.insert(key.cleanFile);
1219 cycle->results.insert(results);
1224 return translatedFiles().value(cleanFile);
1229 translatedFiles().insert(cleanFile, tor);
1234 return blacklistedFiles().contains(cleanFile);
1239 blacklistedFiles().insert(cleanFile);
1245 cycle->fileNames = fileNames;
1247 QSet<IncludeCycle *> intersectingCycles;
1248 for (
const QString &fileName : fileNames) {
1249 const ResultsCacheKey key = { fileName, parserState };
1250 IncludeCycle *intersectingCycle = includeCycles().value(key);
1252 if (intersectingCycle && !intersectingCycles.contains(intersectingCycle)) {
1253 intersectingCycles.insert(intersectingCycle);
1255 cycle->fileNames.unite(intersectingCycle->fileNames);
1256 cycle->results.unite(intersectingCycle->results);
1259 qDeleteAll(intersectingCycles);
1261 for (
const QString &fileName : std::as_const(cycle->fileNames))
1262 includeCycles().insert({ fileName, parserState }, cycle);
1267 QString fileExt = QFileInfo(name).suffix();
1268 return fileExt.isEmpty() || fileExt.startsWith(QLatin1Char(
'h'), Qt::CaseInsensitive);
1272 QSet<QString> &inclusions)
1274 QString cleanFile = QDir::cleanPath(file);
1276 for (
const QRegularExpression &rx : std::as_const(cd.m_excludes)) {
1277 if (rx.match(cleanFile).hasMatch())
1281 const int index = includeStack.indexOf(cleanFile);
1283 CppFiles::addIncludeCycle(QSet<QString>(includeStack.cbegin() + index, includeStack.cend()),
1292 bool isIndirect =
false;
1293 if (!CppFiles::isBlacklisted(cleanFile)
1294 && isHeader(cleanFile)) {
1296 QSet<
const ParseResults *> res = CppFiles::getResults(ResultsCacheKey(cleanFile, *
this));
1297 if (!res.isEmpty()) {
1298 results->includes.unite(res);
1306 if (!f.open(QIODevice::ReadOnly)) {
1307 yyMsg() << qPrintable(
1308 QStringLiteral(
"Cannot open %1: %2\n").arg(cleanFile, f.errorString()));
1313 ts.setEncoding(yySourceEncoding);
1314 ts.setAutoDetectUnicode(
true);
1316 inclusions.insert(cleanFile);
1319 for (
const QString &projectRoot : std::as_const(cd.m_projectRoots))
1320 if (cleanFile.startsWith(projectRoot)) {
1321 parser.setTranslator(
new Translator);
1324 parser.setInput(ts, cleanFile);
1325 QStringList stack = includeStack;
1327 parser.parse(cd, stack, inclusions);
1333 parser.functionContextUnresolved = functionContextUnresolved;
1334 parser.setInput(ts, cleanFile);
1336 QStringList stack = includeStack;
1338 parser.parseInternal(cd, stack, inclusions);
1340 CppFiles::setBlacklisted(cleanFile);
1342 inclusions.remove(cleanFile);
1344 prospectiveContext.clear();
1345 pendingContext.clear();
1349
1350
1351
1352
1353
1354
1355
1356
1360 bool matches = (yyTok == t);
1368 bool matches =
false;
1371 if (yyTok != Tok_String && yyTok != Tok_RawString)
1374 if (yyTok == Tok_String)
1375 *s += ParserTool::transcode(yyWord);
1392 if (yyTok != Tok_Ident)
1394 if (yyWord == strQApplication || yyWord == strQCoreApplication) {
1396 if (yyTok == Tok_ColonColon)
1399 if (yyWord == strUnicodeUTF8) {
1403 if (yyWord == strLatin1 || yyWord == strDefaultCodec || yyWord == strCodecForTr)
1404 yyMsg() <<
"Unsupported encoding Latin1/DefaultCodec/CodecForTr\n";
1408bool CppParser::matchStringOrNull(QString *s)
1410 return matchString(s) || match(Tok_Null);
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1429 if (match(Tok_Null) || match(Tok_Integer))
1433 while (match(Tok_Ident) || parenlevel > 0) {
1434 if (yyTok == Tok_RightParen) {
1435 if (parenlevel == 0)
break;
1438 }
else if (yyTok == Tok_LeftParen) {
1440 if (yyTok == Tok_RightParen) {
1445 }
else if (yyTok == Tok_Ident) {
1447 }
else if (yyTok == Tok_Arrow) {
1449 }
else if (parenlevel == 0 || yyTok == Tok_Cancel) {
1456void CppParser::recordMessage(
int line,
const QString &context,
const QString &text,
const QString &comment,
1460 ParserTool::transcode(context), text, ParserTool::transcode(comment), QString(),
1461 yyFileName, line, QStringList(),
1462 TranslatorMessage::Unfinished, plural);
1463 msg.setExtraComment(ParserTool::transcode(extracomment.simplified()));
1469void CppParser::handleTr(QString &prefix,
bool plural)
1471 if (!sourcetext.isEmpty())
1472 yyMsg() <<
"//% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n";
1473 int line = yyLineNo;
1475 if (matchString(&text) && !text.isEmpty()) {
1478 if (yyTok == Tok_RightParen) {
1480 }
else if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1481 if (yyTok == Tok_RightParen) {
1483 }
else if (match(Tok_Comma)) {
1487 if (!pendingContext.isEmpty() && !prefix.startsWith(QLatin1String(
"::"))) {
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()));
1495 pendingContext.clear();
1497 if (prefix.isEmpty()) {
1498 if (functionContextUnresolved.isEmpty()) {
1501 yyMsg() <<
"tr() cannot be called without context\n";
1507 context = stringifyNamespace(functionContext);
1510 yyMsg() << qPrintable(
1511 QStringLiteral(
"Class '%1' lacks Q_OBJECT macro\n").arg(context));
1518 if (fctx->trQualification.isEmpty()) {
1521 context += functionContext.at(i).value();
1524 context += QLatin1String(
"::");
1526 fctx->trQualification = context;
1528 context = fctx->trQualification;
1531 context = joinNamespaces(stringifyNamespace(functionContext), functionContextUnresolved);
1537 if (fullyQualify(
functionContext, prefix,
false, &nsl, &unresolved)) {
1539 if (fctx->trQualification.isEmpty()) {
1540 context = stringifyNamespace(nsl);
1541 fctx->trQualification = context;
1543 context = fctx->trQualification;
1546 yyMsg() << qPrintable(QStringLiteral(
"Class '%1' lacks Q_OBJECT macro\n")
1551 context = joinNamespaces(stringifyNamespace(nsl), stringifyNamespace(0, unresolved));
1557 recordMessage(line, context, text, comment, extracomment, msgid, extra, plural);
1560 extracomment.clear();
1563 metaExpected =
false;
1566void CppParser::handleTranslate(
bool plural)
1568 if (!sourcetext.isEmpty())
1569 yyMsg() <<
"//% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n";
1570 int line = yyLineNo;
1572 if (matchString(&context)
1574 && matchString(&text) && !text.isEmpty())
1577 if (yyTok != Tok_RightParen) {
1579 if (match(Tok_Comma) && matchStringOrNull(&comment)) {
1580 if (yyTok != Tok_RightParen) {
1582 if (match(Tok_Comma)) {
1583 if (matchEncoding()) {
1584 if (yyTok != Tok_RightParen) {
1591 plural |= match(Tok_Comma);
1596 if (matchExpression() && yyTok == Tok_RightParen) {
1610 recordMessage(line, context, text, comment, extracomment, msgid, extra, plural);
1613 extracomment.clear();
1616 metaExpected =
false;
1621 if (!msgid.isEmpty())
1622 yyMsg() <<
"//= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n";
1623 int line = yyLineNo;
1625 if (matchString(&msgid) && !msgid.isEmpty()) {
1626 plural |= match(Tok_Comma);
1627 recordMessage(line, QString(), ParserTool::transcode(sourcetext), QString(), extracomment,
1628 msgid, extra, plural);
1631 extracomment.clear();
1634 metaExpected =
false;
1637void CppParser::handleDeclareTrFunctions()
1642 if (yyTok != Tok_Ident)
1647 if (yyTok == Tok_RightParen)
1649 if (yyTok != Tok_ColonColon)
1651 name += QLatin1String(
"::");
1653 Namespace *ns = modifyNamespace(&namespaces);
1654 ns->hasTrFunctions =
true;
1655 ns->trQualification = name;
1656 ns->trQualification.detach();
1660 QSet<QString> &inclusions)
1664 functionContextUnresolved.clear();
1666 parseInternal(cd, includeStack, inclusions);
1671 static QString strColons(QLatin1String(
"::"));
1674 bool yyTokColonSeen =
false;
1675 bool yyTokIdentSeen =
false;
1676 bool maybeInTrailingReturnType =
false;
1677 metaExpected =
true;
1679 prospectiveContext.clear();
1680 pendingContext.clear();
1682 yyWord.reserve(yyInStr.size());
1683 yyWordInitialCapacity = yyWord.capacity();
1684 yyInPtr = (
const ushort *)yyInStr.unicode();
1687 while (yyTok != Tok_Eof) {
1692 if (yyBracketDepth && yyBraceDepth == namespaceDepths.size()) {
1698 case Tok_QuotedInclude: {
1699 text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord);
1701 if (QFileInfo(text).isFile()) {
1702 processInclude(text, cd, includeStack, inclusions);
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);
1715 for (
const QString &incPath : std::as_const(cd.m_includePath)) {
1716 text = QDir(incPath).absoluteFilePath(yyWord);
1718 if (QFileInfo(text).isFile()) {
1719 processInclude(text, cd, includeStack, inclusions);
1730 if (yyTok == Tok_class)
1735
1736
1740 if (yyTok == Tok_Equals) {
1743 }
else if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
1754 if (yyTok == Tok_ColonColon) {
1757 }
else if (yyTok == Tok_Ident) {
1758 if (yyWord == strfinal) {
1773 if (yyTok == Tok_Colon || yyTok == Tok_AngleBracket) {
1778 if (yyTok == Tok_Eof)
1780 if (yyTok == Tok_Cancel)
1782 if (yyTok == Tok_class)
1784 }
while (yyTok != Tok_LeftBrace && yyTok != Tok_Semicolon);
1786 if (yyTok != Tok_LeftBrace) {
1793 if (!quali.isEmpty()) {
1796 if (!fullyQualify(namespaces, quali,
true, &nsl, 0)) {
1797 yyMsg() <<
"Ignoring definition of undeclared qualified class\n";
1800 namespaceDepths.push(namespaces.size());
1803 namespaceDepths.push(namespaces.size());
1805 enterNamespace(&namespaces, fct);
1808 functionContextUnresolved.clear();
1809 prospectiveContext.clear();
1810 pendingContext.clear();
1812 metaExpected =
true;
1818 if (yyTok == Tok_Ident) {
1821 HashString ns = HashString(text);
1825 if (yyTok != Tok_ColonColon)
1828 if (yyTok != Tok_Ident)
1830 nestedNamespaces.append(ns);
1833 ns = HashString(text);
1835 if (yyTok == Tok_LeftBrace) {
1836 namespaceDepths.push(namespaces.size());
1837 for (
const auto &nns : nestedNamespaces)
1838 enterNamespace(&namespaces, nns);
1839 enterNamespace(&namespaces, ns);
1841 functionContext = namespaces;
1842 functionContextUnresolved.clear();
1843 prospectiveContext.clear();
1844 pendingContext.clear();
1845 metaExpected =
true;
1847 }
else if (yyTok == Tok_Equals) {
1850 NamespaceList fullName;
1852 if (yyTok == Tok_ColonColon)
1853 fullName.append(HashString(QString()));
1854 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1855 if (yyTok == Tok_Ident) {
1858 fullName.append(HashString(text));
1862 if (fullName.isEmpty())
1864 fullName.append(HashString(QString()));
1865 modifyNamespace(&namespaces)->aliases[ns] = fullName;
1867 }
else if (yyTok == Tok_LeftBrace) {
1869 namespaceDepths.push(namespaces.size());
1870 metaExpected =
true;
1877 if (yyTok == Tok_namespace) {
1880 if (yyTok == Tok_ColonColon)
1881 fullName.append(HashString(QString()));
1882 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1883 if (yyTok == Tok_Ident) {
1886 fullName.append(HashString(text));
1891 if (fullyQualify(
namespaces, fullName,
false, &nsl, 0))
1892 modifyNamespace(&
namespaces)->usings << HashStringList(nsl);
1895 if (yyTok == Tok_ColonColon)
1896 fullName.append(HashString(QString()));
1897 while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
1898 if (yyTok == Tok_Ident) {
1901 fullName.append(HashString(text));
1905 if (fullName.isEmpty())
1911 fullName.append(HashString(QString()));
1912 const HashString &ns = *(fullName.constEnd() - 2);
1913 modifyNamespace(&
namespaces)->aliases[ns] = fullName;
1921 if (yyTokColonSeen &&
1922 yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
1924 yyTokIdentSeen =
true;
1927 if (yyTok == Tok_LeftParen) {
1928 bool forcePlural =
false;
1929 switch (trFunctionAliasManager.trFunctionByName(yyWord)) {
1931 handleDeclareTrFunctions();
1936 case TrFunctionAliasManager::Function_tr:
1937 case TrFunctionAliasManager::Function_trUtf8:
1938 case TrFunctionAliasManager::Function_QT_TR_NOOP:
1939 case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
1941 handleTr(prefix, forcePlural);
1954 handleTranslate(forcePlural);
1962 handleTrId(forcePlural);
1970 if (yyTok == Tok_ColonColon && !maybeInTrailingReturnType) {
1977 metaExpected =
false;
1980 if (yyParenDepth == 0 && yyBraceDepth == namespaceDepths.size())
1981 maybeInTrailingReturnType =
true;
1983 if (yyTok == Tok_Ident) {
1984 switch (trFunctionAliasManager.trFunctionByName(yyWord)) {
1987 yyMsg() <<
"Cannot invoke tr() like this\n";
1992 case Tok_ColonColon:
1993 if (yyTokIdentSeen || maybeInTrailingReturnType) {
1998 if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0 && !yyTokColonSeen)
1999 prospectiveContext = prefix;
2000 prefix += strColons;
2003 case Tok_RightBrace:
2004 if (!yyTokColonSeen) {
2005 if (yyBraceDepth + 1 == namespaceDepths.size()) {
2007 truncateNamespaces(&namespaces, namespaceDepths.pop());
2009 if (yyBraceDepth == namespaceDepths.size()) {
2011 if (!yyBraceDepth && !directInclude)
2015 functionContextUnresolved.clear();
2016 pendingContext.clear();
2021 maybeInTrailingReturnType =
false;
2022 prospectiveContext.clear();
2024 if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) {
2025 yyMsg() <<
"Discarding unconsumed meta data\n";
2027 extracomment.clear();
2031 metaExpected =
true;
2038 }
while (yyTok == Tok_Access);
2039 metaExpected =
true;
2040 if (yyTok == Tok_Colon)
2045 if (yyBraceDepth == namespaceDepths.size() && yyParenDepth == 0) {
2046 if (!prospectiveContext.isEmpty()) {
2047 pendingContext = prospectiveContext;
2048 prospectiveContext.clear();
2051 if (yyTok == Tok_Colon) {
2052 if (lookAheadToSemicolonOrLeftBrace() != Tok_Semicolon)
2053 yyTokColonSeen =
true;
2056 metaExpected =
true;
2060 if (yyBraceDepth == namespaceDepths.size() + 1 && yyParenDepth == 0) {
2061 if (!prospectiveContext.isEmpty()) {
2062 pendingContext = prospectiveContext;
2063 prospectiveContext.clear();
2065 if (!yyTokIdentSeen) {
2067 yyTokColonSeen =
false;
2070 maybeInTrailingReturnType =
false;
2071 yyTokIdentSeen =
false;
2072 metaExpected =
true;
2076 if (!yyTokColonSeen && yyBraceDepth == namespaceDepths.size() && yyParenDepth == 1
2077 && !prospectiveContext.isEmpty()) {
2078 pendingContext = prospectiveContext;
2079 prospectiveContext.clear();
2081 yyTokIdentSeen =
false;
2082 metaExpected =
true;
2086 case Tok_QuestionMark:
2087 metaExpected =
true;
2090 case Tok_RightParen:
2091 if (yyParenDepth == 0) {
2092 if (!yyTokColonSeen && !pendingContext.isEmpty()
2093 && yyBraceDepth == namespaceDepths.size()) {
2095 prospectiveContext = pendingContext;
2096 pendingContext.clear();
2098 metaExpected =
true;
2100 metaExpected =
false;
2107 auto initialParenDepth = yyParenDepth;
2113 while (yyParenDepth != initialParenDepth && yyTok != Tok_Eof)
2120 if (yyTok == Tok_class)
2126 if (yyTok == Tok_Colon)
2130 if (!yyParenDepth && !maybeInTrailingReturnType)
2131 prospectiveContext.clear();
2133 case Tok_RightBracket:
2141 if (yyBraceDepth != 0)
2142 yyMsg(yyBraceLineNo)
2143 <<
"Unbalanced opening brace in C++ code (or abuse of the C++ preprocessor)\n";
2144 else if (yyParenDepth != 0)
2145 yyMsg(yyParenLineNo)
2146 <<
"Unbalanced opening parenthesis in C++ code"
2147 " (or abuse of the C++ preprocessor)\n";
2148 else if (yyBracketDepth != 0)
2149 yyMsg(yyBracketLineNo)
2150 <<
"Unbalanced opening bracket in C++ code"
2151 " (or abuse of the C++ preprocessor)\n";
2156 if (!tor || !metaExpected)
2159 const QChar *ptr = yyWord.unicode();
2160 if (*ptr == QLatin1Char(
':') && ptr[1].isSpace()) {
2161 yyWord.remove(0, 2);
2162 extracomment += yyWord;
2163 extracomment.detach();
2164 }
else if (*ptr == QLatin1Char(
'=') && ptr[1].isSpace()) {
2165 yyWord.remove(0, 2);
2166 msgid = yyWord.simplified();
2168 }
else if (*ptr == QLatin1Char(
'~') && ptr[1].isSpace()) {
2169 yyWord.remove(0, 2);
2170 text = yyWord.trimmed();
2171 int k = text.indexOf(QLatin1Char(
' '));
2173 QString commentvalue = text.mid(k + 1).trimmed();
2174 if (commentvalue.startsWith(QLatin1Char(
'"')) && commentvalue.endsWith(QLatin1Char(
'"'))
2175 && commentvalue.size() != 1) {
2176 commentvalue = commentvalue.sliced(1, commentvalue.size() - 2);
2178 extra.insert(text.left(k), commentvalue);
2181 }
else if (*ptr == QLatin1Char(
'%') && ptr[1].isSpace()) {
2182 sourcetext.reserve(sourcetext.size() + yyWord.size() - 2);
2183 ushort *ptr = (ushort *)sourcetext.data() + sourcetext.size();
2186 if (p >= yyWord.size())
2188 c = yyWord.unicode()[p++].unicode();
2192 yyMsg() <<
"Unexpected character in meta string\n";
2196 if (p >= yyWord.size()) {
2198 yyMsg() <<
"Unterminated meta string\n";
2201 c = yyWord.unicode()[p++].unicode();
2205 if (p >= yyWord.size())
2207 c = yyWord.unicode()[p++].unicode();
2215 sourcetext.resize(ptr - (ushort *)sourcetext.data());
2217 const ushort *uc = (
const ushort *)yyWord.unicode();
2220 while ((c = uc[idx]) ==
' ' || c ==
'\t' || c ==
'\n')
2222 if (!memcmp(uc + idx, CppMagicComment.unicode(), CppMagicComment.size() * 2)) {
2223 idx += CppMagicComment.size();
2224 comment = QString::fromRawData(yyWord.unicode() + idx,
2225 yyWord.size() - idx).simplified();
2226 int k = comment.indexOf(QLatin1Char(
' '));
2230 context = comment.left(k);
2231 comment.remove(0, k + 1);
2233 ParserTool::transcode(context), QString(),
2234 ParserTool::transcode(comment), QString(),
2235 yyFileName, yyLineNo, QStringList(),
2236 TranslatorMessage::Finished,
false);
2237 msg.setExtraComment(ParserTool::transcode(extracomment.simplified()));
2238 extracomment.clear();
2251 CppFiles::setTranslator(yyFileName, tor);
2259 if (!tor && results->includes.size() == 1
2260 && results->rootNamespace.children.isEmpty()
2261 && results->rootNamespace.aliases.isEmpty()
2262 && results->rootNamespace.usings.isEmpty()) {
2264 pr = *results->includes.cbegin();
2270 CppFiles::setResults(ResultsCacheKey(yyFileName, *
this), pr);
2280 QStringConverter::Encoding e = cd.m_sourceIsUtf16 ? QStringConverter::Utf16 : QStringConverter::Utf8;
2282 for (
const QString &filename : filenames) {
2283 if (!CppFiles::getResults(ResultsCacheKey(filename)).isEmpty() || CppFiles::isBlacklisted(filename))
2286 QFile file(filename);
2287 if (!file.open(QIODevice::ReadOnly)) {
2288 cd.appendError(QStringLiteral(
"Cannot open %1: %2").arg(filename,
2289 file.errorString()));
2294 QTextStream ts(&file);
2296 ts.setAutoDetectUnicode(
true);
2297 parser.setInput(ts, filename);
2298 Translator *tor =
new Translator;
2299 parser.setTranslator(tor);
2300 QSet<QString> inclusions;
2301 parser.parse(cd, QStringList(), inclusions);
2302 parser.recordResults(isHeader(filename));
2305 for (
const QString &filename : filenames) {
2306 if (!CppFiles::isBlacklisted(filename)) {
2307 if (
const Translator *tor = CppFiles::getTranslator(filename)) {
2308 for (
const TranslatorMessage &msg : tor->messages())
2309 translator.extend(msg, cd);
static void setResults(const ResultsCacheKey &key, const ParseResults *results)
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 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 const QString CppMagicComment
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
Combined button and popup list for selecting options.
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