/** * @file combine.cpp * Labels the chunks as needed. * * @author Ben Gardner * @author Guy Maurel * @license GPL v2+ */ #include "combine.h" #include "combine_fix_mark.h" #include "combine_skip.h" #include "combine_tools.h" #include "EnumStructUnionParser.h" #include "flag_braced_init_list.h" #include "flag_parens.h" #include "lang_pawn.h" #include "newlines.h" #include "prototypes.h" #include "tokenize_cleanup.h" #include constexpr static auto LCURRENT = LCOMBINE; using namespace std; using namespace uncrustify; /** * Mark the parens and colons in: * asm volatile ( "xx" : "xx" (l), "yy"(h) : ... ); * * @param pc the CT_ASM item */ static void flag_asm(Chunk *pc); /** * Skips the list of class/struct parent types. */ Chunk *skip_parent_types(Chunk *colon); /** * Combines two tokens into {{ and }} if inside parens and nothing is between * either pair. */ static void check_double_brace_init(Chunk *bo1); static void process_returns(void); /** * Processes a return statement, labeling the parens and marking the parent. * May remove or add parens around the return statement * * @param pc Pointer to the return chunk */ static Chunk *process_return(Chunk *pc); /** * Process an ObjC 'class' * pc is the chunk after '@implementation' or '@interface' or '@protocol'. * Change colons, etc. Processes stuff until '@end'. * Skips anything in braces. */ static void handle_oc_class(Chunk *pc); /** * Mark Objective-C blocks (aka lambdas or closures) * The syntax and usage is exactly like C function pointers * but instead of an asterisk they have a caret as pointer symbol. * Although it may look expensive this functions is only triggered * on appearance of an OC_BLOCK_CARET for LANG_OC. * repeat(10, ^{ putc('0'+d); }); * typedef void (^workBlk_t)(void); * * @param pc points to the '^' */ static void handle_oc_block_literal(Chunk *pc); /** * Mark Objective-C block types. * The syntax and usage is exactly like C function pointers * but instead of an asterisk they have a caret as pointer symbol. * typedef void (^workBlk_t)(void); * const char * (^workVar)(void); * -(void)Foo:(void(^)())blk { } * * This is triggered when the sequence '(' '^' is found. * * @param pc points to the '^' */ static void handle_oc_block_type(Chunk *pc); /** * Process an ObjC message spec/dec * * Specs: * -(void) foo ARGS; * * Declaration: * -(void) foo ARGS { } * * LABEL : (ARGTYPE) ARGNAME * * ARGS is ': (ARGTYPE) ARGNAME [MOREARGS...]' * MOREARGS is ' [ LABEL] : (ARGTYPE) ARGNAME ' * -(void) foo: (int) arg: { } * -(void) foo: (int) arg: { } * -(void) insertObject:(id)anObject atIndex:(int)index */ static void handle_oc_message_decl(Chunk *pc); /** * Process an ObjC message send statement: * [ class func: val1 name2: val2 name3: val3] ; // named params * [ class func: val1 : val2 : val3] ; // unnamed params * [ class self method ] ; // with protocol * [[NSMutableString alloc] initWithString: @"" ] // class from msg * [func(a,b,c) lastObject ] // class from func * * Mainly find the matching ']' and ';' and mark the colons. * * @param pc points to the open square '[' */ static void handle_oc_message_send(Chunk *pc); //! Process @Property values and re-arrange them if necessary static void handle_oc_property_decl(Chunk *pc); //! Process @available annotation static void handle_oc_available(Chunk *pc); /** * Process a type that is enclosed in parens in message declarations. * TODO: handle block types, which get special formatting * * @param pc points to the open paren * * @return the chunk after the type */ static Chunk *handle_oc_md_type(Chunk *paren_open, E_Token ptype, pcf_flags_t flags, bool &did_it); /** * Process an C# [] thingy: * [assembly: xxx] * [AttributeUsage()] * [@X] * * Set the next chunk to a statement start after the close ']' * * @param pc points to the open square '[' */ static void handle_cs_square_stmt(Chunk *pc); /** * We are on a brace open that is preceded by a word or square close. * Set the brace parent to CT_CS_PROPERTY and find the first item in the * property and set its parent, too. */ static void handle_cs_property(Chunk *pc); /** * We hit a ']' followed by a WORD. This may be a multidimensional array type. * Example: int[,,] x; * If there is nothing but commas between the open and close, then mark it. */ static void handle_cs_array_type(Chunk *pc); /** * We are on the C++ 'template' keyword. * What follows should be the following: * * template function_declaration; * template function_declaration; * template class class_declaration; * template class class_declaration; * * Change the 'class' inside the <> to CT_TYPE. * Set the parent to the class after the <> to CT_TEMPLATE. * Set the parent of the semicolon to CT_TEMPLATE. */ static void handle_cpp_template(Chunk *pc); /** * Verify and then mark C++ lambda expressions. * The expected format is '[...](...){...}' or '[...](...) -> type {...}' * sq_o is '[' CT_SQUARE_OPEN or '[]' CT_TSQUARE * Split the '[]' so we can control the space */ static void handle_cpp_lambda(Chunk *pc); /** * We are on the D 'template' keyword. * What follows should be the following: * * template NAME ( TYPELIST ) { BODY } * * Set the parent of NAME to template, change NAME to CT_TYPE. * Set the parent of the parens and braces to CT_TEMPLATE. * Scan the body for each type in TYPELIST and change the type to CT_TYPE. */ static void handle_d_template(Chunk *pc); /** * A func wrap chunk and what follows should be treated as a function name. * Create new text for the chunk and call it a CT_FUNCTION. * * A type wrap chunk and what follows should be treated as a simple type. * Create new text for the chunk and call it a CT_TYPE. */ static void handle_wrap(Chunk *pc); /** * A proto wrap chunk and what follows should be treated as a function proto. * * RETTYPE PROTO_WRAP( NAME, PARAMS ); or RETTYPE PROTO_WRAP( NAME, (PARAMS) ); * RETTYPE gets changed with make_type(). * PROTO_WRAP is marked as CT_FUNC_PROTO or CT_FUNC_DEF. * NAME is marked as CT_WORD. * PARAMS is all marked as prototype parameters. */ static void handle_proto_wrap(Chunk *pc); static bool is_oc_block(Chunk *pc); /** * Java assert statements are: "assert EXP1 [: EXP2] ;" * Mark the parent of the colon and semicolon */ static void handle_java_assert(Chunk *pc); static void flag_asm(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *tmp = pc->GetNextNcNnl(E_Scope::PREPROC); if (chunk_is_not_token(tmp, CT_QUALIFIER)) { return; } Chunk *po = tmp->GetNextNcNnl(E_Scope::PREPROC); if (!chunk_is_paren_open(po)) { return; } Chunk *end = chunk_skip_to_match(po, E_Scope::PREPROC); if (end == nullptr) { return; } set_chunk_parent(po, CT_ASM); set_chunk_parent(end, CT_ASM); for ( tmp = po->GetNextNcNnl(E_Scope::PREPROC); tmp->IsNotNullChunk() && tmp != end; tmp = tmp->GetNextNcNnl(E_Scope::PREPROC)) { if (chunk_is_token(tmp, CT_COLON)) { set_chunk_type(tmp, CT_ASM_COLON); } else if (chunk_is_token(tmp, CT_DC_MEMBER)) { // if there is a string on both sides, then this is two ASM_COLONs if ( chunk_is_token(tmp->GetNextNcNnl(E_Scope::PREPROC), CT_STRING) && chunk_is_token(tmp->GetPrevNcNnlNi(E_Scope::PREPROC), CT_STRING)) // Issue #2279 { Chunk nc; nc = *tmp; tmp->str.resize(1); tmp->orig_col_end = tmp->orig_col + 1; set_chunk_type(tmp, CT_ASM_COLON); set_chunk_type(&nc, tmp->type); nc.str.pop_front(); nc.orig_col++; nc.column++; chunk_add_after(&nc, tmp); } } } tmp = end->GetNextNcNnl(E_Scope::PREPROC); if (tmp->IsNullChunk()) { return; } if (chunk_is_token(tmp, CT_SEMICOLON)) { set_chunk_parent(tmp, CT_ASM); } } // flag_asm void do_symbol_check(Chunk *prev, Chunk *pc, Chunk *next) { LOG_FUNC_ENTRY(); LOG_FMT(LFCNR, "%s(%d): prev is '%s' %s\n", __func__, __LINE__, prev->Text(), get_token_name(prev->type)); log_pcf_flags(LFCNR, prev->flags); LOG_FMT(LFCNR, "%s(%d): pc is '%s' %s\n", __func__, __LINE__, pc->Text(), get_token_name(pc->type)); log_pcf_flags(LFCNR, pc->flags); LOG_FMT(LFCNR, "%s(%d): next is '%s' %s\n", __func__, __LINE__, next->Text(), get_token_name(next->type)); log_pcf_flags(LFCNR, next->flags); if ( chunk_is_token(pc, CT_NOEXCEPT) // Issue #3284 && chunk_is_token(next, CT_ASSIGN)) // skip over noexcept { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); pc = next; next = pc->GetNext(); } // separate the uses of CT_ASSIGN sign '=' // into CT_ASSIGN_DEFAULT_ARG, CT_ASSIGN_FUNC_PROTO if ( chunk_is_token(pc, CT_ASSIGN) && get_chunk_parent_type(pc) == CT_FUNC_PROTO && ( pc->flags.test(PCF_IN_FCN_DEF) // Issue #2236 || pc->flags.test(PCF_IN_CONST_ARGS))) { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); log_pcf_flags(LFCNR, pc->flags); set_chunk_type(pc, CT_ASSIGN_DEFAULT_ARG); return; } if ( ( chunk_is_token(prev, CT_FPAREN_CLOSE) || ( ( chunk_is_str(prev, "const") || chunk_is_str(prev, "override")) && chunk_is_token(prev->prev, CT_FPAREN_CLOSE))) && chunk_is_token(pc, CT_ASSIGN) && ( chunk_is_token(next, CT_DEFAULT) || chunk_is_token(next, CT_DELETE) || chunk_is_str(next, "0"))) { set_chunk_type(pc, CT_ASSIGN_FUNC_PROTO); return; // cpp 30031 } if (chunk_is_token(pc, CT_OC_AT)) { if ( chunk_is_token(next, CT_PAREN_OPEN) || chunk_is_token(next, CT_BRACE_OPEN) || chunk_is_token(next, CT_SQUARE_OPEN)) { flag_parens(next, PCF_OC_BOXED, next->type, CT_OC_AT, false); } else { set_chunk_parent(next, CT_OC_AT); return; // objective-c_50095 } } // D stuff if ( language_is_set(LANG_D) && chunk_is_token(pc, CT_QUALIFIER) && chunk_is_str(pc, "const") && chunk_is_token(next, CT_PAREN_OPEN)) { set_chunk_type(pc, CT_D_CAST); set_paren_parent(next, pc->type); return; // d_40061 } if ( chunk_is_token(next, CT_PAREN_OPEN) && ( chunk_is_token(pc, CT_D_CAST) || chunk_is_token(pc, CT_DELEGATE) || chunk_is_token(pc, CT_ALIGN))) { // mark the parenthesis parent Chunk *tmp = set_paren_parent(next, pc->type); // For a D cast - convert the next item if ( chunk_is_token(pc, CT_D_CAST) && tmp != nullptr) { if (chunk_is_token(tmp, CT_STAR)) { set_chunk_type(tmp, CT_DEREF); return; // d_40006 } else if (chunk_is_token(tmp, CT_AMP)) { set_chunk_type(tmp, CT_ADDR); return; // d_40060 } else if (chunk_is_token(tmp, CT_MINUS)) { set_chunk_type(tmp, CT_NEG); return; // d_40060 } else if (chunk_is_token(tmp, CT_PLUS)) { set_chunk_type(tmp, CT_POS); return; // d_40060 } } /* * For a delegate, mark previous words as types and the item after the * close paren as a variable def */ if (chunk_is_token(pc, CT_DELEGATE)) { if (tmp != nullptr) { set_chunk_parent(tmp, CT_DELEGATE); if (tmp->level == tmp->brace_level) { chunk_flags_set(tmp, PCF_VAR_1ST_DEF); } } for (tmp = pc->GetPrevNcNnlNi(); tmp->IsNotNullChunk(); tmp = tmp->GetPrevNcNnlNi()) // Issue #2279 { if ( chunk_is_semicolon(tmp) || chunk_is_token(tmp, CT_BRACE_OPEN) || chunk_is_token(tmp, CT_VBRACE_OPEN)) { break; } make_type(tmp); } return; // c-sharp_10160 } if ( chunk_is_token(pc, CT_ALIGN) && tmp != nullptr) { if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, pc->type); return; // d_40024 } else if (chunk_is_token(tmp, CT_COLON)) { set_chunk_parent(tmp, pc->type); return; // d_40024 } } } // paren open + cast/align/delegate if (chunk_is_token(pc, CT_INVARIANT)) { if (chunk_is_token(next, CT_PAREN_OPEN)) { set_chunk_parent(next, pc->type); Chunk *tmp = next->GetNext(); if (tmp == nullptr) { tmp = Chunk::NullChunkPtr; } while (tmp->IsNotNullChunk()) { if (chunk_is_token(tmp, CT_PAREN_CLOSE)) { set_chunk_parent(tmp, pc->type); break; } make_type(tmp); tmp = tmp->GetNext(); } return; // d_40100 } else { set_chunk_type(pc, CT_QUALIFIER); return; } } if ( chunk_is_token(prev, CT_BRACE_OPEN) && get_chunk_parent_type(prev) != CT_CS_PROPERTY && ( chunk_is_token(pc, CT_GETSET) || chunk_is_token(pc, CT_GETSET_EMPTY))) { flag_parens(prev, PCF_NONE, CT_NONE, CT_GETSET, false); return; } if (chunk_is_token(pc, CT_ASM)) { flag_asm(pc); return; } // clang stuff - A new derived type is introduced to C and, by extension, Objective-C, C++, and Objective-C++ if (language_is_set(LANG_C | LANG_CPP | LANG_OC)) { if (chunk_is_token(pc, CT_CARET)) { if ( pc->flags.test(PCF_EXPR_START) || pc->flags.test(PCF_IN_PREPROC)) { handle_oc_block_literal(pc); return; } } } // Objective C stuff if (language_is_set(LANG_OC)) { // Check for message declarations if (pc->flags.test(PCF_STMT_START)) { if ( ( chunk_is_str(pc, "-") || chunk_is_str(pc, "+")) && chunk_is_str(next, "(")) { handle_oc_message_decl(pc); return; } } if ( pc->flags.test(PCF_EXPR_START) || pc->flags.test(PCF_IN_PREPROC)) { if (chunk_is_token(pc, CT_SQUARE_OPEN)) { handle_oc_message_send(pc); return; // objective-c_50003 } } if (chunk_is_token(pc, CT_OC_PROPERTY)) { handle_oc_property_decl(pc); return; } if (chunk_is_token(pc, CT_OC_AVAILABLE)) { handle_oc_available(pc); return; } } // C# stuff if (language_is_set(LANG_CS)) { // '[assembly: xxx]' stuff if ( pc->flags.test(PCF_EXPR_START) && chunk_is_token(pc, CT_SQUARE_OPEN)) { handle_cs_square_stmt(pc); return; } if ( chunk_is_token(next, CT_BRACE_OPEN) && get_chunk_parent_type(next) == CT_NONE && ( chunk_is_token(pc, CT_SQUARE_CLOSE) || chunk_is_token(pc, CT_ANGLE_CLOSE) || chunk_is_token(pc, CT_WORD))) { handle_cs_property(next); return; } if ( chunk_is_token(pc, CT_SQUARE_CLOSE) && chunk_is_token(next, CT_WORD)) { handle_cs_array_type(pc); return; } if ( ( chunk_is_token(pc, CT_LAMBDA) || chunk_is_token(pc, CT_DELEGATE)) && chunk_is_token(next, CT_BRACE_OPEN)) { set_paren_parent(next, pc->type); return; } if ( chunk_is_token(pc, CT_WHEN) && pc->GetNext()->IsNotNullChunk() && pc->GetNext()->type != CT_SPAREN_OPEN) { set_chunk_type(pc, CT_WORD); return; } } if ( language_is_set(LANG_JAVA) && chunk_is_token(pc, CT_LAMBDA) && chunk_is_token(next, CT_BRACE_OPEN)) { set_paren_parent(next, pc->type); return; } if (chunk_is_token(pc, CT_NEW)) { Chunk *ts = nullptr; Chunk *tmp = next; if (chunk_is_token(tmp, CT_TSQUARE)) { ts = tmp; tmp = tmp->GetNextNcNnl(); } if ( chunk_is_token(tmp, CT_BRACE_OPEN) || chunk_is_token(tmp, CT_PAREN_OPEN)) { set_paren_parent(tmp, pc->type); if (ts != nullptr) { set_chunk_parent(ts, pc->type); } } return; } // C++11 Lambda stuff if ( language_is_set(LANG_CPP) && ( chunk_is_token(pc, CT_SQUARE_OPEN) || chunk_is_token(pc, CT_TSQUARE))) { handle_cpp_lambda(pc); } // FIXME: which language does this apply to? // Issue #2432 if (!language_is_set(LANG_OC)) { if ( chunk_is_token(pc, CT_ASSIGN) && chunk_is_token(next, CT_SQUARE_OPEN)) { set_paren_parent(next, CT_ASSIGN); // Mark one-liner assignment Chunk *tmp = next; while ((tmp = tmp->GetNextNc())->IsNotNullChunk()) { if (chunk_is_newline(tmp)) { break; } if ( chunk_is_token(tmp, CT_SQUARE_CLOSE) && next->level == tmp->level) { chunk_flags_set(tmp, PCF_ONE_LINER); chunk_flags_set(next, PCF_ONE_LINER); break; } } return; } } if (chunk_is_token(pc, CT_ASSERT)) { handle_java_assert(pc); return; } if (chunk_is_token(pc, CT_ANNOTATION)) { Chunk *tmp = pc->GetNextNcNnl(); if (chunk_is_paren_open(tmp)) { set_paren_parent(tmp, CT_ANNOTATION); } return; } if ( chunk_is_token(pc, CT_SIZEOF) && language_is_set(LANG_ALLC)) { Chunk *tmp = pc->GetNextNcNnl(); if (chunk_is_token(tmp, CT_ELLIPSIS)) { set_chunk_parent(tmp, CT_SIZEOF); } return; } if ( chunk_is_token(pc, CT_DECLTYPE) && pc->parent_type != CT_FUNC_DEF) { Chunk *tmp = pc->GetNextNcNnl(); if (chunk_is_paren_open(tmp)) { // decltype may be followed by a braced-init-list tmp = set_paren_parent(tmp, CT_DECLTYPE); if (chunk_is_opening_brace(tmp) && !pc->flags.test(PCF_IN_LAMBDA)) { tmp = set_paren_parent(tmp, CT_BRACED_INIT_LIST); if (tmp) { chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START); } } else { if (chunk_is_token(tmp, CT_WORD)) { chunk_flags_set(tmp, PCF_VAR_1ST_DEF); } } } return; } // A [] in C# and D only follows a type if ( chunk_is_token(pc, CT_TSQUARE) && language_is_set(LANG_D | LANG_CS | LANG_VALA)) { if (chunk_is_token(prev, CT_WORD)) { set_chunk_type(prev, CT_TYPE); } if (chunk_is_token(next, CT_WORD)) { chunk_flags_set(next, PCF_VAR_1ST_DEF); } return; } if ( chunk_is_token(pc, CT_SQL_EXEC) || chunk_is_token(pc, CT_SQL_BEGIN) || chunk_is_token(pc, CT_SQL_END)) { mark_exec_sql(pc); return; } if (chunk_is_token(pc, CT_PROTO_WRAP)) { handle_proto_wrap(pc); return; } // Handle the typedef if (chunk_is_token(pc, CT_TYPEDEF)) { fix_typedef(pc); return; } if ( chunk_is_class_enum_struct_union(pc) && chunk_is_not_token(prev, CT_TYPEDEF)) { EnumStructUnionParser parser; parser.parse(pc); return; } if (chunk_is_token(pc, CT_EXTERN)) { if (chunk_is_paren_open(next)) { Chunk *tmp = flag_parens(next, PCF_NONE, CT_NONE, CT_EXTERN, true); if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, CT_EXTERN); } } else { // next likely is a string (see tokenize_cleanup.cpp) set_chunk_parent(next, CT_EXTERN); Chunk *tmp = next->GetNextNcNnl(); if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, CT_EXTERN); } } return; } if (chunk_is_token(pc, CT_TEMPLATE)) { if (language_is_set(LANG_D)) { handle_d_template(pc); } else { handle_cpp_template(pc); } return; } if ( chunk_is_token(pc, CT_WORD) && chunk_is_token(next, CT_ANGLE_OPEN) && get_chunk_parent_type(next) == CT_TEMPLATE) { mark_template_func(pc, next); return; } if ( chunk_is_token(pc, CT_SQUARE_CLOSE) && chunk_is_token(next, CT_PAREN_OPEN)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_NONE, false); return; } if (chunk_is_token(pc, CT_TYPE_CAST)) { fix_type_cast(pc); return; } if ( get_chunk_parent_type(pc) == CT_ASSIGN && ( chunk_is_token(pc, CT_BRACE_OPEN) || chunk_is_token(pc, CT_SQUARE_OPEN))) { // Mark everything in here as in assign flag_parens(pc, PCF_IN_ARRAY_ASSIGN, pc->type, CT_NONE, false); return; } if (chunk_is_token(pc, CT_D_TEMPLATE)) { set_paren_parent(next, pc->type); return; } /* * A word before an open paren is a function call or definition. * CT_WORD => CT_FUNC_CALL or CT_FUNC_DEF */ if (chunk_is_token(next, CT_PAREN_OPEN)) { Chunk *tmp = next->GetNextNcNnl(); if ( language_is_set(LANG_C | LANG_CPP | LANG_OC) && chunk_is_token(tmp, CT_CARET)) { handle_oc_block_type(tmp); // This is the case where a block literal is passed as the first argument of a C-style method invocation. if ( ( chunk_is_token(tmp, CT_OC_BLOCK_CARET) || chunk_is_token(tmp, CT_CARET)) && chunk_is_token(pc, CT_WORD)) { LOG_FMT(LFCN, "%s(%d): (1) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); set_chunk_type(pc, CT_FUNC_CALL); } } else if ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_OPERATOR_VAL)) { set_chunk_type(pc, CT_FUNCTION); } else if (chunk_is_token(pc, CT_FIXED)) { set_chunk_type(pc, CT_FUNCTION); set_chunk_parent(pc, CT_FIXED); } else if (chunk_is_token(pc, CT_TYPE)) { /* * If we are on a type, then we are either on a C++ style cast, an * array reference, a function or we are on a function type. * The only way to tell for sure is to find the close paren and see * if it is followed by an open paren. * "int(5.6)" * "int()" * "int(foo)(void)" * * FIXME: this check can be done better... */ LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); bool is_byref_array = false; if (language_is_set(LANG_CPP)) { // If the open paren is followed by an ampersand, an optional word, // a close parenthesis, and an open square bracket, then it is an // array being passed by reference, not a cast tmp = next->GetNextNcNnl(); if (chunk_is_token(tmp, CT_AMP)) { auto tmp2 = tmp->GetNextNcNnl(); if (chunk_is_token(tmp2, CT_WORD)) { tmp2 = tmp2->GetNextNcNnl(); } if (chunk_is_token(tmp2, CT_PAREN_CLOSE)) { tmp2 = tmp2->GetNextNcNnl(); if (chunk_is_token(tmp2, CT_SQUARE_OPEN)) { is_byref_array = true; set_chunk_type(tmp, CT_BYREF); } } } } if (!is_byref_array) { tmp = next->GetNextType(CT_PAREN_CLOSE, next->level); if (tmp->IsNotNullChunk()) { tmp = tmp->GetNext(); if (chunk_is_token(tmp, CT_PAREN_OPEN)) { set_chunk_type(pc, CT_FUNCTION); } else { if ( get_chunk_parent_type(pc) == CT_NONE && !pc->flags.test(PCF_IN_TYPEDEF)) { tmp = next->GetNextNcNnl(); if (chunk_is_token(tmp, CT_PAREN_CLOSE)) { // we have TYPE() set_chunk_type(pc, CT_FUNCTION); } else { // we have TYPE(...) set_chunk_type(pc, CT_CPP_CAST); set_paren_parent(next, CT_CPP_CAST); } } } } } } } if (language_is_set(LANG_PAWN)) { if ( chunk_is_token(pc, CT_FUNCTION) && pc->brace_level > 0) { LOG_FMT(LFCN, "%s(%d): (2) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); set_chunk_type(pc, CT_FUNC_CALL); } if ( chunk_is_token(pc, CT_STATE) && chunk_is_token(next, CT_PAREN_OPEN)) { set_paren_parent(next, pc->type); } } else { if ( ( chunk_is_token(pc, CT_FUNCTION) || chunk_is_token(pc, CT_FUNC_DEF)) && ( (get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR) || !is_oc_block(pc))) { mark_function(pc); } } // Detect C99 member stuff if ( chunk_is_token(pc, CT_MEMBER) && ( chunk_is_token(prev, CT_COMMA) || chunk_is_token(prev, CT_BRACE_OPEN))) { set_chunk_type(pc, CT_C99_MEMBER); set_chunk_parent(next, CT_C99_MEMBER); return; } // Mark function parens and braces if ( chunk_is_token(pc, CT_FUNC_DEF) || chunk_is_token(pc, CT_FUNC_CALL) || chunk_is_token(pc, CT_FUNC_CALL_USER) || chunk_is_token(pc, CT_FUNC_PROTO)) { Chunk *tmp = next; if (chunk_is_token(tmp, CT_SQUARE_OPEN)) { tmp = set_paren_parent(tmp, pc->type); } else if ( chunk_is_token(tmp, CT_TSQUARE) || get_chunk_parent_type(tmp) == CT_OPERATOR) { tmp = tmp->GetNextNcNnl(); } if ( tmp != nullptr && tmp->IsNotNullChunk()) { if (chunk_is_paren_open(tmp)) { tmp = flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, pc->type, false); if ( tmp != nullptr && tmp->IsNotNullChunk()) { if (chunk_is_token(tmp, CT_BRACE_OPEN)) { if ( get_chunk_parent_type(tmp) != CT_DOUBLE_BRACE && !pc->flags.test(PCF_IN_CONST_ARGS)) { set_paren_parent(tmp, pc->type); } } else if ( chunk_is_semicolon(tmp) && chunk_is_token(pc, CT_FUNC_PROTO)) { set_chunk_parent(tmp, pc->type); } } } } return; } // Mark the parameters in catch() if ( chunk_is_token(pc, CT_CATCH) && chunk_is_token(next, CT_SPAREN_OPEN)) { fix_fcn_def_params(next); return; } if ( chunk_is_token(pc, CT_THROW) && chunk_is_token(prev, CT_FPAREN_CLOSE)) { set_chunk_parent(pc, get_chunk_parent_type(prev)); if (chunk_is_token(next, CT_PAREN_OPEN)) { set_paren_parent(next, CT_THROW); } return; } // Mark the braces in: "for_each_entry(xxx) { }" if ( chunk_is_token(pc, CT_BRACE_OPEN) && get_chunk_parent_type(pc) != CT_DOUBLE_BRACE && chunk_is_token(prev, CT_FPAREN_CLOSE) && ( get_chunk_parent_type(prev) == CT_FUNC_CALL || get_chunk_parent_type(prev) == CT_FUNC_CALL_USER) && !pc->flags.test(PCF_IN_CONST_ARGS)) { LOG_FMT(LFCN, "%s(%d): (3) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); set_paren_parent(pc, CT_FUNC_CALL); return; } /* * Check for a close parenthesis followed by an open parenthesis, * which means that we are on a function type declaration (C/C++ only?). * Note that typedefs are already taken care of. */ if ( !pc->flags.test(PCF_IN_TEMPLATE) // Issue #3252 && get_chunk_parent_type(pc) != CT_CPP_CAST && get_chunk_parent_type(pc) != CT_C_CAST && !pc->flags.test(PCF_IN_PREPROC) && !is_oc_block(pc) && get_chunk_parent_type(pc) != CT_OC_MSG_DECL && get_chunk_parent_type(pc) != CT_OC_MSG_SPEC && chunk_is_str(pc, ")") && chunk_is_str(next, "(")) { if (language_is_set(LANG_D)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false); } else { mark_function_type(pc); } return; } if (chunk_is_token(pc, CT_OC_CLASS)) { handle_oc_class(pc); return; } // TODO: Check for stuff that can only occur at the start of an statement if (!language_is_set(LANG_D)) { /* * Check a parenthesis pair to see if it is a cast. * Note that SPAREN and FPAREN have already been marked. */ if ( chunk_is_token(pc, CT_PAREN_OPEN) && ( get_chunk_parent_type(pc) == CT_NONE || get_chunk_parent_type(pc) == CT_OC_MSG || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR || get_chunk_parent_type(pc) == CT_CS_SQ_STMT) // Issue # 1256 && ( chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_STRUCT) || chunk_is_token(next, CT_QUALIFIER) || chunk_is_token(next, CT_MEMBER) || chunk_is_token(next, CT_DC_MEMBER) || chunk_is_token(next, CT_ENUM) || chunk_is_token(next, CT_UNION)) && chunk_is_not_token(prev, CT_DECLTYPE) && chunk_is_not_token(prev, CT_SIZEOF) && get_chunk_parent_type(prev) != CT_SIZEOF && get_chunk_parent_type(prev) != CT_OPERATOR && !pc->flags.test(PCF_IN_TYPEDEF)) { fix_casts(pc); return; } } if (language_is_set(LANG_CPP)) { Chunk *nnext = next->GetNextNcNnl(); // handle parent_type of assigns in special functions (ro5 + pure virtual) if ( pc->flags.test_any(PCF_IN_STRUCT | PCF_IN_CLASS) && chunk_is_token(pc, CT_ASSIGN) && chunk_is_token(nnext, CT_SEMICOLON) && ( chunk_is_token(next, CT_DEFAULT) || chunk_is_token(next, CT_DELETE) || ( chunk_is_token(next, CT_NUMBER) && chunk_is_str(next, "0")))) { const size_t level = pc->level; bool found_status = false; Chunk *pprev = pc->GetPrev(); for ( ; ( pprev->IsNotNullChunk() && pprev->level >= level && chunk_is_not_token(pprev, CT_SEMICOLON) && chunk_is_not_token(pprev, CT_ACCESS_COLON)) ; pprev = pprev->GetPrev()) { if (pprev->level != level) { continue; } if (chunk_is_token(next, CT_NUMBER)) { if ( chunk_is_token(pprev, CT_QUALIFIER) && chunk_is_str(pprev, "virtual")) { found_status = true; break; } } else { if ( chunk_is_token(pprev, CT_FUNC_CLASS_PROTO) // ctor/dtor || chunk_is_token(pprev, CT_FUNC_PROTO)) // normal function { found_status = true; break; } } } if (found_status) { set_chunk_parent(pc, pprev->type); } } if (detect_cpp_braced_init_list(pc, next)) { flag_cpp_braced_init_list(pc, next); } } // Check for stuff that can only occur at the start of an expression if ( pc->flags.test(PCF_EXPR_START) || ( prev->flags.test(PCF_EXPR_START) && get_chunk_parent_type(pc) == CT_OC_AT)) { // Change STAR, MINUS, and PLUS in the easy cases if (chunk_is_token(pc, CT_STAR)) { // issue #596 // [0x100062020:IN_SPAREN,IN_FOR,STMT_START,EXPR_START,PUNCTUATOR] // prev->type is CT_COLON ==> CT_DEREF if (chunk_is_token(prev, CT_ANGLE_CLOSE)) { set_chunk_type(pc, CT_PTR_TYPE); } else if (chunk_is_token(prev, CT_COLON)) { set_chunk_type(pc, CT_DEREF); } else { set_chunk_type(pc, CT_DEREF); } } if ( language_is_set(LANG_CPP) && chunk_is_token(pc, CT_CARET) && chunk_is_token(prev, CT_ANGLE_CLOSE)) { set_chunk_type(pc, CT_PTR_TYPE); } if ( language_is_set(LANG_CS | LANG_VALA) && chunk_is_token(pc, CT_QUESTION) && chunk_is_token(prev, CT_ANGLE_CLOSE)) { set_chunk_type(pc, CT_PTR_TYPE); } else if (chunk_is_token(pc, CT_MINUS)) { set_chunk_type(pc, CT_NEG); } else if (chunk_is_token(pc, CT_PLUS)) { set_chunk_type(pc, CT_POS); } else if (chunk_is_token(pc, CT_INCDEC_AFTER)) { set_chunk_type(pc, CT_INCDEC_BEFORE); } else if (chunk_is_token(pc, CT_AMP)) { if (chunk_is_token(prev, CT_ANGLE_CLOSE)) // Issue #2324 { set_chunk_type(pc, CT_BYREF); } else { set_chunk_type(pc, CT_ADDR); } } else if (chunk_is_token(pc, CT_CARET)) { if (language_is_set(LANG_C | LANG_CPP | LANG_OC)) { // This is likely the start of a block literal handle_oc_block_literal(pc); } } } /* * Change the parenthesis pair after a function/macro-function * CT_PAREN_OPEN => CT_FPAREN_OPEN */ if (chunk_is_token(pc, CT_MACRO_FUNC)) { flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_MACRO_FUNC, false); } if ( chunk_is_token(pc, CT_MACRO_OPEN) || chunk_is_token(pc, CT_MACRO_ELSE) || chunk_is_token(pc, CT_MACRO_CLOSE)) { if (chunk_is_token(next, CT_PAREN_OPEN)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, false); } } if ( chunk_is_token(pc, CT_DELETE) && chunk_is_token(next, CT_TSQUARE)) { set_chunk_parent(next, CT_DELETE); } // Change CT_STAR to CT_PTR_TYPE or CT_ARITH or CT_DEREF if ( chunk_is_token(pc, CT_STAR) || ( language_is_set(LANG_CPP) && chunk_is_token(pc, CT_CARET))) { if ( chunk_is_paren_close(next) || chunk_is_token(next, CT_COMMA)) { set_chunk_type(pc, CT_PTR_TYPE); } else if ( language_is_set(LANG_OC) && chunk_is_token(next, CT_STAR)) { /* * Change pointer-to-pointer types in OC_MSG_DECLs * from ARITH <===> DEREF to PTR_TYPE <===> PTR_TYPE */ set_chunk_type(pc, CT_PTR_TYPE); set_chunk_parent(pc, get_chunk_parent_type(prev)); set_chunk_type(next, CT_PTR_TYPE); set_chunk_parent(next, get_chunk_parent_type(pc)); } else if ( chunk_is_token(pc, CT_STAR) && ( chunk_is_token(prev, CT_DECLTYPE) || chunk_is_token(prev, CT_SIZEOF) || chunk_is_token(prev, CT_DELETE) || get_chunk_parent_type(pc) == CT_SIZEOF)) { set_chunk_type(pc, CT_DEREF); } else if ( ( chunk_is_token(prev, CT_WORD) && chunk_ends_type(prev) && !prev->flags.test(PCF_IN_FCN_CTOR) && !prev->flags.test(PCF_IN_ARRAY_ASSIGN)) // Issue #3345 || chunk_is_token(prev, CT_DC_MEMBER) || chunk_is_token(prev, CT_PTR_TYPE)) { LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, Text() is '%s', type is %s\n ", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text(), get_token_name(pc->type)); log_pcf_flags(LFCNR, pc->flags); set_chunk_type(pc, CT_PTR_TYPE); } else if ( chunk_is_token(next, CT_SQUARE_OPEN) && !language_is_set(LANG_OC)) // Issue #408 { set_chunk_type(pc, CT_PTR_TYPE); } else if (chunk_is_token(pc, CT_STAR)) { // Add check for CT_DC_MEMBER CT_WORD CT_STAR sequence // to convert CT_WORD into CT_TYPE // and CT_STAR into CT_PTR_TYPE // look for an assign backward, fuction call, return to distinguish between // double result = Constants::PI * factor; // and // ::some::name * foo; if ( chunk_is_token(prev, CT_WORD) && chunk_is_token(prev->prev, CT_DC_MEMBER) && language_is_set(LANG_CPP)) { // Issue 1402 bool is_multiplication = false; Chunk *tmp = pc; while (tmp->IsNotNullChunk()) { if ( chunk_is_token(tmp, CT_SEMICOLON) || get_chunk_parent_type(tmp) == CT_CLASS) { break; } else if ( chunk_is_token(tmp, CT_ASSIGN) || chunk_is_token(tmp, CT_FUNC_CALL) || chunk_is_token(tmp, CT_RETURN)) { is_multiplication = true; break; } tmp = tmp->GetPrevNcNnlNi(); // Issue #2279 } if (is_multiplication) { // double result = Constants::PI * factor; set_chunk_type(pc, CT_ARITH); } else { // ::some::name * foo; set_chunk_type(prev, CT_TYPE); set_chunk_type(pc, CT_PTR_TYPE); } } /* * A star can have three meanings * 1. CT_DEREF = pointer dereferencing * 2. CT_PTR_TYPE = pointer definition * 3. CT_ARITH = arithmetic multiplication * * most PCF_PUNCTUATOR chunks except a paren close would make this * a deref. A paren close may end a cast or may be part of a macro fcn. */ if (chunk_is_token(prev, CT_TYPE)) { set_chunk_type(pc, CT_PTR_TYPE); } else if ( chunk_is_token(pc->next, CT_SEMICOLON) // Issue #2319 || ( chunk_is_token(pc->next, CT_STAR) && chunk_is_token(pc->next->next, CT_SEMICOLON))) { // example: // using AbstractLinkPtr = AbstractLink*; // using AbstractLinkPtrPtr = AbstractLink**; set_chunk_type(pc, CT_PTR_TYPE); } else if ( ( get_chunk_parent_type(pc) == CT_FUNC_DEF && ( chunk_is_opening_brace(next) || pc->GetNext()->IsStar())) || chunk_is_token(next, CT_QUALIFIER)) // Issue #2648 { // example: // auto getComponent(Color *color) -> Component * { // auto getComponent(Color *color) -> Component ** { // auto getComponent(Color *color) -> Component * _Nonnull // only to help the vim command }} set_chunk_type(pc, CT_PTR_TYPE); } else if ( chunk_is_token(pc->next, CT_SEMICOLON) // Issue #2319 || ( chunk_is_token(pc->next, CT_STAR) && chunk_is_token(pc->next->next, CT_STAR))) { // more pointers are NOT yet possible fprintf(stderr, "Too many pointers: the maximum level of pointer indirection is 3 (i.e., ***p)\n"); fprintf(stderr, "at line %zu, column %zu.\n", pc->orig_line, pc->orig_col); fprintf(stderr, "Please make a report.\n"); log_flush(true); exit(EX_SOFTWARE); } else { // Issue 1402 set_chunk_type(pc, ( prev->flags.test(PCF_PUNCTUATOR) && ( !chunk_is_paren_close(prev) || chunk_is_token(prev, CT_SPAREN_CLOSE) || get_chunk_parent_type(prev) == CT_MACRO_FUNC) && chunk_is_not_token(prev, CT_SQUARE_CLOSE) && chunk_is_not_token(prev, CT_DC_MEMBER)) ? CT_DEREF : CT_ARITH); } if (pc->flags.test(PCF_IN_TYPEDEF)) // Issue #1255/#633 { Chunk *tmp = pc; while (tmp->IsNotNullChunk()) { if ( chunk_is_token(tmp, CT_SEMICOLON) || chunk_is_token(tmp, CT_BRACE_OPEN) || chunk_is_token(tmp, CT_SQUARE_OPEN)) // Issue #3342 { break; } else if (chunk_is_token(tmp, CT_TYPEDEF)) { set_chunk_type(pc, CT_PTR_TYPE); } tmp = tmp->GetPrevNcNnlNi(); // Issue #2279 } } } } if (chunk_is_token(pc, CT_AMP)) { if (chunk_is_token(prev, CT_DELETE)) { set_chunk_type(pc, CT_ADDR); } else if ( chunk_is_token(prev, CT_TYPE) || chunk_is_token(prev, CT_QUALIFIER)) { set_chunk_type(pc, CT_BYREF); } else if ( chunk_is_token(prev, CT_WORD) // Issue #3204 && chunk_is_token(next, CT_OPERATOR)) { set_chunk_type(pc, CT_BYREF); } else if ( chunk_is_token(next, CT_FPAREN_CLOSE) || chunk_is_token(next, CT_COMMA)) { // fix the bug #654 // connect(&mapper, SIGNAL(mapped(QString &)), this, SLOT(onSomeEvent(QString &))); set_chunk_type(pc, CT_BYREF); } else if (get_chunk_parent_type(pc) == CT_USING_ALIAS) { // fix the Issue # 1689 // using reference = value_type &; set_chunk_type(pc->prev, CT_TYPE); set_chunk_type(pc, CT_BYREF); } else { // Issue # 1398 if ( pc->flags.test(PCF_IN_FCN_DEF) && chunk_is_token(prev, CT_WORD) && chunk_is_token(pc, CT_AMP) && chunk_is_token(next, CT_WORD)) { /* * Change CT_WORD before CT_AMP before CT_WORD to CT_TYPE */ set_chunk_type(prev, CT_TYPE); } else { set_chunk_type(pc, CT_ARITH); if ( chunk_is_token(prev, CT_WORD) && !chunk_is_token(next, CT_NUMBER)) // Issue #3407 { Chunk *tmp = prev->GetPrevNcNnlNi(); // Issue #2279 if (tmp->IsNotNullChunk()) { if ( chunk_is_semicolon(tmp) || chunk_is_token(tmp, CT_BRACE_OPEN) || chunk_is_token(tmp, CT_QUALIFIER)) { set_chunk_type(pc, CT_BYREF); set_chunk_type(prev, CT_TYPE); if (!( chunk_is_token(next, CT_OPERATOR) || chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_DC_MEMBER))) { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s', set PCF_VAR_1ST\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); chunk_flags_set(next, PCF_VAR_1ST); } } else if (chunk_is_token(tmp, CT_DC_MEMBER)) { set_chunk_type(prev, CT_TYPE); } } } } } } if ( chunk_is_token(pc, CT_MINUS) || chunk_is_token(pc, CT_PLUS)) { if ( chunk_is_token(prev, CT_POS) || chunk_is_token(prev, CT_NEG) || chunk_is_token(prev, CT_ARITH) || chunk_is_token(prev, CT_SHIFT)) { set_chunk_type(pc, chunk_is_token(pc, CT_MINUS) ? CT_NEG : CT_POS); } else if (chunk_is_token(prev, CT_OC_CLASS)) { set_chunk_type(pc, (chunk_is_token(pc, CT_MINUS)) ? CT_NEG : CT_POS); } else { set_chunk_type(pc, CT_ARITH); } } /* * Bug # 634 * Check for extern "C" NSString* i; * NSString is a type * change CT_WORD => CT_TYPE for pc * change CT_STAR => CT_PTR_TYPE for pc-next */ if (chunk_is_token(pc, CT_WORD)) // here NSString { if (pc->next != nullptr) // here * { if (pc->next->type == CT_STAR) // here * { // compare text with "C" to find extern "C" instructions if (pc->prev != nullptr) { if (pc->prev->type == CT_STRING) { if (unc_text::compare(pc->prev->Text(), "\"C\"") == 0) { if (pc->prev->prev->type == CT_EXTERN) { set_chunk_type(pc, CT_TYPE); // change CT_WORD => CT_TYPE set_chunk_type(pc->next, CT_PTR_TYPE); // change CT_STAR => CT_PTR_TYPE } } } } // Issue #322 STDMETHOD(GetValues)(BSTR bsName, REFDATA** pData); if ( (pc->next->next != nullptr) && pc->next->next->type == CT_STAR && pc->flags.test(PCF_IN_CONST_ARGS)) { // change CT_STAR => CT_PTR_TYPE set_chunk_type(pc->next, CT_PTR_TYPE); set_chunk_type(pc->next->next, CT_PTR_TYPE); } // Issue #222 whatever3 *(func_ptr)( whatever4 *foo2, ... if ( (pc->next->next != nullptr) && pc->next->next->type == CT_WORD && pc->flags.test(PCF_IN_FCN_DEF)) { // look for the opening parenthesis // Issue 1403 Chunk *tmp = pc->GetPrevType(CT_FPAREN_OPEN, pc->level - 1); if ( tmp->IsNotNullChunk() && get_chunk_parent_type(tmp) != CT_FUNC_CTOR_VAR) { set_chunk_type(pc->next, CT_PTR_TYPE); } } } } } /* * Bug # 634 * Check for __attribute__((visibility ("default"))) NSString* i; * NSString is a type * change CT_WORD => CT_TYPE for pc * change CT_STAR => CT_PTR_TYPE for pc-next */ if (chunk_is_token(pc, CT_WORD)) // here NSString { if (pc->next != nullptr) // here * { if (pc->next->type == CT_STAR) // here * { Chunk *tmp = pc; while ( tmp != nullptr && tmp->IsNotNullChunk()) { if (chunk_is_token(tmp, CT_ATTRIBUTE)) { LOG_FMT(LFCNR, "%s(%d): ATTRIBUTE found, type is %s, Text() '%s'\n", __func__, __LINE__, get_token_name(tmp->type), tmp->Text()); LOG_FMT(LFCNR, "for token, type is %s, Text() '%s'\n", get_token_name(pc->type), pc->Text()); // change CT_WORD => CT_TYPE set_chunk_type(pc, CT_TYPE); // change CT_STAR => CT_PTR_TYPE set_chunk_type(pc->next, CT_PTR_TYPE); } if (tmp->flags.test(PCF_STMT_START)) { // we are at beginning of the line break; } tmp = tmp->GetPrev(); } } } } /* * Issue # 1689 * Check for using reference = value_type&; * is it a Type alias, alias template? */ if (chunk_is_token(pc, CT_USING)) { // look for CT_ASSIGN before CT_SEMICOLON at the end of the statement bool is_preproc = pc->flags.test(PCF_IN_PREPROC); auto const search_assign = [&pc, &is_preproc]() { for (Chunk *temp = pc; temp->IsNotNullChunk(); temp = temp->GetNextNcNnl()) { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s', type is %s\n", __func__, __LINE__, temp->orig_line, temp->orig_col, temp->Text(), get_token_name(temp->type)); if (chunk_is_token(temp, CT_ASSIGN)) { return(true); } if ( chunk_is_token(temp, CT_SEMICOLON) || ( is_preproc && ( !temp->flags.test(PCF_IN_PREPROC) || chunk_is_token(temp, CT_PREPROC)))) { return(false); } } return(false); }; const bool assign_found = language_is_set(LANG_D) || search_assign(); if (assign_found) { // it is a Type alias, alias template for (Chunk *temp = pc; temp->IsNotNullChunk(); temp = temp->GetNextNcNnl()) { if (get_chunk_parent_type(temp) == CT_NONE) { set_chunk_parent(temp, CT_USING_ALIAS); } if ( chunk_is_token(temp, CT_SEMICOLON) || ( is_preproc && ( !temp->flags.test(PCF_IN_PREPROC) || chunk_is_token(temp, CT_PREPROC)))) { break; } } } } // Issue #548: inline T && someFunc(foo * *p, bar && q) { } if ( chunk_is_token(pc, CT_BOOL) && !pc->flags.test(PCF_IN_PREPROC) && chunk_is_str(pc, "&&") && chunk_ends_type(pc->prev)) { Chunk *tmp = pc->GetPrev(); // Issue #2688 LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s', type is %s\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->Text(), get_token_name(tmp->type)); log_pcf_flags(LFCNR, tmp->flags); // look for a type if (chunk_is_token(tmp, CT_TYPE)) { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s', type is %s\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text(), get_token_name(pc->type)); log_pcf_flags(LFCNR, pc->flags); set_chunk_type(pc, CT_BYREF); } // look next, is there a "assign" before the ";" Chunk *semi = pc->GetNextType(CT_SEMICOLON, pc->level); // Issue #2688 if (semi->IsNotNullChunk()) { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s', type is %s\n", __func__, __LINE__, semi->orig_line, semi->orig_col, semi->Text(), get_token_name(semi->type)); for (Chunk *test_it = pc; test_it != semi; test_it = test_it->GetNext()) { LOG_FMT(LFCNR, "%s(%d): test_it->orig_line is %zu, orig_col is %zu, Text() '%s', type is %s\n", __func__, __LINE__, test_it->orig_line, test_it->orig_col, test_it->Text(), get_token_name(test_it->type)); if (chunk_is_token(test_it, CT_ASSIGN)) { // the statement is an assigment // && is before assign set_chunk_type(pc, CT_BYREF); break; } } } } // Issue #1704 if ( chunk_is_token(pc, CT_INCDEC_AFTER) && pc->flags.test(PCF_IN_PREPROC)) { Chunk *tmp_2 = pc->GetNext(); LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, Text() '%s', type is %s\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text(), get_token_name(pc->type)); log_pcf_flags(LFTYPE, pc->flags); if (chunk_is_token(tmp_2, CT_WORD)) { set_chunk_type(pc, CT_INCDEC_BEFORE); } } } // do_symbol_check static void check_double_brace_init(Chunk *bo1) { LOG_FUNC_ENTRY(); LOG_FMT(LJDBI, "%s(%d): orig_line is %zu, orig_col is %zu", __func__, __LINE__, bo1->orig_line, bo1->orig_col); Chunk *pc = bo1->GetPrevNcNnlNi(); // Issue #2279 if (pc->IsNullChunk()) { return; } if (chunk_is_paren_close(pc)) { Chunk *bo2 = bo1->GetNext(); if (bo2->IsNullChunk()) { return; } if (chunk_is_token(bo2, CT_BRACE_OPEN)) { // found a potential double brace Chunk *bc2 = chunk_skip_to_match(bo2); if (bc2 == nullptr) { return; } Chunk *bc1 = bc2->GetNext(); if (bc1->IsNullChunk()) { return; } if (chunk_is_token(bc1, CT_BRACE_CLOSE)) { LOG_FMT(LJDBI, " - end, orig_line is %zu, orig_col is %zu\n", bc2->orig_line, bc2->orig_col); // delete bo2 and bc1 bo1->str += bo2->str; bo1->orig_col_end = bo2->orig_col_end; chunk_del(bo2); set_chunk_parent(bo1, CT_DOUBLE_BRACE); bc2->str += bc1->str; bc2->orig_col_end = bc1->orig_col_end; chunk_del(bc1); set_chunk_parent(bc2, CT_DOUBLE_BRACE); return; } } } LOG_FMT(LJDBI, " - no\n"); } // check_double_brace_init void fix_symbols(void) { LOG_FUNC_ENTRY(); Chunk *pc; Chunk dummy; cpd.unc_stage = unc_stage_e::FIX_SYMBOLS; mark_define_expressions(); bool is_cpp = language_is_set(LANG_CPP); bool is_java = language_is_set(LANG_JAVA); for (pc = Chunk::GetHead(); pc->IsNotNullChunk(); pc = pc->GetNextNcNnl()) { if ( chunk_is_token(pc, CT_FUNC_WRAP) || chunk_is_token(pc, CT_TYPE_WRAP)) { handle_wrap(pc); } if (chunk_is_token(pc, CT_ASSIGN)) { mark_lvalue(pc); } // a brace immediately preceded by word in C++11 is an initializer list though it may also // by a type casting initializer list if the word is really a type; sadly uncrustify knows // only built-in types and knows nothing of user-defined types Chunk *prev = pc->GetPrevNcNnlNi(); // Issue #2279 if ( is_cpp && chunk_is_token(pc, CT_BRACE_OPEN) && ( chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE))) { mark_lvalue(pc); } if ( is_java && chunk_is_token(pc, CT_BRACE_OPEN)) { check_double_brace_init(pc); } if (chunk_is_token(pc, CT_ATTRIBUTE)) { Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC); if ( next->IsNotNullChunk() && chunk_is_token(next, CT_PAREN_OPEN)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_ATTRIBUTE, false); } } } pc = Chunk::GetHead(); if (pc->IsCommentOrNewline()) { pc = pc->GetNextNcNnl(); } while (pc->IsNotNullChunk()) { if (chunk_is_token(pc, CT_IGNORED)) { pc = pc->GetNextNcNnl(); continue; } LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, Text() is '%s', type is %s\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text(), get_token_name(pc->type)); Chunk *prev = pc->GetPrevNcNnlNi(E_Scope::PREPROC); // Issue #2279 if (chunk_is_token(prev, CT_QUALIFIER)) { prev = prev->GetPrevNcNnlNi(E_Scope::PREPROC); // Issue #3513 } if (prev->IsNullChunk()) { prev = &dummy; } else { // Issue #2279 LOG_FMT(LFCNR, "%s(%d): prev(ni)->orig_line is %zu, orig_col is %zu, Text() is '%s', type is %s\n", __func__, __LINE__, prev->orig_line, prev->orig_col, prev->Text(), get_token_name(prev->type)); } Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC); if (next->IsNullChunk()) { next = &dummy; } else { // Issue #2279 LOG_FMT(LFCNR, "%s(%d): next->orig_line is %zu, orig_col is %zu, Text() is '%s', type is %s\n", __func__, __LINE__, next->orig_line, next->orig_col, next->Text(), get_token_name(next->type)); } LOG_FMT(LFCNR, "%s(%d): do_symbol_check(%s, %s, %s)\n", __func__, __LINE__, prev->Text(), pc->Text(), next->Text()); do_symbol_check(prev, pc, next); pc = pc->GetNextNcNnl(); } pawn_add_virtual_semicolons(); process_returns(); /* * 2nd pass - handle variable definitions * REVISIT: We need function params marked to do this (?) */ pc = Chunk::GetHead(); int square_level = -1; while ( pc != nullptr && pc->IsNotNullChunk()) { char copy[1000]; LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, Text() is '%s', type is %s, parent_type is %s\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->ElidedText(copy), get_token_name(pc->type), get_token_name(pc->parent_type)); // Can't have a variable definition inside [ ] if (square_level < 0) { if (chunk_is_token(pc, CT_SQUARE_OPEN)) { square_level = pc->level; } } else { if (pc->level <= static_cast(square_level)) { square_level = -1; } } if ( chunk_is_token(pc, CT_EXTERN) && language_is_set(LANG_ALLC)) { Chunk *next = pc->GetNextNcNnl(); if (chunk_is_token(next, CT_STRING)) { Chunk *tmp = next->GetNextNcNnl(); while (tmp->IsNotNullChunk()) { if ( chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_BRACE_OPEN) || chunk_is_token(tmp, CT_ATTRIBUTE)) { break; } if (chunk_is_token(tmp, CT_WORD)) { chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START); break; } tmp = tmp->GetNextNcNnl(); } } } if ( chunk_is_token(pc, CT_ATTRIBUTE) && language_is_set(LANG_ALLC)) { Chunk *tmp = skip_attribute_next(pc); if (chunk_is_token(tmp, CT_WORD)) { chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START); } } if ( chunk_is_token(pc, CT_BRACE_OPEN) // Issue #2332 && get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST) { LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, Text() is '%s', look for CT_BRACE_OPEN\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); pc = pc->GetNextType(CT_BRACE_CLOSE, pc->level); } /* * A variable definition is possible after at the start of a statement * that starts with: DC_MEMBER, QUALIFIER, TYPE, or WORD */ // Issue #2279 // Issue #2478 LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, Text() is '%s', type is %s, parent_type is %s\n ", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->ElidedText(copy), get_token_name(pc->type), get_token_name(pc->parent_type)); log_pcf_flags(LFCNR, pc->flags); if ( (square_level < 0) && pc->flags.test(PCF_STMT_START) && ( chunk_is_token(pc, CT_QUALIFIER) || chunk_is_token(pc, CT_TYPE) || chunk_is_token(pc, CT_TYPENAME) || chunk_is_token(pc, CT_DC_MEMBER) // Issue #2478 || chunk_is_token(pc, CT_WORD)) && get_chunk_parent_type(pc) != CT_BIT_COLON && get_chunk_parent_type(pc) != CT_ENUM && !pc->flags.test(PCF_IN_CLASS_BASE) && !pc->flags.test(PCF_IN_ENUM)) { pc = fix_variable_definition(pc); } else { pc = pc->GetNextNcNnl(); } } } // fix_symbols static void process_returns(void) { LOG_FUNC_ENTRY(); Chunk *pc; pc = Chunk::GetHead(); while (pc->IsNotNullChunk()) { if (chunk_is_not_token(pc, CT_RETURN)) { pc = pc->GetNextType(CT_RETURN, -1); continue; } pc = process_return(pc); } } static Chunk *process_return(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *next; Chunk *temp; Chunk *semi; Chunk *cpar; Chunk chunk; // grab next and bail if it is a semicolon next = pc->PpaGetNextNcNnl(); if ( next->IsNullChunk() || chunk_is_semicolon(next) || chunk_is_token(next, CT_NEWLINE)) { return(next); } log_rule_B("nl_return_expr"); if ( options::nl_return_expr() != IARF_IGNORE && !pc->flags.test(PCF_IN_PREPROC)) { newline_iarf(pc, options::nl_return_expr()); } if (chunk_is_token(next, CT_PAREN_OPEN)) { // See if the return is fully paren'd cpar = next->GetNextType(CT_PAREN_CLOSE, next->level); if (cpar->IsNullChunk()) { return(Chunk::NullChunkPtr); } semi = cpar->PpaGetNextNcNnl(); if (semi->IsNullChunk()) { return(Chunk::NullChunkPtr); } if ( chunk_is_token(semi, CT_NEWLINE) || chunk_is_semicolon(semi)) { log_rule_B("mod_paren_on_return"); if (options::mod_paren_on_return() == IARF_REMOVE) { LOG_FMT(LRETURN, "%s(%d): removing parens on orig_line %zu\n", __func__, __LINE__, pc->orig_line); // lower the level of everything for (temp = next; temp != cpar; temp = temp->GetNext()) { if (temp->level == 0) { fprintf(stderr, "%s(%d): temp->level is ZERO, cannot be decremented, at line %zu, column %zu\n", __func__, __LINE__, temp->orig_line, temp->orig_col); log_flush(true); exit(EX_SOFTWARE); } temp->level--; } // delete the parenthesis chunk_del(next); chunk_del(cpar); // back up following chunks temp = semi; while ( temp->IsNotNullChunk() && chunk_is_not_token(temp, CT_NEWLINE)) { temp->column = temp->column - 2; temp->orig_col = temp->orig_col - 2; temp->orig_col_end = temp->orig_col_end - 2; temp = temp->GetNext(); } } else { LOG_FMT(LRETURN, "%s(%d): keeping parens on orig_line %zu\n", __func__, __LINE__, pc->orig_line); // mark & keep them set_chunk_parent(next, CT_RETURN); set_chunk_parent(cpar, CT_RETURN); } return(semi); } } // We don't have a fully paren'd return. Should we add some? log_rule_B("mod_paren_on_return"); if (!(options::mod_paren_on_return() & IARF_ADD)) { return(next); } // Issue #1917 // Never add parens to a braced init list; that breaks the code // return {args...}; // C++11 type elision; okay // return ({args...}); // ill-formed if ( language_is_set(LANG_CPP) && chunk_is_token(next, CT_BRACE_OPEN) && get_chunk_parent_type(next) == CT_BRACED_INIT_LIST) { LOG_FMT(LRETURN, "%s(%d): not adding parens around braced initializer" " on orig_line %zd\n", __func__, __LINE__, pc->orig_line); return(next); } // find the next semicolon on the same level semi = next; if (pc->flags.test(PCF_IN_PREPROC)) { while ((semi = semi->GetNext())->IsNotNullChunk()) { if (!semi->flags.test(PCF_IN_PREPROC)) { break; } if (semi->level < pc->level) { return(semi); } if ( chunk_is_semicolon(semi) && pc->level == semi->level) { break; } } } else { while ((semi = semi->GetNext())->IsNotNullChunk()) { if (semi->level < pc->level) { return(semi); } if ( chunk_is_semicolon(semi) && pc->level == semi->level) { break; } } } if (semi) { // add the parenthesis set_chunk_type(&chunk, CT_PAREN_OPEN); set_chunk_parent(&chunk, CT_RETURN); chunk.str = "("; chunk.level = pc->level; chunk.pp_level = pc->pp_level; chunk.brace_level = pc->brace_level; chunk.orig_line = pc->orig_line; chunk.orig_col = next->orig_col - 1; chunk.flags = pc->flags & PCF_COPY_FLAGS; chunk_add_before(&chunk, next); set_chunk_type(&chunk, CT_PAREN_CLOSE); chunk.str = ")"; chunk.orig_line = semi->orig_line; chunk.orig_col = semi->orig_col - 1; cpar = chunk_add_before(&chunk, semi); LOG_FMT(LRETURN, "%s(%d): added parens on orig_line %zu\n", __func__, __LINE__, pc->orig_line); for (temp = next; temp != cpar; temp = temp->GetNext()) { temp->level++; } } return(semi); } // process_return static bool is_oc_block(Chunk *pc) { return( pc != nullptr && ( get_chunk_parent_type(pc) == CT_OC_BLOCK_TYPE || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR || get_chunk_parent_type(pc) == CT_OC_BLOCK_ARG || get_chunk_parent_type(pc) == CT_OC_BLOCK || chunk_is_token(pc, CT_OC_BLOCK_CARET) || ( pc->next != nullptr && pc->next->type == CT_OC_BLOCK_CARET) || ( pc->prev != nullptr && pc->prev->type == CT_OC_BLOCK_CARET))); } void mark_comments(void) { LOG_FUNC_ENTRY(); cpd.unc_stage = unc_stage_e::MARK_COMMENTS; bool prev_nl = true; Chunk *cur = Chunk::GetHead(); while (cur->IsNotNullChunk()) { Chunk *next = cur->GetNextNvb(); bool next_nl = next->IsNullChunk() || chunk_is_newline(next); if (cur->IsComment()) { if ( next_nl && prev_nl) { set_chunk_parent(cur, CT_COMMENT_WHOLE); } else if (next_nl) { set_chunk_parent(cur, CT_COMMENT_END); } else if (prev_nl) { set_chunk_parent(cur, CT_COMMENT_START); } else { set_chunk_parent(cur, CT_COMMENT_EMBED); } } prev_nl = chunk_is_newline(cur); cur = next; } } static void handle_cpp_template(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *tmp = pc->GetNextNcNnl(); if (chunk_is_not_token(tmp, CT_ANGLE_OPEN)) { return; } set_chunk_parent(tmp, CT_TEMPLATE); size_t level = tmp->level; tmp = tmp->GetNext(); while (tmp->IsNotNullChunk()) { if ( chunk_is_token(tmp, CT_CLASS) || chunk_is_token(tmp, CT_STRUCT)) { set_chunk_type(tmp, CT_TYPE); } else if ( chunk_is_token(tmp, CT_ANGLE_CLOSE) && tmp->level == level) { set_chunk_parent(tmp, CT_TEMPLATE); break; } tmp = tmp->GetNext(); } if (tmp->IsNotNullChunk()) { tmp = tmp->GetNextNcNnl(); if (chunk_is_token(tmp, CT_FRIEND)) { // Account for a template friend declaration set_chunk_parent(tmp, CT_TEMPLATE); tmp = tmp->GetNextNcNnl(); } if ( chunk_is_token(tmp, CT_CLASS) || chunk_is_token(tmp, CT_STRUCT)) { set_chunk_parent(tmp, CT_TEMPLATE); // REVISIT: This may be a bit risky - might need to track the { }; tmp = tmp->GetNextType(CT_SEMICOLON, tmp->level); if (tmp->IsNotNullChunk()) { set_chunk_parent(tmp, CT_TEMPLATE); } } } } // handle_cpp_template static void handle_cpp_lambda(Chunk *sq_o) { LOG_FUNC_ENTRY(); Chunk *ret = Chunk::NullChunkPtr; // abort if type of the previous token is not contained in this whitelist Chunk *prev = sq_o->GetPrevNcNnlNi(); // Issue #2279 if (prev->IsNullChunk()) { LOG_FMT(LFCNR, "%s(%d): prev is nullptr\n", __func__, __LINE__); } if ( prev->IsNullChunk() || ( chunk_is_not_token(prev, CT_ASSIGN) && chunk_is_not_token(prev, CT_COMMA) && chunk_is_not_token(prev, CT_PAREN_OPEN) // allow Js like self invoking lambda syntax: ([](){})(); && chunk_is_not_token(prev, CT_FPAREN_OPEN) && chunk_is_not_token(prev, CT_SQUARE_OPEN) && chunk_is_not_token(prev, CT_BRACE_OPEN) && chunk_is_not_token(prev, CT_SEMICOLON) && chunk_is_not_token(prev, CT_RETURN))) { LOG_FMT(LFCNR, "%s(%d): return\n", __func__, __LINE__); return; } Chunk *sq_c = sq_o; // assuming '[]' if (chunk_is_token(sq_o, CT_SQUARE_OPEN)) { // make sure there is a ']' sq_c = chunk_skip_to_match(sq_o); if (sq_c == nullptr) { LOG_FMT(LFCNR, "%s(%d): return\n", __func__, __LINE__); return; } } Chunk *pa_o = sq_c->GetNextNcNnl(); // check to see if there is a lambda-specifier in the pa_o chunk; // assuming chunk is CT_EXECUTION_CONTEXT, ignore lambda-specifier while (chunk_is_token(pa_o, CT_EXECUTION_CONTEXT)) { // set pa_o to next chunk after this specifier pa_o = pa_o->GetNextNcNnl(); } if (pa_o->IsNullChunk()) { LOG_FMT(LFCNR, "%s(%d): return\n", __func__, __LINE__); return; } Chunk *pa_c = Chunk::NullChunkPtr; // lambda-declarator '( params )' is optional if (chunk_is_token(pa_o, CT_PAREN_OPEN)) { // and now find the ')' pa_c = chunk_skip_to_match(pa_o); if (pa_c->IsNullChunk()) { LOG_FMT(LFCNR, "%s(%d): return\n", __func__, __LINE__); return; } } // Check for 'mutable' keyword: '[]() mutable {}' or []() mutable -> ret {} Chunk *br_o = pa_c->IsNotNullChunk() ? pa_c->GetNextNcNnl() : pa_o; if (chunk_is_str(br_o, "mutable")) { br_o = br_o->GetNextNcNnl(); } //TODO: also check for exception and attribute between [] ... {} // skip possible arrow syntax: '-> ret' if (chunk_is_str(br_o, "->")) { ret = br_o; // REVISIT: really should check the stuff we are skipping br_o = br_o->GetNextType(CT_BRACE_OPEN, br_o->level); } // skip possible CT_NOEXCEPT if (chunk_is_token(br_o, CT_NOEXCEPT)) // Issue #3321 { ret = br_o; // REVISIT: really should check the stuff we are skipping br_o = br_o->GetNextType(CT_BRACE_OPEN, br_o->level); } if (br_o->IsNullChunk()) { LOG_FMT(LFCNR, "%s(%d): br_o is null. Return\n", __func__, __LINE__); return; } if (chunk_is_not_token(br_o, CT_BRACE_OPEN)) { LOG_FMT(LFCNR, "%s(%d): br_o is '%s'/%s\n", __func__, __LINE__, br_o->Text(), get_token_name(br_o->type)); LOG_FMT(LFCNR, "%s(%d): return\n", __func__, __LINE__); return; } // and now find the '}' Chunk *br_c = chunk_skip_to_match(br_o); if (br_c->IsNullChunk()) { LOG_FMT(LFCNR, "%s(%d): return\n", __func__, __LINE__); return; } // This looks like a lambda expression if (chunk_is_token(sq_o, CT_TSQUARE)) { // split into two chunks Chunk nc; nc = *sq_o; set_chunk_type(sq_o, CT_SQUARE_OPEN); sq_o->str.resize(1); /* * bug # 664 * * The original orig_col of CT_SQUARE_CLOSE is stored at orig_col_end * of CT_TSQUARE. CT_SQUARE_CLOSE orig_col and orig_col_end values * are calculate from orig_col_end of CT_TSQUARE. */ nc.orig_col = sq_o->orig_col_end - 1; nc.column = static_cast(nc.orig_col); nc.orig_col_end = sq_o->orig_col_end; sq_o->orig_col_end = sq_o->orig_col + 1; set_chunk_type(&nc, CT_SQUARE_CLOSE); nc.str.pop_front(); sq_c = chunk_add_after(&nc, sq_o); } set_chunk_parent(sq_o, CT_CPP_LAMBDA); set_chunk_parent(sq_c, CT_CPP_LAMBDA); if (pa_c->IsNotNullChunk()) { set_chunk_type(pa_o, CT_LPAREN_OPEN); // Issue #3054 set_chunk_parent(pa_o, CT_CPP_LAMBDA); chunk_set_parent(pa_o, sq_o); chunk_set_parent(br_o, sq_o); set_chunk_type(pa_c, CT_LPAREN_CLOSE); set_chunk_parent(pa_c, CT_CPP_LAMBDA); chunk_set_parent(pa_c, sq_o); chunk_set_parent(br_c, sq_o); } set_chunk_parent(br_o, CT_CPP_LAMBDA); set_chunk_parent(br_c, CT_CPP_LAMBDA); if (ret->IsNotNullChunk()) { set_chunk_type(ret, CT_CPP_LAMBDA_RET); ret = ret->GetNextNcNnl(); while (ret != br_o) { make_type(ret); ret = ret->GetNextNcNnl(); } } if (pa_c->IsNotNullChunk()) { fix_fcn_def_params(pa_o); } //handle self calling lambda paren Chunk *call_pa_o = br_c->GetNextNcNnl(); if (chunk_is_token(call_pa_o, CT_PAREN_OPEN)) { Chunk *call_pa_c = chunk_skip_to_match(call_pa_o); if (call_pa_c->IsNotNullChunk()) { set_chunk_type(call_pa_o, CT_FPAREN_OPEN); set_chunk_parent(call_pa_o, CT_FUNC_CALL); set_chunk_type(call_pa_c, CT_FPAREN_CLOSE); set_chunk_parent(call_pa_c, CT_FUNC_CALL); } } mark_cpp_lambda(sq_o); } // handle_cpp_lambda static void handle_d_template(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *name = pc->GetNextNcNnl(); Chunk *po = name->GetNextNcNnl(); //if (!name || (name->type != CT_WORD && name->type != CT_WORD)) Coverity CID 76000 Same on both sides, 2016-03-16 if ( name->IsNullChunk() || chunk_is_not_token(name, CT_WORD)) { // TODO: log an error, expected NAME return; } if ( po->IsNullChunk() || chunk_is_not_token(po, CT_PAREN_OPEN)) { // TODO: log an error, expected '(' return; } set_chunk_type(name, CT_TYPE); set_chunk_parent(name, CT_TEMPLATE); set_chunk_parent(po, CT_TEMPLATE); ChunkStack cs; Chunk *tmp = get_d_template_types(cs, po); if ( tmp == nullptr || chunk_is_not_token(tmp, CT_PAREN_CLOSE)) { // TODO: log an error, expected ')' return; } set_chunk_parent(tmp, CT_TEMPLATE); tmp = tmp->GetNextNcNnl(); if (chunk_is_not_token(tmp, CT_BRACE_OPEN)) { // TODO: log an error, expected '{' return; } set_chunk_parent(tmp, CT_TEMPLATE); po = tmp; tmp = tmp->GetNextNcNnl(); while ( tmp->IsNotNullChunk() && tmp->level > po->level) { if ( chunk_is_token(tmp, CT_WORD) && chunkstack_match(cs, tmp)) { set_chunk_type(tmp, CT_TYPE); } tmp = tmp->GetNextNcNnl(); } // if (!chunk_is_token(tmp, CT_BRACE_CLOSE)) // { // // TODO: log an error, expected '}' // } set_chunk_parent(tmp, CT_TEMPLATE); } // handle_d_template Chunk *skip_template_next(Chunk *ang_open) { if (ang_open == nullptr) { return(Chunk::NullChunkPtr); } if (chunk_is_token(ang_open, CT_ANGLE_OPEN)) { Chunk *pc = ang_open->GetNextType(CT_ANGLE_CLOSE, ang_open->level); if (pc->IsNullChunk()) { return(Chunk::NullChunkPtr); } return(pc->GetNextNcNnl()); } return(ang_open); } static void handle_oc_class(Chunk *pc) { enum class angle_state_e : unsigned int { NONE = 0, OPEN = 1, // '<' found CLOSE = 2, // '>' found }; LOG_FUNC_ENTRY(); Chunk *tmp; bool hit_scope = false; bool passed_name = false; // Did we pass the name of the class and now there can be only protocols, not generics int generic_level = 0; // level of depth of generic angle_state_e as = angle_state_e::NONE; LOG_FMT(LOCCLASS, "%s(%d): start [%s] [%s] line %zu\n", __func__, __LINE__, pc->Text(), get_token_name(get_chunk_parent_type(pc)), pc->orig_line); if (get_chunk_parent_type(pc) == CT_OC_PROTOCOL) { tmp = pc->GetNextNcNnl(); if (chunk_is_semicolon(tmp)) { set_chunk_parent(tmp, get_chunk_parent_type(pc)); LOG_FMT(LOCCLASS, "%s(%d): bail on semicolon\n", __func__, __LINE__); return; } } tmp = pc; while ( (tmp = tmp->GetNextNnl()) != nullptr && tmp->IsNotNullChunk()) { LOG_FMT(LOCCLASS, "%s(%d): orig_line is %zu, [%s]\n", __func__, __LINE__, tmp->orig_line, tmp->Text()); if (chunk_is_token(tmp, CT_OC_END)) { break; } if (chunk_is_token(tmp, CT_PAREN_OPEN)) { passed_name = true; } if (chunk_is_str(tmp, "<")) { set_chunk_type(tmp, CT_ANGLE_OPEN); if (passed_name) { set_chunk_parent(tmp, CT_OC_PROTO_LIST); } else { set_chunk_parent(tmp, CT_OC_GENERIC_SPEC); generic_level++; } as = angle_state_e::OPEN; } if (chunk_is_str(tmp, ">")) { set_chunk_type(tmp, CT_ANGLE_CLOSE); if (passed_name) { set_chunk_parent(tmp, CT_OC_PROTO_LIST); as = angle_state_e::CLOSE; } else { set_chunk_parent(tmp, CT_OC_GENERIC_SPEC); if (generic_level == 0) { fprintf(stderr, "%s(%d): generic_level is ZERO, cannot be decremented, at line %zu, column %zu\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col); log_flush(true); exit(EX_SOFTWARE); } generic_level--; if (generic_level == 0) { as = angle_state_e::CLOSE; } } } if (chunk_is_str(tmp, ">>")) { set_chunk_type(tmp, CT_ANGLE_CLOSE); set_chunk_parent(tmp, CT_OC_GENERIC_SPEC); split_off_angle_close(tmp); generic_level -= 1; if (generic_level == 0) { as = angle_state_e::CLOSE; } } if ( chunk_is_token(tmp, CT_BRACE_OPEN) && get_chunk_parent_type(tmp) != CT_ASSIGN) { as = angle_state_e::CLOSE; set_chunk_parent(tmp, CT_OC_CLASS); tmp = tmp->GetNextType(CT_BRACE_CLOSE, tmp->level); if ( tmp->IsNotNullChunk() && get_chunk_parent_type(tmp) != CT_ASSIGN) { set_chunk_parent(tmp, CT_OC_CLASS); } } else if (chunk_is_token(tmp, CT_COLON)) { if (as != angle_state_e::OPEN) { passed_name = true; } set_chunk_type(tmp, hit_scope ? CT_OC_COLON : CT_CLASS_COLON); if (chunk_is_token(tmp, CT_CLASS_COLON)) { set_chunk_parent(tmp, CT_OC_CLASS); } } else if ( chunk_is_str(tmp, "-") || chunk_is_str(tmp, "+")) { as = angle_state_e::CLOSE; if (chunk_is_newline(tmp->GetPrev())) { set_chunk_type(tmp, CT_OC_SCOPE); chunk_flags_set(tmp, PCF_STMT_START); hit_scope = true; } } if (as == angle_state_e::OPEN) { if (passed_name) { set_chunk_parent(tmp, CT_OC_PROTO_LIST); } else { set_chunk_parent(tmp, CT_OC_GENERIC_SPEC); } } } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { tmp = tmp->GetNextType(CT_BRACE_CLOSE, tmp->level); if (tmp->IsNotNullChunk()) { set_chunk_parent(tmp, CT_OC_CLASS); } } } // handle_oc_class static void handle_oc_block_literal(Chunk *pc) { LOG_FUNC_ENTRY(); if (pc == nullptr) { return; // let's be paranoid } Chunk *prev = pc->GetPrevNcNnlNi(); // Issue #2279 Chunk *next = pc->GetNextNcNnl(); if ( prev->IsNullChunk() || next->IsNullChunk()) { return; // let's be paranoid } /* * block literal: '^ RTYPE ( ARGS ) { }' * RTYPE and ARGS are optional */ LOG_FMT(LOCBLK, "%s(%d): block literal @ orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, pc->orig_line, pc->orig_col); Chunk *apo = Chunk::NullChunkPtr; // arg paren open Chunk *bbo = Chunk::NullChunkPtr; // block brace open Chunk *bbc; // block brace close LOG_FMT(LOCBLK, "%s(%d): + scan", __func__, __LINE__); Chunk *tmp; for (tmp = next; tmp->IsNotNullChunk(); tmp = tmp->GetNextNcNnl()) { /* handle '< protocol >' */ if (chunk_is_str(tmp, "<")) { Chunk *ao = tmp; Chunk *ac = ao->GetNextString(">", 1, ao->level); if (ac->IsNotNullChunk()) { set_chunk_type(ao, CT_ANGLE_OPEN); set_chunk_parent(ao, CT_OC_PROTO_LIST); set_chunk_type(ac, CT_ANGLE_CLOSE); set_chunk_parent(ac, CT_OC_PROTO_LIST); for (tmp = ao->GetNext(); tmp != ac; tmp = tmp->GetNext()) { tmp->level += 1; set_chunk_parent(tmp, CT_OC_PROTO_LIST); } tmp = ac->GetNextNcNnl(); } else { tmp = Chunk::NullChunkPtr; } } LOG_FMT(LOCBLK, " '%s'", tmp->Text()); if ( tmp->level < pc->level || chunk_is_token(tmp, CT_SEMICOLON)) { LOG_FMT(LOCBLK, "[DONE]"); break; } if (tmp->level == pc->level) { if (chunk_is_paren_open(tmp)) { apo = tmp; LOG_FMT(LOCBLK, "[PAREN]"); } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { LOG_FMT(LOCBLK, "[BRACE]"); bbo = tmp; break; } } } // make sure we have braces bbc = chunk_skip_to_match(bbo); if ( bbo->IsNullChunk() || bbc == nullptr || bbc->IsNullChunk()) { LOG_FMT(LOCBLK, " -- no braces found\n"); return; } LOG_FMT(LOCBLK, "\n"); // we are on a block literal for sure set_chunk_type(pc, CT_OC_BLOCK_CARET); set_chunk_parent(pc, CT_OC_BLOCK_EXPR); // handle the optional args Chunk *lbp; // last before paren - end of return type, if any if (apo->IsNotNullChunk()) { Chunk *apc = chunk_skip_to_match(apo); // arg parenthesis close if (chunk_is_paren_close(apc)) { LOG_FMT(LOCBLK, " -- marking parens @ apo->orig_line is %zu, apo->orig_col is %zu and apc->orig_line is %zu, apc->orig_col is %zu\n", apo->orig_line, apo->orig_col, apc->orig_line, apc->orig_col); flag_parens(apo, PCF_OC_ATYPE, CT_FPAREN_OPEN, CT_OC_BLOCK_EXPR, true); fix_fcn_def_params(apo); } lbp = apo->GetPrevNcNnlNi(); // Issue #2279 } else { lbp = bbo->GetPrevNcNnlNi(); // Issue #2279 } // mark the return type, if any while (lbp != pc) { LOG_FMT(LOCBLK, " -- lbp %s[%s]\n", lbp->Text(), get_token_name(lbp->type)); make_type(lbp); chunk_flags_set(lbp, PCF_OC_RTYPE); set_chunk_parent(lbp, CT_OC_BLOCK_EXPR); lbp = lbp->GetPrevNcNnlNi(); // Issue #2279 } // mark the braces set_chunk_parent(bbo, CT_OC_BLOCK_EXPR); set_chunk_parent(bbc, CT_OC_BLOCK_EXPR); } // handle_oc_block_literal static void handle_oc_block_type(Chunk *pc) { LOG_FUNC_ENTRY(); if (pc == nullptr) { return; } if (pc->flags.test(PCF_IN_TYPEDEF)) { LOG_FMT(LOCBLK, "%s(%d): skip block type @ orig_line is %zu, orig_col is %zu, -- in typedef\n", __func__, __LINE__, pc->orig_line, pc->orig_col); return; } // make sure we have '( ^' Chunk *tpo = pc->GetPrevNcNnlNi(); // type paren open Issue #2279 if (chunk_is_paren_open(tpo)) { /* * block type: 'RTYPE (^LABEL)(ARGS)' * LABEL is optional. */ Chunk *tpc = chunk_skip_to_match(tpo); // type close paren (after '^') Chunk *nam = tpc->GetPrevNcNnlNi(); // name (if any) or '^' Issue #2279 Chunk *apo = tpc->GetNextNcNnl(); // arg open paren Chunk *apc = chunk_skip_to_match(apo); // arg close paren /* * If this is a block literal instead of a block type, 'nam' * will actually be the closing bracket of the block. We run into * this situation if a block literal is enclosed in parentheses. */ if (chunk_is_closing_brace(nam)) { return(handle_oc_block_literal(pc)); } // Check apo is '(' or else this might be a block literal. Issue 2643. if (!chunk_is_paren_open(apo)) { return(handle_oc_block_literal(pc)); } if (chunk_is_paren_close(apc)) { Chunk *aft = apc->GetNextNcNnl(); E_Token pt; if (chunk_is_str(nam, "^")) { set_chunk_type(nam, CT_PTR_TYPE); pt = CT_FUNC_TYPE; } else if ( chunk_is_token(aft, CT_ASSIGN) || chunk_is_token(aft, CT_SEMICOLON)) { set_chunk_type(nam, CT_FUNC_VAR); pt = CT_FUNC_VAR; } else { set_chunk_type(nam, CT_FUNC_TYPE); pt = CT_FUNC_TYPE; } LOG_FMT(LOCBLK, "%s(%d): block type @ orig_line is %zu, orig_col is %zu, Text() '%s'[%s]\n", __func__, __LINE__, pc->orig_line, pc->orig_col, nam->Text(), get_token_name(nam->type)); set_chunk_type(pc, CT_PTR_TYPE); set_chunk_parent(pc, pt); //CT_OC_BLOCK_TYPE; set_chunk_type(tpo, CT_TPAREN_OPEN); set_chunk_parent(tpo, pt); //CT_OC_BLOCK_TYPE; set_chunk_type(tpc, CT_TPAREN_CLOSE); set_chunk_parent(tpc, pt); //CT_OC_BLOCK_TYPE; set_chunk_type(apo, CT_FPAREN_OPEN); set_chunk_parent(apo, CT_FUNC_PROTO); set_chunk_type(apc, CT_FPAREN_CLOSE); set_chunk_parent(apc, CT_FUNC_PROTO); fix_fcn_def_params(apo); mark_function_return_type(nam, tpo->GetPrevNcNnlNi(), pt); // Issue #2279 } } } // handle_oc_block_type static Chunk *handle_oc_md_type(Chunk *paren_open, E_Token ptype, pcf_flags_t flags, bool &did_it) { Chunk *paren_close; if ( !chunk_is_paren_open(paren_open) || ((paren_close = chunk_skip_to_match(paren_open)) == nullptr)) { did_it = false; return(paren_open); } did_it = true; set_chunk_parent(paren_open, ptype); chunk_flags_set(paren_open, flags); set_chunk_parent(paren_close, ptype); chunk_flags_set(paren_close, flags); for (Chunk *cur = paren_open->GetNextNcNnl(); cur != paren_close; cur = cur->GetNextNcNnl()) { LOG_FMT(LOCMSGD, " <%s|%s>", cur->Text(), get_token_name(cur->type)); chunk_flags_set(cur, flags); make_type(cur); } // returning the chunk after the paren close return(paren_close->GetNextNcNnl()); } static void handle_oc_message_decl(Chunk *pc) { LOG_FUNC_ENTRY(); bool did_it; //bool in_paren = false; //int paren_cnt = 0; //int arg_cnt = 0; // Figure out if this is a spec or decl Chunk *tmp = pc; if (tmp == nullptr) { tmp = Chunk::NullChunkPtr; } while ((tmp = tmp->GetNext())->IsNotNullChunk()) { if (tmp->level < pc->level) { // should not happen return; } if ( chunk_is_token(tmp, CT_SEMICOLON) || chunk_is_token(tmp, CT_BRACE_OPEN)) { break; } } if (tmp == nullptr) { return; } E_Token pt = chunk_is_token(tmp, CT_SEMICOLON) ? CT_OC_MSG_SPEC : CT_OC_MSG_DECL; set_chunk_type(pc, CT_OC_SCOPE); set_chunk_parent(pc, pt); LOG_FMT(LOCMSGD, "%s(%d): %s @ orig_line is %zu, orig_col is %zu -", __func__, __LINE__, get_token_name(pt), pc->orig_line, pc->orig_col); // format: -(TYPE) NAME [: (TYPE)NAME // handle the return type tmp = handle_oc_md_type(pc->GetNextNcNnl(), pt, PCF_OC_RTYPE, did_it); if (!did_it) { LOG_FMT(LOCMSGD, " -- missing type parens\n"); return; } // expect the method name/label if (chunk_is_not_token(tmp, CT_WORD)) { LOG_FMT(LOCMSGD, " -- missing method name\n"); return; } // expect the method name/label Chunk *label = tmp; set_chunk_type(tmp, pt); set_chunk_parent(tmp, pt); pc = tmp->GetNextNcNnl(); LOG_FMT(LOCMSGD, " [%s]%s", pc->Text(), get_token_name(pc->type)); // if we have a colon next, we have args if ( chunk_is_token(pc, CT_COLON) || chunk_is_token(pc, CT_OC_COLON)) { pc = label; while (true) { // skip optional label if ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, pt)) { set_chunk_parent(pc, pt); pc = pc->GetNextNcNnl(); } // a colon must be next if (!chunk_is_str(pc, ":")) { break; } set_chunk_type(pc, CT_OC_COLON); set_chunk_parent(pc, pt); pc = pc->GetNextNcNnl(); // next is the type in parens LOG_FMT(LOCMSGD, " (%s)", pc->Text()); tmp = handle_oc_md_type(pc, pt, PCF_OC_ATYPE, did_it); if (!did_it) { LOG_FMT(LWARN, "%s(%d): orig_line is %zu, orig_col is %zu expected type\n", __func__, __LINE__, pc->orig_line, pc->orig_col); break; } // attributes for a method parameter sit between the parameter type and the parameter name pc = skip_attribute_next(tmp); // we should now be on the arg name chunk_flags_set(pc, PCF_VAR_DEF); LOG_FMT(LOCMSGD, " arg[%s]", pc->Text()); pc = pc->GetNextNcNnl(); } } LOG_FMT(LOCMSGD, " end[%s]", pc->Text()); if (chunk_is_token(pc, CT_BRACE_OPEN)) { set_chunk_parent(pc, pt); pc = chunk_skip_to_match(pc); if (pc != nullptr) { set_chunk_parent(pc, pt); } } else if (chunk_is_token(pc, CT_SEMICOLON)) { set_chunk_parent(pc, pt); } LOG_FMT(LOCMSGD, "\n"); } // handle_oc_message_decl static void handle_oc_message_send(Chunk *os) { LOG_FUNC_ENTRY(); Chunk *cs = Chunk::NullChunkPtr; if (os != nullptr) { cs = os->GetNext(); } while ( cs->IsNotNullChunk() && cs->level > os->level) { cs = cs->GetNext(); } if ( cs->IsNullChunk() || chunk_is_not_token(cs, CT_SQUARE_CLOSE)) { return; } LOG_FMT(LOCMSG, "%s(%d): orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, os->orig_line, os->orig_col); Chunk *tmp = cs->GetNextNcNnl(); if (chunk_is_semicolon(tmp)) { set_chunk_parent(tmp, CT_OC_MSG); } // expect a word first thing or [...] tmp = Chunk::NullChunkPtr; if (os != nullptr) { tmp = os->GetNextNcNnl(); } if ( chunk_is_token(tmp, CT_SQUARE_OPEN) || chunk_is_token(tmp, CT_PAREN_OPEN) || chunk_is_token(tmp, CT_OC_AT)) { Chunk *tt = tmp->GetNextNcNnl(); if ( chunk_is_token(tmp, CT_OC_AT) && tt->IsNotNullChunk()) { if ( chunk_is_token(tt, CT_PAREN_OPEN) || chunk_is_token(tt, CT_BRACE_OPEN) || chunk_is_token(tt, CT_SQUARE_OPEN)) { tmp = tt; } else { LOG_FMT(LOCMSG, "%s(%d): tmp->orig_line is %zu, tmp->orig_col is %zu, expected identifier, not '%s' [%s]\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->Text(), get_token_name(tmp->type)); return; } } tmp = chunk_skip_to_match(tmp); } else if ( chunk_is_not_token(tmp, CT_WORD) && chunk_is_not_token(tmp, CT_TYPE) && chunk_is_not_token(tmp, CT_THIS) && chunk_is_not_token(tmp, CT_STAR) && chunk_is_not_token(tmp, CT_STRING)) { LOG_FMT(LOCMSG, "%s(%d): orig_line is %zu, orig_col is %zu, expected identifier, not '%s' [%s]\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->Text(), get_token_name(tmp->type)); return; } else { if (tmp->IsStar()) // Issue #2722 { set_chunk_type(tmp, CT_PTR_TYPE); tmp = tmp->GetNextNcNnl(); } Chunk *tt = tmp->GetNextNcNnl(); if (chunk_is_paren_open(tt)) { LOG_FMT(LFCN, "%s(%d): (18) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->Text()); set_chunk_type(tmp, CT_FUNC_CALL); tmp = set_paren_parent(tt, CT_FUNC_CALL)->GetPrevNcNnlNi(); // Issue #2279 } else { set_chunk_type(tmp, CT_OC_MSG_CLASS); } } set_chunk_parent(os, CT_OC_MSG); chunk_flags_set(os, PCF_IN_OC_MSG); set_chunk_parent(cs, CT_OC_MSG); chunk_flags_set(cs, PCF_IN_OC_MSG); // handle '< protocol >' tmp = tmp->GetNextNcNnl(); if (chunk_is_str(tmp, "<")) { Chunk *ao = tmp; Chunk *ac = ao->GetNextString(">", 1, ao->level); if (ac->IsNotNullChunk()) { set_chunk_type(ao, CT_ANGLE_OPEN); set_chunk_parent(ao, CT_OC_PROTO_LIST); set_chunk_type(ac, CT_ANGLE_CLOSE); set_chunk_parent(ac, CT_OC_PROTO_LIST); for (tmp = ao->GetNext(); tmp != ac; tmp = tmp->GetNext()) { tmp->level += 1; set_chunk_parent(tmp, CT_OC_PROTO_LIST); } tmp = ac->GetNextNcNnl(); } else { tmp = Chunk::NullChunkPtr; } } // handle 'object.property' and 'collection[index]' else { while (tmp->IsNotNullChunk()) { if (chunk_is_token(tmp, CT_MEMBER)) // move past [object.prop1.prop2 { Chunk *typ = tmp->GetNextNcNnl(); if ( chunk_is_token(typ, CT_WORD) || chunk_is_token(typ, CT_TYPE)) { tmp = typ->GetNextNcNnl(); } else { break; } } else if (chunk_is_token(tmp, CT_SQUARE_OPEN)) // move past [collection[index] { Chunk *tcs = tmp->GetNextNcNnl(); while ( tcs->IsNotNullChunk() && tcs->level > tmp->level) { tcs = tcs->GetNextNcNnl(); } if (chunk_is_token(tcs, CT_SQUARE_CLOSE)) { tmp = tcs->GetNextNcNnl(); } else { break; } } else { break; } } } // [(self.foo.bar) method] if (chunk_is_paren_open(tmp)) { tmp = chunk_skip_to_match(tmp)->GetNextNcNnl(); } if ( chunk_is_token(tmp, CT_WORD) || chunk_is_token(tmp, CT_TYPE)) { set_chunk_type(tmp, CT_OC_MSG_FUNC); } Chunk *prev = Chunk::NullChunkPtr; for (tmp = os->GetNext(); tmp != cs; tmp = tmp->GetNext()) { chunk_flags_set(tmp, PCF_IN_OC_MSG); if (tmp->level == cs->level + 1) { if (chunk_is_token(tmp, CT_COLON)) { set_chunk_type(tmp, CT_OC_COLON); if ( chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE)) { // Might be a named param, check previous block Chunk *pp = prev->GetPrev(); if ( pp->IsNotNullChunk() && chunk_is_not_token(pp, CT_OC_COLON) && chunk_is_not_token(pp, CT_ARITH) && chunk_is_not_token(pp, CT_SHIFT) && chunk_is_not_token(pp, CT_CARET)) { set_chunk_type(prev, CT_OC_MSG_NAME); set_chunk_parent(tmp, CT_OC_MSG_NAME); } } } } prev = tmp; } } // handle_oc_message_send static void handle_oc_available(Chunk *os) { if (os != nullptr) { os = os->GetNext(); } else { os = Chunk::NullChunkPtr; } while (os->IsNotNullChunk()) { E_Token origType = os->type; set_chunk_type(os, CT_OC_AVAILABLE_VALUE); if (origType == CT_PAREN_CLOSE) { break; } os = os->GetNext(); } } static void handle_oc_property_decl(Chunk *os) { log_rule_B("mod_sort_oc_properties"); if (options::mod_sort_oc_properties()) { typedef std::vector ChunkGroup; Chunk *next = Chunk::NullChunkPtr; if (os != nullptr) { next = os->GetNext(); } Chunk *open_paren = nullptr; std::vector class_chunks; // class std::vector thread_chunks; // atomic, nonatomic std::vector readwrite_chunks; // readwrite, readonly std::vector ref_chunks; // retain, copy, assign, weak, strong, unsafe_unretained std::vector getter_chunks; // getter std::vector setter_chunks; // setter std::vector nullability_chunks; // nonnull, nullable, null_unspecified, null_resettable std::vector other_chunks; // any words other than above if (chunk_is_token(next, CT_PAREN_OPEN)) { open_paren = next; next = next->GetNext(); /* * Determine location of the property attributes * NOTE: Did not do this in the combine.cpp do_symbol_check as * I was not sure what the ramifications of adding a new type * for each of the below types would be. It did break some items * when I attempted to add them so this is my hack for now. */ while ( next->IsNotNullChunk() && chunk_is_not_token(next, CT_PAREN_CLOSE)) { if (chunk_is_token(next, CT_OC_PROPERTY_ATTR)) { if ( chunk_is_str(next, "atomic") || chunk_is_str(next, "nonatomic")) { ChunkGroup chunkGroup; chunkGroup.push_back(next); thread_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "readonly") || chunk_is_str(next, "readwrite")) { ChunkGroup chunkGroup; chunkGroup.push_back(next); readwrite_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "assign") || chunk_is_str(next, "retain") || chunk_is_str(next, "copy") || chunk_is_str(next, "strong") || chunk_is_str(next, "weak") || chunk_is_str(next, "unsafe_unretained")) { ChunkGroup chunkGroup; chunkGroup.push_back(next); ref_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "getter")) { ChunkGroup chunkGroup; do { chunkGroup.push_back(next); next = next->GetNext(); } while ( next->IsNotNullChunk() && chunk_is_not_token(next, CT_COMMA) && chunk_is_not_token(next, CT_PAREN_CLOSE)); next = next->GetPrev(); // coverity CID 160946 if (next->IsNullChunk()) { break; } getter_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "setter")) { ChunkGroup chunkGroup; do { chunkGroup.push_back(next); next = next->GetNext(); } while ( next->IsNotNullChunk() && chunk_is_not_token(next, CT_COMMA) && chunk_is_not_token(next, CT_PAREN_CLOSE)); if (next->IsNotNullChunk()) { next = next->GetPrev(); } if (next->IsNullChunk()) { break; } setter_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "nullable") || chunk_is_str(next, "nonnull") || chunk_is_str(next, "null_resettable") || chunk_is_str(next, "null_unspecified")) { ChunkGroup chunkGroup; chunkGroup.push_back(next); nullability_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "class")) { ChunkGroup chunkGroup; chunkGroup.push_back(next); class_chunks.push_back(chunkGroup); } else { ChunkGroup chunkGroup; chunkGroup.push_back(next); other_chunks.push_back(chunkGroup); } } else if (chunk_is_word(next)) { if (chunk_is_str(next, "class")) { ChunkGroup chunkGroup; chunkGroup.push_back(next); class_chunks.push_back(chunkGroup); } else { ChunkGroup chunkGroup; chunkGroup.push_back(next); other_chunks.push_back(chunkGroup); } } next = next->GetNext(); } log_rule_B("mod_sort_oc_property_class_weight"); int class_w = options::mod_sort_oc_property_class_weight(); log_rule_B("mod_sort_oc_property_thread_safe_weight"); int thread_w = options::mod_sort_oc_property_thread_safe_weight(); log_rule_B("mod_sort_oc_property_readwrite_weight"); int readwrite_w = options::mod_sort_oc_property_readwrite_weight(); log_rule_B("mod_sort_oc_property_reference_weight"); int ref_w = options::mod_sort_oc_property_reference_weight(); log_rule_B("mod_sort_oc_property_getter_weight"); int getter_w = options::mod_sort_oc_property_getter_weight(); log_rule_B("mod_sort_oc_property_setter_weight"); int setter_w = options::mod_sort_oc_property_setter_weight(); log_rule_B("mod_sort_oc_property_nullability_weight"); int nullability_w = options::mod_sort_oc_property_nullability_weight(); // std::multimap > sorted_chunk_map; sorted_chunk_map.insert(pair >(class_w, class_chunks)); sorted_chunk_map.insert(pair >(thread_w, thread_chunks)); sorted_chunk_map.insert(pair >(readwrite_w, readwrite_chunks)); sorted_chunk_map.insert(pair >(ref_w, ref_chunks)); sorted_chunk_map.insert(pair >(getter_w, getter_chunks)); sorted_chunk_map.insert(pair >(setter_w, setter_chunks)); sorted_chunk_map.insert(pair >(nullability_w, nullability_chunks)); sorted_chunk_map.insert(pair >(std::numeric_limits::min(), other_chunks)); Chunk *curr_chunk = open_paren; for (multimap >::reverse_iterator it = sorted_chunk_map.rbegin(); it != sorted_chunk_map.rend(); ++it) { std::vector chunk_groups = (*it).second; for (auto chunk_group : chunk_groups) { for (auto chunk : chunk_group) { chunk->orig_prev_sp = 0; if (chunk != curr_chunk) { chunk_move_after(chunk, curr_chunk); curr_chunk = chunk; } else { curr_chunk = curr_chunk->GetNext(); } } // add the parenthesis Chunk endchunk; set_chunk_type(&endchunk, CT_COMMA); set_chunk_parent(&endchunk, get_chunk_parent_type(curr_chunk)); endchunk.str = ","; endchunk.level = curr_chunk->level; endchunk.pp_level = curr_chunk->pp_level; endchunk.brace_level = curr_chunk->brace_level; endchunk.orig_line = curr_chunk->orig_line; endchunk.orig_col = curr_chunk->orig_col; endchunk.column = curr_chunk->orig_col_end + 1; endchunk.flags = curr_chunk->flags & PCF_COPY_FLAGS; chunk_add_after(&endchunk, curr_chunk); curr_chunk = curr_chunk->GetNext(); } } // Remove the extra comma's that we did not move while ( curr_chunk != nullptr && curr_chunk->IsNotNullChunk() && chunk_is_not_token(curr_chunk, CT_PAREN_CLOSE)) { Chunk *rm_chunk = curr_chunk; curr_chunk = curr_chunk->GetNext(); chunk_del(rm_chunk); } } } Chunk *tmp = Chunk::NullChunkPtr; if (os != nullptr) { tmp = os->GetNextNcNnl(); } if (chunk_is_paren_open(tmp)) { tmp = chunk_skip_to_match(tmp)->GetNextNcNnl(); } fix_variable_definition(tmp); } // handle_oc_property_decl static void handle_cs_square_stmt(Chunk *os) { LOG_FUNC_ENTRY(); if (os == nullptr) { os = Chunk::NullChunkPtr; } Chunk *cs = os->GetNext(); while ( cs->IsNotNullChunk() && cs->level > os->level) { cs = cs->GetNext(); } if ( cs->IsNullChunk() || chunk_is_not_token(cs, CT_SQUARE_CLOSE)) { return; } set_chunk_parent(os, CT_CS_SQ_STMT); set_chunk_parent(cs, CT_CS_SQ_STMT); Chunk *tmp; for (tmp = os->GetNext(); tmp != cs; tmp = tmp->GetNext()) { set_chunk_parent(tmp, CT_CS_SQ_STMT); if (chunk_is_token(tmp, CT_COLON)) { set_chunk_type(tmp, CT_CS_SQ_COLON); } } tmp = cs->GetNextNcNnl(); if (tmp->IsNotNullChunk()) { chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START); } } // handle_cs_square_stmt static void handle_cs_property(Chunk *bro) { LOG_FUNC_ENTRY(); set_paren_parent(bro, CT_CS_PROPERTY); bool did_prop = false; Chunk *pc = bro; while ((pc = pc->GetPrevNcNnlNi())->IsNotNullChunk()) // Issue #2279 { if (pc->level == bro->level) { //prevent scanning back past 'new' in expressions like new List {1,2,3} // Issue # 1620, UNI-24090.cs if (chunk_is_token(pc, CT_NEW)) { break; } if ( !did_prop && ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_THIS))) { set_chunk_type(pc, CT_CS_PROPERTY); did_prop = true; } else { set_chunk_parent(pc, CT_CS_PROPERTY); make_type(pc); } if (pc->flags.test(PCF_STMT_START)) { break; } } } } static void handle_cs_array_type(Chunk *pc) { if ( pc == nullptr || pc->IsNullChunk()) { return; } Chunk *prev = pc->GetPrev(); for ( ; chunk_is_token(prev, CT_COMMA); prev = prev->GetPrev()) { // empty } if (chunk_is_token(prev, CT_SQUARE_OPEN)) { while (pc != prev) { set_chunk_parent(pc, CT_TYPE); pc = pc->GetPrev(); } set_chunk_parent(prev, CT_TYPE); } } static void handle_wrap(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *opp = Chunk::NullChunkPtr; if (pc != nullptr) { opp = pc->GetNext(); } Chunk *name = opp->GetNext(); Chunk *clp = name->GetNext(); log_rule_B("sp_func_call_paren"); log_rule_B("sp_cpp_cast_paren"); iarf_e pav = chunk_is_token(pc, CT_FUNC_WRAP) ? options::sp_func_call_paren() : options::sp_cpp_cast_paren(); log_rule_B("sp_inside_fparen"); log_rule_B("sp_inside_paren_cast"); iarf_e av = chunk_is_token(pc, CT_FUNC_WRAP) ? options::sp_inside_fparen() : options::sp_inside_paren_cast(); if ( chunk_is_token(clp, CT_PAREN_CLOSE) && chunk_is_token(opp, CT_PAREN_OPEN) && ( chunk_is_token(name, CT_WORD) || chunk_is_token(name, CT_TYPE))) { const char *psp = (pav & IARF_ADD) ? " " : ""; const char *fsp = (av & IARF_ADD) ? " " : ""; pc->str.append(psp); pc->str.append("("); pc->str.append(fsp); pc->str.append(name->str); pc->str.append(fsp); pc->str.append(")"); set_chunk_type(pc, chunk_is_token(pc, CT_FUNC_WRAP) ? CT_FUNCTION : CT_TYPE); pc->orig_col_end = pc->orig_col + pc->Len(); chunk_del(opp); chunk_del(name); chunk_del(clp); } } // handle_wrap static void handle_proto_wrap(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *opp = pc->GetNextNcNnl(); Chunk *name = opp->GetNextNcNnl(); Chunk *tmp = name->GetNextNcNnl()->GetNextNcNnl(); Chunk *clp = chunk_skip_to_match(opp); Chunk *cma = clp->GetNextNcNnl(); if ( opp->IsNullChunk() || name->IsNullChunk() || tmp->IsNullChunk() || clp == nullptr || cma->IsNullChunk() || ( chunk_is_not_token(name, CT_WORD) && chunk_is_not_token(name, CT_TYPE)) || chunk_is_not_token(opp, CT_PAREN_OPEN)) { return; } if (chunk_is_token(cma, CT_SEMICOLON)) { set_chunk_type(pc, CT_FUNC_PROTO); } else if (chunk_is_token(cma, CT_BRACE_OPEN)) { LOG_FMT(LFCN, "%s(%d): (19) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, Text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->Text()); set_chunk_type(pc, CT_FUNC_DEF); } else { return; } set_chunk_parent(opp, pc->type); set_chunk_parent(clp, pc->type); set_chunk_parent(tmp, CT_PROTO_WRAP); if (chunk_is_token(tmp, CT_PAREN_OPEN)) { fix_fcn_def_params(tmp); } else { fix_fcn_def_params(opp); set_chunk_type(name, CT_WORD); } tmp = chunk_skip_to_match(tmp); if (tmp) { set_chunk_parent(tmp, CT_PROTO_WRAP); } // Mark return type (TODO: move to own function) tmp = pc; while ((tmp = tmp->GetPrevNcNnlNi())->IsNotNullChunk()) // Issue #2279 { if ( !chunk_is_type(tmp) && chunk_is_not_token(tmp, CT_OPERATOR) && chunk_is_not_token(tmp, CT_WORD) && chunk_is_not_token(tmp, CT_ADDR)) { break; } set_chunk_parent(tmp, pc->type); make_type(tmp); } } // handle_proto_wrap /** * Java assert statements are: "assert EXP1 [: EXP2] ;" * Mark the parent of the colon and semicolon */ static void handle_java_assert(Chunk *pc) { LOG_FUNC_ENTRY(); bool did_colon = false; Chunk *tmp = pc; if (tmp == nullptr) { tmp = Chunk::NullChunkPtr; } while ((tmp = tmp->GetNext())->IsNotNullChunk()) { if (tmp->level == pc->level) { if ( !did_colon && chunk_is_token(tmp, CT_COLON)) { did_colon = true; set_chunk_parent(tmp, pc->type); } if (chunk_is_token(tmp, CT_SEMICOLON)) { set_chunk_parent(tmp, pc->type); break; } } } }