1 | /**************************************************************************** |
2 | * Copyright (C) 2013-2016 Woboq GmbH |
3 | * Olivier Goffart <contact at woboq.com> |
4 | * https://woboq.com/ |
5 | * |
6 | * This program is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "propertyparser.h" |
21 | #include <clang/Sema/Lookup.h> |
22 | #include <clang/Sema/SemaDiagnostic.h> |
23 | #include <clang/AST/CanonicalType.h> |
24 | #include <iostream> |
25 | |
26 | std::string PropertyParser::LexemUntil(clang::tok::TokenKind Until, bool Templ) { |
27 | int ParensLevel = 0; |
28 | int BrLevel = 0; |
29 | std::string Result; |
30 | do { |
31 | switch(+CurrentTok.getKind()) { |
32 | case clang::tok::eof: |
33 | return Result; |
34 | case clang::tok::l_square: |
35 | case clang::tok::l_paren: |
36 | case clang::tok::l_brace: |
37 | ++ParensLevel; |
38 | break; |
39 | case clang::tok::r_square: |
40 | case clang::tok::r_paren: |
41 | case clang::tok::r_brace: |
42 | --ParensLevel; |
43 | break; |
44 | case clang::tok::greater: |
45 | if (!ParensLevel) |
46 | BrLevel--; |
47 | break; |
48 | case clang::tok::less: |
49 | if (!ParensLevel && BrLevel >= 0) |
50 | BrLevel++; |
51 | break; |
52 | case clang::tok::greatergreater: |
53 | if (!ParensLevel) |
54 | BrLevel-=2; |
55 | break; |
56 | } |
57 | |
58 | Consume(); |
59 | auto Sp = Spelling(); |
60 | char Last = Result[Result.size()]; |
61 | if ((Last == '<' && Sp[0] == ':') || (IsIdentChar(Last) && IsIdentChar(Sp[0]))) |
62 | Result += " " ; |
63 | Result += Sp; |
64 | } while ((ParensLevel != 0 || !PrevToken.is(Until) || (Templ && BrLevel > 0)) && ParensLevel >= 0); |
65 | return Result; |
66 | } |
67 | |
68 | |
69 | std::string PropertyParser::parseUnsigned() { |
70 | switch(+CurrentTok.getKind()) { |
71 | case clang::tok::kw_int: |
72 | Consume(); |
73 | return "uint" ; |
74 | break; |
75 | case clang::tok::kw_long: |
76 | Consume(); |
77 | if (Test(clang::tok::kw_int)) |
78 | return "unsigned long int" ; |
79 | else if (Test(clang::tok::kw_long)) |
80 | return "unsigned long long" ; |
81 | else |
82 | return "ulong" ; |
83 | break; |
84 | case clang::tok::kw_char: |
85 | case clang::tok::kw_short: |
86 | Consume(); |
87 | if (Test(clang::tok::kw_int)) |
88 | return "unsigned short int" ; |
89 | return "unsigned " + Spelling(); |
90 | break; |
91 | default: |
92 | return "unsigned" ; |
93 | // do not consume; |
94 | } |
95 | } |
96 | |
97 | std::string PropertyParser::parseTemplateType() { |
98 | std::string Result; |
99 | int ParensLevel = 0; |
100 | bool MoveConstToFront = true; |
101 | bool HasConst = false; |
102 | clang::CXXScopeSpec SS; |
103 | do { |
104 | switch(+CurrentTok.getKind()) { |
105 | case clang::tok::eof: |
106 | return {}; |
107 | case clang::tok::greatergreater: |
108 | if (ParensLevel > 0) |
109 | break; |
110 | CurrentTok.setKind(clang::tok::greater); |
111 | PrevToken.setKind(clang::tok::greater); |
112 | if (Result[Result.size()-1] == '>') |
113 | Result += " " ; |
114 | Result += ">" ; |
115 | return Result; |
116 | case clang::tok::greater: |
117 | if (ParensLevel > 0) |
118 | break; |
119 | if (Result[Result.size()-1] == '>') |
120 | Result += " " ; |
121 | Result += ">" ; |
122 | Consume(); |
123 | return Result; |
124 | case clang::tok::less: |
125 | if (ParensLevel > 0 ) |
126 | break; |
127 | Result += "<" ; |
128 | Consume(); |
129 | Result += parseTemplateType(); |
130 | if (!PrevToken.is(clang::tok::greater)) |
131 | return {}; |
132 | MoveConstToFront = false; |
133 | continue; |
134 | case clang::tok::l_square: |
135 | case clang::tok::l_paren: |
136 | case clang::tok::l_brace: |
137 | ++ParensLevel; |
138 | break; |
139 | case clang::tok::r_square: |
140 | case clang::tok::r_paren: |
141 | case clang::tok::r_brace: |
142 | --ParensLevel; |
143 | if (ParensLevel < 0) |
144 | return {}; |
145 | break; |
146 | case clang::tok::comma: |
147 | if (ParensLevel > 0) |
148 | break; |
149 | Result += "," ; |
150 | Consume(); |
151 | return Result + parseTemplateType(); |
152 | |
153 | case clang::tok::kw_const: |
154 | if (MoveConstToFront) { |
155 | HasConst = true; |
156 | continue; |
157 | } |
158 | break; |
159 | case clang::tok::kw_unsigned: |
160 | if (IsIdentChar(Result[Result.size()])) |
161 | Result+=" " ; |
162 | Result += parseUnsigned(); |
163 | continue; |
164 | case clang::tok::amp: |
165 | case clang::tok::ampamp: |
166 | case clang::tok::star: |
167 | MoveConstToFront = false; |
168 | break; |
169 | case clang::tok::identifier: { |
170 | clang::LookupResult Found(Sema, CurrentTok.getIdentifierInfo(), OriginalLocation(CurrentTok.getLocation()), |
171 | clang::Sema::LookupNestedNameSpecifierName); |
172 | Sema.LookupParsedName(Found, Sema.getScopeForContext(RD), &SS); |
173 | clang::CXXRecordDecl* D = Found.getAsSingle<clang::CXXRecordDecl>(); |
174 | if (D && !D->hasDefinition()) |
175 | IsPossiblyForwardDeclared = true; |
176 | Found.suppressDiagnostics(); |
177 | break; |
178 | } |
179 | case clang::tok::coloncolon: |
180 | if (PrevToken.getIdentifierInfo()) |
181 | SS.Extend(Sema.getASTContext(), PrevToken.getIdentifierInfo(), OriginalLocation(), OriginalLocation(CurrentTok.getLocation())); |
182 | break; |
183 | } |
184 | |
185 | Consume(); |
186 | auto Sp = Spelling(); |
187 | char Last = Result[Result.size()]; |
188 | if ((Last == '<' && Sp[0] == ':') || (IsIdentChar(Last) && IsIdentChar(Sp[0]))) |
189 | Result += " " ; |
190 | Result += Sp; |
191 | } while (true); |
192 | if (HasConst) |
193 | Result = "const " + Result; |
194 | return Result; |
195 | } |
196 | |
197 | std::string PropertyParser::parseType(bool SupressDiagnostics) { |
198 | std::string Result; |
199 | bool HasConst = Test(clang::tok::kw_const); |
200 | bool HasVolatile = Test(clang::tok::kw_volatile); |
201 | |
202 | bool NoTemplates = true; |
203 | |
204 | Test(clang::tok::kw_enum) || Test(clang::tok::kw_class) || Test(clang::tok::kw_struct); |
205 | |
206 | if (Test(clang::tok::kw_unsigned)) { |
207 | Result += parseUnsigned(); |
208 | } else if (Test(clang::tok::kw_signed)) { |
209 | Result += "signed" ; |
210 | while (true) { |
211 | switch(+CurrentTok.getKind()) { |
212 | case clang::tok::kw_int: |
213 | case clang::tok::kw_long: |
214 | case clang::tok::kw_short: |
215 | case clang::tok::kw_char: |
216 | Consume(); |
217 | Result += " " + Spelling(); |
218 | continue; |
219 | } |
220 | break; |
221 | } |
222 | } else { |
223 | while(Test(clang::tok::kw_int) |
224 | || Test(clang::tok::kw_long) |
225 | || Test(clang::tok::kw_short) |
226 | || Test(clang::tok::kw_char) |
227 | || Test(clang::tok::kw_void) |
228 | || Test(clang::tok::kw_bool) |
229 | || Test(clang::tok::kw_double) |
230 | || Test(clang::tok::kw_float)) { |
231 | if (!Result.empty()) |
232 | Result += " " ; |
233 | Result += Spelling(); |
234 | } |
235 | } |
236 | |
237 | if (Result.empty()) { |
238 | clang::CXXScopeSpec SS; |
239 | if (Test(clang::tok::coloncolon)) { |
240 | SS.MakeGlobal(Sema.getASTContext(), OriginalLocation()); |
241 | Result += Spelling(); |
242 | } do { |
243 | if (!Test(clang::tok::identifier)) { |
244 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
245 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
246 | "Invalid token while parsing type" )); |
247 | return {}; |
248 | } |
249 | Result += Spelling(); |
250 | |
251 | if (Test(clang::tok::less)) { |
252 | NoTemplates = false; |
253 | Result += "<" ; |
254 | Result += parseTemplateType(); |
255 | |
256 | if (!PrevToken.is(clang::tok::greater)) { |
257 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
258 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
259 | "parse error in type" )); |
260 | return {}; //error; |
261 | } |
262 | } |
263 | |
264 | clang::Token IdentTok = PrevToken; |
265 | |
266 | if (!Test(clang::tok::coloncolon)) |
267 | break; |
268 | |
269 | if (NoTemplates && !SupressDiagnostics) { |
270 | #if CLANG_VERSION_MAJOR >= 4 |
271 | clang::Sema::NestedNameSpecInfo NameInfo(IdentTok.getIdentifierInfo(), |
272 | OriginalLocation(IdentTok.getLocation()), |
273 | OriginalLocation(CurrentTok.getLastLoc())); |
274 | if (Sema.ActOnCXXNestedNameSpecifier(Sema.getScopeForContext(RD), NameInfo, false, SS)) |
275 | #else |
276 | if (Sema.ActOnCXXNestedNameSpecifier(Sema.getScopeForContext(RD), *IdentTok.getIdentifierInfo(), |
277 | OriginalLocation(IdentTok.getLocation()), OriginalLocation(CurrentTok.getLastLoc()), {}, false, SS)) |
278 | #endif |
279 | { |
280 | SS.SetInvalid({OriginalLocation(IdentTok.getLocation()), OriginalLocation(CurrentTok.getLastLoc())}); |
281 | } |
282 | } |
283 | |
284 | Result += Spelling(); |
285 | } while (true); |
286 | |
287 | if (NoTemplates && !SupressDiagnostics) { |
288 | |
289 | IsEnum = true; // That's how moc does it. |
290 | |
291 | if (SS.isNotEmpty() && SS.isValid()) { |
292 | Extra = llvm::dyn_cast_or_null<clang::CXXRecordDecl>(Sema.computeDeclContext(SS)); |
293 | |
294 | clang::LookupResult Found(Sema, PrevToken.getIdentifierInfo(), OriginalLocation(), |
295 | clang::Sema::LookupNestedNameSpecifierName); |
296 | /*if (SS.isEmpty()) |
297 | Sema.LookupQualifiedName(Found, RD); |
298 | else {*/ |
299 | clang::DeclContext* DC = Sema.computeDeclContext(SS); |
300 | Sema.LookupQualifiedName(Found, DC ? DC : RD); |
301 | //} |
302 | clang::EnumDecl* R = Found.getAsSingle<clang::EnumDecl>(); |
303 | /*if (!R) { |
304 | if (clang::TypedefDecl *TD = Found.getAsSingle<clang::TypedefDecl>()) { |
305 | const clang::TemplateSpecializationType* TDR = TD->getUnderlyingType()->getAs<clang::TemplateSpecializationType>(); |
306 | if(TDR && TDR->getNumArgs() == 1 && TDR->getTemplateName().getAsTemplateDecl()->getName() == "QFlags") { |
307 | if (const clang::EnumType* ET = TDR->getArg(0).getAsType()->getAs<clang::EnumType>()) |
308 | R = ET->getDecl(); |
309 | } |
310 | }*/ |
311 | |
312 | /*if (!R) |
313 | IsEnum = false;*/ |
314 | |
315 | if (Extra == RD) Extra = nullptr; |
316 | if(Extra) { |
317 | bool isQObjectOrQGadget = false; |
318 | for (auto it = Extra->decls_begin(); it != Extra->decls_end(); ++it) { |
319 | auto ND = llvm::dyn_cast<clang::NamedDecl>(*it); |
320 | if (ND && ND->getIdentifier() && ND->getName() == "staticMetaObject" ) { |
321 | isQObjectOrQGadget = true; |
322 | break; |
323 | } |
324 | } |
325 | if (!isQObjectOrQGadget) |
326 | Extra = nullptr; |
327 | } |
328 | |
329 | if (!R) { |
330 | clang::CXXRecordDecl* D = Found.getAsSingle<clang::CXXRecordDecl>(); |
331 | if (D && !D->hasDefinition()) |
332 | IsPossiblyForwardDeclared = true; |
333 | } |
334 | } else if (SS.isEmpty()) { |
335 | clang::LookupResult Found(Sema, PrevToken.getIdentifierInfo(), OriginalLocation(), |
336 | clang::Sema::LookupNestedNameSpecifierName); |
337 | Sema.LookupName(Found, Sema.getScopeForContext(RD)); |
338 | clang::CXXRecordDecl* D = Found.getAsSingle<clang::CXXRecordDecl>(); |
339 | if (D && !D->hasDefinition()) { |
340 | IsPossiblyForwardDeclared = true; |
341 | } |
342 | Found.suppressDiagnostics(); |
343 | } |
344 | } |
345 | } |
346 | |
347 | if (NoTemplates && Test(clang::tok::kw_const)) { |
348 | // The official moc don't move the const if there are templates |
349 | HasConst = true; |
350 | } |
351 | |
352 | while (Test(clang::tok::kw_volatile) |
353 | || Test(clang::tok::star) |
354 | || Test(clang::tok::kw_const)) { |
355 | Extra = nullptr; |
356 | IsEnum = false; |
357 | Result += Spelling(); |
358 | } |
359 | |
360 | if (Test(clang::tok::amp)) { |
361 | if (HasConst) |
362 | HasConst = false; // remove const reference |
363 | else |
364 | Result += Spelling(); |
365 | } else { |
366 | Test(clang::tok::ampamp); // skip rvalue ref |
367 | } |
368 | |
369 | |
370 | if (HasVolatile) |
371 | Result = "volatile " + Result; |
372 | if (HasConst) |
373 | Result = "const " + Result; |
374 | |
375 | return Result; |
376 | } |
377 | |
378 | PropertyDef PropertyParser::parseProperty(bool PrivateProperty) { |
379 | PropertyDef Def; |
380 | Consume(); |
381 | std::string type = parseType(false); |
382 | if (type.empty()) { |
383 | //Error |
384 | return Def; |
385 | } |
386 | |
387 | Def.PossiblyForwardDeclared = IsPossiblyForwardDeclared; |
388 | |
389 | // Special logic in moc |
390 | if (type == "QMap" ) |
391 | type = "QMap<QString,QVariant>" ; |
392 | else if (type == "QValueList" ) |
393 | type = "QValueList<QVariant>" ; |
394 | else if (type == "LongLong" ) |
395 | type = "qlonglong" ; |
396 | else if (type == "ULongLong" ) |
397 | type = "qulonglong" ; |
398 | |
399 | Def.type = type; |
400 | |
401 | Def.isEnum = IsEnum; // Well, that's what moc does. |
402 | |
403 | if (!CurrentTok.getIdentifierInfo()) { |
404 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
405 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
406 | "Expected identifier as Q_PROPERTY name" )); |
407 | return Def; |
408 | } |
409 | Consume(); |
410 | |
411 | Def.name = Spelling(); |
412 | |
413 | while(Test(clang::tok::identifier)) { |
414 | std::string l = Spelling(); |
415 | clang::SourceLocation KeywordLocation = OriginalLocation(); |
416 | if (l == "CONSTANT" ) { |
417 | Def.constant = true; |
418 | continue; |
419 | } else if(l == "FINAL" ) { |
420 | Def.final = true; |
421 | continue; |
422 | } else if (l == "REVISION" ) { |
423 | if (!Test(clang::tok::numeric_constant)) { |
424 | PP.getDiagnostics().Report(OriginalLocation(), |
425 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
426 | "Expected numeric constant after REVISION in Q_PROPERTY" )); |
427 | return Def; |
428 | } |
429 | Def.revision = atoi(Spelling().c_str()); |
430 | if (Def.revision < 0) { |
431 | PP.getDiagnostics().Report(OriginalLocation(), |
432 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
433 | "Invalid REVISION number in Q_PROPERTY" )); |
434 | return Def; |
435 | } |
436 | continue; |
437 | } |
438 | |
439 | std::string v, v2; |
440 | bool IsIdent = false; |
441 | clang::SourceLocation ParamLoc = OriginalLocation(CurrentTok.getLocation()); |
442 | if (CurrentTok.getKind() == clang::tok::l_paren) { |
443 | v = LexemUntil(clang::tok::r_paren); |
444 | v = v.substr(1, v.size() - 2); // remove the '(' and ')' |
445 | } else if (Test(clang::tok::identifier)) { |
446 | IsIdent = true; |
447 | v = Spelling(); |
448 | if (CurrentTok.getKind() == clang::tok::l_paren) { |
449 | v2 = LexemUntil(clang::tok::r_paren); |
450 | } else { |
451 | v2 = "()" ; |
452 | } |
453 | } else if(Test(clang::tok::kw_true) || Test(clang::tok::kw_false)) { |
454 | v = Spelling(); |
455 | } else { |
456 | PP.getDiagnostics().Report(OriginalLocation(), |
457 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
458 | "Parse error in Q_PROPERTY: Expected identifier" )); |
459 | return Def; |
460 | } |
461 | |
462 | if (l == "MEMBER" ) |
463 | Def.member = v; |
464 | else if (l == "READ" ) { |
465 | Def.read = v; |
466 | if (IsIdent && !PrivateProperty) { |
467 | clang::LookupResult Found(Sema, PP.getIdentifierInfo(v), ParamLoc, clang::Sema::LookupMemberName); |
468 | Sema.LookupQualifiedName(Found, RD); |
469 | if (Found.empty()) { |
470 | #if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR < 6 |
471 | clang::DeclFilterCCC<clang::CXXMethodDecl> Validator; |
472 | #endif |
473 | if (clang::TypoCorrection Corrected = |
474 | Sema.CorrectTypo(Found.getLookupNameInfo(), clang::Sema::LookupMemberName, |
475 | nullptr, nullptr, |
476 | #if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR < 6 |
477 | Validator, |
478 | #else |
479 | llvm::make_unique<clang::DeclFilterCCC<clang::CXXMethodDecl>>(), |
480 | #endif |
481 | #if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR >= 5 |
482 | clang::Sema::CTK_ErrorRecovery, |
483 | #endif |
484 | RD)) { |
485 | PP.getDiagnostics().Report(Found.getNameLoc(), |
486 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, |
487 | "READ function %0 not found; did you mean %1" )) |
488 | << Found.getLookupName() << Corrected.getCorrection() |
489 | << clang::FixItHint::CreateReplacement(Found.getNameLoc(), |
490 | Corrected.getAsString(PP.getLangOpts())); |
491 | PP.getDiagnostics().Report(Corrected.getCorrectionDecl()->getLocation(), clang::diag::note_previous_decl) |
492 | << Corrected.getCorrection(); |
493 | |
494 | } else { |
495 | PP.getDiagnostics().Report(ParamLoc, |
496 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, |
497 | "READ function %0 not found" )) << Found.getLookupName(); |
498 | } |
499 | } else if (!Found.isAmbiguous()) { |
500 | clang::CXXMethodDecl* M = Found.getAsSingle<clang::CXXMethodDecl>(); |
501 | if (M) { |
502 | #if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR >= 5 |
503 | clang::QualType T = M->getReturnType(); |
504 | #else |
505 | clang::QualType T = M->getResultType(); |
506 | #endif |
507 | if (T->isPointerType() && type.back() != '*') { |
508 | clang::PrintingPolicy PrPo(PP.getLangOpts()); |
509 | PrPo.SuppressTagKeyword = true; |
510 | if (T->getPointeeType().getUnqualifiedType().getAsString(PrPo) == type) |
511 | Def.PointerHack = true; |
512 | } |
513 | } |
514 | } |
515 | Found.suppressDiagnostics(); |
516 | } //FIXME: else |
517 | } else if (l == "RESET" ) |
518 | Def.reset = v + v2; |
519 | else if (l == "SCRIPTABLE" ) |
520 | Def.scriptable = v + v2; |
521 | else if (l == "STORED" ) |
522 | Def.stored = v + v2; |
523 | else if (l == "WRITE" ) |
524 | Def.write = v; |
525 | else if (l == "DESIGNABLE" ) |
526 | Def.designable = v + v2; |
527 | else if (l == "EDITABLE" ) |
528 | Def.editable = v + v2; |
529 | else if (l == "NOTIFY" ) { |
530 | Def.notify.Str = v; |
531 | Def.notify.Loc = ParamLoc; |
532 | } else if (l == "USER" ) |
533 | Def.user = v + v2; |
534 | else { |
535 | PP.getDiagnostics().Report(OriginalLocation(KeywordLocation), |
536 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
537 | "Expected a Q_PROPERTY keyword" )); |
538 | return Def; |
539 | } |
540 | } |
541 | if (!CurrentTok.is(clang::tok::eof)) { |
542 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
543 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
544 | "Expected a Q_PROPERTY keyword" )); |
545 | return Def; |
546 | } |
547 | |
548 | return Def; |
549 | } |
550 | |
551 | |
552 | PrivateSlotDef PropertyParser::parsePrivateSlot() |
553 | { |
554 | PrivateSlotDef Slot; |
555 | Consume(); |
556 | Slot.ReturnType = parseType(); |
557 | |
558 | if (!Test(clang::tok::identifier)) { |
559 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
560 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
561 | "Expected slot name" )); |
562 | return {}; |
563 | } |
564 | |
565 | Slot.Name = Spelling(); |
566 | |
567 | if (!Test(clang::tok::l_paren)) { |
568 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
569 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
570 | "Expected parenthesis in slot signature" )); |
571 | return {}; |
572 | } |
573 | |
574 | do { |
575 | if (CurrentTok.is(clang::tok::eof)) { |
576 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
577 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
578 | "Missing closing parenthesis" )); |
579 | return Slot; |
580 | } |
581 | if (Test(clang::tok::r_paren)) { |
582 | break; |
583 | } |
584 | std::string T = parseType(); |
585 | if (T.empty()) //Error; |
586 | return Slot; |
587 | |
588 | Slot.Args.push_back(std::move(T)); |
589 | |
590 | Test(clang::tok::identifier); |
591 | |
592 | |
593 | if (Test(clang::tok::equal)) { |
594 | Slot.NumDefault++; |
595 | LexemUntil(clang::tok::comma, true); |
596 | if (PrevToken.is(clang::tok::r_paren)) |
597 | break; |
598 | if (!PrevToken.is(clang::tok::comma)) { |
599 | PP.getDiagnostics().Report(OriginalLocation(), |
600 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
601 | "Parse error in default argument" )); |
602 | return Slot; |
603 | } |
604 | continue; |
605 | } else if (Slot.NumDefault) { |
606 | //FIXME: error; |
607 | } |
608 | |
609 | if (Test(clang::tok::comma)) |
610 | continue; |
611 | |
612 | if (Test(clang::tok::r_paren)) { |
613 | break; |
614 | } |
615 | |
616 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
617 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
618 | "Expected comma in slot signature" )); |
619 | return Slot; |
620 | } while (true); |
621 | |
622 | Test(clang::tok::kw_const); |
623 | Test(clang::tok::kw_volatile); |
624 | |
625 | if (!CurrentTok.is(clang::tok::eof)) { |
626 | PP.getDiagnostics().Report(OriginalLocation(CurrentTok.getLocation()), |
627 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
628 | "Unexpected token" )); |
629 | } |
630 | |
631 | return Slot; |
632 | } |
633 | |
634 | |