1 /*
2 * Copyright (c) 2016, Szymon Tomasz Stefanek
3 *
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
6 *
7 * This module contains functions for parsing and scanning C++ source files
8 */
9 #include "cxx_parser.h"
10 #include "cxx_parser_internal.h"
11
12 #include "cxx_debug.h"
13 #include "cxx_keyword.h"
14 #include "cxx_token.h"
15 #include "cxx_token_chain.h"
16 #include "cxx_scope.h"
17 #include "cxx_tag.h"
18 #include "cxx_subparser_internal.h"
19
20 #include "parse.h"
21 #include "vstring.h"
22 #include "../cpreprocessor.h"
23 #include "debug.h"
24 #include "keyword.h"
25 #include "read.h"
26 #include "ptrarray.h"
27 #include "trashbox.h"
28
29 #include <string.h>
30
31 //
32 // The global parser state
33 //
34 CXXParserState g_cxx;
35
36 //
37 // This is set to false once the parser is run at least one time.
38 // Used by cleanup routines.
39 //
40 bool g_bFirstRun = true;
41
42 //
43 // Reset parser state:
44 // - Clear the token chain
45 // - Reset "seen" keywords
46 //
cxxParserNewStatement(void)47 void cxxParserNewStatement(void)
48 {
49 cxxTokenChainClear(g_cxx.pTokenChain);
50 if(g_cxx.pTemplateTokenChain)
51 {
52 cxxTokenChainDestroy(g_cxx.pTemplateTokenChain);
53 g_cxx.pTemplateTokenChain = NULL;
54 g_cxx.oTemplateParameters.uCount = 0;
55 } else {
56 // we don't care about stale specializations as they
57 // are destroyed wen the base template prefix is extracted
58 }
59 g_cxx.uKeywordState = 0;
60
61 // FIXME: this cpp handling of end/statement is kind of broken:
62 // it works only because the moon is in the correct phase.
63 cppEndStatement();
64 }
65
66 //
67 // Parse a subchain of input delimited by matching pairs selected from
68 // [],(),{} and <>.
69 //
70 // On entry g_cxx.pToken is expected to point to the initial token of the pair,
71 // that is one of ([{<. The function will parse input until the matching
72 // terminator token is found. Inner parsing is done by
73 // cxxParserParseAndCondenseSubchainsUpToOneOf() so this is actually a recursive
74 // subchain nesting algorithm.
75 //
76 // Returns true if it has successfully extracted and "condensed" a subchain
77 // replacing the current token with a subchain subtree. Returns false if
78 // extraction fails for some reason.
79 //
80 // This function never leaves the token chain in an incoherent state.
81 // The current token is always replaced with a subchain tree. If the subchain
82 // is broken, its contents are discarded, regardless of the return value.
83 //
cxxParserParseAndCondenseCurrentSubchain(unsigned int uInitialSubchainMarkerTypes,bool bAcceptEOF,bool bCanReduceInnerElements)84 bool cxxParserParseAndCondenseCurrentSubchain(
85 unsigned int uInitialSubchainMarkerTypes,
86 bool bAcceptEOF,
87 bool bCanReduceInnerElements
88 )
89 {
90 CXX_DEBUG_ENTER();
91
92 CXXTokenChain * pCurrentChain = g_cxx.pTokenChain;
93
94 g_cxx.pTokenChain = cxxTokenChainCreate();
95
96 CXXToken * pInitial = cxxTokenChainTakeLast(pCurrentChain);
97 cxxTokenChainAppend(g_cxx.pTokenChain,pInitial);
98
99 CXXToken * pChainToken = cxxTokenCreate();
100
101 pChainToken->iLineNumber = pInitial->iLineNumber;
102 pChainToken->oFilePosition = pInitial->oFilePosition;
103 // see the declaration of CXXTokenType enum.
104 // Shifting by 8 gives the corresponding chain marker
105 pChainToken->eType = (enum CXXTokenType)(g_cxx.pToken->eType << 8);
106 pChainToken->pChain = g_cxx.pTokenChain;
107 cxxTokenChainAppend(pCurrentChain,pChainToken);
108
109 // see the declaration of CXXTokenType enum.
110 // Shifting by 4 gives the corresponding closing token type
111 enum CXXTokenType eTermType = (enum CXXTokenType)(g_cxx.pToken->eType << 4);
112
113 unsigned int uTokenTypes = eTermType;
114 if(bAcceptEOF)
115 uTokenTypes |= CXXTokenTypeEOF;
116
117 bool bRet = cxxParserParseAndCondenseSubchainsUpToOneOf(
118 uTokenTypes,
119 uInitialSubchainMarkerTypes,
120 bCanReduceInnerElements
121 );
122
123 if(
124 // Parsing the subchain failed: input is broken
125 (!bRet) ||
126 // Mismatched terminator (i.e EOF was accepted and encountered)
127 (!cxxTokenTypeIs(cxxTokenChainLast(g_cxx.pTokenChain),eTermType))
128 )
129 {
130 // Input is probably broken: discard it in any case, so no one
131 // is tempted to parse it later.
132
133 CXX_DEBUG_PRINT(
134 "Parsing the subchain failed or EOF found. Discarding broken subtree"
135 );
136
137 while(g_cxx.pTokenChain->iCount > 1)
138 cxxTokenChainDestroyLast(g_cxx.pTokenChain);
139
140 // Fake the terminator
141 CXXToken * pFakeLast = cxxTokenCreate();
142 pFakeLast->iLineNumber = pChainToken->iLineNumber;
143 pFakeLast->oFilePosition = pChainToken->oFilePosition;
144 switch(eTermType)
145 {
146 case CXXTokenTypeClosingBracket:
147 vStringPut(pFakeLast->pszWord,'}');
148 break;
149 case CXXTokenTypeClosingParenthesis:
150 vStringPut(pFakeLast->pszWord,')');
151 break;
152 case CXXTokenTypeClosingSquareParenthesis:
153 vStringPut(pFakeLast->pszWord,']');
154 break;
155 case CXXTokenTypeGreaterThanSign:
156 vStringPut(pFakeLast->pszWord,'>');
157 break;
158 default:
159 CXX_DEBUG_ASSERT(false,"Unhandled terminator type");
160 break;
161 }
162 pFakeLast->eType = eTermType;
163 pFakeLast->pChain = NULL;
164
165 cxxTokenChainAppend(g_cxx.pTokenChain,pFakeLast);
166 }
167
168 g_cxx.pTokenChain = pCurrentChain;
169 g_cxx.pToken = pCurrentChain->pTail;
170
171 CXX_DEBUG_LEAVE();
172 return bRet;
173 }
174
175 //
176 // This function parses input until one of the specified tokens appears.
177 // The current token is NOT checked against the specified tokens.
178 //
179 // The algorithm will also build subchains of matching
180 // pairs ([...],(...),<...>,{...}): within the subchain analysis
181 // of uTokenTypes is completely disabled. Subchains do nest.
182 //
183 // Returns true if it stops before EOF or it stops at EOF and CXXTokenTypeEOF
184 // is present in uTokenTypes. Returns false in all the other stop conditions
185 // and when an unmatched subchain character pair is found (syntax error).
186 //
cxxParserParseAndCondenseSubchainsUpToOneOf(unsigned int uTokenTypes,unsigned int uInitialSubchainMarkerTypes,bool bCanReduceInnerElements)187 bool cxxParserParseAndCondenseSubchainsUpToOneOf(
188 unsigned int uTokenTypes,
189 unsigned int uInitialSubchainMarkerTypes,
190 bool bCanReduceInnerElements
191 )
192 {
193 CXX_DEBUG_ENTER_TEXT("Token types = 0x%x(%s), reduce = %d", uTokenTypes, cxxDebugTypeDecode(uTokenTypes),
194 bCanReduceInnerElements);
195
196 if(!cxxParserParseNextToken())
197 {
198 CXX_DEBUG_LEAVE_TEXT("Found EOF");
199 return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF
200 }
201
202 // see the declaration of CXXTokenType enum.
203 // Shifting by 4 gives the corresponding closing token type
204 unsigned int uFinalSubchainMarkerTypes = uInitialSubchainMarkerTypes << 4;
205
206 for(;;)
207 {
208 //CXX_DEBUG_PRINT(
209 // "Current token is '%s' 0x%x",
210 // vStringValue(g_cxx.pToken->pszWord),
211 // g_cxx.pToken->eType
212 //);
213
214 if(cxxTokenTypeIsOneOf(g_cxx.pToken,uTokenTypes))
215 {
216 if (bCanReduceInnerElements)
217 cxxTokenReduceBackward (g_cxx.pToken);
218 CXX_DEBUG_LEAVE_TEXT(
219 "Got terminator token '%s' 0x%x",
220 vStringValue(g_cxx.pToken->pszWord),
221 g_cxx.pToken->eType
222 );
223 return true;
224 }
225
226 if(cxxTokenTypeIsOneOf(g_cxx.pToken,uInitialSubchainMarkerTypes))
227 {
228 // subchain
229 CXX_DEBUG_PRINT(
230 "Got subchain start token '%s' 0x%x",
231 vStringValue(g_cxx.pToken->pszWord),
232 g_cxx.pToken->eType
233 );
234 CXXToken * pParenthesis;
235
236 if(
237 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket) &&
238 cxxParserCurrentLanguageIsCPP() &&
239 (pParenthesis = cxxParserOpeningBracketIsLambda())
240 )
241 {
242 if(!cxxParserHandleLambda(pParenthesis))
243 {
244 CXX_DEBUG_LEAVE_TEXT("Failed to handle lambda");
245 return false;
246 }
247 } else {
248 g_cxx.iNestingLevels++;
249
250 if(g_cxx.iNestingLevels > CXX_PARSER_MAXIMUM_NESTING_LEVELS)
251 {
252 CXX_DEBUG_LEAVE_TEXT("Nesting level grown too much: something nasty is going on");
253 return false;
254 }
255
256 bool bRet = cxxParserParseAndCondenseCurrentSubchain(
257 uInitialSubchainMarkerTypes,
258 (uTokenTypes & CXXTokenTypeEOF),
259 bCanReduceInnerElements
260 );
261
262 g_cxx.iNestingLevels--;
263
264 if(!bRet)
265 {
266 CXX_DEBUG_LEAVE_TEXT(
267 "Failed to parse subchain of type 0x%x",
268 g_cxx.pToken->eType
269 );
270 return false;
271 }
272 }
273
274 if(cxxTokenTypeIsOneOf(g_cxx.pToken,uTokenTypes))
275 {
276 // was looking for a subchain
277 CXX_DEBUG_LEAVE_TEXT(
278 "Got terminator subchain token 0x%x",
279 g_cxx.pToken->eType
280 );
281 return true;
282 }
283
284 if(!cxxParserParseNextToken())
285 {
286 CXX_DEBUG_LEAVE_TEXT("Found EOF(2)");
287 return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF
288 }
289
290 continue; // jump up to avoid checking for mismatched pairs below
291 }
292
293 // Check for mismatched brackets/parentheses
294 // Note that if we were looking for one of [({ then we would have matched
295 // it at the top of the for
296 if(cxxTokenTypeIsOneOf(g_cxx.pToken,uFinalSubchainMarkerTypes))
297 {
298 CXX_DEBUG_LEAVE_TEXT(
299 "Got mismatched subchain terminator 0x%x",
300 g_cxx.pToken->eType
301 );
302 return false; // unmatched: syntax error
303 }
304
305 if(!cxxParserParseNextToken())
306 {
307 CXX_DEBUG_LEAVE_TEXT("Found EOF(3)");
308 return (uTokenTypes & CXXTokenTypeEOF); // was already at EOF
309 }
310 }
311
312 // not reached
313 CXX_DEBUG_LEAVE_TEXT("Internal error");
314 return false;
315 }
316
317 //
318 // This function parses input until one of the specified tokens appears.
319 // The current token is NOT checked against the specified tokens.
320 //
321 // The algorithm will also build subchains of matching pairs ([...],(...),{...}).
322 // Within the subchain analysis of uTokenTypes is completely disabled.
323 // Subchains do nest.
324 //
325 // Please note that this function will skip entire scopes (matching {} pairs)
326 // unless you pass CXXTokenTypeOpeningBracket to stop at their beginning.
327 // This is usually what you want, unless you're really expecting a scope to begin
328 // in the current statement.
329 //
cxxParserParseUpToOneOf(unsigned int uTokenTypes,bool bCanReduceInnerElements)330 bool cxxParserParseUpToOneOf(unsigned int uTokenTypes,
331 bool bCanReduceInnerElements)
332 {
333 return cxxParserParseAndCondenseSubchainsUpToOneOf(
334 uTokenTypes,
335 CXXTokenTypeOpeningBracket |
336 CXXTokenTypeOpeningParenthesis |
337 CXXTokenTypeOpeningSquareParenthesis,
338 bCanReduceInnerElements
339 );
340 }
341
342 //
343 // Attempts to skip to either a semicolon or an EOF, ignoring anything in between.
344 // May be also used to recovery from certain forms of syntax errors.
345 // This function works also if the current token is a semicolon or an EOF.
346 //
cxxParserSkipToSemicolonOrEOF(void)347 bool cxxParserSkipToSemicolonOrEOF(void)
348 {
349 if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeSemicolon | CXXTokenTypeEOF))
350 return true;
351
352 return cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF,
353 false);
354 }
355
356 // This has to be called when pointing to a double-colon token
357 // or an identifier.
358 //
359 // It tries to parse a qualified name in the form of ...::A::B::C::D ...
360 // and stops at the first token that is not part of such name.
361 //
362 // Returns false if it doesn't find an identifier after a double-colon
363 // or if it finds an EOF. Returns true otherwise.
364 //
365 // Upon exit the token preceding the current is the last identifier
366 // of the qualified name.
cxxParserParseToEndOfQualifedName(void)367 bool cxxParserParseToEndOfQualifedName(void)
368 {
369 CXX_DEBUG_ENTER();
370
371 CXX_DEBUG_ASSERT(
372 cxxTokenTypeIsOneOf(
373 g_cxx.pToken,
374 CXXTokenTypeMultipleColons | CXXTokenTypeIdentifier
375 ),
376 "This function should be called when pointing to a double-colon or an identifier"
377 );
378
379 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeIdentifier))
380 {
381 if(!cxxParserParseNextToken())
382 {
383 // syntax error, but we tolerate this
384 CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken");
385 return false; // EOF
386 }
387 }
388
389 while(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeMultipleColons))
390 {
391 if(!cxxParserParseNextToken())
392 {
393 // syntax error, but we tolerate this
394 CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken");
395 return false; // EOF
396 }
397
398 if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeIdentifier))
399 {
400 CXX_DEBUG_LEAVE_TEXT("Found no identifier after multiple colons");
401 return false;
402 }
403
404 if(!cxxParserParseNextToken())
405 {
406 // syntax error, but we tolerate this
407 CXX_DEBUG_LEAVE_TEXT("EOF in cxxParserParseNextToken");
408 return false; // EOF
409 }
410 }
411
412 CXX_DEBUG_ASSERT(g_cxx.pToken->pPrev,"There should be a previous token here");
413 CXX_DEBUG_ASSERT(
414 cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeIdentifier),
415 "The qualified name should end with an identifier"
416 );
417
418 CXX_DEBUG_LEAVE();
419 return true;
420 }
421
cxxParserSetEndLineForTagInCorkQueue(int iCorkQueueIndex,unsigned long lEndLine)422 void cxxParserSetEndLineForTagInCorkQueue(int iCorkQueueIndex,unsigned long lEndLine)
423 {
424 CXX_DEBUG_ASSERT(iCorkQueueIndex > CORK_NIL,"The cork queue index is not valid");
425
426 tagEntryInfo * tag = getEntryInCorkQueue (iCorkQueueIndex);
427
428 CXX_DEBUG_ASSERT(tag,"No tag entry in the cork queue");
429
430 tag->extensionFields.endLine = lEndLine;
431 }
432
433 //
434 // Attach the current position of input file as "end" field of
435 // the specified tag in the cork queue
436 //
cxxParserMarkEndLineForTagInCorkQueue(int iCorkQueueIndex)437 void cxxParserMarkEndLineForTagInCorkQueue(int iCorkQueueIndex)
438 {
439 cxxParserSetEndLineForTagInCorkQueue(iCorkQueueIndex,getInputLineNumber());
440 }
441
442
443 // Make sure that the token chain contains only the specified keyword and eventually
444 // the "const" or "volatile" type modifiers.
cxxParserCleanupEnumStructClassOrUnionPrefixChain(CXXKeyword eKeyword,CXXToken * pLastToken)445 static void cxxParserCleanupEnumStructClassOrUnionPrefixChain(CXXKeyword eKeyword,CXXToken * pLastToken)
446 {
447 CXXToken * pToken = cxxTokenChainFirst(g_cxx.pTokenChain);
448 while(pToken && (pToken != pLastToken))
449 {
450 if(
451 cxxTokenTypeIs(pToken,CXXTokenTypeKeyword) &&
452 (
453 (pToken->eKeyword == eKeyword) ||
454 (pToken->eKeyword == CXXKeywordCONST) ||
455 (pToken->eKeyword == CXXKeywordVOLATILE)
456 )
457 )
458 {
459 // keep
460 pToken = pToken->pNext;
461 } else {
462 CXXToken * pPrev = pToken->pPrev;
463 if(pPrev)
464 {
465 cxxTokenChainTake(g_cxx.pTokenChain,pToken);
466 cxxTokenDestroy(pToken);
467 pToken = pPrev->pNext;
468 } else {
469 cxxTokenChainDestroyFirst(g_cxx.pTokenChain);
470 pToken = cxxTokenChainFirst(g_cxx.pTokenChain);
471 }
472 }
473 }
474 }
475
476 //
477 // This is called after a full enum/struct/class/union declaration
478 // that ends with a closing bracket.
479 //
cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer(unsigned int uKeywordState,CXXKeyword eTagKeyword,const char * szTypeName)480 static bool cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer(
481 unsigned int uKeywordState,
482 CXXKeyword eTagKeyword,
483 const char * szTypeName
484 )
485 {
486 CXX_DEBUG_ENTER();
487
488 cxxTokenChainClear(g_cxx.pTokenChain);
489
490 CXX_DEBUG_PRINT(
491 "Parse enum/struct/class/union trailer, typename is '%s'",
492 szTypeName
493 );
494
495 MIOPos oFilePosition = getInputFilePosition();
496 int iFileLine = getInputLineNumber();
497
498 if(!cxxParserParseUpToOneOf(
499 CXXTokenTypeEOF | CXXTokenTypeSemicolon |
500 CXXTokenTypeOpeningBracket | CXXTokenTypeAssignment,
501 false
502 ))
503 {
504 CXX_DEBUG_LEAVE_TEXT("Failed to parse up to EOF/semicolon");
505 return false;
506 }
507
508 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF))
509 {
510 // It's a syntax error, but we can be tolerant here.
511 CXX_DEBUG_LEAVE_TEXT("Got EOF after enum/class/struct/union block");
512 return true;
513 }
514
515 if(g_cxx.pTokenChain->iCount < 2)
516 {
517 CXX_DEBUG_LEAVE_TEXT("Nothing interesting after enum/class/struct block");
518 return true;
519 }
520
521 // fake the initial two tokens
522 CXXToken * pIdentifier = cxxTokenCreate();
523 pIdentifier->oFilePosition = oFilePosition;
524 pIdentifier->iLineNumber = iFileLine;
525 pIdentifier->eType = CXXTokenTypeIdentifier;
526 pIdentifier->bFollowedBySpace = true;
527 vStringCatS(pIdentifier->pszWord,szTypeName);
528 cxxTokenChainPrepend(g_cxx.pTokenChain,pIdentifier);
529
530 cxxTokenChainPrepend(
531 g_cxx.pTokenChain,
532 cxxTokenCreateKeyword(iFileLine,oFilePosition,eTagKeyword)
533 );
534
535 if(uKeywordState & CXXParserKeywordStateSeenConst)
536 {
537 cxxTokenChainPrepend(
538 g_cxx.pTokenChain,
539 cxxTokenCreateKeyword(iFileLine,oFilePosition,CXXKeywordCONST)
540 );
541 }
542
543 if(uKeywordState & CXXParserKeywordStateSeenVolatile)
544 {
545 cxxTokenChainPrepend(
546 g_cxx.pTokenChain,
547 cxxTokenCreateKeyword(iFileLine,oFilePosition,CXXKeywordVOLATILE)
548 );
549 }
550
551 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeOpeningBracket))
552 {
553 CXX_DEBUG_PRINT("Found opening bracket: possibly a function declaration?");
554 if(!cxxParserParseBlockHandleOpeningBracket())
555 {
556 CXX_DEBUG_LEAVE_TEXT("Failed to handle the opening bracket");
557 return false;
558 }
559 CXX_DEBUG_LEAVE_TEXT("Opening bracket handled");
560 return true;
561 }
562
563 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeAssignment))
564 {
565 if(!cxxParserParseUpToOneOf(
566 CXXTokenTypeEOF | CXXTokenTypeSemicolon,
567 false
568 ))
569 {
570 CXX_DEBUG_LEAVE_TEXT("Failed to parse up to EOF/semicolon");
571 return false;
572 }
573 }
574
575 if(uKeywordState & CXXParserKeywordStateSeenTypedef)
576 cxxParserExtractTypedef(g_cxx.pTokenChain,true,false);
577 else
578 cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0);
579
580 CXX_DEBUG_LEAVE();
581 return true;
582 }
583
cxxParserParseEnum(void)584 bool cxxParserParseEnum(void)
585 {
586 CXX_DEBUG_ENTER();
587
588 unsigned int uInitialKeywordState = g_cxx.uKeywordState;
589 int iInitialTokenCount = g_cxx.pTokenChain->iCount;
590 CXXToken * pLastToken = cxxTokenChainLast(g_cxx.pTokenChain);
591
592 /*
593 Spec is:
594 enum-key attr(optional) identifier(optional) enum-base(optional)
595 { enumerator-list(optional) } (1)
596 enum-key attr(optional) identifier enum-base(optional) ;
597 (2) (since C++11)
598
599 enum-key - one of enum, enum class(since C++11), or enum struct(since C++11)
600 attr(C++11) - optional sequence of any number of attributes
601 identifier - the name of the enumeration that's being declared.
602 If present, and if this declaration is a re-declaration, it may be preceded by
603 nested-name-specifier(since C++11): sequence of names and scope-resolution
604 operators ::, ending with scope-resolution operator. The name can be omitted
605 only in unscoped enumeration declarations
606
607 enum-base(C++11) - colon (:), followed by a type-specifier-seq that names an
608 integral type (if it is cv-qualified, qualifications are ignored)
609 enumerator-list - comma-separated list of enumerator definitions, each of which is
610 either simply an identifier, which becomes the name of the enumerator, or an
611 identifier with an initializer: identifier = constexpr. In either case, the
612 identifier can be directly followed by an optional attribute specifier
613 sequence. (since C++17)
614 */
615
616 // Skip attr and class-head-name
617 if(!cxxParserParseUpToOneOf(
618 CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeKeyword |
619 CXXTokenTypeSingleColon | CXXTokenTypeParenthesisChain |
620 CXXTokenTypeOpeningBracket,
621 false
622 ))
623 {
624 CXX_DEBUG_LEAVE_TEXT("Could not parse enum name");
625 return false;
626 }
627
628 bool bIsScopedEnum = false; // c++11 scoped enum (enum class | enum struct)
629
630 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeKeyword))
631 {
632 // enum class | enum struct ?
633 if(
634 (g_cxx.pToken->eKeyword == CXXKeywordSTRUCT) ||
635 (g_cxx.pToken->eKeyword == CXXKeywordCLASS)
636 )
637 {
638 bIsScopedEnum = true;
639 }
640
641 if(!cxxParserParseUpToOneOf(
642 CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeSingleColon |
643 CXXTokenTypeParenthesisChain | CXXTokenTypeOpeningBracket,
644 false
645 ))
646 {
647 CXX_DEBUG_LEAVE_TEXT("Could not parse enum name");
648 return false;
649 }
650 }
651
652 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF))
653 {
654 // tolerate EOF, treat as forward declaration
655 cxxParserNewStatement();
656 CXX_DEBUG_LEAVE_TEXT("EOF before enum block: treating as forward declaration");
657 return true;
658 }
659
660 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain))
661 {
662 if(uInitialKeywordState & CXXParserKeywordStateSeenTypedef)
663 {
664 CXX_DEBUG_LEAVE_TEXT("Found parenthesis after typedef: parsing as generic typedef");
665 return cxxParserParseGenericTypedef();
666 }
667 // probably a function declaration/prototype
668 // something like enum x func()....
669 // do not clear statement
670 CXX_DEBUG_LEAVE_TEXT("Probably a function declaration!");
671 return true;
672 }
673
674 // If we have found a semicolon then we might be in the special case of KnR function
675 // declaration. This requires at least 5 tokens and has some additional constraints.
676 // See cxxParserMaybeParseKnRStyleFunctionDefinition() for more informations.
677 if(
678 // found semicolon
679 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon) &&
680 // many tokens before the enum keyword
681 (iInitialTokenCount > 3) &&
682 // C language
683 cxxParserCurrentLanguageIsC() &&
684 // global scope
685 cxxScopeIsGlobal() &&
686 // no typedef
687 (!(uInitialKeywordState & CXXParserKeywordStateSeenTypedef))
688 )
689 {
690 CXX_DEBUG_PRINT("Maybe KnR function definition");
691
692 switch(cxxParserMaybeParseKnRStyleFunctionDefinition())
693 {
694 case 1:
695 // parser moved forward and started a new statement
696 CXX_DEBUG_LEAVE_TEXT("K&R parser did the job");
697 return true;
698 break;
699 case 0:
700 // something else, go ahead
701 break;
702 default:
703 CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition");
704 return false;
705 break;
706 }
707 }
708
709 if(iInitialTokenCount > 1)
710 cxxParserCleanupEnumStructClassOrUnionPrefixChain(CXXKeywordENUM,pLastToken);
711
712 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon))
713 {
714 CXX_DEBUG_PRINT("Found semicolon, maybe typedef or variable declaration");
715
716 // scoped enums can't be used to declare variables.
717 if((!bIsScopedEnum) && (g_cxx.pTokenChain->iCount > 3))
718 {
719 // [typedef] enum X Y; <-- typedef has been removed!
720 if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)
721 cxxParserExtractTypedef(g_cxx.pTokenChain,true,false);
722 else
723 cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0);
724 }
725
726 cxxParserNewStatement();
727 CXX_DEBUG_LEAVE();
728 return true;
729 }
730
731 // colon or opening bracket
732 CXX_DEBUG_ASSERT(
733 cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeSingleColon | CXXTokenTypeOpeningBracket),
734 "We should be pointing to a : or a {"
735 );
736
737 // check if we can extract a class name identifier now
738 CXXToken * pEnumName = cxxTokenChainLastTokenOfType(
739 g_cxx.pTokenChain,
740 CXXTokenTypeIdentifier
741 );
742
743 CXXToken * pTypeBegin; // no need to NULLify, only pTypeEnd matters.
744 CXXToken * pTypeEnd = NULL;
745
746 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSingleColon))
747 {
748 // skip type
749 CXX_DEBUG_PRINT("Single colon, trying to skip type");
750
751 pTypeBegin = g_cxx.pToken;
752
753 if(!cxxParserParseUpToOneOf(
754 CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket,
755 false))
756 {
757 CXX_DEBUG_LEAVE_TEXT("Could not parse enum type");
758 return false;
759 }
760
761 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF))
762 {
763 // tolerate EOF, treat as forward declaration
764 cxxParserNewStatement();
765 CXX_DEBUG_LEAVE_TEXT("EOF before enum block: can't decode this");
766 return true;
767 }
768
769 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon))
770 {
771 bool bMember = (cxxScopeGetVariableKind() == CXXTagKindMEMBER);
772 if (bMember)
773 {
774 // enum type structure member with bit-width:
775 // e.g.
776 // sturct { enum E m: 2; } v;
777 CXX_DEBUG_PRINT("Found semicolon, member definition with bit-width");
778 cxxParserExtractVariableDeclarations(g_cxx.pTokenChain, 0);
779 }
780 cxxParserNewStatement();
781 if (bMember)
782 CXX_DEBUG_LEAVE_TEXT("Semicolon before enum block: can't decode this");
783 else
784 CXX_DEBUG_LEAVE();
785 return true;
786 }
787
788 // certainly opening bracket now.
789 if(g_cxx.pToken->pPrev != pTypeBegin)
790 {
791 // there were tokens between the semicolon and the type begin
792 pTypeBegin = pTypeBegin->pNext;
793 pTypeEnd = g_cxx.pToken->pPrev;
794 }
795 }
796
797
798 int iPushedScopes = 0;
799 bool bAnonymous = false;
800
801 if(pEnumName)
802 {
803 // good.
804 // It may be qualified though.
805 if(cxxParserCurrentLanguageIsCPP())
806 {
807 CXXToken * pNamespaceBegin = pEnumName;
808 CXXToken * pPrev = pEnumName->pPrev;
809 while(pPrev)
810 {
811 if(!cxxTokenTypeIs(pPrev,CXXTokenTypeMultipleColons))
812 break;
813 pPrev = pPrev->pPrev;
814 if(!pPrev)
815 break;
816 if(!cxxTokenTypeIs(pPrev,CXXTokenTypeIdentifier))
817 break;
818 pNamespaceBegin = pPrev;
819 pPrev = pPrev->pPrev;
820 }
821
822 while(pNamespaceBegin != pEnumName)
823 {
824 CXXToken * pNext = pNamespaceBegin->pNext;
825 cxxTokenChainTake(g_cxx.pTokenChain,pNamespaceBegin);
826 if(cxxParserCurrentLanguageIsCPP())
827 {
828 // FIXME: We don't really know if it's a class!
829 cxxScopePush(pNamespaceBegin,CXXScopeTypeClass,CXXScopeAccessUnknown);
830 } else {
831 // it's a syntax error, but be tolerant
832 }
833 iPushedScopes++;
834 pNamespaceBegin = pNext->pNext;
835 }
836 }
837
838 CXX_DEBUG_PRINT("Enum name is %s",vStringValue(pEnumName->pszWord));
839 cxxTokenChainTake(g_cxx.pTokenChain,pEnumName);
840 } else {
841 pEnumName = cxxTokenCreateAnonymousIdentifier(CXXTagKindENUM);
842 bAnonymous = true;
843 CXX_DEBUG_PRINT(
844 "Enum name is %s (anonymous)",
845 vStringValue(pEnumName->pszWord)
846 );
847 }
848
849
850 tagEntryInfo * tag = cxxTagBegin(CXXTagKindENUM,pEnumName);
851
852 int iCorkQueueIndex = CORK_NIL;
853 int iCorkQueueIndexFQ = CORK_NIL;
854
855 if(tag)
856 {
857 // FIXME: this is debatable
858 tag->isFileScope = !isInputHeaderFile();
859
860 if (bAnonymous)
861 markTagExtraBit (tag, XTAG_ANONYMOUS);
862
863 CXXToken * pTypeName = NULL;
864 vString * pszProperties = NULL;
865
866 if(pTypeEnd)
867 {
868 CXX_DEBUG_ASSERT(pTypeBegin,"Type begin should be also set here");
869 pTypeName = cxxTagCheckAndSetTypeField(pTypeBegin,pTypeEnd);
870 }
871
872 if(bIsScopedEnum)
873 pszProperties = cxxTagSetProperties(CXXTagPropertyScopedEnum);
874
875 iCorkQueueIndex = cxxTagCommit(&iCorkQueueIndexFQ);
876
877 if (pszProperties)
878 vStringDelete (pszProperties);
879
880 if(pTypeName)
881 cxxTokenDestroy(pTypeName);
882 }
883
884 cxxScopePush(pEnumName,CXXScopeTypeEnum,CXXScopeAccessPublic);
885 iPushedScopes++;
886
887 vString * pScopeName = cxxScopeGetFullNameAsString();
888
889 // Special kind of block
890 for(;;)
891 {
892 cxxTokenChainClear(g_cxx.pTokenChain);
893
894 if(!cxxParserParseUpToOneOf(
895 CXXTokenTypeComma | CXXTokenTypeClosingBracket | CXXTokenTypeEOF,
896 false
897 ))
898 {
899 CXX_DEBUG_LEAVE_TEXT("Failed to parse enum contents");
900 if(pScopeName)
901 vStringDelete(pScopeName);
902 return false;
903 }
904
905 CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain);
906
907 // enumerator.
908 if(
909 (g_cxx.pTokenChain->iCount > 1) &&
910 cxxTokenTypeIs(pFirst,CXXTokenTypeIdentifier)
911 )
912 {
913 tag = cxxTagBegin(CXXTagKindENUMERATOR,pFirst);
914 if(tag)
915 {
916 tag->isFileScope = !isInputHeaderFile();
917 cxxTagCommit(NULL);
918 }
919 }
920
921 if(cxxTokenTypeIsOneOf(
922 g_cxx.pToken,
923 CXXTokenTypeEOF | CXXTokenTypeClosingBracket
924 ))
925 break;
926 }
927
928 if(iCorkQueueIndex > CORK_NIL)
929 {
930 cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex);
931 if(iCorkQueueIndexFQ > CORK_NIL)
932 cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndexFQ);
933 }
934
935 while(iPushedScopes > 0)
936 {
937 cxxScopePop();
938 iPushedScopes--;
939 }
940
941 bool bRet = cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer(
942 uInitialKeywordState,
943 CXXKeywordENUM,
944 vStringValue(pScopeName)
945 );
946
947 if(pScopeName)
948 vStringDelete(pScopeName);
949
950 cxxParserNewStatement();
951 CXX_DEBUG_LEAVE();
952 return bRet;
953 }
954
cxxParserParseClassStructOrUnionInternal(CXXKeyword eKeyword,unsigned int uTagKind,unsigned int uScopeType)955 static bool cxxParserParseClassStructOrUnionInternal(
956 CXXKeyword eKeyword,
957 unsigned int uTagKind,
958 unsigned int uScopeType
959 )
960 {
961 CXX_DEBUG_ENTER();
962
963 unsigned int uInitialKeywordState = g_cxx.uKeywordState;
964 int iInitialTokenCount = g_cxx.pTokenChain->iCount;
965 CXXToken * pLastToken = cxxTokenChainLast(g_cxx.pTokenChain);
966 bool bAnonymous = false;
967
968 /*
969 Spec is:
970 class-key attr class-head-name base-clause { member-specification }
971
972 class-key - one of class or struct. The keywords are identical
973 except for the default member access and the default base class access.
974 attr(C++11) - optional sequence of any number of attributes,
975 may include alignas specifier
976 class-head-name - the name of the class that's being defined.
977 Optionally qualified, optionally followed by keyword final.
978 The name may be omitted, in which case the class is unnamed (note
979 that unnamed class cannot be final)
980 base-clause - optional list of one or more parent classes and the
981 model of inheritance used for each (see derived class)
982 member-specification - list of access specifiers, member object and
983 member function declarations and definitions (see below)
984 */
985
986 // Skip attr and class-head-name
987
988 // enable "final" keyword handling
989 cxxKeywordEnableFinal(true);
990
991 unsigned int uTerminatorTypes = CXXTokenTypeEOF | CXXTokenTypeSingleColon |
992 CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket |
993 CXXTokenTypeSmallerThanSign | (cxxParserCurrentLanguageIsCPP()? CXXTokenTypeKeyword: 0) |
994 CXXTokenTypeParenthesisChain;
995
996 if(uTagKind != CXXTagCPPKindCLASS)
997 uTerminatorTypes |= CXXTokenTypeAssignment;
998
999 bool bRet;
1000
1001 for(;;)
1002 {
1003 bRet = cxxParserParseUpToOneOf(uTerminatorTypes, false);
1004
1005 if(!bRet)
1006 {
1007 cxxKeywordEnableFinal(false);
1008 CXX_DEBUG_LEAVE_TEXT("Could not parse class/struct/union name");
1009 return false;
1010 }
1011
1012 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeKeyword))
1013 {
1014 /* The statement declears or defines an operator,
1015 * not a class, struct not union. */
1016 if(g_cxx.pToken->eKeyword == CXXKeywordOPERATOR)
1017 return true;
1018 continue;
1019 }
1020
1021 if(
1022 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain) &&
1023 (
1024 (
1025 // struct alignas(n) ...
1026 cxxTokenIsKeyword(g_cxx.pToken->pPrev,CXXKeywordALIGNAS)
1027 ) || (
1028 // things like __builtin_align__(16)
1029 !cxxParserTokenChainLooksLikeFunctionParameterList(g_cxx.pToken->pChain,NULL)
1030 )
1031 )
1032 )
1033 continue;
1034
1035 if(!cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSmallerThanSign))
1036 break;
1037
1038 // Probably a template specialisation
1039 if(!cxxParserCurrentLanguageIsCPP())
1040 {
1041 cxxKeywordEnableFinal(false);
1042 CXX_DEBUG_LEAVE_TEXT("Template specialization in C language?");
1043 return false;
1044 }
1045
1046 // template<typename T> struct X<int>
1047 // {
1048 // }
1049
1050 if(g_cxx.pTemplateSpecializationTokenChain)
1051 cxxTokenChainDestroy(g_cxx.pTemplateSpecializationTokenChain);
1052
1053 g_cxx.pTemplateSpecializationTokenChain = cxxParserParseTemplateAngleBracketsToSeparateChain(false);
1054 if(!g_cxx.pTemplateSpecializationTokenChain)
1055 {
1056 cxxKeywordEnableFinal(false);
1057 CXX_DEBUG_LEAVE_TEXT("Could not parse class/struct/union name");
1058 return false;
1059 }
1060 }
1061
1062 // Once we reached the terminator, "final" is not a keyword anymore.
1063 cxxKeywordEnableFinal(false);
1064
1065 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain))
1066 {
1067 if(uInitialKeywordState & CXXParserKeywordStateSeenTypedef)
1068 {
1069 CXX_DEBUG_LEAVE_TEXT("Found parenthesis after typedef: parsing as generic typedef");
1070 return cxxParserParseGenericTypedef();
1071 }
1072
1073 // probably a function declaration/prototype
1074 // something like struct x * func()....
1075 // do not clear statement
1076 CXX_DEBUG_LEAVE_TEXT("Probably a function declaration!");
1077 return true;
1078 }
1079
1080 // If we have found a semicolon then we might be in the special case of KnR function
1081 // declaration. This requires at least 5 tokens and has some additional constraints.
1082 // See cxxParserMaybeParseKnRStyleFunctionDefinition() for more informations.
1083 // FIXME: This block is duplicated in enum
1084 if(
1085 // found semicolon
1086 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon) &&
1087 // many tokens before the enum keyword
1088 (iInitialTokenCount > 3) &&
1089 // C language
1090 cxxParserCurrentLanguageIsC() &&
1091 // global scope
1092 cxxScopeIsGlobal() &&
1093 // no typedef
1094 (!(uInitialKeywordState & CXXParserKeywordStateSeenTypedef))
1095 )
1096 {
1097 CXX_DEBUG_PRINT("Maybe KnR function definition?");
1098
1099 switch(cxxParserMaybeParseKnRStyleFunctionDefinition())
1100 {
1101 case 1:
1102 // parser moved forward and started a new statement
1103 CXX_DEBUG_LEAVE_TEXT("K&R function definition parser did the job");
1104 return true;
1105 break;
1106 case 0:
1107 // something else, go ahead
1108 break;
1109 default:
1110 CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition");
1111 return false;
1112 break;
1113 }
1114 }
1115
1116 if(iInitialTokenCount > 1)
1117 cxxParserCleanupEnumStructClassOrUnionPrefixChain(eKeyword,pLastToken);
1118
1119 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSemicolon))
1120 {
1121 if(g_cxx.pTokenChain->iCount > 3)
1122 {
1123 // [typedef] struct X Y; <-- typedef has been removed!
1124 if(uInitialKeywordState & CXXParserKeywordStateSeenTypedef)
1125 cxxParserExtractTypedef(g_cxx.pTokenChain,true,false);
1126 else if(!(g_cxx.uKeywordState & CXXParserKeywordStateSeenFriend))
1127 cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0);
1128 }
1129
1130 cxxParserNewStatement();
1131 CXX_DEBUG_LEAVE();
1132 return true;
1133 }
1134
1135 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeAssignment))
1136 {
1137 // struct X Y = ...;
1138 bool bCanExtractVariables = g_cxx.pTokenChain->iCount > 3;
1139
1140 // Skip the initialization (which almost certainly contains a block)
1141 if(!cxxParserParseUpToOneOf(CXXTokenTypeEOF | CXXTokenTypeSemicolon, false))
1142 {
1143 CXX_DEBUG_LEAVE_TEXT("Failed to parse up to EOF/semicolon");
1144 return false;
1145 }
1146
1147 if(bCanExtractVariables)
1148 cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0);
1149
1150 cxxParserNewStatement();
1151 CXX_DEBUG_LEAVE();
1152 return true;
1153 }
1154
1155 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF))
1156 {
1157 // tolerate EOF, just ignore this
1158 cxxParserNewStatement();
1159 CXX_DEBUG_LEAVE_TEXT("EOF: ignoring");
1160 return true;
1161 }
1162
1163 // semicolon or opening bracket
1164
1165 // check if we can extract a class name identifier
1166 CXXToken * pClassName = cxxTokenChainLastTokenOfType(
1167 g_cxx.pTokenChain,
1168 CXXTokenTypeIdentifier
1169 );
1170
1171 // If no identifier has been found we can try some fallbacks.
1172 if(!pClassName)
1173 {
1174 // If we're in C++ mode, but C++ language hasn't been confirmed yet,
1175 // and there is a C++ specific keyword just before the terminator we found
1176 // then we'll try to use it as class/struct/union name.
1177 if(
1178 cxxParserCurrentLanguageIsCPP() &&
1179 (!g_cxx.bConfirmedCPPLanguage) &&
1180 (eKeyword != CXXKeywordCLASS) &&
1181 (g_cxx.pTokenChain->iCount >= 3) &&
1182 cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeKeyword) &&
1183 cxxKeywordIsCPPSpecific(g_cxx.pToken->pPrev->eKeyword)
1184 )
1185 {
1186 pClassName = g_cxx.pToken->pPrev;
1187 pClassName->eType = CXXTokenTypeIdentifier;
1188 CXX_DEBUG_PRINT(
1189 "Found no class/struct/union name identifier but there is '%s' which might look good",
1190 vStringValue(pClassName->pszWord)
1191 );
1192 }
1193 }
1194
1195 int iPushedScopes = 0;
1196
1197 if(pClassName)
1198 {
1199 // good.
1200 // It may be qualified though.
1201 CXXToken * pNamespaceBegin = pClassName;
1202 CXXToken * pPrev = pClassName->pPrev;
1203 while(pPrev)
1204 {
1205 if(!cxxTokenTypeIs(pPrev,CXXTokenTypeMultipleColons))
1206 break;
1207 pPrev = pPrev->pPrev;
1208 if(!pPrev)
1209 break;
1210 if(!cxxTokenTypeIs(pPrev,CXXTokenTypeIdentifier))
1211 break;
1212 pNamespaceBegin = pPrev;
1213 pPrev = pPrev->pPrev;
1214 }
1215
1216 while(pNamespaceBegin != pClassName)
1217 {
1218 CXXToken * pNext = pNamespaceBegin->pNext;
1219 cxxTokenChainTake(g_cxx.pTokenChain,pNamespaceBegin);
1220 if(cxxParserCurrentLanguageIsCPP())
1221 {
1222 // FIXME: We don't really know if it's a class!
1223 cxxScopePush(pNamespaceBegin,CXXScopeTypeClass,CXXScopeAccessUnknown);
1224 iPushedScopes++;
1225 } else {
1226 // it's a syntax error, but be tolerant
1227 cxxTokenDestroy(pNamespaceBegin);
1228 }
1229 pNamespaceBegin = pNext->pNext;
1230 }
1231
1232 CXX_DEBUG_PRINT(
1233 "Class/struct/union name is %s",
1234 vStringValue(pClassName->pszWord)
1235 );
1236 cxxTokenChainTake(g_cxx.pTokenChain,pClassName);
1237 } else {
1238 pClassName = cxxTokenCreateAnonymousIdentifier(uTagKind);
1239 bAnonymous = true;
1240 CXX_DEBUG_PRINT(
1241 "Class/struct/union name is %s (anonymous)",
1242 vStringValue(pClassName->pszWord)
1243 );
1244 }
1245
1246 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSingleColon))
1247 {
1248 // check for base classes
1249 cxxTokenChainClear(g_cxx.pTokenChain);
1250
1251 if(!cxxParserParseUpToOneOf(
1252 CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket,
1253 false
1254 ))
1255 {
1256 cxxTokenDestroy(pClassName);
1257 CXX_DEBUG_LEAVE_TEXT("Failed to parse base class part");
1258 return false;
1259 }
1260
1261 if(cxxTokenTypeIsOneOf(g_cxx.pToken,CXXTokenTypeSemicolon | CXXTokenTypeEOF))
1262 {
1263 cxxTokenDestroy(pClassName);
1264 cxxParserNewStatement();
1265 CXX_DEBUG_LEAVE_TEXT("Syntax error: ignoring");
1266 return true;
1267 }
1268
1269 cxxTokenChainDestroyLast(g_cxx.pTokenChain); // remove the {
1270 } else {
1271 cxxTokenChainClear(g_cxx.pTokenChain);
1272 }
1273
1274 // OK. This seems to be a valid class/struct/union declaration.
1275
1276 if(
1277 (uTagKind == CXXTagCPPKindCLASS) &&
1278 (!g_cxx.bConfirmedCPPLanguage)
1279 )
1280 {
1281 CXX_DEBUG_PRINT("Succeeded in parsing a C++ class: this really seems to be C++");
1282 g_cxx.bConfirmedCPPLanguage = true;
1283 }
1284
1285 tagEntryInfo * tag = cxxTagBegin(uTagKind,pClassName);
1286
1287 int iCorkQueueIndex = CORK_NIL;
1288 int iCorkQueueIndexFQ = CORK_NIL;
1289
1290 bool bGotTemplate = g_cxx.pTemplateTokenChain &&
1291 (g_cxx.pTemplateTokenChain->iCount > 0) &&
1292 cxxParserCurrentLanguageIsCPP();
1293
1294 if(tag)
1295 {
1296 if (bAnonymous)
1297 markTagExtraBit (tag, XTAG_ANONYMOUS);
1298
1299 if(g_cxx.pTokenChain->iCount > 0)
1300 {
1301 // Strip inheritance type information
1302 // FIXME: This could be optional!
1303
1304 CXXToken * t = cxxTokenChainFirst(g_cxx.pTokenChain);
1305 while(t)
1306 {
1307 if(
1308 cxxTokenTypeIs(t,CXXTokenTypeKeyword) &&
1309 (
1310 (t->eKeyword == CXXKeywordPUBLIC) ||
1311 (t->eKeyword == CXXKeywordPROTECTED) ||
1312 (t->eKeyword == CXXKeywordPRIVATE) ||
1313 (t->eKeyword == CXXKeywordVIRTUAL)
1314 )
1315 )
1316 {
1317 CXXToken * pNext = t->pNext;
1318 cxxTokenChainTake(g_cxx.pTokenChain,t);
1319 cxxTokenDestroy(t);
1320 t = pNext;
1321 } else {
1322 t = t->pNext;
1323 }
1324 }
1325
1326 if(g_cxx.pTokenChain->iCount > 0)
1327 {
1328 cxxTokenChainCondense(
1329 g_cxx.pTokenChain,
1330 CXXTokenChainCondenseNoTrailingSpaces
1331 );
1332 tag->extensionFields.inheritance = vStringValue(
1333 g_cxx.pTokenChain->pHead->pszWord
1334 );
1335 }
1336 }
1337
1338 if(bGotTemplate)
1339 cxxTagHandleTemplateFields();
1340
1341 tag->isFileScope = !isInputHeaderFile();
1342
1343 iCorkQueueIndex = cxxTagCommit(&iCorkQueueIndexFQ);
1344
1345 }
1346
1347 cxxScopePush(
1348 pClassName,
1349 uScopeType,
1350 (uTagKind == CXXTagCPPKindCLASS) ?
1351 CXXScopeAccessPrivate : CXXScopeAccessPublic
1352 );
1353
1354 if(
1355 bGotTemplate &&
1356 cxxTagKindEnabled(CXXTagCPPKindTEMPLATEPARAM)
1357 )
1358 cxxParserEmitTemplateParameterTags();
1359
1360 vString * pScopeName = cxxScopeGetFullNameAsString();
1361
1362 if(!cxxParserParseBlock(true))
1363 {
1364 CXX_DEBUG_LEAVE_TEXT("Failed to parse scope");
1365 if(pScopeName)
1366 vStringDelete(pScopeName);
1367 return false;
1368 }
1369
1370 if(iCorkQueueIndex > CORK_NIL)
1371 {
1372 cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndex);
1373 if(iCorkQueueIndexFQ > CORK_NIL)
1374 cxxParserMarkEndLineForTagInCorkQueue(iCorkQueueIndexFQ);
1375 }
1376
1377 iPushedScopes++;
1378 while(iPushedScopes > 0)
1379 {
1380 cxxScopePop();
1381 iPushedScopes--;
1382 }
1383
1384 bRet = cxxParserParseEnumStructClassOrUnionFullDeclarationTrailer(
1385 uInitialKeywordState,
1386 eKeyword,
1387 vStringValue(pScopeName)
1388 );
1389
1390 if(pScopeName)
1391 vStringDelete(pScopeName);
1392
1393 cxxParserNewStatement();
1394 CXX_DEBUG_LEAVE();
1395 return bRet;
1396 }
1397
cxxParserParseClassStructOrUnion(CXXKeyword eKeyword,unsigned int uTagKind,unsigned int uScopeType)1398 bool cxxParserParseClassStructOrUnion(
1399 CXXKeyword eKeyword,
1400 unsigned int uTagKind,
1401 unsigned int uScopeType
1402 )
1403 {
1404 // Trick for "smart" handling of public/protected/private keywords in .h files parsed as C++.
1405 // See the declaration of cxxKeywordEnablePublicProtectedPrivate for more info.
1406
1407 // Enable public/protected/private keywords and save the previous state
1408 bool bEnablePublicProtectedPrivateKeywords = cxxKeywordEnablePublicProtectedPrivate(true);
1409
1410 bool bRet = cxxParserParseClassStructOrUnionInternal(eKeyword,uTagKind,uScopeType);
1411
1412 // If parsing succeeded, we're in C++ mode and the keyword is "class" then
1413 // we're fairly certain that the source code is *really* C++.
1414 if(g_cxx.bConfirmedCPPLanguage)
1415 bEnablePublicProtectedPrivateKeywords = true; // leave it on for good: we're (almost) sure it's C++
1416
1417 cxxKeywordEnablePublicProtectedPrivate(bEnablePublicProtectedPrivateKeywords);
1418
1419 return bRet;
1420 }
1421
1422
1423 //
1424 // This is called at block level, upon encountering a semicolon, an unbalanced
1425 // closing bracket or EOF.The current token is something like:
1426 // static const char * variable;
1427 // int i = ....
1428 // const QString & function(whatever) const;
1429 // QString szText("ascii");
1430 // QString(...)
1431 //
1432 // Notable facts:
1433 // - several special statements never end up here: this includes class,
1434 // struct, union, enum, namespace, typedef, case, try, catch and other
1435 // similar stuff.
1436 // - the terminator is always at the end. It's either a semicolon, a closing
1437 // bracket or an EOF
1438 // - the parentheses and brackets are always condensed in subchains
1439 // (unless unbalanced).
1440 //
1441 // int __attribute__() function();
1442 // | |
1443 // ("whatever") (int var1,type var2)
1444 //
1445 // const char * strings[] = {}
1446 // | |
1447 // [10] { "string","string",.... }
1448 //
1449 // This function tries to extract variable declarations and function prototypes.
1450 //
1451 // Yes, it's complex: it's because C/C++ is complex.
1452 //
cxxParserAnalyzeOtherStatement(void)1453 void cxxParserAnalyzeOtherStatement(void)
1454 {
1455 CXX_DEBUG_ENTER();
1456
1457 #ifdef CXX_DO_DEBUGGING
1458 vString * pChain = cxxTokenChainJoin(g_cxx.pTokenChain,NULL,0);
1459 CXX_DEBUG_PRINT("Analyzing statement '%s'",vStringValue(pChain));
1460 vStringDelete(pChain);
1461 #endif
1462
1463 CXX_DEBUG_ASSERT(
1464 g_cxx.pTokenChain->iCount > 0,
1465 "There should be at least the terminator here!"
1466 );
1467
1468 if(g_cxx.pTokenChain->iCount < 2)
1469 {
1470 CXX_DEBUG_LEAVE_TEXT("Empty statement");
1471 return;
1472 }
1473
1474 if(g_cxx.uKeywordState & CXXParserKeywordStateSeenReturn)
1475 {
1476 CXX_DEBUG_LEAVE_TEXT("Statement after a return is not interesting");
1477 return;
1478 }
1479
1480 // Everything we can make sense of starts with an identifier or keyword.
1481 // This is usually a type name (eventually decorated by some attributes
1482 // and modifiers) with the notable exception of constructor/destructor
1483 // declarations (which are still identifiers tho).
1484
1485 CXXToken * t = cxxTokenChainFirst(g_cxx.pTokenChain);
1486
1487 if(!cxxTokenTypeIsOneOf(t,CXXTokenTypeIdentifier | CXXTokenTypeKeyword))
1488 {
1489 CXX_DEBUG_LEAVE_TEXT("Statement does not start with an identifier or keyword");
1490 return;
1491 }
1492
1493 enum CXXScopeType eScopeType = cxxScopeGetType();
1494
1495 CXXFunctionSignatureInfo oInfo;
1496
1497 // kinda looks like a function or variable instantiation... maybe
1498 if(eScopeType == CXXScopeTypeFunction)
1499 {
1500 // prefer variable declarations.
1501 // if none found then try function prototype
1502 if(cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0))
1503 {
1504 CXX_DEBUG_LEAVE_TEXT("Found variable declarations");
1505 return;
1506 }
1507
1508 // FIXME: This *COULD* work but we should first rule out the possibility
1509 // of simple function calls like func(a). The function signature search
1510 // should be far stricter here.
1511
1512 //if(cxxParserLookForFunctionSignature(g_cxx.pTokenChain,&oInfo,NULL))
1513 // cxxParserEmitFunctionTags(&oInfo,CXXTagKindPROTOTYPE,0);
1514
1515 CXX_DEBUG_LEAVE();
1516 return;
1517 }
1518
1519 // prefer function.
1520 CXXTypedVariableSet oParamInfo;
1521 const bool bPrototypeParams = cxxTagKindEnabled(CXXTagKindPROTOTYPE) && cxxTagKindEnabled(CXXTagKindPARAMETER);
1522 check_function_signature:
1523
1524 if(
1525 cxxParserLookForFunctionSignature(g_cxx.pTokenChain,&oInfo,bPrototypeParams?&oParamInfo:NULL)
1526 // Even if we saw "();", we cannot say it is a function prototype; a macro expansion can be used to
1527 // initialize a top-level variable like:
1528 // #define INIT() 0
1529 // int i = INIT();"
1530 && ! (oInfo.pTypeEnd && cxxTokenTypeIs(oInfo.pTypeEnd,CXXTokenTypeAssignment))
1531 )
1532 {
1533 CXX_DEBUG_PRINT("Found function prototype");
1534
1535 if(g_cxx.uKeywordState & CXXParserKeywordStateSeenFriend)
1536 {
1537 // class X {
1538 // friend void aFunction();
1539 // };
1540 // 'aFunction' is NOT X::aFunction() and in complex cases we can't figure
1541 // out its proper scope. Better avoid emitting this one.
1542 CXX_DEBUG_PRINT("But it has been preceded by the 'friend' keyword: this is not a real prototype");
1543 } else {
1544 int iCorkQueueIndex, iCorkQueueIndexFQ;
1545 int iScopesPushed = cxxParserEmitFunctionTags(&oInfo,CXXTagKindPROTOTYPE,CXXEmitFunctionTagsPushScopes,&iCorkQueueIndex,&iCorkQueueIndexFQ);
1546 if (iCorkQueueIndex != CORK_NIL)
1547 {
1548 CXXToken * t = cxxTokenChainLast(g_cxx.pTokenChain);
1549 cxxParserSetEndLineForTagInCorkQueue (iCorkQueueIndex, t->iLineNumber);
1550 if (iCorkQueueIndexFQ != CORK_NIL)
1551 cxxParserSetEndLineForTagInCorkQueue (iCorkQueueIndexFQ, t->iLineNumber);
1552 }
1553
1554 if(bPrototypeParams)
1555 cxxParserEmitFunctionParameterTags(&oParamInfo);
1556
1557 while(iScopesPushed > 0)
1558 {
1559 cxxScopePop();
1560 iScopesPushed--;
1561 }
1562 }
1563
1564 if(oInfo.pTrailingComma)
1565 {
1566 // got a trailing comma after the function signature.
1567 // This might be a special case of multiple prototypes in a single declaration.
1568 //
1569 // RetType functionA(...), functionB(...), functionC(...);
1570 //
1571 // Let's try to extract also the other declarations.
1572 //
1573 // We cannot rely on oInfo.pIdentifierStart after cxxParserEmitFunctionTags()
1574 // since it has been removed. Manually skip the initial type name.
1575
1576 CXXToken * pBegin = cxxTokenChainFirstTokenNotOfType(
1577 g_cxx.pTokenChain,
1578 CXXTokenTypeIdentifier | CXXTokenTypeKeyword
1579 );
1580
1581 CXX_DEBUG_ASSERT(pBegin,"We should have found a begin token here!");
1582 cxxTokenChainDestroyRange(g_cxx.pTokenChain,pBegin,oInfo.pTrailingComma);
1583 goto check_function_signature;
1584 }
1585
1586 CXX_DEBUG_LEAVE();
1587 return;
1588 }
1589
1590 if(
1591 g_cxx.uKeywordState &
1592 (
1593 // Note that since C++-17 inline can be used as a modifier for variables
1594 // so don't be tempted to put it here.
1595 CXXParserKeywordStateSeenExplicit |
1596 CXXParserKeywordStateSeenOperator | CXXParserKeywordStateSeenVirtual
1597 )
1598 )
1599 {
1600 // must be function!
1601 CXX_DEBUG_LEAVE_TEXT(
1602 "WARNING: Was expecting to find a function prototype " \
1603 "but did not find one"
1604 );
1605 return;
1606 }
1607
1608 cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0);
1609 CXX_DEBUG_LEAVE_TEXT("Nothing else");
1610 }
1611
1612
1613 // This is called when we encounter a "public", "protected" or "private" keyword
1614 // that is NOT in the class declaration header line.
cxxParserParseAccessSpecifier(void)1615 bool cxxParserParseAccessSpecifier(void)
1616 {
1617 CXX_DEBUG_ENTER();
1618
1619 CXX_DEBUG_ASSERT(
1620 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeKeyword) &&
1621 (
1622 (g_cxx.pToken->eKeyword == CXXKeywordPUBLIC) ||
1623 (g_cxx.pToken->eKeyword == CXXKeywordPROTECTED) ||
1624 (g_cxx.pToken->eKeyword == CXXKeywordPRIVATE)
1625 ),
1626 "This must be called just after parsing public/protected/private"
1627 );
1628
1629 unsigned int uExtraType = 0;
1630
1631 enum CXXScopeType eScopeType = cxxScopeGetType();
1632
1633 static ptrArray *pSubparsers;
1634 if (!pSubparsers)
1635 {
1636 pSubparsers = ptrArrayNew(NULL);
1637 DEFAULT_TRASH_BOX (pSubparsers, ptrArrayDelete);
1638 }
1639
1640 if(
1641 (eScopeType != CXXScopeTypeClass) &&
1642 (eScopeType != CXXScopeTypeUnion) &&
1643 (eScopeType != CXXScopeTypeStruct)
1644 )
1645 {
1646 CXX_DEBUG_LEAVE_TEXT(
1647 "Access specified in wrong context (%d)",
1648 eScopeType
1649 );
1650
1651 if(!g_cxx.bConfirmedCPPLanguage)
1652 {
1653 CXX_DEBUG_LEAVE_TEXT("C++ is not confirmed and the scope is not right: likely not access specifier");
1654 g_cxx.pToken->eType = CXXTokenTypeIdentifier;
1655 return true;
1656 }
1657
1658 // this is a syntax error: we're in the wrong scope.
1659 CXX_DEBUG_LEAVE_TEXT("C++ language is confirmed: bailing out to avoid reporting broken structure");
1660 return false;
1661 }
1662
1663 if(!g_cxx.bConfirmedCPPLanguage)
1664 {
1665 if(g_cxx.pToken->pPrev)
1666 {
1667 // ugly, there is something before the public/private/protected keyword.
1668 // This is likely a type or something else.
1669 CXX_DEBUG_LEAVE_TEXT(
1670 "C++ is not confirmed and there is something before: likely not access specifier"
1671 );
1672 g_cxx.pToken->eType = CXXTokenTypeIdentifier;
1673 return true;
1674 }
1675 }
1676
1677 if (cxxSubparserNotifyParseAccessSpecifier (pSubparsers))
1678 uExtraType = CXXTokenTypeIdentifier;
1679
1680 CXXToken * pInitialToken = g_cxx.pToken;
1681
1682 // skip to the next :, without leaving scope.
1683 findColon:
1684 if(!cxxParserParseUpToOneOf(
1685 uExtraType |
1686 CXXTokenTypeSingleColon | CXXTokenTypeSemicolon |
1687 CXXTokenTypeClosingBracket | CXXTokenTypeEOF,
1688 false
1689 ))
1690 {
1691 CXX_DEBUG_LEAVE_TEXT("Failed to parse up to the next ;");
1692 ptrArrayClear (pSubparsers);
1693 return false;
1694 }
1695
1696 if (uExtraType && cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeIdentifier))
1697 {
1698 cxxSubparserNotifyfoundExtraIdentifierAsAccessSpecifier (pSubparsers,
1699 g_cxx.pToken);
1700 goto findColon;
1701 }
1702
1703 if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeSingleColon))
1704 {
1705 if(!pInitialToken->pPrev)
1706 {
1707 CXX_DEBUG_PRINT("The access specifier was the first token and I have found a colon: this is C++");
1708 g_cxx.bConfirmedCPPLanguage = true;
1709 }
1710 }
1711
1712 switch(pInitialToken->eKeyword)
1713 {
1714 case CXXKeywordPUBLIC:
1715 cxxScopeSetAccess(CXXScopeAccessPublic);
1716 break;
1717 case CXXKeywordPRIVATE:
1718 cxxScopeSetAccess(CXXScopeAccessPrivate);
1719 break;
1720 case CXXKeywordPROTECTED:
1721 cxxScopeSetAccess(CXXScopeAccessProtected);
1722 break;
1723 default:
1724 CXX_DEBUG_ASSERT(false,"Bad keyword in cxxParserParseAccessSpecifier!");
1725 break;
1726 }
1727
1728 cxxTokenChainClear(g_cxx.pTokenChain);
1729 ptrArrayClear (pSubparsers);
1730 CXX_DEBUG_LEAVE();
1731 return true;
1732 }
1733
cxxParserParseIfForWhileSwitchCatchParenthesis(void)1734 bool cxxParserParseIfForWhileSwitchCatchParenthesis(void)
1735 {
1736 CXX_DEBUG_ENTER();
1737
1738 CXX_DEBUG_ASSERT(
1739 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeKeyword),
1740 "This function should be called only after encountering one of the keywords"
1741 );
1742
1743 CXXKeyword eKeyword = g_cxx.pToken->eKeyword;
1744
1745 if(!cxxParserParseUpToOneOf(
1746 CXXTokenTypeParenthesisChain | CXXTokenTypeSemicolon |
1747 CXXTokenTypeOpeningBracket | CXXTokenTypeEOF,
1748 false
1749 ))
1750 {
1751 CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch/catch up to parenthesis");
1752 return false;
1753 }
1754
1755 if(cxxTokenTypeIsOneOf(
1756 g_cxx.pToken,
1757 CXXTokenTypeEOF | CXXTokenTypeSemicolon | CXXTokenTypeOpeningBracket
1758 ))
1759 {
1760 CXX_DEBUG_LEAVE_TEXT(
1761 "Found EOF/semicolon/opening bracket while parsing if/for/while/switch/catch"
1762 );
1763 return true;
1764 }
1765
1766 CXX_DEBUG_ASSERT(
1767 cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeParenthesisChain),
1768 "Expected a parenthesis chain here"
1769 );
1770
1771 CXX_DEBUG_PRINT("Found if/for/while/switch/catch parenthesis chain");
1772
1773 // Extract variables from the parenthesis chain
1774
1775 CXXTokenChain * pChain = g_cxx.pToken->pChain;
1776
1777 CXX_DEBUG_ASSERT(
1778 pChain->iCount >= 2,
1779 "The parenthesis chain must have initial and final parenthesis"
1780 );
1781
1782 // There are several constructs that can fool the parser here.
1783 //
1784 // The most frequent problems arise with
1785 //
1786 // if(a & b ...)
1787 // if(a * b ...)
1788 // if(a && b ...)
1789 //
1790 // which may or may not be variable declarations, depending on the
1791 // meaning of identifier a.
1792 // Other problems involve balanced operator that resemble templates:
1793 //
1794 // if(a < b || c > d ...)
1795 //
1796 // Here we attempt to rule out these special cases.
1797
1798 // First try the easy "inclusive" cases.
1799
1800 // catch() always contains variable declarations
1801
1802 bool bOkToExtractVariables = eKeyword == CXXKeywordCATCH;
1803
1804 if(!bOkToExtractVariables)
1805 {
1806 // Another easy one: try parenthesis contents that start with a keyword.
1807 //
1808 // if(const std::exception & e)
1809 // if(int i ...
1810 //
1811 bOkToExtractVariables = cxxTokenTypeIs(
1812 cxxTokenChainAt(pChain,1),
1813 CXXTokenTypeKeyword
1814 );
1815
1816 if(!bOkToExtractVariables)
1817 {
1818 // If there is &, && or * then we expect there to be also a = or
1819 // a semicolon that comes after it.
1820 // This is not 100% foolproof but works most of the times.
1821
1822 CXXToken * pToken = cxxTokenChainFirstTokenOfType(
1823 pChain,
1824 CXXTokenTypeAnd | CXXTokenTypeMultipleAnds | CXXTokenTypeStar |
1825 CXXTokenTypeSmallerThanSign |
1826 CXXTokenTypeAssignment | CXXTokenTypeSemicolon
1827 );
1828
1829 if(pToken)
1830 {
1831 switch(pToken->eType)
1832 {
1833 case CXXTokenTypeAnd:
1834 case CXXTokenTypeMultipleAnds:
1835 case CXXTokenTypeStar:
1836 case CXXTokenTypeSmallerThanSign:
1837 // troublesome cases.
1838 // Require an assignment or a semicolon to follow
1839 bOkToExtractVariables = (cxxTokenChainFirstTokenOfType(
1840 pChain,
1841 CXXTokenTypeAssignment | CXXTokenTypeSemicolon
1842 ) ? true : false); // ternary ?: needed because of MSVC
1843 break;
1844 case CXXTokenTypeAssignment:
1845 case CXXTokenTypeSemicolon:
1846 // looks ok
1847 bOkToExtractVariables = true;
1848 break;
1849 default:
1850 // should NOT happen!
1851 CXX_DEBUG_ASSERT(false,"Unexpected token type");
1852 break;
1853 }
1854 } else {
1855 // looks ok
1856 bOkToExtractVariables = true;
1857 }
1858 }
1859 }
1860
1861 if(bOkToExtractVariables)
1862 {
1863 // Kill the initial parenthesis
1864 cxxTokenChainDestroyFirst(pChain);
1865 // Fake the final semicolon
1866 CXXToken * t = cxxTokenChainLast(pChain);
1867 t->eType = CXXTokenTypeSemicolon;
1868 vStringClear(t->pszWord);
1869 vStringPut(t->pszWord,';');
1870
1871 // and extract variable declarations if possible
1872 cxxParserExtractVariableDeclarations(pChain,0);
1873 }
1874
1875 CXX_DEBUG_LEAVE();
1876 return true;
1877 }
1878
cxxParserMain(const unsigned int passCount)1879 static rescanReason cxxParserMain(const unsigned int passCount)
1880 {
1881 cxxScopeClear();
1882 cxxTokenAPINewFile();
1883 cxxParserNewStatement();
1884
1885 int kind_for_define = CXXTagKindMACRO;
1886 int kind_for_header = CXXTagKindINCLUDE;
1887 int kind_for_macro_param = CXXTagKindMACROPARAM;
1888 int role_for_macro_undef = CR_MACRO_UNDEF;
1889 int role_for_macro_condition = CR_MACRO_CONDITION;
1890 int role_for_header_system = CR_HEADER_SYSTEM;
1891 int role_for_header_local = CR_HEADER_LOCAL;
1892
1893 Assert(passCount < 3);
1894
1895 cppInit(
1896 (bool) (passCount > 1),
1897 false,
1898 true, // raw literals
1899 false,
1900 kind_for_define,
1901 role_for_macro_undef,
1902 role_for_macro_condition,
1903 kind_for_macro_param,
1904 kind_for_header,
1905 role_for_header_system,
1906 role_for_header_local,
1907 g_cxx.pFieldOptions[CXXTagFieldMacrodef].ftype
1908 );
1909
1910 g_cxx.iChar = ' ';
1911
1912 g_cxx.iNestingLevels = 0;
1913
1914 bool bRet = cxxParserParseBlock(false);
1915
1916 cppTerminate ();
1917
1918 // Shut up coveralls: LCOV_EXCL_START
1919 cxxTokenChainClear(g_cxx.pTokenChain);
1920 if(g_cxx.pTemplateTokenChain)
1921 cxxTokenChainClear(g_cxx.pTemplateTokenChain);
1922 if(g_cxx.pTemplateSpecializationTokenChain)
1923 cxxTokenChainClear(g_cxx.pTemplateSpecializationTokenChain);
1924 // Restart coveralls: LCOV_EXCL_END
1925
1926 if(!bRet && (passCount == 1))
1927 {
1928 CXX_DEBUG_PRINT("Processing failed: trying to rescan");
1929 return RESCAN_FAILED;
1930 }
1931
1932 return RESCAN_NONE;
1933 }
1934
cxxCParserMain(const unsigned int passCount)1935 rescanReason cxxCParserMain(const unsigned int passCount)
1936 {
1937 CXX_DEBUG_ENTER();
1938 cxxTagInitForLanguage(g_cxx.eCLangType);
1939
1940 g_cxx.bConfirmedCPPLanguage = false;
1941 cxxKeywordEnablePublicProtectedPrivate(false);
1942
1943 rescanReason r = cxxParserMain(passCount);
1944 CXX_DEBUG_LEAVE();
1945 return r;
1946 }
1947
cxxCUDAParserMain(const unsigned int passCount)1948 rescanReason cxxCUDAParserMain(const unsigned int passCount)
1949 {
1950 CXX_DEBUG_ENTER();
1951 cxxTagInitForLanguage(g_cxx.eCUDALangType);
1952
1953 // CUDA is C.
1954 g_cxx.bConfirmedCPPLanguage = false;
1955 cxxKeywordEnablePublicProtectedPrivate(false);
1956
1957 rescanReason r = cxxParserMain(passCount);
1958 CXX_DEBUG_LEAVE();
1959 return r;
1960 }
1961
cxxCppParserMain(const unsigned int passCount)1962 rescanReason cxxCppParserMain(const unsigned int passCount)
1963 {
1964 CXX_DEBUG_ENTER();
1965 cxxTagInitForLanguage(g_cxx.eCPPLangType);
1966
1967 // In header files we disable processing of public/protected/private keywords
1968 // until we either figure out that this is really C++ or we're start parsing
1969 // a struct/union.
1970 g_cxx.bConfirmedCPPLanguage = !isInputHeaderFile();
1971 cxxKeywordEnablePublicProtectedPrivate(g_cxx.bConfirmedCPPLanguage);
1972
1973 rescanReason r = cxxParserMain(passCount);
1974 CXX_DEBUG_LEAVE();
1975 return r;
1976 }
1977
cxxParserFirstInit(void)1978 static void cxxParserFirstInit(void)
1979 {
1980 memset(&g_cxx,0,sizeof(CXXParserState));
1981
1982 g_cxx.eCLangType = -1;
1983 g_cxx.eCPPLangType = -1;
1984 g_cxx.eCUDALangType = -1;
1985
1986 cxxTokenAPIInit();
1987
1988 g_cxx.pTokenChain = cxxTokenChainCreate();
1989
1990 cxxScopeInit();
1991
1992 g_bFirstRun = false;
1993 }
1994
cxxCUDAParserInitialize(const langType language)1995 void cxxCUDAParserInitialize(const langType language)
1996 {
1997 CXX_DEBUG_PRINT("Parser initialize for language CUDA");
1998 if(g_bFirstRun)
1999 cxxParserFirstInit();
2000
2001 g_cxx.eCUDALangType = language;
2002
2003 cxxBuildKeywordHash(language,CXXLanguageCUDA);
2004 }
2005
cxxCppParserInitialize(const langType language)2006 void cxxCppParserInitialize(const langType language)
2007 {
2008 CXX_DEBUG_PRINT("Parser initialize for language C++");
2009 if(g_bFirstRun)
2010 cxxParserFirstInit();
2011
2012 g_cxx.eCPPLangType = language;
2013
2014 cxxBuildKeywordHash(language,CXXLanguageCPP);
2015 }
2016
cxxCParserInitialize(const langType language)2017 void cxxCParserInitialize(const langType language)
2018 {
2019 CXX_DEBUG_PRINT("Parser initialize for language C");
2020 if(g_bFirstRun)
2021 cxxParserFirstInit();
2022
2023 g_cxx.eCLangType = language;
2024
2025 cxxBuildKeywordHash(language,CXXLanguageC);
2026 }
2027
cxxParserCleanup(langType language CTAGS_ATTR_UNUSED,bool initialized CTAGS_ATTR_UNUSED)2028 void cxxParserCleanup(langType language CTAGS_ATTR_UNUSED,bool initialized CTAGS_ATTR_UNUSED)
2029 {
2030 if(g_bFirstRun)
2031 return; // didn't run at all
2032
2033 // This function is used as finalizer for all the sub-language parsers.
2034 // The next line forces this function to be called only once
2035 g_bFirstRun = true;
2036
2037 // Shut up coveralls: LCOV_EXCL_START
2038 if(g_cxx.pUngetToken)
2039 cxxTokenDestroy(g_cxx.pUngetToken);
2040 if(g_cxx.pTokenChain)
2041 cxxTokenChainDestroy(g_cxx.pTokenChain);
2042 if(g_cxx.pTemplateTokenChain)
2043 cxxTokenChainDestroy(g_cxx.pTemplateTokenChain);
2044 if(g_cxx.pTemplateSpecializationTokenChain)
2045 cxxTokenChainDestroy(g_cxx.pTemplateSpecializationTokenChain);
2046 // Restart coveralls: LCOV_EXCL_END
2047
2048 cxxScopeDone();
2049
2050 cxxTokenAPIDone();
2051 }
2052