|
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 tools applications 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 "moc.h" |
|
43 #include "generator.h" |
|
44 #include "qdatetime.h" |
|
45 #include "utils.h" |
|
46 #include "outputrevision.h" |
|
47 |
|
48 // for normalizeTypeInternal |
|
49 #include <private/qmetaobject_p.h> |
|
50 |
|
51 QT_BEGIN_NAMESPACE |
|
52 |
|
53 // only moc needs this function |
|
54 static QByteArray normalizeType(const char *s, bool fixScope = false) |
|
55 { |
|
56 int len = qstrlen(s); |
|
57 char stackbuf[64]; |
|
58 char *buf = (len >= 64 ? new char[len + 1] : stackbuf); |
|
59 char *d = buf; |
|
60 char last = 0; |
|
61 while(*s && is_space(*s)) |
|
62 s++; |
|
63 while (*s) { |
|
64 while (*s && !is_space(*s)) |
|
65 last = *d++ = *s++; |
|
66 while (*s && is_space(*s)) |
|
67 s++; |
|
68 if (*s && ((is_ident_char(*s) && is_ident_char(last)) |
|
69 || ((*s == ':') && (last == '<')))) { |
|
70 last = *d++ = ' '; |
|
71 } |
|
72 } |
|
73 *d = '\0'; |
|
74 QByteArray result; |
|
75 if (strncmp("void", buf, d - buf) != 0) |
|
76 result = normalizeTypeInternal(buf, d, fixScope); |
|
77 if (buf != stackbuf) |
|
78 delete [] buf; |
|
79 return result; |
|
80 } |
|
81 |
|
82 bool Moc::parseClassHead(ClassDef *def) |
|
83 { |
|
84 // figure out whether this is a class declaration, or only a |
|
85 // forward or variable declaration. |
|
86 int i = 0; |
|
87 Token token; |
|
88 do { |
|
89 token = lookup(i++); |
|
90 if (token == COLON || token == LBRACE) |
|
91 break; |
|
92 if (token == SEMIC || token == RANGLE) |
|
93 return false; |
|
94 } while (token); |
|
95 |
|
96 if (!test(IDENTIFIER)) // typedef struct { ... } |
|
97 return false; |
|
98 QByteArray name = lexem(); |
|
99 |
|
100 // support "class IDENT name" and "class IDENT(IDENT) name" |
|
101 if (test(LPAREN)) { |
|
102 until(RPAREN); |
|
103 if (!test(IDENTIFIER)) |
|
104 return false; |
|
105 name = lexem(); |
|
106 } else if (test(IDENTIFIER)) { |
|
107 name = lexem(); |
|
108 } |
|
109 |
|
110 def->qualified += name; |
|
111 while (test(SCOPE)) { |
|
112 def->qualified += lexem(); |
|
113 if (test(IDENTIFIER)) { |
|
114 name = lexem(); |
|
115 def->qualified += name; |
|
116 } |
|
117 } |
|
118 def->classname = name; |
|
119 if (test(COLON)) { |
|
120 do { |
|
121 test(VIRTUAL); |
|
122 FunctionDef::Access access = FunctionDef::Public; |
|
123 if (test(PRIVATE)) |
|
124 access = FunctionDef::Private; |
|
125 else if (test(PROTECTED)) |
|
126 access = FunctionDef::Protected; |
|
127 else |
|
128 test(PUBLIC); |
|
129 test(VIRTUAL); |
|
130 const QByteArray type = parseType().name; |
|
131 // ignore the 'class Foo : BAR(Baz)' case |
|
132 if (test(LPAREN)) { |
|
133 until(RPAREN); |
|
134 } else { |
|
135 def->superclassList += qMakePair(type, access); |
|
136 } |
|
137 } while (test(COMMA)); |
|
138 } |
|
139 if (!test(LBRACE)) |
|
140 return false; |
|
141 def->begin = index - 1; |
|
142 bool foundRBrace = until(RBRACE); |
|
143 def->end = index; |
|
144 index = def->begin + 1; |
|
145 return foundRBrace; |
|
146 } |
|
147 |
|
148 Type Moc::parseType() |
|
149 { |
|
150 Type type; |
|
151 bool hasSignedOrUnsigned = false; |
|
152 bool isVoid = false; |
|
153 type.firstToken = lookup(); |
|
154 for (;;) { |
|
155 switch (next()) { |
|
156 case SIGNED: |
|
157 case UNSIGNED: |
|
158 hasSignedOrUnsigned = true; |
|
159 // fall through |
|
160 case CONST: |
|
161 case VOLATILE: |
|
162 type.name += lexem(); |
|
163 type.name += ' '; |
|
164 if (lookup(0) == VOLATILE) |
|
165 type.isVolatile = true; |
|
166 continue; |
|
167 case Q_MOC_COMPAT_TOKEN: |
|
168 case Q_QT3_SUPPORT_TOKEN: |
|
169 case Q_INVOKABLE_TOKEN: |
|
170 case Q_SCRIPTABLE_TOKEN: |
|
171 case Q_SIGNALS_TOKEN: |
|
172 case Q_SLOTS_TOKEN: |
|
173 case Q_SIGNAL_TOKEN: |
|
174 case Q_SLOT_TOKEN: |
|
175 type.name += lexem(); |
|
176 return type; |
|
177 default: |
|
178 prev(); |
|
179 break; |
|
180 } |
|
181 break; |
|
182 } |
|
183 test(ENUM) || test(CLASS) || test(STRUCT); |
|
184 for(;;) { |
|
185 switch (next()) { |
|
186 case IDENTIFIER: |
|
187 // void mySlot(unsigned myArg) |
|
188 if (hasSignedOrUnsigned) { |
|
189 prev(); |
|
190 break; |
|
191 } |
|
192 case CHAR: |
|
193 case SHORT: |
|
194 case INT: |
|
195 case LONG: |
|
196 type.name += lexem(); |
|
197 // preserve '[unsigned] long long', 'short int', 'long int', 'long double' |
|
198 if (test(LONG) || test(INT) || test(DOUBLE)) { |
|
199 type.name += ' '; |
|
200 prev(); |
|
201 continue; |
|
202 } |
|
203 break; |
|
204 case FLOAT: |
|
205 case DOUBLE: |
|
206 case VOID: |
|
207 case BOOL: |
|
208 type.name += lexem(); |
|
209 isVoid |= (lookup(0) == VOID); |
|
210 break; |
|
211 default: |
|
212 prev(); |
|
213 ; |
|
214 } |
|
215 if (test(LANGLE)) { |
|
216 QByteArray templ = lexemUntil(RANGLE); |
|
217 for (int i = 0; i < templ.size(); ++i) { |
|
218 type.name += templ.at(i); |
|
219 if ((templ.at(i) == '<' && i < templ.size()-1 && templ.at(i+1) == ':') |
|
220 || (templ.at(i) == '>' && i < templ.size()-1 && templ.at(i+1) == '>')) { |
|
221 type.name += ' '; |
|
222 } |
|
223 } |
|
224 } |
|
225 if (test(SCOPE)) { |
|
226 type.name += lexem(); |
|
227 type.isScoped = true; |
|
228 } else { |
|
229 break; |
|
230 } |
|
231 } |
|
232 while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED) |
|
233 || test(STAR) || test(AND)) { |
|
234 type.name += ' '; |
|
235 type.name += lexem(); |
|
236 if (lookup(0) == AND) |
|
237 type.referenceType = Type::Reference; |
|
238 else if (lookup(0) == STAR) |
|
239 type.referenceType = Type::Pointer; |
|
240 } |
|
241 // transform stupid things like 'const void' or 'void const' into 'void' |
|
242 if (isVoid && type.referenceType == Type::NoReference) { |
|
243 type.name = "void"; |
|
244 } |
|
245 return type; |
|
246 } |
|
247 |
|
248 bool Moc::parseEnum(EnumDef *def) |
|
249 { |
|
250 bool isTypdefEnum = false; // typedef enum { ... } Foo; |
|
251 |
|
252 if (test(IDENTIFIER)) { |
|
253 def->name = lexem(); |
|
254 } else { |
|
255 if (lookup(-1) != TYPEDEF) |
|
256 return false; // anonymous enum |
|
257 isTypdefEnum = true; |
|
258 } |
|
259 if (!test(LBRACE)) |
|
260 return false; |
|
261 do { |
|
262 if (lookup() == RBRACE) // accept trailing comma |
|
263 break; |
|
264 next(IDENTIFIER); |
|
265 def->values += lexem(); |
|
266 } while (test(EQ) ? until(COMMA) : test(COMMA)); |
|
267 next(RBRACE); |
|
268 if (isTypdefEnum) { |
|
269 if (!test(IDENTIFIER)) |
|
270 return false; |
|
271 def->name = lexem(); |
|
272 } |
|
273 return true; |
|
274 } |
|
275 |
|
276 void Moc::parseFunctionArguments(FunctionDef *def) |
|
277 { |
|
278 Q_UNUSED(def); |
|
279 while (hasNext()) { |
|
280 ArgumentDef arg; |
|
281 arg.type = parseType(); |
|
282 if (arg.type.name == "void") |
|
283 break; |
|
284 if (test(IDENTIFIER)) |
|
285 arg.name = lexem(); |
|
286 while (test(LBRACK)) { |
|
287 arg.rightType += lexemUntil(RBRACK); |
|
288 } |
|
289 if (test(CONST) || test(VOLATILE)) { |
|
290 arg.rightType += ' '; |
|
291 arg.rightType += lexem(); |
|
292 } |
|
293 arg.normalizedType = normalizeType(arg.type.name + ' ' + arg.rightType); |
|
294 arg.typeNameForCast = normalizeType(noRef(arg.type.name) + "(*)" + arg.rightType); |
|
295 if (test(EQ)) |
|
296 arg.isDefault = true; |
|
297 def->arguments += arg; |
|
298 if (!until(COMMA)) |
|
299 break; |
|
300 } |
|
301 } |
|
302 |
|
303 bool Moc::testFunctionAttribute(FunctionDef *def) |
|
304 { |
|
305 if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) { |
|
306 ++index; |
|
307 return true; |
|
308 } |
|
309 return false; |
|
310 } |
|
311 |
|
312 bool Moc::testFunctionAttribute(Token tok, FunctionDef *def) |
|
313 { |
|
314 switch (tok) { |
|
315 case Q_MOC_COMPAT_TOKEN: |
|
316 case Q_QT3_SUPPORT_TOKEN: |
|
317 def->isCompat = true; |
|
318 return true; |
|
319 case Q_INVOKABLE_TOKEN: |
|
320 def->isInvokable = true; |
|
321 return true; |
|
322 case Q_SIGNAL_TOKEN: |
|
323 def->isSignal = true; |
|
324 return true; |
|
325 case Q_SLOT_TOKEN: |
|
326 def->isSlot = true; |
|
327 return true; |
|
328 case Q_SCRIPTABLE_TOKEN: |
|
329 def->isInvokable = def->isScriptable = true; |
|
330 return true; |
|
331 default: break; |
|
332 } |
|
333 return false; |
|
334 } |
|
335 |
|
336 // returns false if the function should be ignored |
|
337 bool Moc::parseFunction(FunctionDef *def, bool inMacro) |
|
338 { |
|
339 def->isVirtual = false; |
|
340 //skip modifiers and attributes |
|
341 while (test(INLINE) || test(STATIC) || |
|
342 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual |
|
343 || testFunctionAttribute(def)) {} |
|
344 bool templateFunction = (lookup() == TEMPLATE); |
|
345 def->type = parseType(); |
|
346 if (def->type.name.isEmpty()) { |
|
347 if (templateFunction) |
|
348 error("Template function as signal or slot"); |
|
349 else |
|
350 error(); |
|
351 } |
|
352 bool scopedFunctionName = false; |
|
353 if (test(LPAREN)) { |
|
354 def->name = def->type.name; |
|
355 scopedFunctionName = def->type.isScoped; |
|
356 def->type = Type("int"); |
|
357 } else { |
|
358 Type tempType = parseType();; |
|
359 while (!tempType.name.isEmpty() && lookup() != LPAREN) { |
|
360 if (testFunctionAttribute(def->type.firstToken, def)) |
|
361 ; // fine |
|
362 else if (def->type.firstToken == Q_SIGNALS_TOKEN) |
|
363 error(); |
|
364 else if (def->type.firstToken == Q_SLOTS_TOKEN) |
|
365 error(); |
|
366 else { |
|
367 if (!def->tag.isEmpty()) |
|
368 def->tag += ' '; |
|
369 def->tag += def->type.name; |
|
370 } |
|
371 def->type = tempType; |
|
372 tempType = parseType(); |
|
373 } |
|
374 next(LPAREN, "Not a signal or slot declaration"); |
|
375 def->name = tempType.name; |
|
376 scopedFunctionName = tempType.isScoped; |
|
377 } |
|
378 |
|
379 // we don't support references as return types, it's too dangerous |
|
380 if (def->type.referenceType == Type::Reference) |
|
381 def->type = Type("void"); |
|
382 |
|
383 def->normalizedType = normalizeType(def->type.name); |
|
384 |
|
385 if (!test(RPAREN)) { |
|
386 parseFunctionArguments(def); |
|
387 next(RPAREN); |
|
388 } |
|
389 |
|
390 // support optional macros with compiler specific options |
|
391 while (test(IDENTIFIER)) |
|
392 ; |
|
393 |
|
394 def->isConst = test(CONST); |
|
395 |
|
396 while (test(IDENTIFIER)) |
|
397 ; |
|
398 |
|
399 if (inMacro) { |
|
400 next(RPAREN); |
|
401 prev(); |
|
402 } else { |
|
403 if (test(THROW)) { |
|
404 next(LPAREN); |
|
405 until(RPAREN); |
|
406 } |
|
407 if (test(SEMIC)) |
|
408 ; |
|
409 else if ((def->inlineCode = test(LBRACE))) |
|
410 until(RBRACE); |
|
411 else if ((def->isAbstract = test(EQ))) |
|
412 until(SEMIC); |
|
413 else |
|
414 error(); |
|
415 } |
|
416 |
|
417 if (scopedFunctionName) { |
|
418 QByteArray msg("Function declaration "); |
|
419 msg += def->name; |
|
420 msg += " contains extra qualification. Ignoring as signal or slot."; |
|
421 warning(msg.constData()); |
|
422 return false; |
|
423 } |
|
424 return true; |
|
425 } |
|
426 |
|
427 // like parseFunction, but never aborts with an error |
|
428 bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def) |
|
429 { |
|
430 def->isVirtual = false; |
|
431 //skip modifiers and attributes |
|
432 while (test(EXPLICIT) || test(INLINE) || test(STATIC) || |
|
433 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual |
|
434 || testFunctionAttribute(def)) {} |
|
435 bool tilde = test(TILDE); |
|
436 def->type = parseType(); |
|
437 if (def->type.name.isEmpty()) |
|
438 return false; |
|
439 bool scopedFunctionName = false; |
|
440 if (test(LPAREN)) { |
|
441 def->name = def->type.name; |
|
442 scopedFunctionName = def->type.isScoped; |
|
443 if (def->name == cdef->classname) { |
|
444 def->isDestructor = tilde; |
|
445 def->isConstructor = !tilde; |
|
446 def->type = Type(); |
|
447 } else { |
|
448 def->type = Type("int"); |
|
449 } |
|
450 } else { |
|
451 Type tempType = parseType();; |
|
452 while (!tempType.name.isEmpty() && lookup() != LPAREN) { |
|
453 if (testFunctionAttribute(def->type.firstToken, def)) |
|
454 ; // fine |
|
455 else if (def->type.name == "Q_SIGNAL") |
|
456 def->isSignal = true; |
|
457 else if (def->type.name == "Q_SLOT") |
|
458 def->isSlot = true; |
|
459 else { |
|
460 if (!def->tag.isEmpty()) |
|
461 def->tag += ' '; |
|
462 def->tag += def->type.name; |
|
463 } |
|
464 def->type = tempType; |
|
465 tempType = parseType(); |
|
466 } |
|
467 if (!test(LPAREN)) |
|
468 return false; |
|
469 def->name = tempType.name; |
|
470 scopedFunctionName = tempType.isScoped; |
|
471 } |
|
472 |
|
473 // we don't support references as return types, it's too dangerous |
|
474 if (def->type.referenceType == Type::Reference) |
|
475 def->type = Type("void"); |
|
476 |
|
477 def->normalizedType = normalizeType(def->type.name); |
|
478 |
|
479 if (!test(RPAREN)) { |
|
480 parseFunctionArguments(def); |
|
481 if (!test(RPAREN)) |
|
482 return false; |
|
483 } |
|
484 def->isConst = test(CONST); |
|
485 if (scopedFunctionName |
|
486 && (def->isSignal || def->isSlot || def->isInvokable)) { |
|
487 QByteArray msg("parsemaybe: Function declaration "); |
|
488 msg += def->name; |
|
489 msg += " contains extra qualification. Ignoring as signal or slot."; |
|
490 warning(msg.constData()); |
|
491 return false; |
|
492 } |
|
493 return true; |
|
494 } |
|
495 |
|
496 |
|
497 void Moc::parse() |
|
498 { |
|
499 QList<NamespaceDef> namespaceList; |
|
500 bool templateClass = false; |
|
501 while (hasNext()) { |
|
502 Token t = next(); |
|
503 switch (t) { |
|
504 case NAMESPACE: { |
|
505 int rewind = index; |
|
506 if (test(IDENTIFIER)) { |
|
507 if (test(EQ)) { |
|
508 // namespace Foo = Bar::Baz; |
|
509 until(SEMIC); |
|
510 } else if (!test(SEMIC)) { |
|
511 NamespaceDef def; |
|
512 def.name = lexem(); |
|
513 next(LBRACE); |
|
514 def.begin = index - 1; |
|
515 until(RBRACE); |
|
516 def.end = index; |
|
517 index = def.begin + 1; |
|
518 namespaceList += def; |
|
519 index = rewind; |
|
520 } |
|
521 } |
|
522 break; |
|
523 } |
|
524 case SEMIC: |
|
525 case RBRACE: |
|
526 templateClass = false; |
|
527 break; |
|
528 case TEMPLATE: |
|
529 templateClass = true; |
|
530 break; |
|
531 case MOC_INCLUDE_BEGIN: |
|
532 currentFilenames.push(symbol().unquotedLexem()); |
|
533 break; |
|
534 case MOC_INCLUDE_END: |
|
535 currentFilenames.pop(); |
|
536 break; |
|
537 case Q_DECLARE_INTERFACE_TOKEN: |
|
538 parseDeclareInterface(); |
|
539 break; |
|
540 case Q_DECLARE_METATYPE_TOKEN: |
|
541 parseDeclareMetatype(); |
|
542 break; |
|
543 case USING: |
|
544 if (test(NAMESPACE)) { |
|
545 while (test(SCOPE) || test(IDENTIFIER)) |
|
546 ; |
|
547 next(SEMIC); |
|
548 } |
|
549 break; |
|
550 case CLASS: |
|
551 case STRUCT: { |
|
552 if (currentFilenames.size() <= 1) |
|
553 break; |
|
554 |
|
555 ClassDef def; |
|
556 if (!parseClassHead(&def)) |
|
557 continue; |
|
558 |
|
559 while (inClass(&def) && hasNext()) { |
|
560 if (next() == Q_OBJECT_TOKEN) { |
|
561 def.hasQObject = true; |
|
562 break; |
|
563 } |
|
564 } |
|
565 |
|
566 if (!def.hasQObject) |
|
567 continue; |
|
568 |
|
569 for (int i = namespaceList.size() - 1; i >= 0; --i) |
|
570 if (inNamespace(&namespaceList.at(i))) |
|
571 def.qualified.prepend(namespaceList.at(i).name + "::"); |
|
572 |
|
573 knownQObjectClasses.insert(def.classname); |
|
574 knownQObjectClasses.insert(def.qualified); |
|
575 |
|
576 continue; } |
|
577 default: break; |
|
578 } |
|
579 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1) |
|
580 continue; |
|
581 ClassDef def; |
|
582 if (parseClassHead(&def)) { |
|
583 FunctionDef::Access access = FunctionDef::Private; |
|
584 for (int i = namespaceList.size() - 1; i >= 0; --i) |
|
585 if (inNamespace(&namespaceList.at(i))) |
|
586 def.qualified.prepend(namespaceList.at(i).name + "::"); |
|
587 while (inClass(&def) && hasNext()) { |
|
588 switch ((t = next())) { |
|
589 case PRIVATE: |
|
590 access = FunctionDef::Private; |
|
591 if (test(Q_SIGNALS_TOKEN)) |
|
592 error("Signals cannot have access specifier"); |
|
593 break; |
|
594 case PROTECTED: |
|
595 access = FunctionDef::Protected; |
|
596 if (test(Q_SIGNALS_TOKEN)) |
|
597 error("Signals cannot have access specifier"); |
|
598 break; |
|
599 case PUBLIC: |
|
600 access = FunctionDef::Public; |
|
601 if (test(Q_SIGNALS_TOKEN)) |
|
602 error("Signals cannot have access specifier"); |
|
603 break; |
|
604 case CLASS: { |
|
605 ClassDef nestedDef; |
|
606 if (parseClassHead(&nestedDef)) { |
|
607 while (inClass(&nestedDef) && inClass(&def)) { |
|
608 t = next(); |
|
609 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END) |
|
610 error("Meta object features not supported for nested classes"); |
|
611 } |
|
612 } |
|
613 } break; |
|
614 case Q_SIGNALS_TOKEN: |
|
615 parseSignals(&def); |
|
616 break; |
|
617 case Q_SLOTS_TOKEN: |
|
618 switch (lookup(-1)) { |
|
619 case PUBLIC: |
|
620 case PROTECTED: |
|
621 case PRIVATE: |
|
622 parseSlots(&def, access); |
|
623 break; |
|
624 default: |
|
625 error("Missing access specifier for slots"); |
|
626 } |
|
627 break; |
|
628 case Q_OBJECT_TOKEN: |
|
629 def.hasQObject = true; |
|
630 if (templateClass) |
|
631 error("Template classes not supported by Q_OBJECT"); |
|
632 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty()) |
|
633 error("Class contains Q_OBJECT macro but does not inherit from QObject"); |
|
634 break; |
|
635 case Q_GADGET_TOKEN: |
|
636 def.hasQGadget = true; |
|
637 if (templateClass) |
|
638 error("Template classes not supported by Q_GADGET"); |
|
639 break; |
|
640 case Q_PROPERTY_TOKEN: |
|
641 parseProperty(&def); |
|
642 break; |
|
643 case Q_ENUMS_TOKEN: |
|
644 parseEnumOrFlag(&def, false); |
|
645 break; |
|
646 case Q_FLAGS_TOKEN: |
|
647 parseEnumOrFlag(&def, true); |
|
648 break; |
|
649 case Q_DECLARE_FLAGS_TOKEN: |
|
650 parseFlag(&def); |
|
651 break; |
|
652 case Q_CLASSINFO_TOKEN: |
|
653 parseClassInfo(&def); |
|
654 break; |
|
655 case Q_INTERFACES_TOKEN: |
|
656 parseInterfaces(&def); |
|
657 break; |
|
658 case Q_PRIVATE_SLOT_TOKEN: |
|
659 parseSlotInPrivate(&def, access); |
|
660 break; |
|
661 case ENUM: { |
|
662 EnumDef enumDef; |
|
663 if (parseEnum(&enumDef)) |
|
664 def.enumList += enumDef; |
|
665 } break; |
|
666 default: |
|
667 FunctionDef funcDef; |
|
668 funcDef.access = access; |
|
669 int rewind = index; |
|
670 if (parseMaybeFunction(&def, &funcDef)) { |
|
671 if (funcDef.isConstructor) { |
|
672 if ((access == FunctionDef::Public) && funcDef.isInvokable) { |
|
673 def.constructorList += funcDef; |
|
674 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
675 funcDef.wasCloned = true; |
|
676 funcDef.arguments.removeLast(); |
|
677 def.constructorList += funcDef; |
|
678 } |
|
679 } |
|
680 } else if (funcDef.isDestructor) { |
|
681 // don't care about destructors |
|
682 } else { |
|
683 if (access == FunctionDef::Public) |
|
684 def.publicList += funcDef; |
|
685 if (funcDef.isSlot) { |
|
686 def.slotList += funcDef; |
|
687 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
688 funcDef.wasCloned = true; |
|
689 funcDef.arguments.removeLast(); |
|
690 def.slotList += funcDef; |
|
691 } |
|
692 } else if (funcDef.isSignal) { |
|
693 def.signalList += funcDef; |
|
694 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
695 funcDef.wasCloned = true; |
|
696 funcDef.arguments.removeLast(); |
|
697 def.signalList += funcDef; |
|
698 } |
|
699 } else if (funcDef.isInvokable) { |
|
700 def.methodList += funcDef; |
|
701 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
702 funcDef.wasCloned = true; |
|
703 funcDef.arguments.removeLast(); |
|
704 def.methodList += funcDef; |
|
705 } |
|
706 } |
|
707 } |
|
708 } else { |
|
709 index = rewind; |
|
710 } |
|
711 } |
|
712 } |
|
713 |
|
714 next(RBRACE); |
|
715 |
|
716 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty() |
|
717 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty()) |
|
718 continue; // no meta object code required |
|
719 |
|
720 |
|
721 if (!def.hasQObject && !def.hasQGadget) |
|
722 error("Class declarations lacks Q_OBJECT macro."); |
|
723 |
|
724 checkSuperClasses(&def); |
|
725 |
|
726 classList += def; |
|
727 knownQObjectClasses.insert(def.classname); |
|
728 knownQObjectClasses.insert(def.qualified); |
|
729 } |
|
730 } |
|
731 } |
|
732 |
|
733 void Moc::generate(FILE *out) |
|
734 { |
|
735 |
|
736 QDateTime dt = QDateTime::currentDateTime(); |
|
737 QByteArray dstr = dt.toString().toLatin1(); |
|
738 QByteArray fn = filename; |
|
739 int i = filename.length()-1; |
|
740 while (i>0 && filename[i-1] != '/' && filename[i-1] != '\\') |
|
741 --i; // skip path |
|
742 if (i >= 0) |
|
743 fn = filename.mid(i); |
|
744 fprintf(out, "/****************************************************************************\n" |
|
745 "** Meta object code from reading C++ file '%s'\n**\n" , (const char*)fn); |
|
746 fprintf(out, "** Created: %s\n" |
|
747 "** by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , dstr.data(), mocOutputRevision, QT_VERSION_STR); |
|
748 fprintf(out, "** WARNING! All changes made in this file will be lost!\n" |
|
749 "*****************************************************************************/\n\n"); |
|
750 |
|
751 |
|
752 if (!noInclude) { |
|
753 if (includePath.size() && !includePath.endsWith('/')) |
|
754 includePath += '/'; |
|
755 for (int i = 0; i < includeFiles.size(); ++i) { |
|
756 QByteArray inc = includeFiles.at(i); |
|
757 if (inc[0] != '<' && inc[0] != '"') { |
|
758 if (includePath.size() && includePath != "./") |
|
759 inc.prepend(includePath); |
|
760 inc = '\"' + inc + '\"'; |
|
761 } |
|
762 fprintf(out, "#include %s\n", inc.constData()); |
|
763 } |
|
764 } |
|
765 if (classList.size() && classList.first().classname == "Qt") |
|
766 fprintf(out, "#include <QtCore/qobject.h>\n"); |
|
767 |
|
768 if (mustIncludeQMetaTypeH) |
|
769 fprintf(out, "#include <QtCore/qmetatype.h>\n"); |
|
770 |
|
771 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n" |
|
772 "#error \"The header file '%s' doesn't include <QObject>.\"\n", (const char *)fn); |
|
773 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision); |
|
774 fprintf(out, "#error \"This file was generated using the moc from %s." |
|
775 " It\"\n#error \"cannot be used with the include files from" |
|
776 " this version of Qt.\"\n#error \"(The moc has changed too" |
|
777 " much.)\"\n", QT_VERSION_STR); |
|
778 fprintf(out, "#endif\n\n"); |
|
779 |
|
780 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n"); |
|
781 |
|
782 for (i = 0; i < classList.size(); ++i) { |
|
783 Generator generator(&classList[i], metaTypes, out); |
|
784 generator.generateCode(); |
|
785 } |
|
786 |
|
787 fprintf(out, "QT_END_MOC_NAMESPACE\n"); |
|
788 } |
|
789 |
|
790 |
|
791 QList<QMetaObject*> Moc::generate(bool ignoreProperties) |
|
792 { |
|
793 QList<QMetaObject*> result; |
|
794 for (int i = 0; i < classList.size(); ++i) { |
|
795 Generator generator(&classList[i], metaTypes); |
|
796 result << generator.generateMetaObject(ignoreProperties); |
|
797 } |
|
798 return result; |
|
799 } |
|
800 |
|
801 void Moc::parseSlots(ClassDef *def, FunctionDef::Access access) |
|
802 { |
|
803 next(COLON); |
|
804 while (inClass(def) && hasNext()) { |
|
805 switch (next()) { |
|
806 case PUBLIC: |
|
807 case PROTECTED: |
|
808 case PRIVATE: |
|
809 case Q_SIGNALS_TOKEN: |
|
810 case Q_SLOTS_TOKEN: |
|
811 prev(); |
|
812 return; |
|
813 case SEMIC: |
|
814 continue; |
|
815 case FRIEND: |
|
816 until(SEMIC); |
|
817 continue; |
|
818 case USING: |
|
819 error("'using' directive not supported in 'slots' section"); |
|
820 default: |
|
821 prev(); |
|
822 } |
|
823 |
|
824 FunctionDef funcDef; |
|
825 funcDef.access = access; |
|
826 if (!parseFunction(&funcDef)) |
|
827 continue; |
|
828 def->slotList += funcDef; |
|
829 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
830 funcDef.wasCloned = true; |
|
831 funcDef.arguments.removeLast(); |
|
832 def->slotList += funcDef; |
|
833 } |
|
834 } |
|
835 } |
|
836 |
|
837 void Moc::parseSignals(ClassDef *def) |
|
838 { |
|
839 next(COLON); |
|
840 while (inClass(def) && hasNext()) { |
|
841 switch (next()) { |
|
842 case PUBLIC: |
|
843 case PROTECTED: |
|
844 case PRIVATE: |
|
845 case Q_SIGNALS_TOKEN: |
|
846 case Q_SLOTS_TOKEN: |
|
847 prev(); |
|
848 return; |
|
849 case SEMIC: |
|
850 continue; |
|
851 case FRIEND: |
|
852 until(SEMIC); |
|
853 continue; |
|
854 case USING: |
|
855 error("'using' directive not supported in 'signals' section"); |
|
856 default: |
|
857 prev(); |
|
858 } |
|
859 FunctionDef funcDef; |
|
860 funcDef.access = FunctionDef::Protected; |
|
861 parseFunction(&funcDef); |
|
862 if (funcDef.isVirtual) |
|
863 warning("Signals cannot be declared virtual"); |
|
864 if (funcDef.inlineCode) |
|
865 error("Not a signal declaration"); |
|
866 def->signalList += funcDef; |
|
867 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
868 funcDef.wasCloned = true; |
|
869 funcDef.arguments.removeLast(); |
|
870 def->signalList += funcDef; |
|
871 } |
|
872 } |
|
873 } |
|
874 |
|
875 |
|
876 void Moc::parseProperty(ClassDef *def) |
|
877 { |
|
878 next(LPAREN); |
|
879 PropertyDef propDef; |
|
880 QByteArray type = parseType().name; |
|
881 if (type.isEmpty()) |
|
882 error(); |
|
883 propDef.designable = propDef.scriptable = propDef.stored = "true"; |
|
884 propDef.user = "false"; |
|
885 /* |
|
886 The Q_PROPERTY construct cannot contain any commas, since |
|
887 commas separate macro arguments. We therefore expect users |
|
888 to type "QMap" instead of "QMap<QString, QVariant>". For |
|
889 coherence, we also expect the same for |
|
890 QValueList<QVariant>, the other template class supported by |
|
891 QVariant. |
|
892 */ |
|
893 type = normalizeType(type); |
|
894 if (type == "QMap") |
|
895 type = "QMap<QString,QVariant>"; |
|
896 else if (type == "QValueList") |
|
897 type = "QValueList<QVariant>"; |
|
898 else if (type == "LongLong") |
|
899 type = "qlonglong"; |
|
900 else if (type == "ULongLong") |
|
901 type = "qulonglong"; |
|
902 else if (type == "qreal") |
|
903 mustIncludeQMetaTypeH = true; |
|
904 |
|
905 propDef.type = type; |
|
906 |
|
907 next(); |
|
908 propDef.name = lexem(); |
|
909 while (test(IDENTIFIER)) { |
|
910 QByteArray l = lexem(); |
|
911 |
|
912 if (l[0] == 'C' && l == "CONSTANT") { |
|
913 propDef.constant = true; |
|
914 continue; |
|
915 } else if(l[0] == 'F' && l == "FINAL") { |
|
916 propDef.final = true; |
|
917 continue; |
|
918 } |
|
919 |
|
920 QByteArray v, v2; |
|
921 if (test(LPAREN)) { |
|
922 v = lexemUntil(RPAREN); |
|
923 } else { |
|
924 next(IDENTIFIER); |
|
925 v = lexem(); |
|
926 if (test(LPAREN)) |
|
927 v2 = lexemUntil(RPAREN); |
|
928 else if (v != "true" && v != "false") |
|
929 v2 = "()"; |
|
930 } |
|
931 switch (l[0]) { |
|
932 case 'R': |
|
933 if (l == "READ") |
|
934 propDef.read = v; |
|
935 else if (l == "RESET") |
|
936 propDef.reset = v + v2; |
|
937 else |
|
938 error(2); |
|
939 break; |
|
940 case 'S': |
|
941 if (l == "SCRIPTABLE") |
|
942 propDef.scriptable = v + v2; |
|
943 else if (l == "STORED") |
|
944 propDef.stored = v + v2; |
|
945 else |
|
946 error(2); |
|
947 break; |
|
948 case 'W': if (l != "WRITE") error(2); |
|
949 propDef.write = v; |
|
950 break; |
|
951 case 'D': if (l != "DESIGNABLE") error(2); |
|
952 propDef.designable = v + v2; |
|
953 break; |
|
954 case 'E': if (l != "EDITABLE") error(2); |
|
955 propDef.editable = v + v2; |
|
956 break; |
|
957 case 'N': if (l != "NOTIFY") error(2); |
|
958 propDef.notify = v; |
|
959 break; |
|
960 case 'U': if (l != "USER") error(2); |
|
961 propDef.user = v + v2; |
|
962 break; |
|
963 default: |
|
964 error(2); |
|
965 } |
|
966 } |
|
967 next(RPAREN); |
|
968 if (propDef.read.isNull()) { |
|
969 QByteArray msg; |
|
970 msg += "Property declaration "; |
|
971 msg += propDef.name; |
|
972 msg += " has no READ accessor function. The property will be invalid."; |
|
973 warning(msg.constData()); |
|
974 } |
|
975 if (propDef.constant && !propDef.write.isNull()) { |
|
976 QByteArray msg; |
|
977 msg += "Property declaration "; |
|
978 msg += propDef.name; |
|
979 msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored."; |
|
980 propDef.constant = false; |
|
981 warning(msg.constData()); |
|
982 } |
|
983 if (propDef.constant && !propDef.notify.isNull()) { |
|
984 QByteArray msg; |
|
985 msg += "Property declaration "; |
|
986 msg += propDef.name; |
|
987 msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored."; |
|
988 propDef.constant = false; |
|
989 warning(msg.constData()); |
|
990 } |
|
991 |
|
992 if(!propDef.notify.isEmpty()) |
|
993 def->notifyableProperties++; |
|
994 |
|
995 def->propertyList += propDef; |
|
996 } |
|
997 |
|
998 void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag) |
|
999 { |
|
1000 next(LPAREN); |
|
1001 QByteArray identifier; |
|
1002 while (test(IDENTIFIER)) { |
|
1003 identifier = lexem(); |
|
1004 while (test(SCOPE) && test(IDENTIFIER)) { |
|
1005 identifier += "::"; |
|
1006 identifier += lexem(); |
|
1007 } |
|
1008 def->enumDeclarations[identifier] = isFlag; |
|
1009 } |
|
1010 next(RPAREN); |
|
1011 } |
|
1012 |
|
1013 void Moc::parseFlag(ClassDef *def) |
|
1014 { |
|
1015 next(LPAREN); |
|
1016 QByteArray flagName, enumName; |
|
1017 while (test(IDENTIFIER)) { |
|
1018 flagName = lexem(); |
|
1019 while (test(SCOPE) && test(IDENTIFIER)) { |
|
1020 flagName += "::"; |
|
1021 flagName += lexem(); |
|
1022 } |
|
1023 } |
|
1024 next(COMMA); |
|
1025 while (test(IDENTIFIER)) { |
|
1026 enumName = lexem(); |
|
1027 while (test(SCOPE) && test(IDENTIFIER)) { |
|
1028 enumName += "::"; |
|
1029 enumName += lexem(); |
|
1030 } |
|
1031 } |
|
1032 |
|
1033 def->flagAliases.insert(enumName, flagName); |
|
1034 next(RPAREN); |
|
1035 } |
|
1036 |
|
1037 void Moc::parseClassInfo(ClassDef *def) |
|
1038 { |
|
1039 next(LPAREN); |
|
1040 ClassInfoDef infoDef; |
|
1041 next(STRING_LITERAL); |
|
1042 infoDef.name = symbol().unquotedLexem(); |
|
1043 next(COMMA); |
|
1044 if (test(STRING_LITERAL)) { |
|
1045 infoDef.value = symbol().unquotedLexem(); |
|
1046 } else { |
|
1047 // support Q_CLASSINFO("help", QT_TR_NOOP("blah")) |
|
1048 next(IDENTIFIER); |
|
1049 next(LPAREN); |
|
1050 next(STRING_LITERAL); |
|
1051 infoDef.value = symbol().unquotedLexem(); |
|
1052 next(RPAREN); |
|
1053 } |
|
1054 next(RPAREN); |
|
1055 def->classInfoList += infoDef; |
|
1056 } |
|
1057 |
|
1058 void Moc::parseInterfaces(ClassDef *def) |
|
1059 { |
|
1060 next(LPAREN); |
|
1061 while (test(IDENTIFIER)) { |
|
1062 QList<ClassDef::Interface> iface; |
|
1063 iface += ClassDef::Interface(lexem()); |
|
1064 while (test(SCOPE)) { |
|
1065 iface.last().className += lexem(); |
|
1066 next(IDENTIFIER); |
|
1067 iface.last().className += lexem(); |
|
1068 } |
|
1069 while (test(COLON)) { |
|
1070 next(IDENTIFIER); |
|
1071 iface += ClassDef::Interface(lexem()); |
|
1072 while (test(SCOPE)) { |
|
1073 iface.last().className += lexem(); |
|
1074 next(IDENTIFIER); |
|
1075 iface.last().className += lexem(); |
|
1076 } |
|
1077 } |
|
1078 // resolve from classnames to interface ids |
|
1079 for (int i = 0; i < iface.count(); ++i) { |
|
1080 const QByteArray iid = interface2IdMap.value(iface.at(i).className); |
|
1081 if (iid.isEmpty()) |
|
1082 error("Undefined interface"); |
|
1083 |
|
1084 iface[i].interfaceId = iid; |
|
1085 } |
|
1086 def->interfaceList += iface; |
|
1087 } |
|
1088 next(RPAREN); |
|
1089 } |
|
1090 |
|
1091 void Moc::parseDeclareInterface() |
|
1092 { |
|
1093 next(LPAREN); |
|
1094 QByteArray interface; |
|
1095 next(IDENTIFIER); |
|
1096 interface += lexem(); |
|
1097 while (test(SCOPE)) { |
|
1098 interface += lexem(); |
|
1099 next(IDENTIFIER); |
|
1100 interface += lexem(); |
|
1101 } |
|
1102 next(COMMA); |
|
1103 QByteArray iid; |
|
1104 if (test(STRING_LITERAL)) { |
|
1105 iid = lexem(); |
|
1106 } else { |
|
1107 next(IDENTIFIER); |
|
1108 iid = lexem(); |
|
1109 } |
|
1110 interface2IdMap.insert(interface, iid); |
|
1111 next(RPAREN); |
|
1112 } |
|
1113 |
|
1114 void Moc::parseDeclareMetatype() |
|
1115 { |
|
1116 next(LPAREN); |
|
1117 QByteArray typeName = lexemUntil(RPAREN); |
|
1118 typeName.remove(0, 1); |
|
1119 typeName.chop(1); |
|
1120 metaTypes.append(typeName); |
|
1121 } |
|
1122 |
|
1123 void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access) |
|
1124 { |
|
1125 next(LPAREN); |
|
1126 FunctionDef funcDef; |
|
1127 next(IDENTIFIER); |
|
1128 funcDef.inPrivateClass = lexem(); |
|
1129 // also allow void functions |
|
1130 if (test(LPAREN)) { |
|
1131 next(RPAREN); |
|
1132 funcDef.inPrivateClass += "()"; |
|
1133 } |
|
1134 next(COMMA); |
|
1135 funcDef.access = access; |
|
1136 parseFunction(&funcDef, true); |
|
1137 def->slotList += funcDef; |
|
1138 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) { |
|
1139 funcDef.wasCloned = true; |
|
1140 funcDef.arguments.removeLast(); |
|
1141 def->slotList += funcDef; |
|
1142 } |
|
1143 } |
|
1144 |
|
1145 QByteArray Moc::lexemUntil(Token target) |
|
1146 { |
|
1147 int from = index; |
|
1148 until(target); |
|
1149 QByteArray s; |
|
1150 while (from <= index) { |
|
1151 QByteArray n = symbols.at(from++-1).lexem(); |
|
1152 if (s.size() && n.size() |
|
1153 && is_ident_char(s.at(s.size()-1)) |
|
1154 && is_ident_char(n.at(0))) |
|
1155 s += ' '; |
|
1156 s += n; |
|
1157 } |
|
1158 return s; |
|
1159 } |
|
1160 |
|
1161 bool Moc::until(Token target) { |
|
1162 int braceCount = 0; |
|
1163 int brackCount = 0; |
|
1164 int parenCount = 0; |
|
1165 int angleCount = 0; |
|
1166 if (index) { |
|
1167 switch(symbols.at(index-1).token) { |
|
1168 case LBRACE: ++braceCount; break; |
|
1169 case LBRACK: ++brackCount; break; |
|
1170 case LPAREN: ++parenCount; break; |
|
1171 case LANGLE: ++angleCount; break; |
|
1172 default: break; |
|
1173 } |
|
1174 } |
|
1175 while (index < symbols.size()) { |
|
1176 Token t = symbols.at(index++).token; |
|
1177 switch (t) { |
|
1178 case LBRACE: ++braceCount; break; |
|
1179 case RBRACE: --braceCount; break; |
|
1180 case LBRACK: ++brackCount; break; |
|
1181 case RBRACK: --brackCount; break; |
|
1182 case LPAREN: ++parenCount; break; |
|
1183 case RPAREN: --parenCount; break; |
|
1184 case LANGLE: ++angleCount; break; |
|
1185 case RANGLE: --angleCount; break; |
|
1186 case GTGT: angleCount -= 2; t = RANGLE; break; |
|
1187 default: break; |
|
1188 } |
|
1189 if (t == target |
|
1190 && braceCount <= 0 |
|
1191 && brackCount <= 0 |
|
1192 && parenCount <= 0 |
|
1193 && (target != RANGLE || angleCount <= 0)) |
|
1194 return true; |
|
1195 |
|
1196 if (braceCount < 0 || brackCount < 0 || parenCount < 0 |
|
1197 || (target == RANGLE && angleCount < 0)) { |
|
1198 --index; |
|
1199 break; |
|
1200 } |
|
1201 } |
|
1202 return false; |
|
1203 } |
|
1204 |
|
1205 void Moc::checkSuperClasses(ClassDef *def) |
|
1206 { |
|
1207 const QByteArray firstSuperclass = def->superclassList.value(0).first; |
|
1208 |
|
1209 if (!knownQObjectClasses.contains(firstSuperclass)) { |
|
1210 // enable once we /require/ include paths |
|
1211 #if 0 |
|
1212 QByteArray msg; |
|
1213 msg += "Class "; |
|
1214 msg += def->className; |
|
1215 msg += " contains the Q_OBJECT macro and inherits from "; |
|
1216 msg += def->superclassList.value(0); |
|
1217 msg += " but that is not a known QObject subclass. You may get compilation errors."; |
|
1218 warning(msg.constData()); |
|
1219 #endif |
|
1220 return; |
|
1221 } |
|
1222 for (int i = 1; i < def->superclassList.count(); ++i) { |
|
1223 const QByteArray superClass = def->superclassList.at(i).first; |
|
1224 if (knownQObjectClasses.contains(superClass)) { |
|
1225 QByteArray msg; |
|
1226 msg += "Class "; |
|
1227 msg += def->classname; |
|
1228 msg += " inherits from two QObject subclasses "; |
|
1229 msg += firstSuperclass; |
|
1230 msg += " and "; |
|
1231 msg += superClass; |
|
1232 msg += ". This is not supported!"; |
|
1233 warning(msg.constData()); |
|
1234 } |
|
1235 |
|
1236 if (interface2IdMap.contains(superClass)) { |
|
1237 bool registeredInterface = false; |
|
1238 for (int i = 0; i < def->interfaceList.count(); ++i) |
|
1239 if (def->interfaceList.at(i).first().className == superClass) { |
|
1240 registeredInterface = true; |
|
1241 break; |
|
1242 } |
|
1243 |
|
1244 if (!registeredInterface) { |
|
1245 QByteArray msg; |
|
1246 msg += "Class "; |
|
1247 msg += def->classname; |
|
1248 msg += " implements the interface "; |
|
1249 msg += superClass; |
|
1250 msg += " but does not list it in Q_INTERFACES. qobject_cast to "; |
|
1251 msg += superClass; |
|
1252 msg += " will not work!"; |
|
1253 warning(msg.constData()); |
|
1254 } |
|
1255 } |
|
1256 } |
|
1257 } |
|
1258 |
|
1259 |
|
1260 QT_END_NAMESPACE |