tools/linguist/lupdate/cpp.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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