xref: /Universal-ctags/parsers/cxx/cxx_parser.c (revision 4c6a120c94b6b6ef72bdb3d80a35d8ca5f413b03)
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