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