File indexing completed on 2024-04-28 16:57:51

0001 /****************************************************************************
0002 **
0003 ** Copyright (C) 2015 The Qt Company Ltd.
0004 ** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
0005 ** Contact: http://www.qt.io/licensing/
0006 **
0007 ** This file contains code copied from Qt 5.6 (https://github.com/qt/qtbase/blob/5.6/src/corelib/kernel/qmetaobject.cpp).
0008 **
0009 ** $QT_BEGIN_LICENSE:LGPL21$
0010 ** Commercial License Usage
0011 ** Licensees holding valid commercial Qt licenses may use this file in
0012 ** accordance with the commercial license agreement provided with the
0013 ** Software or, alternatively, in accordance with the terms contained in
0014 ** a written agreement between you and The Qt Company. For licensing terms
0015 ** and conditions see http://www.qt.io/terms-conditions. For further
0016 ** information use the contact form at http://www.qt.io/contact-us.
0017 **
0018 ** GNU Lesser General Public License Usage
0019 ** Alternatively, this file may be used under the terms of the GNU Lesser
0020 ** General Public License version 2.1 or version 3 as published by the Free
0021 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
0022 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
0023 ** following information to ensure the GNU Lesser General Public License
0024 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
0025 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
0026 **
0027 ** As a special exception, The Qt Company gives you certain additional
0028 ** rights. These rights are described in The Qt Company LGPL Exception
0029 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
0030 **
0031 ** $QT_END_LICENSE$
0032 **
0033 ****************************************************************************/
0034 
0035 #ifndef CLAZY_NORMALIZED_SIGNATURE_UTILS_H
0036 #define CLAZY_NORMALIZED_SIGNATURE_UTILS_H
0037 
0038 #include <vector>
0039 #include <string>
0040 
0041 namespace clazy
0042 {
0043 
0044 inline bool is_space(char s)
0045 {
0046     return (s == ' ' || s == '\t');
0047 }
0048 
0049 inline bool is_ident_start(char s)
0050 {
0051     return ((s >= 'a' && s <= 'z')
0052             || (s >= 'A' && s <= 'Z')
0053             || s == '_' || s == '$'
0054             );
0055 }
0056 
0057 inline bool is_ident_char(char s)
0058 {
0059     return ((s >= 'a' && s <= 'z')
0060             || (s >= 'A' && s <= 'Z')
0061             || (s >= '0' && s <= '9')
0062             || s == '_' || s == '$'
0063             );
0064 }
0065 
0066 static void qRemoveWhitespace(const char *s, char *d)
0067 {
0068     char last = 0;
0069     while (*s && is_space(*s))
0070         s++;
0071     while (*s) {
0072         while (*s && !is_space(*s))
0073             last = *d++ = *s++;
0074         while (*s && is_space(*s))
0075             s++;
0076         if (*s && ((is_ident_char(*s) && is_ident_char(last))
0077                    || ((*s == ':') && (last == '<')))) {
0078             last = *d++ = ' ';
0079         }
0080     }
0081     *d = '\0';
0082 }
0083 
0084 // This function is shared with moc.cpp. This file should be included where needed.
0085 static std::string normalizeTypeInternal(const char *t, const char *e, bool fixScope = false, bool adjustConst = true)
0086 {
0087     int len = e - t;
0088     /*
0089       Convert 'char const *' into 'const char *'. Start at index 1,
0090       not 0, because 'const char *' is already OK.
0091     */
0092     std::string constbuf;
0093     for (int i = 1; i < len; i++) {
0094         if ( t[i] == 'c'
0095              && strncmp(t + i + 1, "onst", 4) == 0
0096              && (i + 5 >= len || !is_ident_char(t[i + 5]))
0097              && !is_ident_char(t[i-1])
0098              ) {
0099             constbuf = std::string(t, len);
0100             if (is_space(t[i-1]))
0101                 constbuf.erase(i-1, 6);
0102             else
0103                 constbuf.erase(i, 5);
0104             constbuf = "const " + constbuf;
0105             t = constbuf.data();
0106             e = constbuf.data() + constbuf.length();
0107             break;
0108         }
0109         /*
0110           We mustn't convert 'char * const *' into 'const char **'
0111           and we must beware of 'Bar<const Bla>'.
0112         */
0113         if (t[i] == '&' || t[i] == '*' ||t[i] == '<')
0114             break;
0115     }
0116     if (adjustConst && e > t + 6 && strncmp("const ", t, 6) == 0) {
0117         if (*(e-1) == '&') { // treat const reference as value
0118             t += 6;
0119             --e;
0120         } else if (is_ident_char(*(e-1)) || *(e-1) == '>') { // treat const value as value
0121             t += 6;
0122         }
0123     }
0124     std::string result;
0125 
0126 #if 1
0127     // consume initial 'const '
0128     if (strncmp("const ", t, 6) == 0) {
0129         t+= 6;
0130         result += "const ";
0131     }
0132 #endif
0133 
0134     // some type substitutions for 'unsigned x'
0135     if (strncmp("unsigned", t, 8) == 0) {
0136         // make sure "unsigned" is an isolated word before making substitutions
0137         if (!t[8] || !is_ident_char(t[8])) {
0138             if (strncmp(" int", t+8, 4) == 0) {
0139                 t += 8+4;
0140                 result += "uint";
0141             } else if (strncmp(" long", t+8, 5) == 0) {
0142                 if ((strlen(t + 8 + 5) < 4 || strncmp(t + 8 + 5, " int", 4) != 0) // preserve '[unsigned] long int'
0143                     && (strlen(t + 8 + 5) < 5 || strncmp(t + 8 + 5, " long", 5) != 0) // preserve '[unsigned] long long'
0144                     ) {
0145                     t += 8+5;
0146                     result += "ulong";
0147                 }
0148             } else if (strncmp(" short", t+8, 6) != 0  // preserve unsigned short
0149                        && strncmp(" char", t+8, 5) != 0) { // preserve unsigned char
0150                 //  treat rest (unsigned) as uint
0151                 t += 8;
0152                 result += "uint";
0153             }
0154         }
0155     } else {
0156         // discard 'struct', 'class', and 'enum'; they are optional
0157         // and we don't want them in the normalized signature
0158         struct {
0159             const char *keyword;
0160             int len;
0161         } optional[] = {
0162             { "struct ", 7 },
0163             { "class ", 6 },
0164             { "enum ", 5 },
0165             { 0, 0 }
0166         };
0167         int i = 0;
0168         do {
0169             if (strncmp(optional[i].keyword, t, optional[i].len) == 0) {
0170                 t += optional[i].len;
0171                 break;
0172             }
0173         } while (optional[++i].keyword != 0);
0174     }
0175 
0176     bool star = false;
0177     while (t != e) {
0178         char c = *t++;
0179         if (fixScope && c == ':' && *t == ':' ) {
0180             ++t;
0181             c = *t++;
0182             int i = result.size() - 1;
0183             while (i >= 0 && is_ident_char(result.at(i)))
0184                 --i;
0185             result.resize(i + 1);
0186         }
0187         star = star || c == '*';
0188         result += c;
0189         if (c == '<') {
0190             //template recursion
0191             const char* tt = t;
0192             int templdepth = 1;
0193             int scopeDepth = 0;
0194             while (t != e) {
0195                 c = *t++;
0196                 if (c == '{' || c == '(' || c == '[')
0197                     ++scopeDepth;
0198                 if (c == '}' || c == ')' || c == ']')
0199                     --scopeDepth;
0200                 if (scopeDepth == 0) {
0201                     if (c == '<')
0202                         ++templdepth;
0203                     if (c == '>')
0204                         --templdepth;
0205                     if (templdepth == 0 || (templdepth == 1 && c == ',')) {
0206                         result += normalizeTypeInternal(tt, t-1, fixScope, false);
0207                         result += c;
0208                         if (templdepth == 0) {
0209                             if (*t == '>')
0210                                 result += ' '; // avoid >>
0211                             break;
0212                         }
0213                         tt = t;
0214                     }
0215                 }
0216             }
0217         }
0218 
0219         // cv qualifers can appear after the type as well
0220         if (!is_ident_char(c) && t != e && (e - t >= 5 && strncmp("const", t, 5) == 0)
0221             && (e - t == 5 || !is_ident_char(t[5]))) {
0222             t += 5;
0223             while (t != e && is_space(*t))
0224                 ++t;
0225             if (adjustConst && t != e && *t == '&') {
0226                 // treat const ref as value
0227                 ++t;
0228             } else if (adjustConst && !star) {
0229                 // treat const as value
0230             } else if (!star) {
0231                 // move const to the front (but not if const comes after a *)
0232                 result = "const " + result;
0233             } else {
0234                 // keep const after a *
0235                 result += "const";
0236             }
0237         }
0238     }
0239 
0240     return result;
0241 }
0242 
0243 inline char *qNormalizeType(char *d, int &templdepth, std::string &result)
0244 {
0245     const char *t = d;
0246     while (*d && (templdepth
0247                   || (*d != ',' && *d != ')'))) {
0248         if (*d == '<')
0249             ++templdepth;
0250         if (*d == '>')
0251             --templdepth;
0252         ++d;
0253     }
0254     // "void" should only be removed if this is part of a signature that has
0255     // an explicit void argument; e.g., "void foo(void)" --> "void foo()"
0256     if (strncmp("void)", t, d - t + 1) != 0)
0257         result += normalizeTypeInternal(t, d);
0258 
0259     return d;
0260 }
0261 
0262 inline std::string normalizedType(const char *type)
0263 {
0264     std::string result;
0265 
0266     if (!type || !*type)
0267         return result;
0268 
0269     char *stackbuf = new char[strlen(type) + 1];
0270     qRemoveWhitespace(type, stackbuf);
0271     int templdepth = 0;
0272     qNormalizeType(stackbuf, templdepth, result);
0273     delete []stackbuf;
0274 
0275     return result;
0276 }
0277 
0278 inline std::string normalizedSignature(const char *method)
0279 {
0280     std::string result;
0281     if (!method || !*method)
0282         return result;
0283     int len = int(strlen(method));
0284     //char *stackbuf = new char[len + 1];
0285     char *stackbuf = new char[len + 1];
0286     char *d = stackbuf;
0287     qRemoveWhitespace(method, d);
0288 
0289     result.reserve(len);
0290 
0291     int argdepth = 0;
0292     int templdepth = 0;
0293     while (*d) {
0294         if (argdepth == 1) {
0295             d = qNormalizeType(d, templdepth, result);
0296             if (!*d) //most likely an invalid signature.
0297                 break;
0298         }
0299         if (*d == '(')
0300             ++argdepth;
0301         if (*d == ')')
0302             --argdepth;
0303         result += *d++;
0304     }
0305 
0306     delete []stackbuf;
0307     return result;
0308 }
0309 
0310 }
0311 
0312 #endif