File indexing completed on 2024-04-21 05:38:48

0001 /*
0002 
0003     SPDX-FileCopyrightText: 2015 The Qt Company Ltd.
0004     SPDX-FileCopyrightText: 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 SPDX-License-Identifier: LGPL-2.1-only WITH Qt-LGPL-exception-1.1 OR LGPL-3.0-only WITH Qt-LGPL-exception-1.1 OR LicenseRef-Qt-Commercial
0010 
0011 */
0012 
0013 #ifndef CLAZY_NORMALIZED_SIGNATURE_UTILS_H
0014 #define CLAZY_NORMALIZED_SIGNATURE_UTILS_H
0015 
0016 #include <string>
0017 
0018 namespace clazy
0019 {
0020 inline bool is_space(char s)
0021 {
0022     return (s == ' ' || s == '\t');
0023 }
0024 
0025 inline bool is_ident_start(char s)
0026 {
0027     return ((s >= 'a' && s <= 'z') || (s >= 'A' && s <= 'Z') || s == '_' || s == '$');
0028 }
0029 
0030 inline bool is_ident_char(char s)
0031 {
0032     return ((s >= 'a' && s <= 'z') || (s >= 'A' && s <= 'Z') || (s >= '0' && s <= '9') || s == '_' || s == '$');
0033 }
0034 
0035 static void qRemoveWhitespace(const char *s, char *d)
0036 {
0037     char last = 0;
0038     while (*s && is_space(*s)) {
0039         s++;
0040     }
0041     while (*s) {
0042         while (*s && !is_space(*s)) {
0043             last = *d++ = *s++;
0044         }
0045         while (*s && is_space(*s)) {
0046             s++;
0047         }
0048         if (*s && ((is_ident_char(*s) && is_ident_char(last)) || ((*s == ':') && (last == '<')))) {
0049             last = *d++ = ' ';
0050         }
0051     }
0052     *d = '\0';
0053 }
0054 
0055 // This function is shared with moc.cpp. This file should be included where needed.
0056 static std::string normalizeTypeInternal(const char *t, const char *e, bool fixScope = false, bool adjustConst = true)
0057 {
0058     int len = e - t;
0059     /*
0060       Convert 'char const *' into 'const char *'. Start at index 1,
0061       not 0, because 'const char *' is already OK.
0062     */
0063     std::string constbuf;
0064     for (int i = 1; i < len; i++) {
0065         if (t[i] == 'c' && strncmp(t + i + 1, "onst", 4) == 0 && (i + 5 >= len || !is_ident_char(t[i + 5])) && !is_ident_char(t[i - 1])) {
0066             constbuf = std::string(t, len);
0067             if (is_space(t[i - 1])) {
0068                 constbuf.erase(i - 1, 6);
0069             } else {
0070                 constbuf.erase(i, 5);
0071             }
0072             constbuf = "const " + constbuf;
0073             t = constbuf.data();
0074             e = constbuf.data() + constbuf.length();
0075             break;
0076         }
0077         /*
0078           We mustn't convert 'char * const *' into 'const char **'
0079           and we must beware of 'Bar<const Bla>'.
0080         */
0081         if (t[i] == '&' || t[i] == '*' || t[i] == '<') {
0082             break;
0083         }
0084     }
0085     if (adjustConst && e > t + 6 && strncmp("const ", t, 6) == 0) {
0086         if (*(e - 1) == '&') { // treat const reference as value
0087             t += 6;
0088             --e;
0089         } else if (is_ident_char(*(e - 1)) || *(e - 1) == '>') { // treat const value as value
0090             t += 6;
0091         }
0092     }
0093     std::string result;
0094 
0095 #if 1
0096     // consume initial 'const '
0097     if (strncmp("const ", t, 6) == 0) {
0098         t += 6;
0099         result += "const ";
0100     }
0101 #endif
0102 
0103     // some type substitutions for 'unsigned x'
0104     if (strncmp("unsigned", t, 8) == 0) {
0105         // make sure "unsigned" is an isolated word before making substitutions
0106         if (!t[8] || !is_ident_char(t[8])) {
0107             if (strncmp(" int", t + 8, 4) == 0) {
0108                 t += 8 + 4;
0109                 result += "uint";
0110             } else if (strncmp(" long", t + 8, 5) == 0) {
0111                 if ((strlen(t + 8 + 5) < 4 || strncmp(t + 8 + 5, " int", 4) != 0) // preserve '[unsigned] long int'
0112                     && (strlen(t + 8 + 5) < 5 || strncmp(t + 8 + 5, " long", 5) != 0) // preserve '[unsigned] long long'
0113                 ) {
0114                     t += 8 + 5;
0115                     result += "ulong";
0116                 }
0117             } else if (strncmp(" short", t + 8, 6) != 0 // preserve unsigned short
0118                        && strncmp(" char", t + 8, 5) != 0) { // preserve unsigned char
0119                 //  treat rest (unsigned) as uint
0120                 t += 8;
0121                 result += "uint";
0122             }
0123         }
0124     } else {
0125         // discard 'struct', 'class', and 'enum'; they are optional
0126         // and we don't want them in the normalized signature
0127         struct {
0128             const char *keyword;
0129             int len;
0130         } optional[] = {{"struct ", 7}, {"class ", 6}, {"enum ", 5}, {nullptr, 0}};
0131         int i = 0;
0132         do {
0133             if (strncmp(optional[i].keyword, t, optional[i].len) == 0) {
0134                 t += optional[i].len;
0135                 break;
0136             }
0137         } while (optional[++i].keyword != nullptr);
0138     }
0139 
0140     bool star = false;
0141     while (t != e) {
0142         char c = *t++;
0143         if (fixScope && c == ':' && *t == ':') {
0144             ++t;
0145             c = *t++;
0146             int i = result.size() - 1;
0147             while (i >= 0 && is_ident_char(result.at(i))) {
0148                 --i;
0149             }
0150             result.resize(i + 1);
0151         }
0152         star = star || c == '*';
0153         result += c;
0154         if (c == '<') {
0155             // template recursion
0156             const char *tt = t;
0157             int templdepth = 1;
0158             int scopeDepth = 0;
0159             while (t != e) {
0160                 c = *t++;
0161                 if (c == '{' || c == '(' || c == '[') {
0162                     ++scopeDepth;
0163                 }
0164                 if (c == '}' || c == ')' || c == ']') {
0165                     --scopeDepth;
0166                 }
0167                 if (scopeDepth == 0) {
0168                     if (c == '<') {
0169                         ++templdepth;
0170                     }
0171                     if (c == '>') {
0172                         --templdepth;
0173                     }
0174                     if (templdepth == 0 || (templdepth == 1 && c == ',')) {
0175                         result += normalizeTypeInternal(tt, t - 1, fixScope, false);
0176                         result += c;
0177                         if (templdepth == 0) {
0178                             if (*t == '>') {
0179                                 result += ' '; // avoid >>
0180                             }
0181                             break;
0182                         }
0183                         tt = t;
0184                     }
0185                 }
0186             }
0187         }
0188 
0189         // cv qualifers can appear after the type as well
0190         if (!is_ident_char(c) && t != e && (e - t >= 5 && strncmp("const", t, 5) == 0) && (e - t == 5 || !is_ident_char(t[5]))) {
0191             t += 5;
0192             while (t != e && is_space(*t)) {
0193                 ++t;
0194             }
0195             if (adjustConst && t != e && *t == '&') {
0196                 // treat const ref as value
0197                 ++t;
0198             } else if (adjustConst && !star) {
0199                 // treat const as value
0200             } else if (!star) {
0201                 // move const to the front (but not if const comes after a *)
0202                 result = "const " + result;
0203             } else {
0204                 // keep const after a *
0205                 result += "const";
0206             }
0207         }
0208     }
0209 
0210     return result;
0211 }
0212 
0213 inline char *qNormalizeType(char *d, int &templdepth, std::string &result)
0214 {
0215     const char *t = d;
0216     while (*d && (templdepth || (*d != ',' && *d != ')'))) {
0217         if (*d == '<') {
0218             ++templdepth;
0219         }
0220         if (*d == '>') {
0221             --templdepth;
0222         }
0223         ++d;
0224     }
0225     // "void" should only be removed if this is part of a signature that has
0226     // an explicit void argument; e.g., "void foo(void)" --> "void foo()"
0227     if (strncmp("void)", t, d - t + 1) != 0) {
0228         result += normalizeTypeInternal(t, d);
0229     }
0230 
0231     return d;
0232 }
0233 
0234 inline std::string normalizedType(const char *type)
0235 {
0236     std::string result;
0237 
0238     if (!type || !*type) {
0239         return result;
0240     }
0241 
0242     char *stackbuf = new char[strlen(type) + 1];
0243     qRemoveWhitespace(type, stackbuf);
0244     int templdepth = 0;
0245     qNormalizeType(stackbuf, templdepth, result);
0246     delete[] stackbuf;
0247 
0248     return result;
0249 }
0250 
0251 inline std::string normalizedSignature(const char *method)
0252 {
0253     std::string result;
0254     if (!method || !*method) {
0255         return result;
0256     }
0257     int len = int(strlen(method));
0258     // char *stackbuf = new char[len + 1];
0259     char *stackbuf = new char[len + 1];
0260     char *d = stackbuf;
0261     qRemoveWhitespace(method, d);
0262 
0263     result.reserve(len);
0264 
0265     int argdepth = 0;
0266     int templdepth = 0;
0267     while (*d) {
0268         if (argdepth == 1) {
0269             d = qNormalizeType(d, templdepth, result);
0270             if (!*d) { // most likely an invalid signature.
0271                 break;
0272             }
0273         }
0274         if (*d == '(') {
0275             ++argdepth;
0276         }
0277         if (*d == ')') {
0278             --argdepth;
0279         }
0280         result += *d++;
0281     }
0282 
0283     delete[] stackbuf;
0284     return result;
0285 }
0286 
0287 }
0288 
0289 #endif