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