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; }
// 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; }
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); }
// // 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"); }
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; }
// // 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; }
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); }