Пример #1
0
tagEntryInfo * cxxTagBegin(enum CXXTagKind eKindId,CXXToken * pToken)
{
	if(!g_aCXXKinds[eKindId].enabled)
	{
		//CXX_DEBUG_PRINT("Tag kind %s is not enabled",g_aCXXKinds[eKindId].name);
		return NULL;
	}

	initTagEntry(
			&g_oCXXTag,
			vStringValue(pToken->pszWord),
			&(g_aCXXKinds[eKindId])
		);

	g_oCXXTag.lineNumber = pToken->iLineNumber;
	g_oCXXTag.filePosition = pToken->oFilePosition;
	g_oCXXTag.isFileScope = FALSE;

	if(!cxxScopeIsGlobal())
	{
		g_oCXXTag.extensionFields.scopeKind = &g_aCXXKinds[cxxScopeGetKind()];
		g_oCXXTag.extensionFields.scopeName = cxxScopeGetFullName();
	}

	// FIXME: meaning of "is file scope" is quite debatable...
	g_oCXXTag.extensionFields.access = g_aCXXAccessStrings[cxxScopeGetAccess()];

	return &g_oCXXTag;
}
Пример #2
0
// This is called when we encounter a "public", "protected" or "private" keyword
// that is NOT in the class declaration header line.
boolean cxxParserParseAccessSpecifier(void)
{
	CXX_DEBUG_ENTER();

	enum CXXTagKind eScopeKind = cxxScopeGetKind();

	if(
			(eScopeKind != CXXTagKindCLASS) &&
			(eScopeKind != CXXTagKindSTRUCT) &&
			(eScopeKind != CXXTagKindUNION)
		)
	{
		// this is a syntax error: we're in the wrong scope.
		CXX_DEBUG_LEAVE_TEXT(
				"Access specified in wrong context (%d): "
					"bailing out to avoid reporting broken structure",
				eScopeKind
			);
		return FALSE;
	}

	switch(g_cxx.pToken->eKeyword)
	{
		case CXXKeywordPUBLIC:
			cxxScopeSetAccess(CXXScopeAccessPublic);
		break;
		case CXXKeywordPRIVATE:
			cxxScopeSetAccess(CXXScopeAccessPrivate);
		break;
		case CXXKeywordPROTECTED:
			cxxScopeSetAccess(CXXScopeAccessProtected);
		break;
		default:
			CXX_DEBUG_ASSERT(FALSE,"Bad keyword in cxxParserParseAccessSpecifier!");
		break;
	}

	// skip to the next :, without leaving scope.
	if(!cxxParserParseUpToOneOf(
			CXXTokenTypeSingleColon | CXXTokenTypeSemicolon |
				CXXTokenTypeClosingBracket | CXXTokenTypeEOF
		))
	{
		CXX_DEBUG_LEAVE_TEXT("Failed to parse up to the next ;");
		return FALSE;
	}

	cxxTokenChainClear(g_cxx.pTokenChain);
	CXX_DEBUG_LEAVE();
	return TRUE;
}
Пример #3
0
static void qtMocMakeTagForProperty (CXXToken * pToken, const char *pszType)
{
	tagEntryInfo tag;

	initTagEntry(&tag,
				 vStringValue(pToken->pszWord),
				 K_PROPERTY);
	tag.lineNumber = pToken->iLineNumber;
	tag.filePosition = pToken->oFilePosition;
	tag.isFileScope = false;

	if(!cxxScopeIsGlobal())
	{
		tag.extensionFields.scopeLangType = getNamedLanguage ("C++", 0); /* ??? */
		tag.extensionFields.scopeKindIndex = cxxScopeGetKind();
		tag.extensionFields.scopeName = cxxScopeGetFullName();
	}

	tag.extensionFields.typeRef[0] = "typename";
	tag.extensionFields.typeRef[1] = pszType;

	makeTagEntry(&tag);
}
Пример #4
0
//
// This is called at block level, upon encountering a semicolon, an unbalanced
// closing bracket or EOF.The current token is something like:
//   static const char * variable;
//   int i = ....
//   const QString & function(whatever) const;
//   QString szText("ascii");
//   QString(...)
//
// Notable facts:
//   - several special statements never end up here: this includes class,
//     struct, union, enum, namespace, typedef, case, try, catch and other
//     similar stuff.
//   - the terminator is always at the end. It's either a semicolon, a closing
//     bracket or an EOF
//   - the parentheses and brackets are always condensed in subchains
//     (unless unbalanced).
//
//                int __attribute__() function();
//                                  |          |
//                             ("whatever")  (int var1,type var2)
//
//                const char * strings[] = {}
//                                    |     |
//                                   [10]  { "string","string",.... }
//
// This function tries to extract variable declarations and function prototypes.
//
// Yes, it's complex: it's because C/C++ is complex.
//
void cxxParserAnalyzeOtherStatement(void)
{
	CXX_DEBUG_ENTER();

#ifdef CXX_DO_DEBUGGING
	vString * pChain = cxxTokenChainJoin(g_cxx.pTokenChain,NULL,0);
	CXX_DEBUG_PRINT("Analyzing statement '%s'",vStringValue(pChain));
	vStringDelete(pChain);
#endif

	CXX_DEBUG_ASSERT(
			g_cxx.pTokenChain->iCount > 0,
			"There should be at least the terminator here!"
		);

	if(g_cxx.pTokenChain->iCount < 2)
	{
		CXX_DEBUG_LEAVE_TEXT("Empty statement");
		return;
	}

	if(g_cxx.uKeywordState & CXXParserKeywordStateSeenReturn)
	{
		CXX_DEBUG_LEAVE_TEXT("Statement after a return is not interesting");
		return;
	}

	// Everything we can make sense of starts with an identifier or keyword.
	// This is usually a type name (eventually decorated by some attributes
	// and modifiers) with the notable exception of constructor/destructor
	// declarations (which are still identifiers tho).

	CXXToken * t = cxxTokenChainFirst(g_cxx.pTokenChain);

	if(!cxxTokenTypeIsOneOf(t,CXXTokenTypeIdentifier | CXXTokenTypeKeyword))
	{
		CXX_DEBUG_LEAVE_TEXT("Statement does not start with an identifier or keyword");
		return;
	}

	enum CXXTagKind eScopeKind = cxxScopeGetKind();

	CXXFunctionSignatureInfo oInfo;

	// kinda looks like a function or variable instantiation... maybe
	if(eScopeKind == CXXTagKindFUNCTION)
	{
		// prefer variable declarations.
		// if none found then try function prototype
		if(cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0))
		{
			CXX_DEBUG_LEAVE_TEXT("Found variable declarations");
			return;
		}

		// FIXME: This *COULD* work but we should first rule out the possibility
		// of simple function calls like func(a). The function signature search
		// should be far stricter here.

		//if(cxxParserLookForFunctionSignature(g_cxx.pTokenChain,&oInfo,NULL))
		//	cxxParserEmitFunctionTags(&oInfo,CXXTagKindPROTOTYPE,0);

		CXX_DEBUG_LEAVE();
		return;
	}

	// prefer function.
	if(cxxParserLookForFunctionSignature(g_cxx.pTokenChain,&oInfo,NULL))
	{
		int iScopesPushed = cxxParserEmitFunctionTags(&oInfo,CXXTagKindPROTOTYPE,CXXEmitFunctionTagsPushScopes,NULL);
		while(iScopesPushed > 0)
		{
			cxxScopePop();
			iScopesPushed--;
		}
		CXX_DEBUG_LEAVE_TEXT("Found function prototypes");
		return;
	}

	if(
		g_cxx.uKeywordState &
		(
			CXXParserKeywordStateSeenInline | CXXParserKeywordStateSeenExplicit |
			CXXParserKeywordStateSeenOperator | CXXParserKeywordStateSeenVirtual
		)
	)
	{
		// must be function!
		CXX_DEBUG_LEAVE_TEXT(
				"WARNING: Was expecting to find a function prototype " \
					"but did not find one"
			);
		return;
	}

	cxxParserExtractVariableDeclarations(g_cxx.pTokenChain,0);
	CXX_DEBUG_LEAVE_TEXT("Nothing else");
}
Пример #5
0
static boolean cxxParserParseBlockHandleOpeningBracket(void)
{
	CXX_DEBUG_ENTER();

	CXX_DEBUG_ASSERT(g_cxx.pToken->eType == CXXTokenTypeOpeningBracket,"This must be called when pointing at an opening bracket!");

	enum CXXTagKind eScopeKind = cxxScopeGetKind();

	if(
			(g_cxx.pToken->pPrev) &&
			cxxTokenTypeIs(g_cxx.pToken->pPrev,CXXTokenTypeAssignment) &&
			(
				(eScopeKind == CXXTagKindFUNCTION) ||
				(eScopeKind == CXXTagKindNAMESPACE)
			)
		)
	{
		// array or C++11-list-like initialisation
		boolean bRet = cxxParserParseAndCondenseCurrentSubchain(
				CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis,
				FALSE
			);

		CXX_DEBUG_LEAVE_TEXT("Handled array or C++11-list-like initialisation");
		return bRet;
	}

	int iScopes;
	if(eScopeKind != CXXTagKindFUNCTION)
	{
		// very likely a function definition
		iScopes = cxxParserExtractFunctionSignatureBeforeOpeningBracket();
	} else {
		// some kind of other block:
		// - anonymous block
		// - block after for(),while(),foreach(),if() and other similar stuff
		// - lambda

		// check for lambdas
		CXXToken * pParenthesis;

		if(
			cxxParserCurrentLanguageIsCPP() &&
			(pParenthesis = cxxParserOpeningBracketIsLambda())
		)
		{
			CXX_DEBUG_LEAVE_TEXT("Will handle lambda");
			return cxxParserHandleLambda(pParenthesis);
		}

		iScopes = 0;
	}

	cxxParserNewStatement();

	if(!cxxParserParseBlock(TRUE))
	{
		CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block");
		return FALSE;
	}

	while(iScopes > 0)
	{
		cxxScopePop();
		iScopes--;
	}

	CXX_DEBUG_LEAVE();
	return TRUE;
}
Пример #6
0
//
// This is the toplevel scanning function. It's a forward-only scanner that keeps
// accumulating tokens in the chain until either a characteristic token is found
// or the statement ends. When a characteristic token is found it usually enters
// a specialized scanning routine (e.g for classes, namespaces, structs...).
// When the statement ends without finding any characteristic token the chain
// is passed to an analysis routine which does a second scan pass.
//
boolean cxxParserParseBlock(boolean bExpectClosingBracket)
{
	CXX_DEBUG_ENTER();

	//char * szScopeName = cxxScopeGetFullName();
	//CXX_DEBUG_PRINT("Scope name is '%s'",szScopeName ? szScopeName : "");

	cxxParserNewStatement();

	if(bExpectClosingBracket)
		cppBeginStatement(); // FIXME: this cpp handling is broken: it works only because the moon is in the correct phase.

	for(;;)
	{
		if(!cxxParserParseNextToken())
		{
			if(bExpectClosingBracket)
			{
				CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!");
				return FALSE;
			}
			CXX_DEBUG_LEAVE_TEXT("EOF in main block");
			return TRUE; // EOF
		}

		CXX_DEBUG_PRINT("Token '%s' of type 0x%02x",vStringValue(g_cxx.pToken->pszWord),g_cxx.pToken->eType);

		switch(g_cxx.pToken->eType)
		{
			case CXXTokenTypeKeyword:
			{
				switch(g_cxx.pToken->eKeyword)
				{
					case CXXKeywordNAMESPACE:
					{
						int iCurrentScopeKind = cxxScopeGetKind();
						if(iCurrentScopeKind == CXXTagKindNAMESPACE)
						{
							// namespaces can be nested only within themselves
							if(!cxxParserParseNamespace())
							{
								CXX_DEBUG_LEAVE_TEXT("Failed to parse namespace");
								return FALSE;
							}
						} else {
							// hm... syntax error?
							CXX_DEBUG_LEAVE_TEXT("Found namespace in a wrong place: we're probably out of sync");
							return FALSE;
						}

						//cxxParserNewStatement(); <-- already called by cxxParserParseNamespace()
					}
					break;
					case CXXKeywordTEMPLATE:
						if(!cxxParserParseTemplatePrefix())
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse template");
							return FALSE;
						}
						// Here we are just after the "template<parameters>" prefix.
					break;
					case CXXKeywordTYPEDEF:
						// Mark the next declaration as a typedef
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenTypedef;
						cxxTokenChainClear(g_cxx.pTokenChain);
					break;
					case CXXKeywordENUM:
						if(!cxxParserParseEnum())
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse enum");
							return FALSE;
						}
					break;
					case CXXKeywordCLASS:
						if(!cxxParserParseClassStructOrUnion(CXXKeywordCLASS,CXXTagKindCLASS))
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union");
							return FALSE;
						}
					break;
					case CXXKeywordSTRUCT:
						if(!cxxParserParseClassStructOrUnion(CXXKeywordSTRUCT,CXXTagKindSTRUCT))
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union");
							return FALSE;
						}
					break;
					case CXXKeywordUNION:
						if(!cxxParserParseClassStructOrUnion(CXXKeywordUNION,CXXTagKindUNION))
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse class/struct/union");
							return FALSE;
						}
					break;
					case CXXKeywordPUBLIC:
					case CXXKeywordPROTECTED:
					case CXXKeywordPRIVATE:
						// Note that the class keyword has its own handler so the only possibility here is an access specifier
						if(!cxxParserParseAccessSpecifier())
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse access specifier");
							return FALSE;
						}
					break;
					case CXXKeywordUSING:
						if(!cxxParserParseUsingClause())
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse using clause");
							return FALSE;
						}
						cxxParserNewStatement();
					break;
					case CXXKeywordIF:
					case CXXKeywordFOR:
					case CXXKeywordWHILE:
					case CXXKeywordSWITCH:
						if(!cxxParserParseIfForWhileSwitch())
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse if/for/while/switch");
							return FALSE;
						}
						cxxParserNewStatement();
					break;
					case CXXKeywordTRY:
					case CXXKeywordELSE:
					case CXXKeywordDO:
						// parse as normal statement/block
						cxxParserNewStatement();
					break;
					case CXXKeywordRETURN:
						if(cxxParserCurrentLanguageIsCPP())
						{
							// may be followed by a lambda, otherwise it's not interesting.
							g_cxx.uKeywordState |= CXXParserKeywordStateSeenReturn;
							cxxParserNewStatement();
						} else {
							// ignore
							if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF))
							{
								CXX_DEBUG_LEAVE_TEXT("Failed to parse return");
								return FALSE;
							}
							cxxParserNewStatement();
						}
					break;
					case CXXKeywordCONTINUE:
					case CXXKeywordBREAK:
					case CXXKeywordGOTO:
						// ignore
						if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF))
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse continue/break/goto");
							return FALSE;
						}
						cxxParserNewStatement();
					break;
					case CXXKeywordTHROW:
						// ignore when inside a function
						if(cxxScopeGetKind() == CXXTagKindFUNCTION)
						{
							if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF))
							{
								CXX_DEBUG_LEAVE_TEXT("Failed to parse return/continue/break");
								return FALSE;
							}
							cxxParserNewStatement();
						}
					break;
					break;
					case CXXKeywordCASE:
						// ignore
						if(!cxxParserParseUpToOneOf(CXXTokenTypeSemicolon | CXXTokenTypeEOF | CXXTokenTypeSingleColon))
						{
							CXX_DEBUG_LEAVE_TEXT("Failed to parse case keyword");
							return FALSE;
						}
						cxxParserNewStatement();
					break;
					case CXXKeywordEXTERN:
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenExtern;
						cxxTokenChainClear(g_cxx.pTokenChain);
					break;
					case CXXKeywordSTATIC:
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenStatic;
						cxxTokenChainClear(g_cxx.pTokenChain);
					break;
					case CXXKeywordINLINE:
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenInline;
						cxxTokenChainClear(g_cxx.pTokenChain);
					break;
					case CXXKeywordEXPLICIT:
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenExplicit;
						cxxTokenChainClear(g_cxx.pTokenChain);
					break;
					case CXXKeywordOPERATOR:
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenOperator;
					break;
					case CXXKeywordVIRTUAL:
						g_cxx.uKeywordState |= CXXParserKeywordStateSeenVirtual;
					break;
					default:
						if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)
						{
							g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef;
							if(!cxxParserParseGenericTypedef())
							{
								CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef");
								return FALSE;
							}
							cxxParserNewStatement();
						}
					break;
				}
			}
			break;
			case CXXTokenTypeSemicolon:
			{
				if(
						(g_cxx.eLanguage == g_cxx.eCLanguage) &&
						cxxScopeIsGlobal() &&
						(!(g_cxx.uKeywordState & CXXParserKeywordStateSeenExtern)) &&
						(!(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef))
					)
				{
					// Special handling of K&R style function declarations.
					// We might be in the following situation:
					//
					//  type whatever fname(par1,par2) int par1; int par2; {
					//                                        ^
					//
					switch(cxxParserMaybeExtractKnRStyleFunctionDefinition())
					{
						case 1:
							// got K&R style function definition, one scope was pushed.
							cxxParserNewStatement();
							if(!cxxParserParseBlock(TRUE))
							{
								CXX_DEBUG_LEAVE_TEXT("Failed to parse nested block");
								return FALSE;
							}
							cxxScopePop();
						break;
						case 0:
							// something else
							cxxParserAnalyzeOtherStatement();
						break;
						default:
							CXX_DEBUG_LEAVE_TEXT("Failed to check for K&R style function definition");
							return FALSE;
						break;
					}
				} else {
					// K&R style function declarations not allowed here.
					cxxParserAnalyzeOtherStatement();
				}
				cxxParserNewStatement();
			}
			break;
			case CXXTokenTypeSingleColon:
			{
				// label ?
				if((g_cxx.pTokenChain->iCount == 2) && cxxTokenTypeIs(cxxTokenChainFirst(g_cxx.pTokenChain),CXXTokenTypeIdentifier))
				{
					CXXToken * pFirst = cxxTokenChainFirst(g_cxx.pTokenChain);
					// assume it's label
					tagEntryInfo * tag = cxxTagBegin(CXXTagKindLABEL,pFirst);

					if(tag)
					{
						tag->isFileScope = TRUE;
						cxxTagCommit();
					}
				} else {
					// what is this? (default: and similar things have been handled at keyword level)
				}
			}
			break;
			case CXXTokenTypeOpeningBracket:
				if(!cxxParserParseBlockHandleOpeningBracket())
				{
					CXX_DEBUG_LEAVE_TEXT("Failed to handle opening bracket");
					return FALSE;
				}
			break;
			case CXXTokenTypeClosingBracket:
				// scope finished
				CXX_DEBUG_LEAVE_TEXT("Closing bracket!");
				cxxParserNewStatement();
				return TRUE;
			break;
			case CXXTokenTypeOpeningParenthesis:
			case CXXTokenTypeOpeningSquareParenthesis:
				if(!cxxParserParseAndCondenseCurrentSubchain(
						CXXTokenTypeOpeningBracket | CXXTokenTypeOpeningParenthesis | CXXTokenTypeOpeningSquareParenthesis,
						TRUE
					))
				{
					CXX_DEBUG_LEAVE_TEXT("Parsing the parenthesis failed");
					return FALSE;
				}

				if(cxxTokenTypeIs(g_cxx.pToken,CXXTokenTypeEOF))
				{
					if(bExpectClosingBracket)
					{
						CXX_DEBUG_LEAVE_TEXT("Syntax error: found EOF in block but a closing bracket was expected!");
						return FALSE;
					}
					return TRUE; // EOF
				}
			break;
			case CXXTokenTypeIdentifier:
				if(g_cxx.uKeywordState & CXXParserKeywordStateSeenTypedef)
				{
					g_cxx.uKeywordState &= ~CXXParserKeywordStateSeenTypedef;
					if(!cxxParserParseGenericTypedef())
					{
						CXX_DEBUG_LEAVE_TEXT("Failed to parse generic typedef");
						return FALSE;
					}
					cxxParserNewStatement();
				}
			break;
			default:
				// something else we didn't handle
			break;
		}
	}

	CXX_DEBUG_LEAVE_TEXT("WARNING: Not reached");
	return TRUE;
}
Пример #7
0
void cxxTagCommit(void)
{
	if(g_oCXXTag.isFileScope)
	{
		if (isXtagEnabled(XTAG_FILE_SCOPE))
			markTagExtraBit (&g_oCXXTag, XTAG_FILE_SCOPE);
		else
			return;
	}

	CXX_DEBUG_PRINT(
			"Emitting tag for symbol '%s', kind '%s', line %d",
			g_oCXXTag.name,
			g_oCXXTag.kind->name,
			g_oCXXTag.lineNumber
		);
	if(
			g_oCXXTag.extensionFields.typeRef[0] &&
			g_oCXXTag.extensionFields.typeRef[1]
		)
		CXX_DEBUG_PRINT(
				"Tag has typeref %s %s",
				g_oCXXTag.extensionFields.typeRef[0],
				g_oCXXTag.extensionFields.typeRef[1]
			);

	makeTagEntry(&g_oCXXTag);

	// Handle --extra=+q
	if(!isXtagEnabled(XTAG_QUALIFIED_TAGS))
		return;
	else
		markTagExtraBit (&g_oCXXTag, XTAG_QUALIFIED_TAGS);

	if(!g_oCXXTag.extensionFields.scopeName)
		return;

	// WARNING: The following code assumes that the scope
	// didn't change between cxxTagBegin() and cxxTagCommit().

	enum CXXTagKind eScopeKind = cxxScopeGetKind();

	if(eScopeKind == CXXTagKindFUNCTION)
	{
		// old ctags didn't do this, and --extra=+q is mainly
		// for backward compatibility so...
		return;
	}

	// Same tag. Only the name changes.

	vString * x;

	if(eScopeKind == CXXTagKindENUM)
	{
		// If the scope kind is enumeration then we need to remove the
		// last scope part. This is what old ctags did.
		if(cxxScopeGetSize() < 2)
			return; // toplevel enum

		x = cxxScopeGetFullNameExceptLastComponentAsString();
		CXX_DEBUG_ASSERT(x,"Scope with size >= 2 should have returned a value here");
	} else {
		x = vStringNewInit(g_oCXXTag.extensionFields.scopeName);
	}

	vStringCatS(x,"::");
	vStringCatS(x,g_oCXXTag.name);

	g_oCXXTag.name = vStringValue(x);

	CXX_DEBUG_PRINT(
			"Emitting extra tag for symbol '%s', kind '%s', line %d",
			g_oCXXTag.name,
			g_oCXXTag.kind->name,
			g_oCXXTag.lineNumber
		);

	makeTagEntry(&g_oCXXTag);

	vStringDelete(x);
}