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