Hello! Using the appended two files: git diff combine.cpp-2020-04-16-A combine.cpp-2020-04-16-R > git-diff.diff-Y doesn't show the same differences as with: meld combine.cpp-2020-04-16-R combine.cpp-2020-04-16-A Have a look at git-diff.diff-Y at line 210: -static void process_returns(void) +static void mark_lvalue(chunk_t *pc) which is NOT correct. What is to do? Thanks -- Guy Maurel Sebastian-Fischer-Weg 13 89077 Ulm
/** * @file combine.cpp * Labels the chunks as needed. * * @author Ben Gardner * @author Guy Maurel since version 0.62 for uncrustify4Qt * October 2015, 2016 * @license GPL v2+ */ #include "combine.h" #include "chunk_list.h" #include "combine_labels.h" #include "ChunkStack.h" #include "error_types.h" #include "flag_parens.h" #include "lang_pawn.h" #include "language_tools.h" #include "log_rules.h" #include "newlines.h" #include "prototypes.h" #include "tokenize_cleanup.h" #include "unc_ctype.h" #include "uncrustify.h" #include "uncrustify_types.h" #include <cassert> #include <cstdio> #include <cstdlib> #include <limits> #include <map> 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_t *pc); /** * Combines two tokens into {{ and }} if inside parens and nothing is between * either pair. */ static void check_double_brace_init(chunk_t *bo1); /** * We are on a typedef. * If the next word is not enum/union/struct, then the last word before the * next ',' or ';' or '__attribute__' is a type. * * typedef [type...] [*] type [, [*]type] ; * typedef <return type>([*]func)(); * typedef <return type>([*]func)(params); * typedef <return type>(__stdcall *func)(); Bug # 633 MS-specific extension * include the config-file "test/config/MS-calling_conventions.cfg" * typedef <return type>func(params); * typedef <enum/struct/union> [type] [*] type [, [*]type] ; * typedef <enum/struct/union> [type] { ... } [*] type [, [*]type] ; */ static void fix_typedef(chunk_t *pc); /** * We are on an enum/struct/union tag that is NOT inside a typedef. * If there is a {...} and words before the ';', then they are variables. * * tag { ... } [*] word [, [*]word] ; * tag [word/type] { ... } [*] word [, [*]word] ; * enum [word/type [: int_type]] { ... } [*] word [, [*]word] ; * tag [word/type] [word]; -- this gets caught later. * fcn(tag [word/type] [word]) * a = (tag [word/type] [*])&b; * * REVISIT: should this be consolidated with the typedef code? */ static void fix_enum_struct_union(chunk_t *pc); /** * Checks to see if the current paren is part of a cast. * We already verified that this doesn't follow function, TYPE, IF, FOR, * SWITCH, or WHILE and is followed by WORD, TYPE, STRUCT, ENUM, or UNION. * * @param start Pointer to the open paren */ static void fix_casts(chunk_t *pc); /** * CT_TYPE_CAST follows this pattern: * dynamic_cast<...>(...) * * Mark everything between the <> as a type and set the paren parent */ static void fix_type_cast(chunk_t *pc); 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_t *process_return(chunk_t *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_t *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_t *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_t *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_t *pc); /** * Process an ObjC message send statement: * [ class func: val1 name2: val2 name3: val3] ; // named params * [ class func: val1 : val2 : val3] ; // unnamed params * [ class <proto> 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_t *pc); //! Process @Property values and re-arrange them if necessary static void handle_oc_property_decl(chunk_t *pc); //! Process @available annotation static void handle_oc_available(chunk_t *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_t *handle_oc_md_type(chunk_t *paren_open, c_token_t 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_t *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_t *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_t *pc); /** * We are on the C++ 'template' keyword. * What follows should be the following: * * template <class identifier> function_declaration; * template <typename identifier> function_declaration; * template <class identifier> class class_declaration; * template <typename identifier> 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_t *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_t *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_t *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_t *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_t *pc); static bool is_oc_block(chunk_t *pc); /** * Java assert statements are: "assert EXP1 [: EXP2] ;" * Mark the parent of the colon and semicolon */ static void handle_java_assert(chunk_t *pc); void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_t clr_flags, scope_e nav) { LOG_FUNC_ENTRY(); while (start != nullptr && start != end) { chunk_flags_upd(start, clr_flags, set_flags); start = chunk_get_next(start, nav); if (start == nullptr) { return; } } if (end != nullptr) { chunk_flags_upd(end, clr_flags, set_flags); } } static void flag_asm(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *tmp = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (!chunk_is_token(tmp, CT_QUALIFIER)) { return; } chunk_t *po = chunk_get_next_ncnl(tmp, scope_e::PREPROC); if (!chunk_is_paren_open(po)) { return; } chunk_t *end = chunk_skip_to_match(po, scope_e::PREPROC); if (end == nullptr) { return; } set_chunk_parent(po, CT_ASM); set_chunk_parent(end, CT_ASM); for ( tmp = chunk_get_next_ncnl(po, scope_e::PREPROC); tmp != nullptr && tmp != end; tmp = chunk_get_next_ncnl(tmp, scope_e::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(chunk_get_next_ncnl(tmp, scope_e::PREPROC), CT_STRING) && chunk_is_token(chunk_get_prev_ncnlni(tmp, scope_e::PREPROC), CT_STRING)) // Issue #2279 { chunk_t 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 = chunk_get_next_ncnl(end, scope_e::PREPROC); if (tmp == nullptr) { return; } if (chunk_is_token(tmp, CT_SEMICOLON)) { set_chunk_parent(tmp, CT_ASM); } } // flag_asm void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next) { LOG_FUNC_ENTRY(); chunk_t *tmp; // 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); } if ( ( chunk_is_token(prev, CT_FPAREN_CLOSE) || ( ( chunk_is_str(prev, "const", 5) || chunk_is_str(prev, "override", 8)) && 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", 1))) { set_chunk_type(pc, CT_ASSIGN_FUNC_PROTO); } 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); } } // D stuff if ( language_is_set(LANG_D) && chunk_is_token(pc, CT_QUALIFIER) && chunk_is_str(pc, "const", 5) && chunk_is_token(next, CT_PAREN_OPEN)) { set_chunk_type(pc, CT_D_CAST); set_paren_parent(next, pc->type); } 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 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); } else if (chunk_is_token(tmp, CT_AMP)) { set_chunk_type(tmp, CT_ADDR); } else if (chunk_is_token(tmp, CT_MINUS)) { set_chunk_type(tmp, CT_NEG); } else if (chunk_is_token(tmp, CT_PLUS)) { set_chunk_type(tmp, CT_POS); } } /* * 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 = chunk_get_prev_ncnlni(pc); tmp != nullptr; tmp = chunk_get_prev_ncnlni(tmp)) // 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); } } if ( chunk_is_token(pc, CT_ALIGN) && tmp != nullptr) { if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, pc->type); } else if (chunk_is_token(tmp, CT_COLON)) { set_chunk_parent(tmp, pc->type); } } } // 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); tmp = chunk_get_next(next); while (tmp != nullptr) { if (chunk_is_token(tmp, CT_PAREN_CLOSE)) { set_chunk_parent(tmp, pc->type); break; } make_type(tmp); tmp = chunk_get_next(tmp); } } else { set_chunk_type(pc, CT_QUALIFIER); } } 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); } if (chunk_is_token(pc, CT_ASM)) { flag_asm(pc); } // 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); } } } // 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, "-", 1) || chunk_is_str(pc, "+", 1)) && chunk_is_str(next, "(", 1)) { handle_oc_message_decl(pc); } } 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); } } if (chunk_is_token(pc, CT_OC_PROPERTY)) { handle_oc_property_decl(pc); } if (chunk_is_token(pc, CT_OC_AVAILABLE)) { handle_oc_available(pc); } } // 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); } 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); } if ( chunk_is_token(pc, CT_SQUARE_CLOSE) && chunk_is_token(next, CT_WORD)) { handle_cs_array_type(pc); } 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); } if (chunk_is_token(pc, CT_WHEN) && pc->next->type != CT_SPAREN_OPEN) { set_chunk_type(pc, CT_WORD); } } 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); } if (chunk_is_token(pc, CT_NEW)) { chunk_t *ts = nullptr; tmp = next; if (chunk_is_token(tmp, CT_TSQUARE)) { ts = tmp; tmp = chunk_get_next_ncnl(tmp); } 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); } } } // 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 tmp = next; while ((tmp = chunk_get_next_nc(tmp)) != nullptr) { 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; } } } } if (chunk_is_token(pc, CT_ASSERT)) { handle_java_assert(pc); } if (chunk_is_token(pc, CT_ANNOTATION)) { tmp = chunk_get_next_ncnl(pc); if (chunk_is_paren_open(tmp)) { set_paren_parent(tmp, CT_ANNOTATION); } } if (chunk_is_token(pc, CT_SIZEOF) && language_is_set(LANG_ALLC)) { tmp = chunk_get_next_ncnl(pc); if (chunk_is_token(tmp, CT_ELLIPSIS)) { set_chunk_parent(tmp, CT_SIZEOF); } } if (chunk_is_token(pc, CT_DECLTYPE)) { tmp = chunk_get_next_ncnl(pc); 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)) { 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); } } } } // 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); } } 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); } if (chunk_is_token(pc, CT_PROTO_WRAP)) { handle_proto_wrap(pc); } // Handle the typedef if (chunk_is_token(pc, CT_TYPEDEF)) { fix_typedef(pc); } if ( chunk_is_token(pc, CT_ENUM) || chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_UNION) || ( chunk_is_token(pc, CT_CLASS) && !language_is_set(LANG_D))) { if (prev->type != CT_TYPEDEF) { fix_enum_struct_union(pc); } } if (chunk_is_token(pc, CT_EXTERN)) { if (chunk_is_paren_open(next)) { 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); tmp = chunk_get_next_ncnl(next); if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, CT_EXTERN); } } } if (chunk_is_token(pc, CT_TEMPLATE)) { if (language_is_set(LANG_D)) { handle_d_template(pc); } else { handle_cpp_template(pc); } } 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); } 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); } if (chunk_is_token(pc, CT_TYPE_CAST)) { fix_type_cast(pc); } 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); } if (chunk_is_token(pc, CT_D_TEMPLATE)) { set_paren_parent(next, pc->type); } /* * 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)) { tmp = chunk_get_next_ncnl(next); 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 = chunk_get_next_ncnl(next); if (chunk_is_token(tmp, CT_AMP)) { auto tmp2 = chunk_get_next_ncnl(tmp); if (chunk_is_token(tmp2, CT_WORD)) { tmp2 = chunk_get_next_ncnl(tmp2); } if (chunk_is_token(tmp2, CT_PAREN_CLOSE)) { tmp2 = chunk_get_next_ncnl(tmp2); if (chunk_is_token(tmp2, CT_SQUARE_OPEN)) { is_byref_array = true; set_chunk_type(tmp, CT_BYREF); } } } } if (!is_byref_array) { tmp = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level); if (tmp != nullptr) { tmp = chunk_get_next(tmp); 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 = chunk_get_next_ncnl(next); 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); } // 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)) { 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 = chunk_get_next_ncnl(tmp); } if (tmp != nullptr) { if (chunk_is_paren_open(tmp)) { tmp = flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, pc->type, false); if (tmp != nullptr) { 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); } } } } } // Mark the parameters in catch() if (chunk_is_token(pc, CT_CATCH) && chunk_is_token(next, CT_SPAREN_OPEN)) { fix_fcn_def_params(next); } 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); } } // 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); } /* * 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_any(PCF_IN_TYPEDEF | PCF_IN_TEMPLATE) && 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, ")", 1) && chunk_is_str(next, "(", 1)) { if (language_is_set(LANG_D)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false); } else { mark_function_type(pc); } } if ( (chunk_is_token(pc, CT_CLASS) || chunk_is_token(pc, CT_STRUCT)) && pc->level == pc->brace_level) { if (pc->type != CT_STRUCT || !language_is_set(LANG_C)) { mark_class_ctor(pc); } } if (chunk_is_token(pc, CT_OC_CLASS)) { handle_oc_class(pc); } // 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)) && prev->type != CT_DECLTYPE && prev->type != 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); } } if (language_is_set(LANG_CPP)) { chunk_t *nnext = chunk_get_next_ncnl(next); // 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", 1)))) { const size_t level = pc->level; bool found_status = false; chunk_t *pprev = chunk_get_prev(pc); for ( ; ( pprev != nullptr && pprev->level >= level && pprev->type != CT_SEMICOLON && pprev->type != CT_ACCESS_COLON) ; pprev = chunk_get_prev(pprev)) { if (pprev->level != level) { continue; } if (chunk_is_token(next, CT_NUMBER)) { if ( pprev->type == CT_QUALIFIER && chunk_is_str(pprev, "virtual", 7)) { found_status = true; break; } } else { if ( pprev->type == CT_FUNC_CLASS_PROTO // ctor/dtor || pprev->type == CT_FUNC_PROTO) // normal function { found_status = true; break; } } } if (found_status) { set_chunk_parent(pc, pprev->type); } } // Issue #2332 bool we_have_a_case_before = false; if (chunk_is_token(pc, CT_COLON)) { // check if we have a case before chunk_t *switch_before = chunk_get_prev_type(pc, CT_CASE, pc->level); if (switch_before != nullptr) { LOG_FMT(LFCNR, "%s(%d): switch_before->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n", __func__, __LINE__, switch_before->orig_line, switch_before->orig_col, switch_before->text(), get_token_name(switch_before->type)); we_have_a_case_before = true; } } // Detect a braced-init-list if ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE) || chunk_is_token(pc, CT_ASSIGN) || chunk_is_token(pc, CT_RETURN) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ANGLE_CLOSE) || chunk_is_token(pc, CT_SQUARE_CLOSE) || chunk_is_token(pc, CT_TSQUARE) || chunk_is_token(pc, CT_FPAREN_OPEN) || chunk_is_token(pc, CT_QUESTION) || ( chunk_is_token(pc, CT_COLON) && !we_have_a_case_before) || ( chunk_is_token(pc, CT_BRACE_OPEN) && ( get_chunk_parent_type(pc) == CT_NONE || get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST))) { log_pcf_flags(LFCNR, pc->flags); auto brace_open = chunk_get_next_ncnl(pc); if ( chunk_is_token(brace_open, CT_BRACE_OPEN) && ( get_chunk_parent_type(brace_open) == CT_NONE || get_chunk_parent_type(brace_open) == CT_ASSIGN || get_chunk_parent_type(brace_open) == CT_RETURN || get_chunk_parent_type(brace_open) == CT_BRACED_INIT_LIST)) { log_pcf_flags(LFCNR, brace_open->flags); auto brace_close = chunk_skip_to_match(next); if (chunk_is_token(brace_close, CT_BRACE_CLOSE)) { set_chunk_parent(brace_open, CT_BRACED_INIT_LIST); set_chunk_parent(brace_close, CT_BRACED_INIT_LIST); tmp = chunk_get_next_ncnl(brace_close); if (tmp) { chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START); } // TODO: Change pc->type CT_WORD -> CT_TYPE // for the case CT_ASSIGN (and others). // TODO: Move this block to the fix_fcn_call_args function. if (chunk_is_token(pc, CT_WORD) && pc->flags.test(PCF_IN_FCN_CALL)) { set_chunk_type(pc, CT_TYPE); } } } } } // 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) && (chunk_is_token(pc, CT_QUESTION)) && (chunk_is_token(prev, CT_ANGLE_CLOSE))) { set_chunk_type(pc, CT_PTR_TYPE); } if (chunk_is_token(pc, CT_MINUS)) { set_chunk_type(pc, CT_NEG); } if (chunk_is_token(pc, CT_PLUS)) { set_chunk_type(pc, CT_POS); } if (chunk_is_token(pc, CT_INCDEC_AFTER)) { set_chunk_type(pc, CT_INCDEC_BEFORE); } 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); } } 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); } } } // Detect a variable definition that starts with struct/enum/union/class if ( !pc->flags.test(PCF_IN_TYPEDEF) && get_chunk_parent_type(prev) != CT_CPP_CAST && !prev->flags.test(PCF_IN_FCN_DEF) && ( chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_UNION) || chunk_is_token(pc, CT_CLASS) || chunk_is_token(pc, CT_ENUM))) { tmp = chunk_skip_dc_member(next); if ((chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD))) { set_chunk_parent(tmp, pc->type); set_chunk_type(tmp, CT_TYPE); tmp = chunk_get_next_ncnl(tmp); } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { tmp = chunk_skip_to_match(tmp); if (tmp != nullptr) { tmp = chunk_get_next_ncnl(tmp); } } if ( tmp != nullptr && (chunk_is_ptr_operator(tmp) || chunk_is_token(tmp, CT_WORD))) { mark_variable_definition(tmp); } } /* * 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) || (pc && 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)) || 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 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 assign_found = false; tmp = pc; while (tmp != nullptr) { if (chunk_is_token(tmp, CT_SEMICOLON)) { break; } else if (chunk_is_token(tmp, CT_ASSIGN)) { assign_found = true; break; } tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279 } if (assign_found) { // 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) || chunk_is_star(pc->next))) || (next->type == CT_QUALIFIER)) // Issue #2648 { // example: // auto getComponent(Color *color) -> Component * { // auto getComponent(Color *color) -> Component ** { // auto getComponent(Color *color) -> Component * _Nonnull 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\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) && prev->type != CT_SQUARE_CLOSE && prev->type != CT_DC_MEMBER) ? CT_DEREF : CT_ARITH); } if (pc->flags.test(PCF_IN_TYPEDEF)) // Issue #1255/#633 { tmp = pc; while (tmp != nullptr) { if ( chunk_is_token(tmp, CT_SEMICOLON) || chunk_is_token(tmp, CT_BRACE_OPEN)) { break; } else if (chunk_is_token(tmp, CT_TYPEDEF)) { set_chunk_type(pc, CT_PTR_TYPE); } tmp = chunk_get_prev_ncnlni(tmp); // 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)) { 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)) { tmp = chunk_get_prev_ncnlni(prev); // Issue #2279 if (tmp != nullptr) { 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(next, CT_TYPE)) // Issue #2103 { set_chunk_type(pc, CT_BYREF); } } } } } } } 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)) { set_chunk_type(pc, (pc->type == 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 tmp = chunk_get_prev_type(pc, CT_FPAREN_OPEN, pc->level - 1); if ( tmp != nullptr && 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 * { tmp = pc; while ((tmp != nullptr)) { 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 = chunk_get_prev(tmp); } } } } /* * 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 assign_found = false; bool is_preproc = pc->flags.test(PCF_IN_PREPROC); chunk_t *temp; for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp)) { 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)) { assign_found = true; break; } if ( chunk_is_token(temp, CT_SEMICOLON) || ( is_preproc && ( !temp->flags.test(PCF_IN_PREPROC) || chunk_is_token(temp, CT_PREPROC)))) { break; } } if (assign_found) { // it is a Type alias, alias template for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp)) { 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 ( pc->type == CT_BOOL && !pc->flags.test(PCF_IN_PREPROC) && chunk_is_str(pc, "&&", 2) && chunk_ends_type(pc->prev)) { set_chunk_type(pc, CT_BYREF); } // Issue #1704 if ( chunk_is_token(pc, CT_INCDEC_AFTER) && pc->flags.test(PCF_IN_PREPROC)) { chunk_t *tmp_2 = chunk_get_next(pc); 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_t *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_t *pc = chunk_get_prev_ncnlni(bo1); // Issue #2279 if (pc == nullptr) { return; } if (chunk_is_paren_close(pc)) { chunk_t *bo2 = chunk_get_next(bo1); if (bo2 == nullptr) { return; } if (chunk_is_token(bo2, CT_BRACE_OPEN)) { // found a potential double brace chunk_t *bc2 = chunk_skip_to_match(bo2); if (bc2 == nullptr) { return; } chunk_t *bc1 = chunk_get_next(bc2); if (bc1 == nullptr) { 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_t *pc; chunk_t 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_get_head(); pc != nullptr; pc = chunk_get_next_ncnl(pc)) { 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 preceeded 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 unucustify knows // only builtin types and knows nothing of user-defined types chunk_t *prev = chunk_get_prev_ncnlni(pc); // 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_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC); if ( next != nullptr && chunk_is_token(next, CT_PAREN_OPEN)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_ATTRIBUTE, false); } } } pc = chunk_get_head(); if (pc == nullptr) { return; } if ( chunk_is_newline(pc) || chunk_is_comment(pc)) { pc = chunk_get_next_ncnl(pc); } while (pc != nullptr) { if (chunk_is_token(pc, CT_IGNORED)) { pc = chunk_get_next_ncnl(pc); 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_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC); // Issue #2279 if (prev == nullptr) { 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_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (next == nullptr) { 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 = chunk_get_next_ncnl(pc); } pawn_add_virtual_semicolons(); process_returns(); /* * 2nd pass - handle variable definitions * REVISIT: We need function params marked to do this (?) */ pc = chunk_get_head(); int square_level = -1; while (pc != nullptr) { 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->text(), 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<size_t>(square_level)) { square_level = -1; } } if ( chunk_is_token(pc, CT_EXTERN) && language_is_set(LANG_ALLC)) { chunk_t *next = chunk_get_next_ncnl(pc); if (chunk_is_token(next, CT_STRING)) { chunk_t *tmp = chunk_get_next_ncnl(next); while (tmp != nullptr) { 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 = chunk_get_next_ncnl(tmp); } } } if ( chunk_is_token(pc, CT_ATTRIBUTE) && language_is_set(LANG_ALLC)) { chunk_t *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 = chunk_get_next_type(pc, 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->text(), 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_ENUM && !pc->flags.test(PCF_IN_ENUM)) { pc = fix_variable_definition(pc); } else { pc = chunk_get_next_ncnl(pc); } } } // fix_symbols static void process_returns(void) { LOG_FUNC_ENTRY(); chunk_t *pc; pc = chunk_get_head(); while (pc != nullptr) { if (pc->type != CT_RETURN) { pc = chunk_get_next_type(pc, CT_RETURN, -1); continue; } pc = process_return(pc); } } static chunk_t *process_return(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *next; chunk_t *temp; chunk_t *semi; chunk_t *cpar; chunk_t chunk; // grab next and bail if it is a semicolon next = chunk_ppa_get_next_ncnl(pc); if ( next == nullptr || 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 = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level); if (cpar == nullptr) { return(nullptr); } semi = chunk_ppa_get_next_ncnl(cpar); if (semi == nullptr) { return(nullptr); } 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 = chunk_get_next(temp)) { 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 != nullptr && temp->type != CT_NEWLINE) { temp->column = temp->column - 2; temp->orig_col = temp->orig_col - 2; temp->orig_col_end = temp->orig_col_end - 2; temp = chunk_get_next(temp); } } 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->next) != nullptr) { 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 = chunk_get_next(semi)) != nullptr) { 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.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 = chunk_get_next(temp)) { temp->level++; } } return(semi); } // process_return static bool is_ucase_str(const char *str, size_t len) { while (len-- > 0) { if (unc_toupper(*str) != *str) { return(false); } str++; } return(true); } static bool is_oc_block(chunk_t *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))); } static void fix_casts(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc; chunk_t *prev; chunk_t *first; chunk_t *after; chunk_t *last = nullptr; chunk_t *paren_close; const char *verb = "likely"; const char *detail = ""; size_t count = 0; int word_count = 0; bool nope; bool doubtful_cast = false; LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, start->text(), start->orig_line, start->orig_col); prev = chunk_get_prev_ncnlni(start); // Issue #2279 if (prev == nullptr) { return; } if (chunk_is_token(prev, CT_PP_DEFINED)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - after defined\n", __func__, __LINE__); return; } if (chunk_is_token(prev, CT_ANGLE_CLOSE)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - after > (template)\n", __func__, __LINE__); return; } // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren pc = chunk_get_next_ncnl(start); first = pc; while ( pc != nullptr && ( chunk_is_type(pc) || chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_QUALIFIER) || chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_PP) || chunk_is_token(pc, CT_STAR) || chunk_is_token(pc, CT_QUESTION) || chunk_is_token(pc, CT_CARET) || chunk_is_token(pc, CT_TSQUARE) || ( ( chunk_is_token(pc, CT_ANGLE_OPEN) || chunk_is_token(pc, CT_ANGLE_CLOSE)) && language_is_set(LANG_OC | LANG_JAVA)) || ( ( chunk_is_token(pc, CT_QUESTION) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_MEMBER)) && language_is_set(LANG_JAVA)) || chunk_is_token(pc, CT_AMP))) { LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER))) { word_count++; } else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP)) { // might be negativ, such as with: // a = val + (CFoo::bar_t)7; word_count--; } last = pc; pc = chunk_get_next_ncnl(pc); count++; } if ( pc == nullptr || pc->type != CT_PAREN_CLOSE || chunk_is_token(prev, CT_OC_CLASS)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast, hit type is %s\n", __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type)); return; } if (word_count > 1) { LOG_FMT(LCASTS, "%s(%d): -- too many words: %d\n", __func__, __LINE__, word_count); return; } paren_close = pc; // If last is a type or star/caret, we have a cast for sure if ( chunk_is_token(last, CT_STAR) || chunk_is_token(last, CT_CARET) || chunk_is_token(last, CT_PTR_TYPE) || chunk_is_token(last, CT_TYPE) || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA))) { verb = "for sure"; } else if (count == 1) { /* * We are on a potential cast of the form "(word)". * We don't know if the word is a type. So lets guess based on some * simple rules: * - if all caps, likely a type * - if it ends in _t, likely a type * - if it's objective-c and the type is id, likely valid */ verb = "guessed"; if ( (last->len() > 3) && (last->str[last->len() - 2] == '_') && (last->str[last->len() - 1] == 't')) { detail = " -- '_t'"; } else if (is_ucase_str(last->text(), last->len())) { detail = " -- upper case"; } else if (language_is_set(LANG_OC) && chunk_is_str(last, "id", 2)) { detail = " -- Objective-C id"; } else { // If we can't tell for sure whether this is a cast, decide against it detail = " -- mixed case"; doubtful_cast = true; } /* * If the next item is a * or &, the next item after that can't be a * number or string. * * If the next item is a +, the next item has to be a number. * * If the next item is a -, the next item can't be a string. * * For this to be a cast, the close paren must be followed by: * - constant (number or string) * - paren open * - word * * Find the next non-open paren item. */ pc = chunk_get_next_ncnl(paren_close); after = pc; do { after = chunk_get_next_ncnl(after); } while (chunk_is_token(after, CT_PAREN_OPEN)); if (after == nullptr) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - hit NULL\n", __func__, __LINE__); return; } nope = false; if (chunk_is_ptr_operator(pc)) { // star (*) and address (&) are ambiguous if ( chunk_is_token(after, CT_NUMBER_FP) || chunk_is_token(after, CT_NUMBER) || chunk_is_token(after, CT_STRING) || doubtful_cast) { nope = true; } } else if (chunk_is_token(pc, CT_MINUS)) { // (UINT8)-1 or (foo)-1 or (FOO)-'a' if (chunk_is_token(after, CT_STRING) || doubtful_cast) { nope = true; } } else if (chunk_is_token(pc, CT_PLUS)) { // (UINT8)+1 or (foo)+1 if ( (after->type != CT_NUMBER && after->type != CT_NUMBER_FP) || doubtful_cast) { nope = true; } } else if ( pc->type != CT_NUMBER_FP && pc->type != CT_NUMBER && pc->type != CT_WORD && pc->type != CT_THIS && pc->type != CT_TYPE && pc->type != CT_PAREN_OPEN && pc->type != CT_STRING && pc->type != CT_DECLTYPE && pc->type != CT_SIZEOF && get_chunk_parent_type(pc) != CT_SIZEOF && pc->type != CT_FUNC_CALL && pc->type != CT_FUNC_CALL_USER && pc->type != CT_FUNCTION && pc->type != CT_BRACE_OPEN && (!( chunk_is_token(pc, CT_SQUARE_OPEN) && language_is_set(LANG_OC)))) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); return; } if (nope) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - text() '%s' followed by type %s\n", __func__, __LINE__, pc->text(), get_token_name(after->type)); return; } } // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't pc = chunk_get_next_ncnl(paren_close); if (pc == nullptr) { return; } if ( chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_BOOL) // Issue #2151 || chunk_is_paren_close(pc)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by type %s\n", __func__, __LINE__, get_token_name(pc->type)); return; } set_chunk_parent(start, CT_C_CAST); set_chunk_parent(paren_close, CT_C_CAST); LOG_FMT(LCASTS, "%s(%d): -- %s c-cast: (", __func__, __LINE__, verb); for (pc = first; pc != nullptr && pc != paren_close; pc = chunk_get_next_ncnl(pc)) { set_chunk_parent(pc, CT_C_CAST); make_type(pc); LOG_FMT(LCASTS, " %s", pc->text()); } LOG_FMT(LCASTS, " )%s\n", detail); // Mark the next item as an expression start pc = chunk_get_next_ncnl(paren_close); if (pc != nullptr) { chunk_flags_set(pc, PCF_EXPR_START); if (chunk_is_opening_brace(pc)) { set_paren_parent(pc, get_chunk_parent_type(start)); } } } // fix_casts static void fix_type_cast(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc; pc = chunk_get_next_ncnl(start); if (pc == nullptr || pc->type != CT_ANGLE_OPEN) { return; } while ( ((pc = chunk_get_next_ncnl(pc)) != nullptr) && pc->level >= start->level) { if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE)) { pc = chunk_get_next_ncnl(pc); if (pc == nullptr) { return; } if (chunk_is_str(pc, "(", 1)) { set_paren_parent(pc, CT_TYPE_CAST); } return; } make_type(pc); } } static void fix_enum_struct_union(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *next; chunk_t *prev = nullptr; pcf_flags_t flags = PCF_VAR_1ST_DEF; auto const in_fcn_paren = pc->flags & PCF_IN_FCN_DEF; // Make sure this wasn't a cast if (get_chunk_parent_type(pc) == CT_C_CAST) { return; } // the next item is either a type or open brace next = chunk_get_next_ncnl(pc); // the enum-key might be enum, enum class or enum struct (TODO) if (chunk_is_token(next, CT_ENUM_CLASS)) { next = chunk_get_next_ncnl(next); // get the next one } if (language_is_set(LANG_CPP)) { next = skip_attribute_next(next); // get the next one } // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON)) { // i.e. "enum xyz : unsigned int { ... };" // i.e. "enum class xyz : unsigned int { ... };" // i.e. "enum : unsigned int { ... };" // xyz is a type // save the type if it exists if (!chunk_is_token(next, CT_COLON)) { set_chunk_parent(next, pc->type); prev = next; next = chunk_get_next_ncnl(next); } if (next == nullptr) { return; } set_chunk_parent(next, pc->type); auto const is_struct_or_class = (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS)); // next up is either a colon, open brace, or open parenthesis (pawn) if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN)) { next = set_paren_parent(next, CT_ENUM); } else if (chunk_is_token(next, CT_COLON)) { if (chunk_is_token(pc, CT_ENUM)) { // enum TYPE : INT_TYPE { ... }; next = chunk_get_next_ncnl(next); if (next != nullptr) { make_type(next); next = chunk_get_next_ncnl(next); // enum TYPE : unsigned int { ... }; if (chunk_is_token(next, CT_TYPE)) { // get the next part of the type next = chunk_get_next_ncnl(next); } } } else if (is_struct_or_class) { next = skip_parent_types(next); } } else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN)) { // Fix #1267 structure attributes // struct __attribute__(align(x)) struct_name; // skip to matching parenclose and make next token as type. next = chunk_skip_to_match(next); next = chunk_get_next_ncnl(next); set_chunk_type(next, CT_TYPE); set_chunk_parent(next, pc->type); } if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration { set_chunk_parent(next, pc->type); flag_series(pc, prev, PCF_INCOMPLETE); return; } } if (chunk_is_token(next, CT_BRACE_OPEN)) { auto const flag = [pc] { switch (pc->type) { case CT_ENUM: return(PCF_IN_ENUM); case CT_STRUCT: return(PCF_IN_STRUCT); case CT_CLASS: return(PCF_IN_CLASS); default: return(PCF_NONE); } }(); flag_parens(next, flag, CT_NONE, CT_NONE, false); if ( chunk_is_token(pc, CT_UNION) || chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS)) { mark_struct_union_body(next); } // Skip to the closing brace set_chunk_parent(next, pc->type); next = chunk_get_next_type(next, CT_BRACE_CLOSE, pc->level); flags |= PCF_VAR_INLINE; if (next != nullptr) { set_chunk_parent(next, pc->type); next = chunk_get_next_ncnl(next); } prev = nullptr; } // reset var name parent type else if (next && prev) { set_chunk_parent(prev, CT_NONE); } if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE)) { return; } if (!chunk_is_semicolon(next)) { // Pawn does not require a semicolon after an enum if (language_is_set(LANG_PAWN)) { return; } /* * D does not require a semicolon after an enum, but we add one to make * other code happy. */ if (language_is_set(LANG_D)) { next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next)); // Issue #2279 } } // We are either pointing to a ';' or a variable while ( next != nullptr && !chunk_is_semicolon(next) && next->type != CT_ASSIGN && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any()) { if (next->level == pc->level) { if (chunk_is_token(next, CT_WORD)) { chunk_flags_set(next, flags); flags &= ~PCF_VAR_1ST; // clear the first flag for the next items LOG_FMT(LCASTS, "%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()); } if ( chunk_is_token(next, CT_STAR) || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET))) { set_chunk_type(next, CT_PTR_TYPE); } // If we hit a comma in a function param, we are done if ( (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE)) && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL))) { return; } } next = chunk_get_next_ncnl(next); } if ( next != nullptr && chunk_is_token(next, CT_SEMICOLON)) { set_chunk_parent(next, pc->type); } } // fix_enum_struct_union static void fix_typedef(chunk_t *start) { LOG_FUNC_ENTRY(); if (start == nullptr) { return; } LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n", __func__, __LINE__, start->orig_line, start->orig_col); chunk_t *the_type = nullptr; chunk_t *last_op = nullptr; /* * Mark everything in the typedef and scan for ")(", which makes it a * function type */ for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC) ; next != nullptr && next->level >= start->level ; next = chunk_get_next_ncnl(next, scope_e::PREPROC)) { chunk_flags_set(next, PCF_IN_TYPEDEF); if (start->level == next->level) { if (chunk_is_semicolon(next)) { set_chunk_parent(next, CT_TYPEDEF); break; } if (chunk_is_token(next, CT_ATTRIBUTE)) { break; } if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN)) { set_chunk_parent(next, CT_TYPEDEF); break; } make_type(next); if (chunk_is_token(next, CT_TYPE)) { the_type = next; } chunk_flags_clr(next, PCF_VAR_1ST_DEF); if (*next->str.c_str() == '(') { last_op = next; } } } // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def if ( last_op != nullptr && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM)) { flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false); fix_fcn_def_params(last_op); the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC); // Issue #2279 if (the_type == nullptr) { return; } chunk_t *open_paren = nullptr; if (chunk_is_paren_close(the_type)) { open_paren = chunk_skip_to_match_rev(the_type); mark_function_type(the_type); the_type = chunk_get_prev_ncnlni(the_type, scope_e::PREPROC); // Issue #2279 if (the_type == nullptr) { return; } } else { // must be: "typedef <return type>func(params);" set_chunk_type(the_type, CT_FUNC_TYPE); } set_chunk_parent(the_type, CT_TYPEDEF); LOG_FMT(LTYPEDEF, "%s(%d): fcn typedef text() '%s', on orig_line %zu\n", __func__, __LINE__, the_type->text(), the_type->orig_line); // If we are aligning on the open parenthesis, grab that instead log_rule_B("align_typedef_func"); if (open_paren != nullptr && options::align_typedef_func() == 1) { the_type = open_paren; } log_rule_B("align_typedef_func"); if (options::align_typedef_func() != 0) { LOG_FMT(LTYPEDEF, "%s(%d): -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n", __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col); chunk_flags_set(the_type, PCF_ANCHOR); } // already did everything we need to do return; } /* * Skip over enum/struct/union stuff, as we know it isn't a return type * for a function type */ chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC); if (after == nullptr) { return; } if ( after->type != CT_ENUM && after->type != CT_STRUCT && after->type != CT_UNION) { if (the_type != nullptr) { // We have just a regular typedef LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n", __func__, __LINE__, the_type->text(), the_type->orig_line); chunk_flags_set(the_type, PCF_ANCHOR); } return; } // We have a struct/union/enum, next should be either a type or { chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC); if (next == nullptr) { return; } if (chunk_is_token(next, CT_TYPE)) { next = chunk_get_next_ncnl(next, scope_e::PREPROC); if (next == nullptr) { return; } } if (chunk_is_token(next, CT_BRACE_OPEN)) { // Skip to the closing brace chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC); if (br_c != nullptr) { const c_token_t tag = after->type; set_chunk_parent(next, tag); set_chunk_parent(br_c, tag); if (tag == CT_ENUM) { flag_series(after, br_c, PCF_IN_ENUM); } else if (tag == CT_STRUCT) { flag_series(after, br_c, PCF_IN_STRUCT); } } } if (the_type != nullptr) { LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n", __func__, __LINE__, get_token_name(after->type), the_type->text(), the_type->orig_line); chunk_flags_set(the_type, PCF_ANCHOR); } } // fix_typedef //static void mark_variable_stack(ChunkStack &cs, log_sev_t sev) //{ // UNUSED(sev); // LOG_FUNC_ENTRY(); // // // throw out the last word and mark the rest // chunk_t *var_name = cs.Pop_Back(); // // if (var_name && var_name->prev->type == CT_DC_MEMBER) // { // cs.Push_Back(var_name); // } // // if (var_name != nullptr) // { // LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n", // __func__, __LINE__, var_name->orig_line, var_name->orig_col); // // size_t word_cnt = 0; // chunk_t *word_type; // // while ((word_type = cs.Pop_Back()) != nullptr) // { // if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE)) // { // LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", // __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text()); // set_chunk_type(word_type, CT_TYPE); // chunk_flags_set(word_type, PCF_VAR_TYPE); // } // word_cnt++; // } // // if (chunk_is_token(var_name, CT_WORD)) // { // if (word_cnt > 0) // { // LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n", // __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); // chunk_flags_set(var_name, PCF_VAR_DEF); // } // else // { // LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", // __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); // set_chunk_type(var_name, CT_TYPE); // chunk_flags_set(var_name, PCF_VAR_TYPE); // } // } // } //} // mark_variable_stack //static void fix_fcn_def_params(chunk_t *start) //{ // LOG_FUNC_ENTRY(); // // if (start == nullptr) // { // return; // } // LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n", // __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level); // // while (start != nullptr && !chunk_is_paren_open(start)) // { // start = chunk_get_next_ncnl(start); // } // // if (start == nullptr)// Coverity CID 76003, 1100782 // { // return; // } // // ensure start chunk holds a single '(' character // assert((start->len() == 1) && (start->str[0] == '(')); // // ChunkStack cs; // size_t level = start->level + 1; // chunk_t *pc = start; // // while ((pc = chunk_get_next_ncnl(pc)) != nullptr) // { // if ( ((start->len() == 1) && (start->str[0] == ')')) // || pc->level < level) // { // LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n", // __func__, __LINE__, pc->text(), pc->orig_line); // break; // } // LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n", // __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at", // pc->text(), pc->orig_line, pc->level); // // if (pc->level > level) // { // continue; // } // // if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc)) // { // set_chunk_type(pc, CT_PTR_TYPE); // cs.Push_Back(pc); // } // else if ( chunk_is_token(pc, CT_AMP) // || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2))) // { // set_chunk_type(pc, CT_BYREF); // cs.Push_Back(pc); // } // else if (chunk_is_token(pc, CT_TYPE_WRAP)) // { // cs.Push_Back(pc); // } // else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)) // { // cs.Push_Back(pc); // } // else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN)) // { // mark_variable_stack(cs, LFCNP); // // if (chunk_is_token(pc, CT_ASSIGN)) // { // // Mark assignment for default param spacing // set_chunk_parent(pc, CT_FUNC_PROTO); // } // } // } // mark_variable_stack(cs, LFCNP); //} // fix_fcn_def_params static void handle_cpp_template(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *tmp = chunk_get_next_ncnl(pc); if (tmp->type != CT_ANGLE_OPEN) { return; } set_chunk_parent(tmp, CT_TEMPLATE); size_t level = tmp->level; while ((tmp = chunk_get_next(tmp)) != nullptr) { 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; } } if (tmp != nullptr) { tmp = chunk_get_next_ncnl(tmp); 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 = chunk_get_next_type(tmp, CT_SEMICOLON, tmp->level); if (tmp != nullptr) { set_chunk_parent(tmp, CT_TEMPLATE); } } } } // handle_cpp_template static void handle_cpp_lambda(chunk_t *sq_o) { LOG_FUNC_ENTRY(); chunk_t *ret = nullptr; // abort if type of the previous token is not contained in this whitelist chunk_t *prev = chunk_get_prev_ncnlni(sq_o); // Issue #2279 if ( prev == nullptr || ( prev->type != CT_ASSIGN && prev->type != CT_COMMA && prev->type != CT_PAREN_OPEN // allow Js like self invoking lambda syntax: ([](){})(); && prev->type != CT_FPAREN_OPEN && prev->type != CT_SQUARE_OPEN && prev->type != CT_BRACE_OPEN && prev->type != CT_SEMICOLON && prev->type != CT_RETURN)) { return; } chunk_t *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) { return; } } chunk_t *pa_o = chunk_get_next_ncnl(sq_c); // check to see if there is a lambda-specifier in the pa_o chunk; // assuming chunk is CT_EXECUTION_CONTEXT, ignore lambda-specifier while (pa_o->type == CT_EXECUTION_CONTEXT) { // set pa_o to next chunk after this specifier pa_o = chunk_get_next_ncnl(pa_o); } if (pa_o == nullptr) { return; } chunk_t *pa_c = nullptr; // 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 == nullptr) { return; } } // Check for 'mutable' keyword: '[]() mutable {}' or []() mutable -> ret {} chunk_t *br_o = pa_c ? chunk_get_next_ncnl(pa_c) : pa_o; if (chunk_is_str(br_o, "mutable", 7)) { br_o = chunk_get_next_ncnl(br_o); } //TODO: also check for exception and attribute between [] ... {} // skip possible arrow syntax: '-> ret' if (chunk_is_str(br_o, "->", 2)) { ret = br_o; // REVISIT: really should check the stuff we are skipping br_o = chunk_get_next_type(br_o, CT_BRACE_OPEN, br_o->level); } if ( br_o == nullptr || br_o->type != CT_BRACE_OPEN) { return; } // and now find the '}' chunk_t *br_c = chunk_skip_to_match(br_o); if (br_c == nullptr) { return; } // This looks like a lambda expression if (chunk_is_token(sq_o, CT_TSQUARE)) { // split into two chunks chunk_t 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<int>(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 != nullptr) { set_chunk_type(pa_o, CT_FPAREN_OPEN); set_chunk_parent(pa_o, CT_CPP_LAMBDA); set_chunk_type(pa_c, CT_FPAREN_CLOSE); set_chunk_parent(pa_c, CT_CPP_LAMBDA); } set_chunk_parent(br_o, CT_CPP_LAMBDA); set_chunk_parent(br_c, CT_CPP_LAMBDA); if (ret != nullptr) { set_chunk_type(ret, CT_CPP_LAMBDA_RET); ret = chunk_get_next_ncnl(ret); while (ret != br_o) { make_type(ret); ret = chunk_get_next_ncnl(ret); } } if (pa_c != nullptr) { fix_fcn_def_params(pa_o); } //handle self calling lambda paren chunk_t *call_pa_o = chunk_get_next_ncnl(br_c); if (chunk_is_token(call_pa_o, CT_PAREN_OPEN)) { chunk_t *call_pa_c = chunk_skip_to_match(call_pa_o); if (call_pa_c != nullptr) { 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); } } } // handle_cpp_lambda static void handle_d_template(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *name = chunk_get_next_ncnl(pc); chunk_t *po = chunk_get_next_ncnl(name); //if (!name || (name->type != CT_WORD && name->type != CT_WORD)) Coverity CID 76000 Same on both sides, 2016-03-16 if (!name || name->type != CT_WORD) { // TODO: log an error, expected NAME return; } if ( po == nullptr || po->type != 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_t *tmp = get_d_template_types(cs, po); if ( tmp == nullptr || tmp->type != CT_PAREN_CLOSE) { // TODO: log an error, expected ')' return; } set_chunk_parent(tmp, CT_TEMPLATE); tmp = chunk_get_next_ncnl(tmp); if (tmp->type != CT_BRACE_OPEN) { // TODO: log an error, expected '{' return; } set_chunk_parent(tmp, CT_TEMPLATE); po = tmp; tmp = po; while ( ((tmp = chunk_get_next_ncnl(tmp)) != nullptr) && tmp->level > po->level) { if (chunk_is_token(tmp, CT_WORD) && chunkstack_match(cs, tmp)) { set_chunk_type(tmp, CT_TYPE); } } // if (!chunk_is_token(tmp, CT_BRACE_CLOSE)) // { // // TODO: log an error, expected '}' // } set_chunk_parent(tmp, CT_TEMPLATE); } // handle_d_template chunk_t *skip_template_next(chunk_t *ang_open) { if (chunk_is_token(ang_open, CT_ANGLE_OPEN)) { chunk_t *pc = chunk_get_next_type(ang_open, CT_ANGLE_CLOSE, ang_open->level); return(chunk_get_next_ncnl(pc)); } return(ang_open); } static void handle_oc_class(chunk_t *pc) { enum class angle_state_e : unsigned int { NONE = 0, OPEN = 1, // '<' found CLOSE = 2, // '>' found }; LOG_FUNC_ENTRY(); chunk_t *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 = chunk_get_next_ncnl(pc); 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 = chunk_get_next_nnl(tmp)) != nullptr) { 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, "<", 1)) { 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, ">", 1)) { 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, ">>", 2)) { 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 = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level); if ( tmp != nullptr && 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, "-", 1) || chunk_is_str(tmp, "+", 1)) { as = angle_state_e::CLOSE; if (chunk_is_newline(chunk_get_prev(tmp))) { 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 = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level); if (tmp != nullptr) { set_chunk_parent(tmp, CT_OC_CLASS); } } } // handle_oc_class static void handle_oc_block_literal(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *prev = chunk_get_prev_ncnlni(pc); // Issue #2279 chunk_t *next = chunk_get_next_ncnl(pc); if ( pc == nullptr || prev == nullptr || next == nullptr) { 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_t *apo = nullptr; // arg paren open chunk_t *bbo = nullptr; // block brace open chunk_t *bbc; // block brace close LOG_FMT(LOCBLK, "%s(%d): + scan", __func__, __LINE__); chunk_t *tmp; for (tmp = next; tmp; tmp = chunk_get_next_ncnl(tmp)) { /* handle '< protocol >' */ if (chunk_is_str(tmp, "<", 1)) { chunk_t *ao = tmp; chunk_t *ac = chunk_get_next_str(ao, ">", 1, ao->level); if (ac) { 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 = chunk_get_next(ao); tmp != ac; tmp = chunk_get_next(tmp)) { tmp->level += 1; set_chunk_parent(tmp, CT_OC_PROTO_LIST); } } tmp = chunk_get_next_ncnl(ac); } 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 == nullptr || bbc == nullptr) { 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_t *lbp; // last before paren - end of return type, if any if (apo) { chunk_t *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 = chunk_get_prev_ncnlni(apo); // Issue #2279 } else { lbp = chunk_get_prev_ncnlni(bbo); // 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 = chunk_get_prev_ncnlni(lbp); // 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_t *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_t *tpo = chunk_get_prev_ncnlni(pc); // type paren open Issue #2279 if (chunk_is_paren_open(tpo)) { /* * block type: 'RTYPE (^LABEL)(ARGS)' * LABEL is optional. */ chunk_t *tpc = chunk_skip_to_match(tpo); // type close paren (after '^') chunk_t *nam = chunk_get_prev_ncnlni(tpc); // name (if any) or '^' Issue #2279 chunk_t *apo = chunk_get_next_ncnl(tpc); // arg open paren chunk_t *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_t *aft = chunk_get_next_ncnl(apc); c_token_t pt; if (chunk_is_str(nam, "^", 1)) { 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, chunk_get_prev_ncnlni(tpo), pt); // Issue #2279 } } } // handle_oc_block_type static chunk_t *handle_oc_md_type(chunk_t *paren_open, c_token_t ptype, pcf_flags_t flags, bool &did_it) { chunk_t *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_t *cur = chunk_get_next_ncnl(paren_open); cur != paren_close; cur = chunk_get_next_ncnl(cur)) { 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(chunk_get_next_ncnl(paren_close)); } static void handle_oc_message_decl(chunk_t *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_t *tmp = pc; while ((tmp = chunk_get_next(tmp)) != nullptr) { 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; } c_token_t pt = (tmp->type == 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(chunk_get_next_ncnl(pc), 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_token(tmp, CT_WORD)) { LOG_FMT(LOCMSGD, " -- missing method name\n"); return; } // expect the method name/label chunk_t *label = tmp; set_chunk_type(tmp, pt); set_chunk_parent(tmp, pt); pc = chunk_get_next_ncnl(tmp); 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 = chunk_get_next_ncnl(pc); } // a colon must be next if (!chunk_is_str(pc, ":", 1)) { break; } set_chunk_type(pc, CT_OC_COLON); set_chunk_parent(pc, pt); pc = chunk_get_next_ncnl(pc); // 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 = chunk_get_next_ncnl(pc); } } 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_t *os) { LOG_FUNC_ENTRY(); chunk_t *cs = chunk_get_next(os); while (cs != nullptr && cs->level > os->level) { cs = chunk_get_next(cs); } if (cs == nullptr || cs->type != 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_t *tmp = chunk_get_next_ncnl(cs); if (chunk_is_semicolon(tmp)) { set_chunk_parent(tmp, CT_OC_MSG); } // expect a word first thing or [...] tmp = chunk_get_next_ncnl(os); if ( chunk_is_token(tmp, CT_SQUARE_OPEN) || chunk_is_token(tmp, CT_PAREN_OPEN) || (chunk_is_token(tmp, CT_OC_AT))) { chunk_t *tt = chunk_get_next_ncnl(tmp); if ((chunk_is_token(tmp, CT_OC_AT)) && tt) { 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 ( tmp->type != CT_WORD && tmp->type != CT_TYPE && tmp->type != CT_THIS && tmp->type != CT_STAR && tmp->type != 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 (chunk_is_star(tmp)) // Issue #2722 { set_chunk_type(tmp, CT_PTR_TYPE); tmp = chunk_get_next_ncnl(tmp); } chunk_t *tt = chunk_get_next_ncnl(tmp); 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 = chunk_get_prev_ncnlni(set_paren_parent(tt, CT_FUNC_CALL)); // 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 = chunk_get_next_ncnl(tmp); if (chunk_is_str(tmp, "<", 1)) { chunk_t *ao = tmp; chunk_t *ac = chunk_get_next_str(ao, ">", 1, ao->level); if (ac) { 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 = chunk_get_next(ao); tmp != ac; tmp = chunk_get_next(tmp)) { tmp->level += 1; set_chunk_parent(tmp, CT_OC_PROTO_LIST); } } tmp = chunk_get_next_ncnl(ac); } // handle 'object.property' and 'collection[index]' else { while (tmp) { if (chunk_is_token(tmp, CT_MEMBER)) // move past [object.prop1.prop2 { chunk_t *typ = chunk_get_next_ncnl(tmp); if (chunk_is_token(typ, CT_WORD) || chunk_is_token(typ, CT_TYPE)) { tmp = chunk_get_next_ncnl(typ); } else { break; } } else if (chunk_is_token(tmp, CT_SQUARE_OPEN)) // move past [collection[index] { chunk_t *tcs = chunk_get_next_ncnl(tmp); while (tcs != nullptr && tcs->level > tmp->level) { tcs = chunk_get_next_ncnl(tcs); } if (chunk_is_token(tcs, CT_SQUARE_CLOSE)) { tmp = chunk_get_next_ncnl(tcs); } else { break; } } else { break; } } } // [(self.foo.bar) method] if (chunk_is_paren_open(tmp)) { tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp)); } if (chunk_is_token(tmp, CT_WORD) || chunk_is_token(tmp, CT_TYPE)) { set_chunk_type(tmp, CT_OC_MSG_FUNC); } chunk_t *prev = nullptr; for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp)) { 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_t *pp = chunk_get_prev(prev); if ( pp != nullptr && pp->type != CT_OC_COLON && pp->type != CT_ARITH && pp->type != 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_t *os) { os = chunk_get_next(os); while (os != nullptr) { c_token_t origType = os->type; set_chunk_type(os, CT_OC_AVAILABLE_VALUE); if (origType == CT_PAREN_CLOSE) { break; } os = chunk_get_next(os); } } static void handle_oc_property_decl(chunk_t *os) { log_rule_B("mod_sort_oc_properties"); if (options::mod_sort_oc_properties()) { typedef std::vector<chunk_t *> ChunkGroup; chunk_t *next = chunk_get_next(os); chunk_t *open_paren = nullptr; std::vector<ChunkGroup> class_chunks; // class std::vector<ChunkGroup> thread_chunks; // atomic, nonatomic std::vector<ChunkGroup> readwrite_chunks; // readwrite, readonly std::vector<ChunkGroup> ref_chunks; // retain, copy, assign, weak, strong, unsafe_unretained std::vector<ChunkGroup> getter_chunks; // getter std::vector<ChunkGroup> setter_chunks; // setter std::vector<ChunkGroup> nullability_chunks; // nonnull, nullable, null_unspecified, null_resettable std::vector<ChunkGroup> other_chunks; // any words other than above if (chunk_is_token(next, CT_PAREN_OPEN)) { open_paren = next; next = chunk_get_next(next); /* * 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 != nullptr && next->type != CT_PAREN_CLOSE) { if (chunk_is_token(next, CT_OC_PROPERTY_ATTR)) { if ( chunk_is_str(next, "atomic", 6) || chunk_is_str(next, "nonatomic", 9)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); thread_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "readonly", 8) || chunk_is_str(next, "readwrite", 9)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); readwrite_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "assign", 6) || chunk_is_str(next, "retain", 6) || chunk_is_str(next, "copy", 4) || chunk_is_str(next, "strong", 6) || chunk_is_str(next, "weak", 4) || chunk_is_str(next, "unsafe_unretained", 17)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); ref_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "getter", 6)) { ChunkGroup chunkGroup; do { chunkGroup.push_back(next); next = chunk_get_next(next); } while ( next && next->type != CT_COMMA && next->type != CT_PAREN_CLOSE); next = next->prev; // coverity CID 160946 if (next == nullptr) { break; } getter_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "setter", 6)) { ChunkGroup chunkGroup; do { chunkGroup.push_back(next); next = chunk_get_next(next); } while ( next && next->type != CT_COMMA && next->type != CT_PAREN_CLOSE); next = chunk_get_prev(next); if (next == nullptr) { break; } setter_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "nullable", 8) || chunk_is_str(next, "nonnull", 7) || chunk_is_str(next, "null_resettable", 15) || chunk_is_str(next, "null_unspecified", 16)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); nullability_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "class", 5)) { 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", 5)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); class_chunks.push_back(chunkGroup); } else { ChunkGroup chunkGroup; chunkGroup.push_back(next); other_chunks.push_back(chunkGroup); } } next = chunk_get_next(next); } 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<int, std::vector<ChunkGroup> > sorted_chunk_map; sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(class_w, class_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(thread_w, thread_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(readwrite_w, readwrite_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(ref_w, ref_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(getter_w, getter_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(setter_w, setter_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(nullability_w, nullability_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(std::numeric_limits<int>::min(), other_chunks)); chunk_t *curr_chunk = open_paren; for (multimap<int, std::vector<ChunkGroup> >::reverse_iterator it = sorted_chunk_map.rbegin(); it != sorted_chunk_map.rend(); ++it) { std::vector<ChunkGroup> 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 = chunk_get_next(curr_chunk); } } // add the parenthesis chunk_t 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.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->next; } } // Remove the extra comma's that we did not move while (curr_chunk && curr_chunk->type != CT_PAREN_CLOSE) { chunk_t *rm_chunk = curr_chunk; curr_chunk = chunk_get_next(curr_chunk); chunk_del(rm_chunk); } } } chunk_t *tmp = chunk_get_next_ncnl(os); if (chunk_is_paren_open(tmp)) { tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp)); } fix_variable_definition(tmp); } // handle_oc_property_decl static void handle_cs_square_stmt(chunk_t *os) { LOG_FUNC_ENTRY(); chunk_t *cs = chunk_get_next(os); while (cs != nullptr && cs->level > os->level) { cs = chunk_get_next(cs); } if (cs == nullptr || cs->type != CT_SQUARE_CLOSE) { return; } set_chunk_parent(os, CT_CS_SQ_STMT); set_chunk_parent(cs, CT_CS_SQ_STMT); chunk_t *tmp; for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp)) { set_chunk_parent(tmp, CT_CS_SQ_STMT); if (chunk_is_token(tmp, CT_COLON)) { set_chunk_type(tmp, CT_CS_SQ_COLON); } } tmp = chunk_get_next_ncnl(cs); if (tmp != nullptr) { chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START); } } static void handle_cs_property(chunk_t *bro) { LOG_FUNC_ENTRY(); set_paren_parent(bro, CT_CS_PROPERTY); bool did_prop = false; chunk_t *pc = bro; while ((pc = chunk_get_prev_ncnlni(pc)) != nullptr) // Issue #2279 { if (pc->level == bro->level) { //prevent scanning back past 'new' in expressions like new List<int> {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_t *pc) { chunk_t *prev; for (prev = chunk_get_prev(pc); chunk_is_token(prev, CT_COMMA); prev = chunk_get_prev(prev)) { // empty } if (chunk_is_token(prev, CT_SQUARE_OPEN)) { while (pc != prev) { set_chunk_parent(pc, CT_TYPE); pc = chunk_get_prev(pc); } set_chunk_parent(prev, CT_TYPE); } } static void handle_wrap(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *opp = chunk_get_next(pc); chunk_t *name = chunk_get_next(opp); chunk_t *clp = chunk_get_next(name); log_rule_B("sp_func_call_paren"); log_rule_B("sp_cpp_cast_paren"); iarf_e pav = (pc->type == 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 = (pc->type == 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, (pc->type == 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_t *pc) { LOG_FUNC_ENTRY(); chunk_t *opp = chunk_get_next_ncnl(pc); chunk_t *name = chunk_get_next_ncnl(opp); chunk_t *tmp = chunk_get_next_ncnl(chunk_get_next_ncnl(name)); chunk_t *clp = chunk_skip_to_match(opp); chunk_t *cma = chunk_get_next_ncnl(clp); if ( !opp || !name || !clp || !cma || !tmp || (name->type != CT_WORD && name->type != CT_TYPE) || opp->type != 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 = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 { if ( !chunk_is_type(tmp) && tmp->type != CT_OPERATOR && tmp->type != CT_WORD && tmp->type != 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_t *pc) { LOG_FUNC_ENTRY(); bool did_colon = false; chunk_t *tmp = pc; while ((tmp = chunk_get_next(tmp)) != nullptr) { 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; } } } }
/** * @file combine.cpp * Labels the chunks as needed. * * @author Ben Gardner * @author Guy Maurel since version 0.62 for uncrustify4Qt * October 2015, 2016 * @license GPL v2+ */ #include "combine.h" #include "chunk_list.h" #include "combine_labels.h" #include "ChunkStack.h" #include "error_types.h" #include "flag_parens.h" #include "lang_pawn.h" #include "language_tools.h" #include "log_rules.h" #include "newlines.h" #include "prototypes.h" #include "tokenize_cleanup.h" #include "unc_ctype.h" #include "uncrustify.h" #include "uncrustify_types.h" #include <cassert> #include <cstdio> #include <cstdlib> #include <limits> #include <map> 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_t *pc); /** * Combines two tokens into {{ and }} if inside parens and nothing is between * either pair. */ static void check_double_brace_init(chunk_t *bo1); /** * We are on a typedef. * If the next word is not enum/union/struct, then the last word before the * next ',' or ';' or '__attribute__' is a type. * * typedef [type...] [*] type [, [*]type] ; * typedef <return type>([*]func)(); * typedef <return type>([*]func)(params); * typedef <return type>(__stdcall *func)(); Bug # 633 MS-specific extension * include the config-file "test/config/MS-calling_conventions.cfg" * typedef <return type>func(params); * typedef <enum/struct/union> [type] [*] type [, [*]type] ; * typedef <enum/struct/union> [type] { ... } [*] type [, [*]type] ; */ static void fix_typedef(chunk_t *pc); /** * We are on an enum/struct/union tag that is NOT inside a typedef. * If there is a {...} and words before the ';', then they are variables. * * tag { ... } [*] word [, [*]word] ; * tag [word/type] { ... } [*] word [, [*]word] ; * enum [word/type [: int_type]] { ... } [*] word [, [*]word] ; * tag [word/type] [word]; -- this gets caught later. * fcn(tag [word/type] [word]) * a = (tag [word/type] [*])&b; * * REVISIT: should this be consolidated with the typedef code? */ static void fix_enum_struct_union(chunk_t *pc); /** * Checks to see if the current paren is part of a cast. * We already verified that this doesn't follow function, TYPE, IF, FOR, * SWITCH, or WHILE and is followed by WORD, TYPE, STRUCT, ENUM, or UNION. * * @param start Pointer to the open paren */ static void fix_casts(chunk_t *pc); /** * CT_TYPE_CAST follows this pattern: * dynamic_cast<...>(...) * * Mark everything between the <> as a type and set the paren parent */ static void fix_type_cast(chunk_t *pc); 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_t *process_return(chunk_t *pc); /** * TODO: add doc cmt * */ static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags); /** * We're on a 'class' or 'struct'. * Scan for CT_FUNCTION with a string that matches pclass->str */ static void mark_class_ctor(chunk_t *pclass); static void mark_cpp_constructor(chunk_t *pc); /** * Just hit an assign. Go backwards until we hit an open brace/paren/square or * semicolon (TODO: other limiter?) and mark as a LValue. */ static void mark_lvalue(chunk_t *pc); /** * We are on a word followed by a angle open which is part of a template. * If the angle close is followed by a open paren, then we are on a template * function def or a template function call: * Vector2<float>(...) [: ...[, ...]] { ... } * Or we could be on a variable def if it's followed by a word: * Renderer<rgb32> rend; */ static void mark_template_func(chunk_t *pc, chunk_t *pc_next); /** * Just mark every CT_WORD until a semicolon as CT_SQL_WORD. * Adjust the levels if pc is CT_SQL_BEGIN */ static void mark_exec_sql(chunk_t *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_t *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_t *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_t *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_t *pc); /** * Process an ObjC message send statement: * [ class func: val1 name2: val2 name3: val3] ; // named params * [ class func: val1 : val2 : val3] ; // unnamed params * [ class <proto> 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_t *pc); //! Process @Property values and re-arrange them if necessary static void handle_oc_property_decl(chunk_t *pc); //! Process @available annotation static void handle_oc_available(chunk_t *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_t *handle_oc_md_type(chunk_t *paren_open, c_token_t 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_t *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_t *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_t *pc); /** * We are on the C++ 'template' keyword. * What follows should be the following: * * template <class identifier> function_declaration; * template <typename identifier> function_declaration; * template <class identifier> class class_declaration; * template <typename identifier> 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_t *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_t *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_t *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_t *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_t *pc); static bool is_oc_block(chunk_t *pc); /** * Java assert statements are: "assert EXP1 [: EXP2] ;" * Mark the parent of the colon and semicolon */ static void handle_java_assert(chunk_t *pc); /** * Parse off the types in the D template args, adds to cs * returns the close_paren */ static chunk_t *get_d_template_types(ChunkStack &cs, chunk_t *open_paren); static bool chunkstack_match(ChunkStack &cs, chunk_t *pc); void make_type(chunk_t *pc) { LOG_FUNC_ENTRY(); if (pc != nullptr) { if (chunk_is_token(pc, CT_WORD)) { set_chunk_type(pc, CT_TYPE); } else if ( ( chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc)) && chunk_is_type(pc->prev)) // Issue # 2640 { set_chunk_type(pc, CT_PTR_TYPE); } else if ( chunk_is_addr(pc) && !chunk_is_token(pc->prev, CT_SQUARE_OPEN)) // Issue # 2166 { set_chunk_type(pc, CT_BYREF); } } } void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_t clr_flags, scope_e nav) { LOG_FUNC_ENTRY(); while (start != nullptr && start != end) { chunk_flags_upd(start, clr_flags, set_flags); start = chunk_get_next(start, nav); if (start == nullptr) { return; } } if (end != nullptr) { chunk_flags_upd(end, clr_flags, set_flags); } } chunk_t *set_paren_parent(chunk_t *start, c_token_t parent) { LOG_FUNC_ENTRY(); chunk_t *end; end = chunk_skip_to_match(start, scope_e::PREPROC); if (end != nullptr) { LOG_FMT(LFLPAREN, "%s(%d): %zu:%zu '%s' and %zu:%zu '%s' type is %s, parent_type is %s", __func__, __LINE__, start->orig_line, start->orig_col, start->text(), end->orig_line, end->orig_col, end->text(), get_token_name(start->type), get_token_name(parent)); log_func_stack_inline(LFLPAREN); set_chunk_parent(start, parent); set_chunk_parent(end, parent); } LOG_FMT(LFLPAREN, "%s(%d):\n", __func__, __LINE__); return(chunk_get_next_ncnl(end, scope_e::PREPROC)); } static void flag_asm(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *tmp = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (!chunk_is_token(tmp, CT_QUALIFIER)) { return; } chunk_t *po = chunk_get_next_ncnl(tmp, scope_e::PREPROC); if (!chunk_is_paren_open(po)) { return; } chunk_t *end = chunk_skip_to_match(po, scope_e::PREPROC); if (end == nullptr) { return; } set_chunk_parent(po, CT_ASM); set_chunk_parent(end, CT_ASM); for ( tmp = chunk_get_next_ncnl(po, scope_e::PREPROC); tmp != nullptr && tmp != end; tmp = chunk_get_next_ncnl(tmp, scope_e::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(chunk_get_next_ncnl(tmp, scope_e::PREPROC), CT_STRING) && chunk_is_token(chunk_get_prev_ncnlni(tmp, scope_e::PREPROC), CT_STRING)) // Issue #2279 { chunk_t 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 = chunk_get_next_ncnl(end, scope_e::PREPROC); if (tmp == nullptr) { return; } if (chunk_is_token(tmp, CT_SEMICOLON)) { set_chunk_parent(tmp, CT_ASM); } } // flag_asm static bool chunk_ends_type(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc = start; bool ret = false; size_t cnt = 0; bool last_expr = false; bool last_lval = false; for ( ; pc != nullptr; pc = chunk_get_prev_ncnlni(pc)) // Issue #2279 { LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s', orig_line %zu, orig_col %zu\n ", __func__, __LINE__, get_token_name(pc->type), pc->text(), pc->orig_line, pc->orig_col); log_pcf_flags(LFTYPE, pc->flags); if ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE) || chunk_is_token(pc, CT_PTR_TYPE) || chunk_is_token(pc, CT_STAR) || chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_PP) || chunk_is_token(pc, CT_QUALIFIER) || ( language_is_set(LANG_CPP | LANG_OC) // Issue #2727 && get_chunk_parent_type(pc) == CT_TEMPLATE && ( chunk_is_token(pc, CT_ANGLE_OPEN) || chunk_is_token(pc, CT_ANGLE_CLOSE))) || ( language_is_set(LANG_CS) && (chunk_is_token(pc, CT_MEMBER)))) { cnt++; last_expr = pc->flags.test(PCF_EXPR_START) && !pc->flags.test(PCF_IN_FCN_CALL); last_lval = pc->flags.test(PCF_LVALUE); continue; } if ( ( chunk_is_semicolon(pc) && !pc->flags.test(PCF_IN_FOR)) || chunk_is_token(pc, CT_TYPEDEF) || chunk_is_token(pc, CT_BRACE_OPEN) || chunk_is_token(pc, CT_BRACE_CLOSE) || chunk_is_token(pc, CT_VBRACE_CLOSE) || chunk_is_token(pc, CT_FPAREN_CLOSE) || chunk_is_forin(pc) || chunk_is_token(pc, CT_MACRO) || chunk_is_token(pc, CT_PP_IF) || chunk_is_token(pc, CT_PP_ELSE) || chunk_is_token(pc, CT_PP_ENDIF) || ( ( chunk_is_token(pc, CT_COMMA) && !pc->flags.test(PCF_IN_FCN_CALL)) && last_expr) || ( chunk_is_token(pc, CT_SPAREN_OPEN) && last_lval)) { ret = cnt > 0; } break; } if (pc == nullptr) { // first token ret = true; } LOG_FMT(LFTYPE, "%s(%d): first token verdict: %s\n", __func__, __LINE__, ret ? "yes" : "no"); return(ret); } // chunk_ends_type void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next) { LOG_FUNC_ENTRY(); chunk_t *tmp; // 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); } if ( ( chunk_is_token(prev, CT_FPAREN_CLOSE) || ( ( chunk_is_str(prev, "const", 5) || chunk_is_str(prev, "override", 8)) && 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", 1))) { set_chunk_type(pc, CT_ASSIGN_FUNC_PROTO); } 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); } } // D stuff if ( language_is_set(LANG_D) && chunk_is_token(pc, CT_QUALIFIER) && chunk_is_str(pc, "const", 5) && chunk_is_token(next, CT_PAREN_OPEN)) { set_chunk_type(pc, CT_D_CAST); set_paren_parent(next, pc->type); } 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 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); } else if (chunk_is_token(tmp, CT_AMP)) { set_chunk_type(tmp, CT_ADDR); } else if (chunk_is_token(tmp, CT_MINUS)) { set_chunk_type(tmp, CT_NEG); } else if (chunk_is_token(tmp, CT_PLUS)) { set_chunk_type(tmp, CT_POS); } } /* * 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 = chunk_get_prev_ncnlni(pc); tmp != nullptr; tmp = chunk_get_prev_ncnlni(tmp)) // 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); } } if ( chunk_is_token(pc, CT_ALIGN) && tmp != nullptr) { if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, pc->type); } else if (chunk_is_token(tmp, CT_COLON)) { set_chunk_parent(tmp, pc->type); } } } // 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); tmp = chunk_get_next(next); while (tmp != nullptr) { if (chunk_is_token(tmp, CT_PAREN_CLOSE)) { set_chunk_parent(tmp, pc->type); break; } make_type(tmp); tmp = chunk_get_next(tmp); } } else { set_chunk_type(pc, CT_QUALIFIER); } } 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); } if (chunk_is_token(pc, CT_ASM)) { flag_asm(pc); } // 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); } } } // 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, "-", 1) || chunk_is_str(pc, "+", 1)) && chunk_is_str(next, "(", 1)) { handle_oc_message_decl(pc); } } 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); } } if (chunk_is_token(pc, CT_OC_PROPERTY)) { handle_oc_property_decl(pc); } if (chunk_is_token(pc, CT_OC_AVAILABLE)) { handle_oc_available(pc); } } // 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); } 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); } if ( chunk_is_token(pc, CT_SQUARE_CLOSE) && chunk_is_token(next, CT_WORD)) { handle_cs_array_type(pc); } 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); } if (chunk_is_token(pc, CT_WHEN) && pc->next->type != CT_SPAREN_OPEN) { set_chunk_type(pc, CT_WORD); } } 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); } if (chunk_is_token(pc, CT_NEW)) { chunk_t *ts = nullptr; tmp = next; if (chunk_is_token(tmp, CT_TSQUARE)) { ts = tmp; tmp = chunk_get_next_ncnl(tmp); } 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); } } } // 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 tmp = next; while ((tmp = chunk_get_next_nc(tmp)) != nullptr) { 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; } } } } if (chunk_is_token(pc, CT_ASSERT)) { handle_java_assert(pc); } if (chunk_is_token(pc, CT_ANNOTATION)) { tmp = chunk_get_next_ncnl(pc); if (chunk_is_paren_open(tmp)) { set_paren_parent(tmp, CT_ANNOTATION); } } if (chunk_is_token(pc, CT_SIZEOF) && language_is_set(LANG_ALLC)) { tmp = chunk_get_next_ncnl(pc); if (chunk_is_token(tmp, CT_ELLIPSIS)) { set_chunk_parent(tmp, CT_SIZEOF); } } if (chunk_is_token(pc, CT_DECLTYPE)) { tmp = chunk_get_next_ncnl(pc); 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)) { 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); } } } } // 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); } } 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); } if (chunk_is_token(pc, CT_PROTO_WRAP)) { handle_proto_wrap(pc); } // Handle the typedef if (chunk_is_token(pc, CT_TYPEDEF)) { fix_typedef(pc); } if ( chunk_is_token(pc, CT_ENUM) || chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_UNION) || ( chunk_is_token(pc, CT_CLASS) && !language_is_set(LANG_D))) { if (prev->type != CT_TYPEDEF) { fix_enum_struct_union(pc); } } if (chunk_is_token(pc, CT_EXTERN)) { if (chunk_is_paren_open(next)) { 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); tmp = chunk_get_next_ncnl(next); if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(tmp, CT_EXTERN); } } } if (chunk_is_token(pc, CT_TEMPLATE)) { if (language_is_set(LANG_D)) { handle_d_template(pc); } else { handle_cpp_template(pc); } } 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); } 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); } if (chunk_is_token(pc, CT_TYPE_CAST)) { fix_type_cast(pc); } 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); } if (chunk_is_token(pc, CT_D_TEMPLATE)) { set_paren_parent(next, pc->type); } /* * 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)) { tmp = chunk_get_next_ncnl(next); 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 = chunk_get_next_ncnl(next); if (chunk_is_token(tmp, CT_AMP)) { auto tmp2 = chunk_get_next_ncnl(tmp); if (chunk_is_token(tmp2, CT_WORD)) { tmp2 = chunk_get_next_ncnl(tmp2); } if (chunk_is_token(tmp2, CT_PAREN_CLOSE)) { tmp2 = chunk_get_next_ncnl(tmp2); if (chunk_is_token(tmp2, CT_SQUARE_OPEN)) { is_byref_array = true; set_chunk_type(tmp, CT_BYREF); } } } } if (!is_byref_array) { tmp = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level); if (tmp != nullptr) { tmp = chunk_get_next(tmp); 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 = chunk_get_next_ncnl(next); 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); } // 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)) { 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 = chunk_get_next_ncnl(tmp); } if (tmp != nullptr) { if (chunk_is_paren_open(tmp)) { tmp = flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, pc->type, false); if (tmp != nullptr) { 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); } } } } } // Mark the parameters in catch() if (chunk_is_token(pc, CT_CATCH) && chunk_is_token(next, CT_SPAREN_OPEN)) { fix_fcn_def_params(next); } 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); } } // 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); } /* * 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_any(PCF_IN_TYPEDEF | PCF_IN_TEMPLATE) && 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, ")", 1) && chunk_is_str(next, "(", 1)) { if (language_is_set(LANG_D)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false); } else { mark_function_type(pc); } } if ( (chunk_is_token(pc, CT_CLASS) || chunk_is_token(pc, CT_STRUCT)) && pc->level == pc->brace_level) { if (pc->type != CT_STRUCT || !language_is_set(LANG_C)) { mark_class_ctor(pc); } } if (chunk_is_token(pc, CT_OC_CLASS)) { handle_oc_class(pc); } // 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)) && prev->type != CT_DECLTYPE && prev->type != 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); } } if (language_is_set(LANG_CPP)) { chunk_t *nnext = chunk_get_next_ncnl(next); // 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", 1)))) { const size_t level = pc->level; bool found_status = false; chunk_t *pprev = chunk_get_prev(pc); for ( ; ( pprev != nullptr && pprev->level >= level && pprev->type != CT_SEMICOLON && pprev->type != CT_ACCESS_COLON) ; pprev = chunk_get_prev(pprev)) { if (pprev->level != level) { continue; } if (chunk_is_token(next, CT_NUMBER)) { if ( pprev->type == CT_QUALIFIER && chunk_is_str(pprev, "virtual", 7)) { found_status = true; break; } } else { if ( pprev->type == CT_FUNC_CLASS_PROTO // ctor/dtor || pprev->type == CT_FUNC_PROTO) // normal function { found_status = true; break; } } } if (found_status) { set_chunk_parent(pc, pprev->type); } } // Issue #2332 bool we_have_a_case_before = false; if (chunk_is_token(pc, CT_COLON)) { // check if we have a case before chunk_t *switch_before = chunk_get_prev_type(pc, CT_CASE, pc->level); if (switch_before != nullptr) { LOG_FMT(LFCNR, "%s(%d): switch_before->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n", __func__, __LINE__, switch_before->orig_line, switch_before->orig_col, switch_before->text(), get_token_name(switch_before->type)); we_have_a_case_before = true; } } // Detect a braced-init-list if ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE) || chunk_is_token(pc, CT_ASSIGN) || chunk_is_token(pc, CT_RETURN) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ANGLE_CLOSE) || chunk_is_token(pc, CT_SQUARE_CLOSE) || chunk_is_token(pc, CT_TSQUARE) || chunk_is_token(pc, CT_FPAREN_OPEN) || chunk_is_token(pc, CT_QUESTION) || ( chunk_is_token(pc, CT_COLON) && !we_have_a_case_before) || ( chunk_is_token(pc, CT_BRACE_OPEN) && ( get_chunk_parent_type(pc) == CT_NONE || get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST))) { log_pcf_flags(LFCNR, pc->flags); auto brace_open = chunk_get_next_ncnl(pc); if ( chunk_is_token(brace_open, CT_BRACE_OPEN) && ( get_chunk_parent_type(brace_open) == CT_NONE || get_chunk_parent_type(brace_open) == CT_ASSIGN || get_chunk_parent_type(brace_open) == CT_RETURN || get_chunk_parent_type(brace_open) == CT_BRACED_INIT_LIST)) { log_pcf_flags(LFCNR, brace_open->flags); auto brace_close = chunk_skip_to_match(next); if (chunk_is_token(brace_close, CT_BRACE_CLOSE)) { set_chunk_parent(brace_open, CT_BRACED_INIT_LIST); set_chunk_parent(brace_close, CT_BRACED_INIT_LIST); tmp = chunk_get_next_ncnl(brace_close); if (tmp) { chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START); } // TODO: Change pc->type CT_WORD -> CT_TYPE // for the case CT_ASSIGN (and others). // TODO: Move this block to the fix_fcn_call_args function. if (chunk_is_token(pc, CT_WORD) && pc->flags.test(PCF_IN_FCN_CALL)) { set_chunk_type(pc, CT_TYPE); } } } } } // 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) && (chunk_is_token(pc, CT_QUESTION)) && (chunk_is_token(prev, CT_ANGLE_CLOSE))) { set_chunk_type(pc, CT_PTR_TYPE); } if (chunk_is_token(pc, CT_MINUS)) { set_chunk_type(pc, CT_NEG); } if (chunk_is_token(pc, CT_PLUS)) { set_chunk_type(pc, CT_POS); } if (chunk_is_token(pc, CT_INCDEC_AFTER)) { set_chunk_type(pc, CT_INCDEC_BEFORE); } 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); } } 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); } } } // Detect a variable definition that starts with struct/enum/union/class if ( !pc->flags.test(PCF_IN_TYPEDEF) && get_chunk_parent_type(prev) != CT_CPP_CAST && !prev->flags.test(PCF_IN_FCN_DEF) && ( chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_UNION) || chunk_is_token(pc, CT_CLASS) || chunk_is_token(pc, CT_ENUM))) { tmp = chunk_skip_dc_member(next); if ((chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD))) { set_chunk_parent(tmp, pc->type); set_chunk_type(tmp, CT_TYPE); tmp = chunk_get_next_ncnl(tmp); } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { tmp = chunk_skip_to_match(tmp); if (tmp != nullptr) { tmp = chunk_get_next_ncnl(tmp); } } if ( tmp != nullptr && (chunk_is_ptr_operator(tmp) || chunk_is_token(tmp, CT_WORD))) { mark_variable_definition(tmp); } } /* * 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) || (pc && 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)) || 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 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 assign_found = false; tmp = pc; while (tmp != nullptr) { if (chunk_is_token(tmp, CT_SEMICOLON)) { break; } else if (chunk_is_token(tmp, CT_ASSIGN)) { assign_found = true; break; } tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279 } if (assign_found) { // 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) || chunk_is_star(pc->next))) || (next->type == CT_QUALIFIER)) // Issue #2648 { // example: // auto getComponent(Color *color) -> Component * { // auto getComponent(Color *color) -> Component ** { // auto getComponent(Color *color) -> Component * _Nonnull 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\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) && prev->type != CT_SQUARE_CLOSE && prev->type != CT_DC_MEMBER) ? CT_DEREF : CT_ARITH); } if (pc->flags.test(PCF_IN_TYPEDEF)) // Issue #1255/#633 { tmp = pc; while (tmp != nullptr) { if ( chunk_is_token(tmp, CT_SEMICOLON) || chunk_is_token(tmp, CT_BRACE_OPEN)) { break; } else if (chunk_is_token(tmp, CT_TYPEDEF)) { set_chunk_type(pc, CT_PTR_TYPE); } tmp = chunk_get_prev_ncnlni(tmp); // 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)) { 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)) { tmp = chunk_get_prev_ncnlni(prev); // Issue #2279 if (tmp != nullptr) { 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(next, CT_TYPE)) // Issue #2103 { set_chunk_type(pc, CT_BYREF); } } } } } } } 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)) { set_chunk_type(pc, (pc->type == 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 tmp = chunk_get_prev_type(pc, CT_FPAREN_OPEN, pc->level - 1); if ( tmp != nullptr && 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 * { tmp = pc; while ((tmp != nullptr)) { 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 = chunk_get_prev(tmp); } } } } /* * 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 assign_found = false; bool is_preproc = pc->flags.test(PCF_IN_PREPROC); chunk_t *temp; for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp)) { 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)) { assign_found = true; break; } if ( chunk_is_token(temp, CT_SEMICOLON) || ( is_preproc && ( !temp->flags.test(PCF_IN_PREPROC) || chunk_is_token(temp, CT_PREPROC)))) { break; } } if (assign_found) { // it is a Type alias, alias template for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp)) { 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 ( pc->type == CT_BOOL && !pc->flags.test(PCF_IN_PREPROC) && chunk_is_str(pc, "&&", 2) && chunk_ends_type(pc->prev)) { set_chunk_type(pc, CT_BYREF); } // Issue #1704 if ( chunk_is_token(pc, CT_INCDEC_AFTER) && pc->flags.test(PCF_IN_PREPROC)) { chunk_t *tmp_2 = chunk_get_next(pc); 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_t *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_t *pc = chunk_get_prev_ncnlni(bo1); // Issue #2279 if (pc == nullptr) { return; } if (chunk_is_paren_close(pc)) { chunk_t *bo2 = chunk_get_next(bo1); if (bo2 == nullptr) { return; } if (chunk_is_token(bo2, CT_BRACE_OPEN)) { // found a potential double brace chunk_t *bc2 = chunk_skip_to_match(bo2); if (bc2 == nullptr) { return; } chunk_t *bc1 = chunk_get_next(bc2); if (bc1 == nullptr) { 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_t *pc; chunk_t 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_get_head(); pc != nullptr; pc = chunk_get_next_ncnl(pc)) { 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 preceeded 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 unucustify knows // only builtin types and knows nothing of user-defined types chunk_t *prev = chunk_get_prev_ncnlni(pc); // 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_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC); if ( next != nullptr && chunk_is_token(next, CT_PAREN_OPEN)) { flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_ATTRIBUTE, false); } } } pc = chunk_get_head(); if (pc == nullptr) { return; } if ( chunk_is_newline(pc) || chunk_is_comment(pc)) { pc = chunk_get_next_ncnl(pc); } while (pc != nullptr) { if (chunk_is_token(pc, CT_IGNORED)) { pc = chunk_get_next_ncnl(pc); 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_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC); // Issue #2279 if (prev == nullptr) { 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_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (next == nullptr) { 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 = chunk_get_next_ncnl(pc); } pawn_add_virtual_semicolons(); process_returns(); /* * 2nd pass - handle variable definitions * REVISIT: We need function params marked to do this (?) */ pc = chunk_get_head(); int square_level = -1; while (pc != nullptr) { 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->text(), 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<size_t>(square_level)) { square_level = -1; } } if ( chunk_is_token(pc, CT_EXTERN) && language_is_set(LANG_ALLC)) { chunk_t *next = chunk_get_next_ncnl(pc); if (chunk_is_token(next, CT_STRING)) { chunk_t *tmp = chunk_get_next_ncnl(next); while (tmp != nullptr) { 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 = chunk_get_next_ncnl(tmp); } } } if ( chunk_is_token(pc, CT_ATTRIBUTE) && language_is_set(LANG_ALLC)) { chunk_t *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 = chunk_get_next_type(pc, 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->text(), 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_ENUM && !pc->flags.test(PCF_IN_ENUM)) { pc = fix_variable_definition(pc); } else { pc = chunk_get_next_ncnl(pc); } } } // fix_symbols static void mark_lvalue(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *prev; if (pc->flags.test(PCF_IN_PREPROC)) { return; } for (prev = chunk_get_prev_ncnlni(pc); // Issue #2279 prev != nullptr; prev = chunk_get_prev_ncnlni(prev)) // Issue #2279 { if ( prev->level < pc->level || chunk_is_token(prev, CT_ASSIGN) || chunk_is_token(prev, CT_COMMA) || chunk_is_token(prev, CT_BOOL) || chunk_is_semicolon(prev) || chunk_is_str(prev, "(", 1) || chunk_is_str(prev, "{", 1) || chunk_is_str(prev, "[", 1) || prev->flags.test(PCF_IN_PREPROC)) { break; } chunk_flags_set(prev, PCF_LVALUE); if (prev->level == pc->level && chunk_is_str(prev, "&", 1)) { make_type(prev); } } } static void mark_function_return_type(chunk_t *fname, chunk_t *start, c_token_t parent_type) { LOG_FUNC_ENTRY(); chunk_t *pc = start; if (pc != nullptr) { // Step backwards from pc and mark the parent of the return type LOG_FMT(LFCNR, "%s(%d): (backwards) return type for '%s' @ orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, fname->text(), fname->orig_line, fname->orig_col); chunk_t *first = pc; while (pc != nullptr) { LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', type is %s, ", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type)); log_pcf_flags(LFCNR, pc->flags); if (chunk_is_token(pc, CT_ANGLE_CLOSE)) { pc = skip_template_prev(pc); if (pc == nullptr || chunk_is_token(pc, CT_TEMPLATE)) { //either expression is not complete or this is smth like 'template<T> void func()' // - we are not interested in 'template<T>' part break; } else { //this is smth like 'vector<int> func()' and 'pc' is currently on 'vector' - just proceed } } if ( ( !chunk_is_type(pc) && pc->type != CT_OPERATOR && pc->type != CT_WORD && pc->type != CT_ADDR) || pc->flags.test(PCF_IN_PREPROC)) { break; } if (!chunk_is_ptr_operator(pc)) { first = pc; } pc = chunk_get_prev_ncnlni(pc); // Issue #2279 } LOG_FMT(LFCNR, "%s(%d): marking returns...", __func__, __LINE__); // Changing words to types into tuple return types in CS. bool is_return_tuple = false; if (chunk_is_token(pc, CT_PAREN_CLOSE) && !pc->flags.test(PCF_IN_PREPROC)) { first = chunk_skip_to_match_rev(pc); is_return_tuple = true; } pc = first; while (pc != nullptr) { LOG_FMT(LFCNR, " text() '%s', type is %s", pc->text(), get_token_name(pc->type)); if (parent_type != CT_NONE) { set_chunk_parent(pc, parent_type); } chunk_t *prev = chunk_get_prev_ncnlni(pc); // Issue #2279 if ( !is_return_tuple || pc->type != CT_WORD || (prev != nullptr && prev->type != CT_TYPE)) { make_type(pc); } if (pc == start) { break; } pc = chunk_get_next_ncnl(pc); //template angles should keep parent type CT_TEMPLATE if (chunk_is_token(pc, CT_ANGLE_OPEN)) { pc = chunk_get_next_type(pc, CT_ANGLE_CLOSE, pc->level); if (pc == start) { break; } pc = chunk_get_next_ncnl(pc); } } LOG_FMT(LFCNR, "\n"); // Back up and mark parent type on friend declarations if (parent_type != CT_NONE && first && first->flags.test(PCF_IN_CLASS)) { pc = chunk_get_prev_ncnlni(first); // Issue #2279 if (chunk_is_token(pc, CT_FRIEND)) { LOG_FMT(LFCNR, "%s(%d): marking friend\n", __func__, __LINE__); set_chunk_parent(pc, parent_type); // A friend might be preceded by a template specification, as in: // template <...> friend type func(...); // If so, we need to mark that also pc = chunk_get_prev_ncnlni(pc); // Issue #2279 if (chunk_is_token(pc, CT_ANGLE_CLOSE)) { pc = skip_template_prev(pc); if (chunk_is_token(pc, CT_TEMPLATE)) { LOG_FMT(LFCNR, "%s(%d): marking friend template\n", __func__, __LINE__); set_chunk_parent(pc, parent_type); } } } } } } // mark_function_return_type static bool mark_function_type(chunk_t *pc) { LOG_FUNC_ENTRY(); LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s' @ orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, get_token_name(pc->type), pc->text(), pc->orig_line, pc->orig_col); size_t star_count = 0; size_t word_count = 0; chunk_t *ptrcnk = nullptr; chunk_t *tmp; chunk_t *apo; chunk_t *apc; chunk_t *aft; bool anon = false; c_token_t pt, ptp; // Scan backwards across the name, which can only be a word and single star chunk_t *varcnk = chunk_get_prev_ncnlni(pc); // Issue #2279 varcnk = chunk_get_prev_ssq(varcnk); if (varcnk != nullptr && !chunk_is_word(varcnk)) { if ( language_is_set(LANG_OC) && chunk_is_str(varcnk, "^", 1) && chunk_is_paren_open(chunk_get_prev_ncnlni(varcnk))) // Issue #2279 { // anonymous ObjC block type -- RTYPE (^)(ARGS) anon = true; } else { LOG_FMT(LFTYPE, "%s(%d): not a word: text() '%s', type is %s, @ orig_line is %zu:, orig_col is %zu\n", __func__, __LINE__, varcnk->text(), get_token_name(varcnk->type), varcnk->orig_line, varcnk->orig_col); goto nogo_exit; } } apo = chunk_get_next_ncnl(pc); if (apo == nullptr) { return(false); } apc = chunk_skip_to_match(apo); if ( apc != nullptr && ( !chunk_is_paren_open(apo) || ((apc = chunk_skip_to_match(apo)) == nullptr))) { LOG_FMT(LFTYPE, "%s(%d): not followed by parens\n", __func__, __LINE__); goto nogo_exit; } aft = chunk_get_next_ncnl(apc); if (chunk_is_token(aft, CT_BRACE_OPEN)) { pt = CT_FUNC_DEF; } else if (chunk_is_token(aft, CT_SEMICOLON) || chunk_is_token(aft, CT_ASSIGN)) { pt = CT_FUNC_PROTO; } else { LOG_FMT(LFTYPE, "%s(%d): not followed by '{' or ';'\n", __func__, __LINE__); goto nogo_exit; } ptp = pc->flags.test(PCF_IN_TYPEDEF) ? CT_FUNC_TYPE : CT_FUNC_VAR; tmp = pc; while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 { tmp = chunk_get_prev_ssq(tmp); LOG_FMT(LFTYPE, " -- type is %s, %s on orig_line %zu, orig_col is %zu", get_token_name(tmp->type), tmp->text(), tmp->orig_line, tmp->orig_col); if ( chunk_is_star(tmp) || chunk_is_token(tmp, CT_PTR_TYPE) || chunk_is_token(tmp, CT_CARET)) { star_count++; ptrcnk = tmp; LOG_FMT(LFTYPE, " -- PTR_TYPE\n"); } else if ( chunk_is_word(tmp) || chunk_is_token(tmp, CT_WORD) || chunk_is_token(tmp, CT_TYPE)) { word_count++; LOG_FMT(LFTYPE, " -- TYPE(%s)\n", tmp->text()); } else if (chunk_is_token(tmp, CT_DC_MEMBER)) { word_count = 0; LOG_FMT(LFTYPE, " -- :: reset word_count\n"); } else if (chunk_is_str(tmp, "(", 1)) { LOG_FMT(LFTYPE, " -- open paren (break)\n"); break; } else { LOG_FMT(LFTYPE, " -- unexpected token: type is %s, text() '%s', on orig_line %zu, orig_col %zu\n", get_token_name(tmp->type), tmp->text(), tmp->orig_line, tmp->orig_col); goto nogo_exit; } } // Fixes #issue 1577 // Allow word count 2 incase of function pointer declaration. // Ex: bool (__stdcall* funcptr)(int, int); if ( star_count > 1 || (word_count > 1 && !(word_count == 2 && ptp == CT_FUNC_VAR)) || ((star_count + word_count) == 0)) { LOG_FMT(LFTYPE, "%s(%d): bad counts word: %zu, star: %zu\n", __func__, __LINE__, word_count, star_count); goto nogo_exit; } // make sure what appears before the first open paren can be a return type if (!chunk_ends_type(chunk_get_prev_ncnlni(tmp))) // Issue #2279 { goto nogo_exit; } if (ptrcnk) { set_chunk_type(ptrcnk, CT_PTR_TYPE); } if (!anon) { if (pc->flags.test(PCF_IN_TYPEDEF)) { set_chunk_type(varcnk, CT_TYPE); } else { set_chunk_type(varcnk, CT_FUNC_VAR); chunk_flags_set(varcnk, PCF_VAR_1ST_DEF); } } set_chunk_type(pc, CT_TPAREN_CLOSE); set_chunk_parent(pc, ptp); set_chunk_type(apo, CT_FPAREN_OPEN); set_chunk_parent(apo, pt); set_chunk_type(apc, CT_FPAREN_CLOSE); set_chunk_parent(apc, pt); fix_fcn_def_params(apo); if (chunk_is_semicolon(aft)) { set_chunk_parent(aft, aft->flags.test(PCF_IN_TYPEDEF) ? CT_TYPEDEF : CT_FUNC_VAR); } else if (chunk_is_token(aft, CT_BRACE_OPEN)) { flag_parens(aft, PCF_NONE, CT_NONE, pt, false); } // Step backwards to the previous open paren and mark everything a tmp = pc; while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 { LOG_FMT(LFTYPE, " ++ type is %s, text() '%s', on orig_line %zu, orig_col %zu\n", get_token_name(tmp->type), tmp->text(), tmp->orig_line, tmp->orig_col); if (*tmp->str.c_str() == '(') { if (!pc->flags.test(PCF_IN_TYPEDEF)) { chunk_flags_set(tmp, PCF_VAR_1ST_DEF); } set_chunk_type(tmp, CT_TPAREN_OPEN); set_chunk_parent(tmp, ptp); tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279 if ( chunk_is_token(tmp, CT_FUNCTION) || chunk_is_token(tmp, CT_FUNC_CALL) || chunk_is_token(tmp, CT_FUNC_CALL_USER) || chunk_is_token(tmp, CT_FUNC_DEF) || chunk_is_token(tmp, CT_FUNC_PROTO)) { set_chunk_type(tmp, CT_TYPE); chunk_flags_clr(tmp, PCF_VAR_1ST_DEF); } mark_function_return_type(varcnk, tmp, ptp); break; } } return(true); nogo_exit: tmp = chunk_get_next_ncnl(pc); if (chunk_is_paren_open(tmp)) { LOG_FMT(LFTYPE, "%s(%d): setting FUNC_CALL on orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col); flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false); } return(false); } // mark_function_type static void process_returns(void) { LOG_FUNC_ENTRY(); chunk_t *pc; pc = chunk_get_head(); while (pc != nullptr) { if (pc->type != CT_RETURN) { pc = chunk_get_next_type(pc, CT_RETURN, -1); continue; } pc = process_return(pc); } } static chunk_t *process_return(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *next; chunk_t *temp; chunk_t *semi; chunk_t *cpar; chunk_t chunk; // grab next and bail if it is a semicolon next = chunk_ppa_get_next_ncnl(pc); if ( next == nullptr || 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 = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level); if (cpar == nullptr) { return(nullptr); } semi = chunk_ppa_get_next_ncnl(cpar); if (semi == nullptr) { return(nullptr); } 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 = chunk_get_next(temp)) { 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 != nullptr && temp->type != CT_NEWLINE) { temp->column = temp->column - 2; temp->orig_col = temp->orig_col - 2; temp->orig_col_end = temp->orig_col_end - 2; temp = chunk_get_next(temp); } } 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->next) != nullptr) { 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 = chunk_get_next(semi)) != nullptr) { 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.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 = chunk_get_next(temp)) { temp->level++; } } return(semi); } // process_return static bool is_ucase_str(const char *str, size_t len) { while (len-- > 0) { if (unc_toupper(*str) != *str) { return(false); } str++; } return(true); } static bool is_oc_block(chunk_t *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))); } static void fix_casts(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc; chunk_t *prev; chunk_t *first; chunk_t *after; chunk_t *last = nullptr; chunk_t *paren_close; const char *verb = "likely"; const char *detail = ""; size_t count = 0; int word_count = 0; bool nope; bool doubtful_cast = false; LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, start->text(), start->orig_line, start->orig_col); prev = chunk_get_prev_ncnlni(start); // Issue #2279 if (prev == nullptr) { return; } if (chunk_is_token(prev, CT_PP_DEFINED)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - after defined\n", __func__, __LINE__); return; } if (chunk_is_token(prev, CT_ANGLE_CLOSE)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - after > (template)\n", __func__, __LINE__); return; } // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren pc = chunk_get_next_ncnl(start); first = pc; while ( pc != nullptr && ( chunk_is_type(pc) || chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_QUALIFIER) || chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_PP) || chunk_is_token(pc, CT_STAR) || chunk_is_token(pc, CT_QUESTION) || chunk_is_token(pc, CT_CARET) || chunk_is_token(pc, CT_TSQUARE) || ( ( chunk_is_token(pc, CT_ANGLE_OPEN) || chunk_is_token(pc, CT_ANGLE_CLOSE)) && language_is_set(LANG_OC | LANG_JAVA)) || ( ( chunk_is_token(pc, CT_QUESTION) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_MEMBER)) && language_is_set(LANG_JAVA)) || chunk_is_token(pc, CT_AMP))) { LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER))) { word_count++; } else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP)) { // might be negativ, such as with: // a = val + (CFoo::bar_t)7; word_count--; } last = pc; pc = chunk_get_next_ncnl(pc); count++; } if ( pc == nullptr || pc->type != CT_PAREN_CLOSE || chunk_is_token(prev, CT_OC_CLASS)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast, hit type is %s\n", __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type)); return; } if (word_count > 1) { LOG_FMT(LCASTS, "%s(%d): -- too many words: %d\n", __func__, __LINE__, word_count); return; } paren_close = pc; // If last is a type or star/caret, we have a cast for sure if ( chunk_is_token(last, CT_STAR) || chunk_is_token(last, CT_CARET) || chunk_is_token(last, CT_PTR_TYPE) || chunk_is_token(last, CT_TYPE) || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA))) { verb = "for sure"; } else if (count == 1) { /* * We are on a potential cast of the form "(word)". * We don't know if the word is a type. So lets guess based on some * simple rules: * - if all caps, likely a type * - if it ends in _t, likely a type * - if it's objective-c and the type is id, likely valid */ verb = "guessed"; if ( (last->len() > 3) && (last->str[last->len() - 2] == '_') && (last->str[last->len() - 1] == 't')) { detail = " -- '_t'"; } else if (is_ucase_str(last->text(), last->len())) { detail = " -- upper case"; } else if (language_is_set(LANG_OC) && chunk_is_str(last, "id", 2)) { detail = " -- Objective-C id"; } else { // If we can't tell for sure whether this is a cast, decide against it detail = " -- mixed case"; doubtful_cast = true; } /* * If the next item is a * or &, the next item after that can't be a * number or string. * * If the next item is a +, the next item has to be a number. * * If the next item is a -, the next item can't be a string. * * For this to be a cast, the close paren must be followed by: * - constant (number or string) * - paren open * - word * * Find the next non-open paren item. */ pc = chunk_get_next_ncnl(paren_close); after = pc; do { after = chunk_get_next_ncnl(after); } while (chunk_is_token(after, CT_PAREN_OPEN)); if (after == nullptr) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - hit NULL\n", __func__, __LINE__); return; } nope = false; if (chunk_is_ptr_operator(pc)) { // star (*) and address (&) are ambiguous if ( chunk_is_token(after, CT_NUMBER_FP) || chunk_is_token(after, CT_NUMBER) || chunk_is_token(after, CT_STRING) || doubtful_cast) { nope = true; } } else if (chunk_is_token(pc, CT_MINUS)) { // (UINT8)-1 or (foo)-1 or (FOO)-'a' if (chunk_is_token(after, CT_STRING) || doubtful_cast) { nope = true; } } else if (chunk_is_token(pc, CT_PLUS)) { // (UINT8)+1 or (foo)+1 if ( (after->type != CT_NUMBER && after->type != CT_NUMBER_FP) || doubtful_cast) { nope = true; } } else if ( pc->type != CT_NUMBER_FP && pc->type != CT_NUMBER && pc->type != CT_WORD && pc->type != CT_THIS && pc->type != CT_TYPE && pc->type != CT_PAREN_OPEN && pc->type != CT_STRING && pc->type != CT_DECLTYPE && pc->type != CT_SIZEOF && get_chunk_parent_type(pc) != CT_SIZEOF && pc->type != CT_FUNC_CALL && pc->type != CT_FUNC_CALL_USER && pc->type != CT_FUNCTION && pc->type != CT_BRACE_OPEN && (!( chunk_is_token(pc, CT_SQUARE_OPEN) && language_is_set(LANG_OC)))) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); return; } if (nope) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - text() '%s' followed by type %s\n", __func__, __LINE__, pc->text(), get_token_name(after->type)); return; } } // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't pc = chunk_get_next_ncnl(paren_close); if (pc == nullptr) { return; } if ( chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_BOOL) // Issue #2151 || chunk_is_paren_close(pc)) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by type %s\n", __func__, __LINE__, get_token_name(pc->type)); return; } set_chunk_parent(start, CT_C_CAST); set_chunk_parent(paren_close, CT_C_CAST); LOG_FMT(LCASTS, "%s(%d): -- %s c-cast: (", __func__, __LINE__, verb); for (pc = first; pc != nullptr && pc != paren_close; pc = chunk_get_next_ncnl(pc)) { set_chunk_parent(pc, CT_C_CAST); make_type(pc); LOG_FMT(LCASTS, " %s", pc->text()); } LOG_FMT(LCASTS, " )%s\n", detail); // Mark the next item as an expression start pc = chunk_get_next_ncnl(paren_close); if (pc != nullptr) { chunk_flags_set(pc, PCF_EXPR_START); if (chunk_is_opening_brace(pc)) { set_paren_parent(pc, get_chunk_parent_type(start)); } } } // fix_casts static void fix_type_cast(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc; pc = chunk_get_next_ncnl(start); if (pc == nullptr || pc->type != CT_ANGLE_OPEN) { return; } while ( ((pc = chunk_get_next_ncnl(pc)) != nullptr) && pc->level >= start->level) { if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE)) { pc = chunk_get_next_ncnl(pc); if (pc == nullptr) { return; } if (chunk_is_str(pc, "(", 1)) { set_paren_parent(pc, CT_TYPE_CAST); } return; } make_type(pc); } } static void fix_enum_struct_union(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *next; chunk_t *prev = nullptr; pcf_flags_t flags = PCF_VAR_1ST_DEF; auto const in_fcn_paren = pc->flags & PCF_IN_FCN_DEF; // Make sure this wasn't a cast if (get_chunk_parent_type(pc) == CT_C_CAST) { return; } // the next item is either a type or open brace next = chunk_get_next_ncnl(pc); // the enum-key might be enum, enum class or enum struct (TODO) if (chunk_is_token(next, CT_ENUM_CLASS)) { next = chunk_get_next_ncnl(next); // get the next one } if (language_is_set(LANG_CPP)) { next = skip_attribute_next(next); // get the next one } // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON)) { // i.e. "enum xyz : unsigned int { ... };" // i.e. "enum class xyz : unsigned int { ... };" // i.e. "enum : unsigned int { ... };" // xyz is a type // save the type if it exists if (!chunk_is_token(next, CT_COLON)) { set_chunk_parent(next, pc->type); prev = next; next = chunk_get_next_ncnl(next); } if (next == nullptr) { return; } set_chunk_parent(next, pc->type); auto const is_struct_or_class = (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS)); // next up is either a colon, open brace, or open parenthesis (pawn) if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN)) { next = set_paren_parent(next, CT_ENUM); } else if (chunk_is_token(next, CT_COLON)) { if (chunk_is_token(pc, CT_ENUM)) { // enum TYPE : INT_TYPE { ... }; next = chunk_get_next_ncnl(next); if (next != nullptr) { make_type(next); next = chunk_get_next_ncnl(next); // enum TYPE : unsigned int { ... }; if (chunk_is_token(next, CT_TYPE)) { // get the next part of the type next = chunk_get_next_ncnl(next); } } } else if (is_struct_or_class) { next = skip_parent_types(next); } } else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN)) { // Fix #1267 structure attributes // struct __attribute__(align(x)) struct_name; // skip to matching parenclose and make next token as type. next = chunk_skip_to_match(next); next = chunk_get_next_ncnl(next); set_chunk_type(next, CT_TYPE); set_chunk_parent(next, pc->type); } if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration { set_chunk_parent(next, pc->type); flag_series(pc, prev, PCF_INCOMPLETE); return; } } if (chunk_is_token(next, CT_BRACE_OPEN)) { auto const flag = [pc] { switch (pc->type) { case CT_ENUM: return(PCF_IN_ENUM); case CT_STRUCT: return(PCF_IN_STRUCT); case CT_CLASS: return(PCF_IN_CLASS); default: return(PCF_NONE); } }(); flag_parens(next, flag, CT_NONE, CT_NONE, false); if ( chunk_is_token(pc, CT_UNION) || chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS)) { mark_struct_union_body(next); } // Skip to the closing brace set_chunk_parent(next, pc->type); next = chunk_get_next_type(next, CT_BRACE_CLOSE, pc->level); flags |= PCF_VAR_INLINE; if (next != nullptr) { set_chunk_parent(next, pc->type); next = chunk_get_next_ncnl(next); } prev = nullptr; } // reset var name parent type else if (next && prev) { set_chunk_parent(prev, CT_NONE); } if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE)) { return; } if (!chunk_is_semicolon(next)) { // Pawn does not require a semicolon after an enum if (language_is_set(LANG_PAWN)) { return; } /* * D does not require a semicolon after an enum, but we add one to make * other code happy. */ if (language_is_set(LANG_D)) { next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next)); // Issue #2279 } } // We are either pointing to a ';' or a variable while ( next != nullptr && !chunk_is_semicolon(next) && next->type != CT_ASSIGN && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any()) { if (next->level == pc->level) { if (chunk_is_token(next, CT_WORD)) { chunk_flags_set(next, flags); flags &= ~PCF_VAR_1ST; // clear the first flag for the next items LOG_FMT(LCASTS, "%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()); } if ( chunk_is_token(next, CT_STAR) || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET))) { set_chunk_type(next, CT_PTR_TYPE); } // If we hit a comma in a function param, we are done if ( (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE)) && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL))) { return; } } next = chunk_get_next_ncnl(next); } if ( next != nullptr && chunk_is_token(next, CT_SEMICOLON)) { set_chunk_parent(next, pc->type); } } // fix_enum_struct_union static void fix_typedef(chunk_t *start) { LOG_FUNC_ENTRY(); if (start == nullptr) { return; } LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n", __func__, __LINE__, start->orig_line, start->orig_col); chunk_t *the_type = nullptr; chunk_t *last_op = nullptr; /* * Mark everything in the typedef and scan for ")(", which makes it a * function type */ for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC) ; next != nullptr && next->level >= start->level ; next = chunk_get_next_ncnl(next, scope_e::PREPROC)) { chunk_flags_set(next, PCF_IN_TYPEDEF); if (start->level == next->level) { if (chunk_is_semicolon(next)) { set_chunk_parent(next, CT_TYPEDEF); break; } if (chunk_is_token(next, CT_ATTRIBUTE)) { break; } if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN)) { set_chunk_parent(next, CT_TYPEDEF); break; } make_type(next); if (chunk_is_token(next, CT_TYPE)) { the_type = next; } chunk_flags_clr(next, PCF_VAR_1ST_DEF); if (*next->str.c_str() == '(') { last_op = next; } } } // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def if ( last_op != nullptr && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM)) { flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false); fix_fcn_def_params(last_op); the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC); // Issue #2279 if (the_type == nullptr) { return; } chunk_t *open_paren = nullptr; if (chunk_is_paren_close(the_type)) { open_paren = chunk_skip_to_match_rev(the_type); mark_function_type(the_type); the_type = chunk_get_prev_ncnlni(the_type, scope_e::PREPROC); // Issue #2279 if (the_type == nullptr) { return; } } else { // must be: "typedef <return type>func(params);" set_chunk_type(the_type, CT_FUNC_TYPE); } set_chunk_parent(the_type, CT_TYPEDEF); LOG_FMT(LTYPEDEF, "%s(%d): fcn typedef text() '%s', on orig_line %zu\n", __func__, __LINE__, the_type->text(), the_type->orig_line); // If we are aligning on the open parenthesis, grab that instead log_rule_B("align_typedef_func"); if (open_paren != nullptr && options::align_typedef_func() == 1) { the_type = open_paren; } log_rule_B("align_typedef_func"); if (options::align_typedef_func() != 0) { LOG_FMT(LTYPEDEF, "%s(%d): -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n", __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col); chunk_flags_set(the_type, PCF_ANCHOR); } // already did everything we need to do return; } /* * Skip over enum/struct/union stuff, as we know it isn't a return type * for a function type */ chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC); if (after == nullptr) { return; } if ( after->type != CT_ENUM && after->type != CT_STRUCT && after->type != CT_UNION) { if (the_type != nullptr) { // We have just a regular typedef LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n", __func__, __LINE__, the_type->text(), the_type->orig_line); chunk_flags_set(the_type, PCF_ANCHOR); } return; } // We have a struct/union/enum, next should be either a type or { chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC); if (next == nullptr) { return; } if (chunk_is_token(next, CT_TYPE)) { next = chunk_get_next_ncnl(next, scope_e::PREPROC); if (next == nullptr) { return; } } if (chunk_is_token(next, CT_BRACE_OPEN)) { // Skip to the closing brace chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC); if (br_c != nullptr) { const c_token_t tag = after->type; set_chunk_parent(next, tag); set_chunk_parent(br_c, tag); if (tag == CT_ENUM) { flag_series(after, br_c, PCF_IN_ENUM); } else if (tag == CT_STRUCT) { flag_series(after, br_c, PCF_IN_STRUCT); } } } if (the_type != nullptr) { LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n", __func__, __LINE__, get_token_name(after->type), the_type->text(), the_type->orig_line); chunk_flags_set(the_type, PCF_ANCHOR); } } // fix_typedef static void mark_variable_stack(ChunkStack &cs, log_sev_t sev) { UNUSED(sev); LOG_FUNC_ENTRY(); // throw out the last word and mark the rest chunk_t *var_name = cs.Pop_Back(); if (var_name && var_name->prev->type == CT_DC_MEMBER) { cs.Push_Back(var_name); } if (var_name != nullptr) { LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n", __func__, __LINE__, var_name->orig_line, var_name->orig_col); size_t word_cnt = 0; chunk_t *word_type; while ((word_type = cs.Pop_Back()) != nullptr) { if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE)) { LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text()); set_chunk_type(word_type, CT_TYPE); chunk_flags_set(word_type, PCF_VAR_TYPE); } word_cnt++; } if (chunk_is_token(var_name, CT_WORD)) { if (word_cnt > 0) { LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n", __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); chunk_flags_set(var_name, PCF_VAR_DEF); } else { LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); set_chunk_type(var_name, CT_TYPE); chunk_flags_set(var_name, PCF_VAR_TYPE); } } } } // mark_variable_stack static void fix_fcn_def_params(chunk_t *start) { LOG_FUNC_ENTRY(); if (start == nullptr) { return; } LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n", __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level); while (start != nullptr && !chunk_is_paren_open(start)) { start = chunk_get_next_ncnl(start); } if (start == nullptr)// Coverity CID 76003, 1100782 { return; } // ensure start chunk holds a single '(' character assert((start->len() == 1) && (start->str[0] == '(')); ChunkStack cs; size_t level = start->level + 1; chunk_t *pc = start; while ((pc = chunk_get_next_ncnl(pc)) != nullptr) { if ( ((start->len() == 1) && (start->str[0] == ')')) || pc->level < level) { LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n", __func__, __LINE__, pc->text(), pc->orig_line); break; } LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n", __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at", pc->text(), pc->orig_line, pc->level); if (pc->level > level) { continue; } if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc)) { set_chunk_type(pc, CT_PTR_TYPE); cs.Push_Back(pc); } else if ( chunk_is_token(pc, CT_AMP) || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2))) { set_chunk_type(pc, CT_BYREF); cs.Push_Back(pc); } else if (chunk_is_token(pc, CT_TYPE_WRAP)) { cs.Push_Back(pc); } else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)) { cs.Push_Back(pc); } else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN)) { mark_variable_stack(cs, LFCNP); if (chunk_is_token(pc, CT_ASSIGN)) { // Mark assignment for default param spacing set_chunk_parent(pc, CT_FUNC_PROTO); } } } mark_variable_stack(cs, LFCNP); } // fix_fcn_def_params static chunk_t *skip_to_next_statement(chunk_t *pc) { while ( pc != nullptr && !chunk_is_semicolon(pc) && pc->type != CT_BRACE_OPEN && pc->type != CT_BRACE_CLOSE) { pc = chunk_get_next_ncnl(pc); } return(pc); } static chunk_t *fix_variable_definition(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc = start; chunk_t *end; chunk_t *tmp_pc; ChunkStack cs; int idx; int ref_idx; LOG_FMT(LFVD, "%s(%d): start at pc->orig_line is %zu, pc->orig_col is %zu\n", __func__, __LINE__, pc->orig_line, pc->orig_col); // Scan for words and types and stars oh my! while ( chunk_is_token(pc, CT_TYPE) || chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_QUALIFIER) || chunk_is_token(pc, CT_TYPENAME) || chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_ptr_operator(pc)) { LOG_FMT(LFVD, "%s(%d): 1:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); cs.Push_Back(pc); pc = chunk_get_next_ncnl(pc); if (pc == nullptr) { LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__); return(nullptr); } LOG_FMT(LFVD, "%s(%d): 2:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); // Skip templates and attributes pc = skip_template_next(pc); if (pc == nullptr) { LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__); return(nullptr); } LOG_FMT(LFVD, "%s(%d): 3:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); pc = skip_attribute_next(pc); if (pc == nullptr) { LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__); return(nullptr); } LOG_FMT(LFVD, "%s(%d): 4:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); if (language_is_set(LANG_JAVA)) { pc = skip_tsquare_next(pc); LOG_FMT(LFVD, "%s(%d): 5:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); } } end = pc; if (end == nullptr) { LOG_FMT(LFVD, "%s(%d): end is nullptr\n", __func__, __LINE__); return(nullptr); } LOG_FMT(LFVD, "%s(%d): end->type is %s\n", __func__, __LINE__, get_token_name(end->type)); if ( cs.Len() == 1 && chunk_is_token(end, CT_BRACE_OPEN) && get_chunk_parent_type(end) == CT_BRACED_INIT_LIST) { set_chunk_type(cs.Get(0)->m_pc, CT_TYPE); } // Function defs are handled elsewhere if ( (cs.Len() <= 1) || chunk_is_token(end, CT_FUNC_DEF) || chunk_is_token(end, CT_FUNC_PROTO) || chunk_is_token(end, CT_FUNC_CLASS_DEF) || chunk_is_token(end, CT_FUNC_CLASS_PROTO) || chunk_is_token(end, CT_OPERATOR)) { return(skip_to_next_statement(end)); } // ref_idx points to the alignable part of the variable definition ref_idx = cs.Len() - 1; // Check for the '::' stuff: "char *Engine::name" if ( (cs.Len() >= 3) && ( (cs.Get(cs.Len() - 2)->m_pc->type == CT_MEMBER) || (cs.Get(cs.Len() - 2)->m_pc->type == CT_DC_MEMBER))) { idx = cs.Len() - 2; while (idx > 0) { tmp_pc = cs.Get(idx)->m_pc; if ( tmp_pc->type != CT_DC_MEMBER && tmp_pc->type != CT_MEMBER) { break; } if (idx == 0) { fprintf(stderr, "%s(%d): idx is ZERO, cannot be decremented, at line %zu, column %zu\n", __func__, __LINE__, tmp_pc->orig_line, tmp_pc->orig_col); log_flush(true); exit(EX_SOFTWARE); } idx--; tmp_pc = cs.Get(idx)->m_pc; if ( tmp_pc->type != CT_WORD && tmp_pc->type != CT_TYPE) { break; } make_type(tmp_pc); idx--; } ref_idx = idx + 1; } tmp_pc = cs.Get(ref_idx)->m_pc; LOG_FMT(LFVD, "%s(%d): ref_idx(%d) is '%s'\n", __func__, __LINE__, ref_idx, tmp_pc->text()); // No type part found! if (ref_idx <= 0) { return(skip_to_next_statement(end)); } LOG_FMT(LFVD2, "%s(%d): orig_line is %zu, TYPE : ", __func__, __LINE__, start->orig_line); for (size_t idxForCs = 0; idxForCs < cs.Len() - 1; idxForCs++) { tmp_pc = cs.Get(idxForCs)->m_pc; make_type(tmp_pc); chunk_flags_set(tmp_pc, PCF_VAR_TYPE); LOG_FMT(LFVD2, " text() is '%s', type is %s", tmp_pc->text(), get_token_name(tmp_pc->type)); } LOG_FMT(LFVD2, "\n"); // OK we have two or more items, mark types up to the end. LOG_FMT(LFVD, "%s(%d): pc->orig_line is %zu, pc->orig_col is %zu\n", __func__, __LINE__, pc->orig_line, pc->orig_col); mark_variable_definition(cs.Get(cs.Len() - 1)->m_pc); if (chunk_is_token(end, CT_COMMA)) { return(chunk_get_next_ncnl(end)); } return(skip_to_next_statement(end)); } // fix_variable_definition static chunk_t *skip_expression(chunk_t *start) { chunk_t *pc = start; while (pc != nullptr && pc->level >= start->level) { if ( pc->level == start->level && (chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA))) { return(pc); } pc = chunk_get_next_ncnl(pc); } return(pc); } bool go_on(chunk_t *pc, chunk_t *start) { if (pc == nullptr || pc->level != start->level) { return(false); } if (pc->flags.test(PCF_IN_FOR)) { return((!chunk_is_semicolon(pc)) && (!(chunk_is_token(pc, CT_COLON)))); } return(!chunk_is_semicolon(pc)); } // go_on static chunk_t *mark_variable_definition(chunk_t *start) { LOG_FUNC_ENTRY(); if (start == nullptr) { return(nullptr); } chunk_t *pc = start; pcf_flags_t flags = PCF_VAR_1ST_DEF; LOG_FMT(LVARDEF, "%s(%d): orig_line %zu, orig_col %zu, text() '%s', type is %s\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type)); // Issue #596 bool bit_field_colon_is_present = false; while (go_on(pc, start)) { if ( chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_FUNC_CTOR_VAR)) { auto const orig_flags = pc->flags; if (!pc->flags.test(PCF_IN_ENUM)) { chunk_flags_set(pc, flags); } flags &= ~PCF_VAR_1ST; LOG_FMT(LVARDEF, "%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()); LOG_FMT(LVARDEF, "%s(%d): orig_line is %zu, marked text() '%s'[%s] " "in orig_col %zu, flags: %s -> %s\n", __func__, __LINE__, pc->orig_line, pc->text(), get_token_name(pc->type), pc->orig_col, pcf_flags_str(orig_flags).c_str(), pcf_flags_str(pc->flags).c_str()); } else if ( !bit_field_colon_is_present // Issue #2689 && ( chunk_is_star(pc) || chunk_is_msref(pc))) { set_chunk_type(pc, CT_PTR_TYPE); } else if (chunk_is_addr(pc)) { set_chunk_type(pc, CT_BYREF); } else if ( chunk_is_token(pc, CT_SQUARE_OPEN) || chunk_is_token(pc, CT_ASSIGN)) { pc = skip_expression(pc); continue; } else if (chunk_is_token(pc, CT_COLON)) { bit_field_colon_is_present = true; // Issue #2689 } pc = chunk_get_next_ncnl(pc); } return(pc); } // mark_variable_definition static bool can_be_full_param(chunk_t *start, chunk_t *end) { LOG_FUNC_ENTRY(); LOG_FMT(LFPARAM, "%s:", __func__); int word_count = 0; int type_count = 0; chunk_t *pc; for (pc = start; pc != nullptr && pc != end; pc = chunk_get_next_ncnl(pc, scope_e::PREPROC)) { LOG_FMT(LFPARAM, " [%s]", pc->text()); if ( chunk_is_token(pc, CT_QUALIFIER) || chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_ENUM) || chunk_is_token(pc, CT_UNION) || chunk_is_token(pc, CT_TYPENAME)) { LOG_FMT(LFPARAM, " <== %s! (yes)\n", get_token_name(pc->type)); return(true); } if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)) { ++word_count; if (chunk_is_token(pc, CT_TYPE)) { ++type_count; } } else if (chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_DC_MEMBER)) { if (word_count > 0) { --word_count; } } else if (pc != start && chunk_is_ptr_operator(pc)) { // chunk is OK } else if (chunk_is_token(pc, CT_ASSIGN)) { // chunk is OK (default values) break; } else if (chunk_is_token(pc, CT_ANGLE_OPEN)) { LOG_FMT(LFPARAM, " <== template\n"); return(true); } else if (chunk_is_token(pc, CT_ELLIPSIS)) { LOG_FMT(LFPARAM, " <== elipses\n"); return(true); } else if (word_count == 0 && chunk_is_token(pc, CT_PAREN_OPEN)) { // Check for old-school func proto param '(type)' chunk_t *tmp1 = chunk_skip_to_match(pc, scope_e::PREPROC); if (tmp1 == nullptr) { return(false); } chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC); if (tmp2 == nullptr) { return(false); } if (chunk_is_token(tmp2, CT_COMMA) || chunk_is_paren_close(tmp2)) { do { pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (pc == nullptr) { return(false); } LOG_FMT(LFPARAM, " [%s]", pc->text()); } while (pc != tmp1); // reset some vars to allow [] after parens word_count = 1; type_count = 1; } else { LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type)); return(false); } } else if ( (word_count == 1 || (word_count == type_count)) && chunk_is_token(pc, CT_PAREN_OPEN)) { // Check for func proto param 'void (*name)' or 'void (*name)(params)' or 'void (^name)(params)' // <name> can be optional chunk_t *tmp1 = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (tmp1 == nullptr) { return(false); } chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC); if (tmp2 == nullptr) { return(false); } chunk_t *tmp3 = (chunk_is_str(tmp2, ")", 1)) ? tmp2 : chunk_get_next_ncnl(tmp2, scope_e::PREPROC); if (tmp3 == nullptr) { return(false); } if ( !chunk_is_str(tmp3, ")", 1) || !(chunk_is_str(tmp1, "*", 1) || chunk_is_str(tmp1, "^", 1)) // Issue #2656 || !(tmp2->type == CT_WORD || chunk_is_str(tmp2, ")", 1))) { LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type)); return(false); } LOG_FMT(LFPARAM, " <skip fcn type>"); tmp1 = chunk_get_next_ncnl(tmp3, scope_e::PREPROC); if (tmp1 == nullptr) { return(false); } if (chunk_is_str(tmp1, "(", 1)) { tmp3 = chunk_skip_to_match(tmp1, scope_e::PREPROC); } pc = tmp3; // reset some vars to allow [] after parens word_count = 1; type_count = 1; } else if (chunk_is_token(pc, CT_TSQUARE)) { // ignore it } else if (word_count == 1 && chunk_is_token(pc, CT_SQUARE_OPEN)) { // skip over any array stuff pc = chunk_skip_to_match(pc, scope_e::PREPROC); } else if (word_count == 2 && chunk_is_token(pc, CT_SQUARE_OPEN)) { // Bug #671: is it such as: bool foo[FOO_MAX] pc = chunk_skip_to_match(pc, scope_e::PREPROC); } else if ( word_count == 1 && language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2)) { // ignore possible 'move' operator } else { LOG_FMT(LFPARAM, " <== [%s] no way! tc=%d wc=%d\n", get_token_name(pc->type), type_count, word_count); return(false); } } chunk_t *last = chunk_get_prev_ncnlni(pc); // Issue #2279 if (chunk_is_ptr_operator(last)) { LOG_FMT(LFPARAM, " <== [%s] sure!\n", get_token_name(pc->type)); return(true); } if (word_count < 2 && type_count < 1 && start->brace_level > 0) { LOG_FMT(LFPARAM, " !MVP!"); // Oh, joy, we are in Most Vexing Parse territory auto const brace = chunk_get_prev_type(start, CT_BRACE_OPEN, start->brace_level - 1); if (brace) { LOG_FMT(LFPARAM, " (matching %s brace at %zu:%zu)", get_token_name(get_chunk_parent_type(brace)), brace->orig_line, brace->orig_col); } if ( brace && ( get_chunk_parent_type(brace) == CT_CLASS || get_chunk_parent_type(brace) == CT_STRUCT)) { // A Most Vexing Parse variable declaration cannot occur in the body // of a struct/class, so we probably have a function prototype LOG_FMT(LFPARAM, " <== [%s] Likely!\n", (pc == nullptr ? "nullptr" : get_token_name(pc->type))); return(true); } } bool ret = ( word_count >= 2 || (word_count == 1 && type_count == 1)); LOG_FMT(LFPARAM, " <== [%s] %s!\n", (pc == nullptr ? "nullptr" : get_token_name(pc->type)), ret ? "Yup" : "Unlikely"); return(ret); } // can_be_full_param static void mark_function(chunk_t *pc) { LOG_FUNC_ENTRY(); if (pc == nullptr) { return; } LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); chunk_t *prev = chunk_get_prev_ncnlni(pc); // Issue #2279 chunk_t *next = chunk_get_next_ncnlnp(pc); if (next == nullptr) { return; } chunk_t *tmp; chunk_t *semi = nullptr; chunk_t *paren_open; chunk_t *paren_close; // Find out what is before the operator if (get_chunk_parent_type(pc) == CT_OPERATOR) { LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); log_pcf_flags(LGUY, pc->flags); chunk_t *pc_op = chunk_get_prev_type(pc, CT_OPERATOR, pc->level); if ( pc_op != nullptr && pc_op->flags.test(PCF_EXPR_START)) { LOG_FMT(LFCN, "%s(%d): (4) 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 (language_is_set(LANG_CPP)) { tmp = pc; while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 { if ( chunk_is_token(tmp, CT_BRACE_CLOSE) || chunk_is_token(tmp, CT_BRACE_OPEN) // Issue 575 || chunk_is_token(tmp, CT_SEMICOLON)) { break; } if ( chunk_is_paren_open(tmp) && !pc->flags.test(PCF_IN_PREPROC)) // Issue #2703 { LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); LOG_FMT(LFCN, "%s(%d): (5) 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); break; } if (chunk_is_token(tmp, CT_ASSIGN)) { LOG_FMT(LFCN, "%s(%d): (6) 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); break; } if (chunk_is_token(tmp, CT_TEMPLATE)) { LOG_FMT(LFCN, "%s(%d): (7) 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); break; } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { if (get_chunk_parent_type(tmp) == CT_FUNC_DEF) { LOG_FMT(LFCN, "%s(%d): (8) 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 ( get_chunk_parent_type(tmp) == CT_CLASS || get_chunk_parent_type(tmp) == CT_STRUCT) { LOG_FMT(LFCN, "%s(%d): (9) 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); } break; } } if ( tmp != nullptr && pc->type != CT_FUNC_CALL) { // Mark the return type while ( (tmp = chunk_get_next_ncnl(tmp)) != pc && tmp != nullptr) { make_type(tmp); // Mark the return type } } } } if (chunk_is_ptr_operator(next)) { next = chunk_get_next_ncnlnp(next); if (next == nullptr) { return; } } LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s, type is %s, parent_type is %s\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type), get_token_name(get_chunk_parent_type(pc))); LOG_FMT(LFCN, " level is %zu, brace_level is %zu, next->text() '%s', next->type is %s, next->level is %zu\n", pc->level, pc->brace_level, next->text(), get_token_name(next->type), next->level); if (pc->flags.test(PCF_IN_CONST_ARGS)) { set_chunk_type(pc, CT_FUNC_CTOR_VAR); LOG_FMT(LFCN, "%s(%d): 1) Marked [%s] as FUNC_CTOR_VAR on line %zu col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); next = skip_template_next(next); if (next == nullptr) { return; } flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, true); return; } // Skip over any template and attribute madness next = skip_template_next(next); if (next == nullptr) { return; } next = skip_attribute_next(next); if (next == nullptr) { return; } // Find the open and close parenthesis paren_open = chunk_get_next_str(pc, "(", 1, pc->level); paren_close = chunk_get_next_str(paren_open, ")", 1, pc->level); if ( paren_open == nullptr || paren_close == nullptr) { LOG_FMT(LFCN, "%s(%d): No parens found for [%s] on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); return; } /* * This part detects either chained function calls or a function ptr definition. * MYTYPE (*func)(void); * mWriter( "class Clst_"c )( somestr.getText() )( " : Cluster {"c ).newline; * * For it to be a function variable def, there must be a '*' followed by a * single word. * * Otherwise, it must be chained function calls. */ tmp = chunk_get_next_ncnl(paren_close); if ( tmp != nullptr && chunk_is_str(tmp, "(", 1)) { chunk_t *tmp1; chunk_t *tmp2; chunk_t *tmp3; // skip over any leading class/namespace in: "T(F::*A)();" tmp1 = chunk_get_next_ncnl(next); while (tmp1 != nullptr) { tmp2 = chunk_get_next_ncnl(tmp1); if (!chunk_is_word(tmp1) || !chunk_is_token(tmp2, CT_DC_MEMBER)) { break; } tmp1 = chunk_get_next_ncnl(tmp2); } tmp2 = chunk_get_next_ncnl(tmp1); if (chunk_is_str(tmp2, ")", 1)) { tmp3 = tmp2; tmp2 = nullptr; } else { tmp3 = chunk_get_next_ncnl(tmp2); } tmp3 = chunk_get_next_ssq(tmp3); if ( chunk_is_str(tmp3, ")", 1) && ( chunk_is_star(tmp1) || chunk_is_msref(tmp1) || (language_is_set(LANG_OC) && chunk_is_token(tmp1, CT_CARET))) && (tmp2 == nullptr || chunk_is_token(tmp2, CT_WORD))) { if (tmp2) { LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function variable '%s', changing '%s' into a type\n", __func__, __LINE__, pc->orig_line, pc->orig_col, tmp2->text(), pc->text()); set_chunk_type(tmp2, CT_FUNC_VAR); flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_VAR, false); LOG_FMT(LFCN, "%s(%d): paren open @ orig_line %zu, orig_col %zu\n", __func__, __LINE__, paren_open->orig_line, paren_open->orig_col); } else { LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function type, changing '%s' into a type\n", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); if (tmp2) { set_chunk_type(tmp2, CT_FUNC_TYPE); } flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_TYPE, false); } set_chunk_type(pc, CT_TYPE); set_chunk_type(tmp1, CT_PTR_TYPE); chunk_flags_clr(pc, PCF_VAR_1ST_DEF); if (tmp2 != nullptr) { chunk_flags_set(tmp2, PCF_VAR_1ST_DEF); } flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_PROTO, false); fix_fcn_def_params(tmp); return; } LOG_FMT(LFCN, "%s(%d): chained function calls? text() is '%s', orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); } // Assume it is a function call if not already labeled if (chunk_is_token(pc, CT_FUNCTION)) { LOG_FMT(LFCN, "%s(%d): examine: text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); // look for an assigment. Issue #575 chunk_t *temp = chunk_get_next_type(pc, CT_ASSIGN, pc->level); if (temp != nullptr) { LOG_FMT(LFCN, "%s(%d): assigment found, orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, temp->orig_line, temp->orig_col, temp->text()); LOG_FMT(LFCN, "%s(%d): (10) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'", __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); set_chunk_type(pc, CT_FUNC_CALL); } else { LOG_FMT(LFCN, "%s(%d): (11) SET TO %s: orig_line is %zu, orig_col is %zu, text() '%s'", __func__, __LINE__, (get_chunk_parent_type(pc) == CT_OPERATOR) ? "CT_FUNC_DEF" : "CT_FUNC_CALL", pc->orig_line, pc->orig_col, pc->text()); set_chunk_type(pc, (get_chunk_parent_type(pc) == CT_OPERATOR) ? CT_FUNC_DEF : CT_FUNC_CALL); } } LOG_FMT(LFCN, "%s(%d): Check for C++ function def, text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); if (prev != nullptr) { LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); } // Check for C++ function def if ( chunk_is_token(pc, CT_FUNC_CLASS_DEF) || ( prev != nullptr && ( chunk_is_token(prev, CT_INV) || chunk_is_token(prev, CT_DC_MEMBER)))) { chunk_t *destr = nullptr; if (chunk_is_token(prev, CT_INV)) { // TODO: do we care that this is the destructor? set_chunk_type(prev, CT_DESTRUCTOR); set_chunk_type(pc, CT_FUNC_CLASS_DEF); set_chunk_parent(pc, CT_DESTRUCTOR); destr = prev; // Point to the item previous to the class name prev = chunk_get_prev_ncnlnp(prev); } if (chunk_is_token(prev, CT_DC_MEMBER)) { prev = chunk_get_prev_ncnlnp(prev); LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); prev = skip_template_prev(prev); LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); prev = skip_attribute_prev(prev); LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); if (chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE)) { if (pc->str.equals(prev->str)) { LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(prev->type)); set_chunk_type(pc, CT_FUNC_CLASS_DEF); LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu - FOUND %sSTRUCTOR for '%s', type is %s\n", __func__, __LINE__, prev->orig_line, prev->orig_col, (destr != nullptr) ? "DE" : "CON", prev->text(), get_token_name(prev->type)); mark_cpp_constructor(pc); return; } // Point to the item previous to the class name prev = chunk_get_prev_ncnlnp(prev); } } } /* * Determine if this is a function call or a function def/proto * We check for level==1 to allow the case that a function prototype is * wrapped in a macro: "MACRO(void foo(void));" */ if ( chunk_is_token(pc, CT_FUNC_CALL) && ( pc->level == pc->brace_level || pc->level == 1) && !pc->flags.test(PCF_IN_ARRAY_ASSIGN)) { bool isa_def = false; bool hit_star = false; LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); if (prev == nullptr) { LOG_FMT(LFCN, "%s(%d): Checking func call: prev is NULL\n", __func__, __LINE__); } else { LOG_FMT(LFCN, "%s(%d): Checking func call: prev->text() '%s', prev->type is %s\n", __func__, __LINE__, prev->text(), get_token_name(prev->type)); } // if (!chunk_ends_type(prev)) // { // goto bad_ret_type; // } /* * REVISIT: * a function def can only occur at brace level, but not inside an * assignment, structure, enum, or union. * The close paren must be followed by an open brace, with an optional * qualifier (const) in between. * There can be all sorts of template stuff and/or '[]' in the type. * This hack mostly checks that. * * Examples: * foo->bar(maid); -- fcn call * FOO * bar(); -- fcn proto or class variable * FOO foo(); -- fcn proto or class variable * FOO foo(1); -- class variable * a = FOO * bar(); -- fcn call * a.y = foo() * bar(); -- fcn call * static const char * const fizz(); -- fcn def */ while (prev != nullptr) { LOG_FMT(LFCN, "%s(%d): next step with: prev->orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text()); if (get_chunk_parent_type(pc) == CT_FIXED) { isa_def = true; } if (prev->flags.test(PCF_IN_PREPROC)) { prev = chunk_get_prev_ncnlnp(prev); continue; } // Some code slips an attribute between the type and function if ( chunk_is_token(prev, CT_FPAREN_CLOSE) && get_chunk_parent_type(prev) == CT_ATTRIBUTE) { prev = skip_attribute_prev(prev); continue; } // skip const(TYPE) if ( chunk_is_token(prev, CT_PAREN_CLOSE) && get_chunk_parent_type(prev) == CT_D_CAST) { LOG_FMT(LFCN, "%s(%d): --> For sure a prototype or definition\n", __func__, __LINE__); isa_def = true; break; } if (get_chunk_parent_type(prev) == CT_DECLSPEC) // Issue 1289 { prev = chunk_skip_to_match_rev(prev); prev = chunk_get_prev(prev); if (chunk_is_token(prev, CT_DECLSPEC)) { prev = chunk_get_prev(prev); } } // if it was determined that this could be a function definition // but one of the preceding tokens is a CT_MEMBER than this is not a // fcn def, issue #1466 if ( isa_def && chunk_is_token(prev, CT_MEMBER)) { isa_def = false; } // get first chunk before: A::B::pc | this.B.pc | this->B->pc if ( chunk_is_token(prev, CT_DC_MEMBER) || chunk_is_token(prev, CT_MEMBER)) { while ( chunk_is_token(prev, CT_DC_MEMBER) || chunk_is_token(prev, CT_MEMBER)) { prev = chunk_get_prev_ncnlnp(prev); if ( prev == nullptr || ( prev->type != CT_WORD && prev->type != CT_TYPE && prev->type != CT_THIS)) { LOG_FMT(LFCN, "%s(%d): --? skipped MEMBER and landed on %s\n", __func__, __LINE__, (prev == nullptr) ? "<null>" : get_token_name(prev->type)); break; } LOG_FMT(LFCN, "%s(%d): <skip> '%s'\n", __func__, __LINE__, prev->text()); // Issue #1112 // clarification: this will skip the CT_WORD, CT_TYPE or CT_THIS landing on either // another CT_DC_MEMBER or CT_MEMBER or a token that indicates the context of the // token in question; therefore, exit loop when not a CT_DC_MEMBER or CT_MEMBER prev = chunk_get_prev_ncnlnp(prev); if (prev == nullptr) { LOG_FMT(LFCN, "%s(%d): prev is nullptr\n", __func__, __LINE__); } else { LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text()); } } if (prev == nullptr) { break; } } // If we are on a TYPE or WORD, then this could be a proto or def if ( chunk_is_token(prev, CT_TYPE) || chunk_is_token(prev, CT_WORD)) { if (!hit_star) { LOG_FMT(LFCN, "%s(%d): --> For sure a prototype or definition\n", __func__, __LINE__); isa_def = true; break; } chunk_t *prev_prev = chunk_get_prev_ncnlnp(prev); if (!chunk_is_token(prev_prev, CT_QUESTION)) // Issue #1753 { LOG_FMT(LFCN, "%s(%d): --> maybe a proto/def\n", __func__, __LINE__); LOG_FMT(LFCN, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s, parent_type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type), get_token_name(get_chunk_parent_type(prev))); log_pcf_flags(LFCN, pc->flags); isa_def = true; } } if (chunk_is_ptr_operator(prev)) { hit_star = true; } if ( prev->type != CT_OPERATOR && prev->type != CT_TSQUARE && prev->type != CT_ANGLE_CLOSE && prev->type != CT_QUALIFIER && prev->type != CT_TYPE && prev->type != CT_WORD && !chunk_is_ptr_operator(prev)) { LOG_FMT(LFCN, "%s(%d): --> Stopping on prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); // certain tokens are unlikely to precede a prototype or definition if ( chunk_is_token(prev, CT_ARITH) || chunk_is_token(prev, CT_ASSIGN) || chunk_is_token(prev, CT_COMMA) || (chunk_is_token(prev, CT_STRING) && get_chunk_parent_type(prev) != CT_EXTERN) // fixes issue 1259 || chunk_is_token(prev, CT_STRING_MULTI) || chunk_is_token(prev, CT_NUMBER) || chunk_is_token(prev, CT_NUMBER_FP) || chunk_is_token(prev, CT_FPAREN_OPEN)) // issue #1464 { isa_def = false; } break; } // Skip over template and attribute stuff if (chunk_is_token(prev, CT_ANGLE_CLOSE)) { prev = skip_template_prev(prev); } else { prev = chunk_get_prev_ncnlnp(prev); } } //LOG_FMT(LFCN, " -- stopped on %s [%s]\n", // prev->text(), get_token_name(prev->type)); // Fixes issue #1634 if (chunk_is_paren_close(prev)) { chunk_t *preproc = chunk_get_next_ncnl(prev); if (chunk_is_token(preproc, CT_PREPROC)) { size_t pp_level = preproc->pp_level; if (chunk_is_token(chunk_get_next_ncnl(preproc), CT_PP_ELSE)) { do { preproc = chunk_get_prev_ncnlni(preproc); // Issue #2279 if (chunk_is_token(preproc, CT_PP_IF)) { preproc = chunk_get_prev_ncnlni(preproc); // Issue #2279 if (preproc->pp_level == pp_level) { prev = chunk_get_prev_ncnlnp(preproc); break; } } } while (preproc != nullptr); } } } if ( isa_def && prev != nullptr && ( ( chunk_is_paren_close(prev) && get_chunk_parent_type(prev) != CT_D_CAST && get_chunk_parent_type(prev) != CT_MACRO_OPEN // Issue #2726 && get_chunk_parent_type(prev) != CT_MACRO_CLOSE) || prev->type == CT_ASSIGN || prev->type == CT_RETURN)) { LOG_FMT(LFCN, "%s(%d): -- overriding DEF due to prev is '%s', type is %s\n", __func__, __LINE__, prev->text(), get_token_name(prev->type)); isa_def = false; } // Fixes issue #1266, identification of a tuple return type in CS. if ( !isa_def && chunk_is_token(prev, CT_PAREN_CLOSE) && chunk_get_next_ncnl(prev) == pc) { tmp = chunk_skip_to_match_rev(prev); while ( tmp != nullptr // Issue #2315 && tmp != prev) { if (chunk_is_token(tmp, CT_COMMA) && tmp->level == prev->level + 1) { LOG_FMT(LFCN, "%s(%d): -- overriding call due to tuple return type -- prev is '%s', type is %s\n", __func__, __LINE__, prev->text(), get_token_name(prev->type)); isa_def = true; break; } tmp = chunk_get_next_ncnl(tmp); } } if (isa_def) { LOG_FMT(LFCN, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); LOG_FMT(LFCN, "%s(%d): (12) 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); if (prev == nullptr) { prev = chunk_get_head(); } for ( tmp = prev; (tmp != nullptr) && tmp != pc; tmp = chunk_get_next_ncnlnp(tmp)) { LOG_FMT(LFCN, "%s(%d): text() is '%s', type is %s\n", __func__, __LINE__, tmp->text(), get_token_name(tmp->type)); make_type(tmp); } } } if (pc->type != CT_FUNC_DEF) { LOG_FMT(LFCN, "%s(%d): Detected type %s, text() is '%s', on orig_line %zu, orig_col %zu\n", __func__, __LINE__, get_token_name(pc->type), pc->text(), pc->orig_line, pc->orig_col); tmp = flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false); if ( chunk_is_token(tmp, CT_BRACE_OPEN) && get_chunk_parent_type(tmp) != CT_DOUBLE_BRACE) { set_paren_parent(tmp, pc->type); } return; } /* * We have a function definition or prototype * Look for a semicolon or a brace open after the close parenthesis to figure * out whether this is a prototype or definition */ // See if this is a prototype or implementation // FIXME: this doesn't take the old K&R parameter definitions into account // Scan tokens until we hit a brace open (def) or semicolon (proto) tmp = paren_close; while ((tmp = chunk_get_next_ncnl(tmp)) != nullptr) { // Only care about brace or semicolon on the same level if (tmp->level < pc->level) { // No semicolon - guess that it is a prototype chunk_flags_clr(pc, PCF_VAR_1ST_DEF); set_chunk_type(pc, CT_FUNC_PROTO); break; } else if (tmp->level == pc->level) { if (chunk_is_token(tmp, CT_BRACE_OPEN)) { // its a function def for sure break; } else if (chunk_is_semicolon(tmp)) { // Set the parent for the semicolon for later semi = tmp; chunk_flags_clr(pc, PCF_VAR_1ST_DEF); set_chunk_type(pc, CT_FUNC_PROTO); LOG_FMT(LFCN, "%s(%d): 2) Marked text() is '%s', as FUNC_PROTO on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); break; } else if (chunk_is_token(pc, CT_COMMA)) { set_chunk_type(pc, CT_FUNC_CTOR_VAR); LOG_FMT(LFCN, "%s(%d): 2) Marked text() is '%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); break; } } } /* * C++ syntax is wacky. We need to check to see if a prototype is really a * variable definition with parameters passed into the constructor. * Unfortunately, without being able to accurately determine if an * identifier is a type (which would require us to more or less be a full * compiler), the only mostly reliable way to do so is to guess that it is * a constructor variable if inside a function body and scan the 'parameter * list' for items that are not allowed in a prototype. We search backwards * and checking the parent of the containing open braces. If the parent is a * class or namespace, then it probably is a prototype. */ if ( language_is_set(LANG_CPP) && chunk_is_token(pc, CT_FUNC_PROTO) && get_chunk_parent_type(pc) != CT_OPERATOR) { LOG_FMT(LFPARAM, "%s(%d):", __func__, __LINE__); LOG_FMT(LFPARAM, " checking '%s' for constructor variable %s %s\n", pc->text(), get_token_name(paren_open->type), get_token_name(paren_close->type)); /* * Check the token at the start of the statement. If it's 'extern', we * definitely have a function prototype. */ tmp = pc; while ( tmp != nullptr && !tmp->flags.test(PCF_STMT_START)) { tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279 } const bool is_extern = (tmp && tmp->str.equals("extern")); /* * Scan the parameters looking for: * - constant strings * - numbers * - non-type fields * - function calls */ chunk_t *ref = chunk_get_next_ncnl(paren_open); chunk_t *tmp2; bool is_param = true; tmp = ref; while (tmp != paren_close) { tmp2 = chunk_get_next_ncnl(tmp); if ( chunk_is_token(tmp, CT_COMMA) && (tmp->level == (paren_open->level + 1))) { if (!can_be_full_param(ref, tmp)) { is_param = false; break; } ref = tmp2; } tmp = tmp2; } if ( !is_extern && is_param && ref != tmp) { if (!can_be_full_param(ref, tmp)) { is_param = false; } } if ( !is_extern && !is_param) { set_chunk_type(pc, CT_FUNC_CTOR_VAR); LOG_FMT(LFCN, "%s(%d): 3) Marked text() '%s' as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); } else if (pc->brace_level > 0) { chunk_t *br_open = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1); if ( br_open != nullptr && get_chunk_parent_type(br_open) != CT_EXTERN && get_chunk_parent_type(br_open) != CT_NAMESPACE) { // Do a check to see if the level is right prev = chunk_get_prev_ncnlni(pc); // Issue #2279 if ( !chunk_is_str(prev, "*", 1) && !chunk_is_str(prev, "&", 1)) { chunk_t *p_op = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1); if ( p_op != nullptr && get_chunk_parent_type(p_op) != CT_CLASS && get_chunk_parent_type(p_op) != CT_STRUCT && get_chunk_parent_type(p_op) != CT_NAMESPACE) { set_chunk_type(pc, CT_FUNC_CTOR_VAR); LOG_FMT(LFCN, "%s(%d): 4) Marked text() is'%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); } } } } } if (semi != nullptr) { set_chunk_parent(semi, pc->type); } // Issue # 1403, 2152 if (chunk_is_token(paren_open->prev, CT_FUNC_CTOR_VAR)) { flag_parens(paren_open, PCF_IN_FCN_CTOR, CT_FPAREN_OPEN, pc->type, false); } else { flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, false); } //flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, true); if (chunk_is_token(pc, CT_FUNC_CTOR_VAR)) { chunk_flags_set(pc, PCF_VAR_1ST_DEF); return; } if (chunk_is_token(next, CT_TSQUARE)) { next = chunk_get_next_ncnl(next); if (next == nullptr) { return; } } // Mark parameters and return type fix_fcn_def_params(next); mark_function_return_type(pc, chunk_get_prev_ncnlni(pc), pc->type); // Issue #2279 /* mark C# where chunk */ if ( language_is_set(LANG_CS) && ( (chunk_is_token(pc, CT_FUNC_DEF)) || (chunk_is_token(pc, CT_FUNC_PROTO)))) { tmp = chunk_get_next_ncnl(paren_close); pcf_flags_t in_where_spec_flags = PCF_NONE; while ( (tmp != nullptr) && (tmp->type != CT_BRACE_OPEN) && (tmp->type != CT_SEMICOLON)) { mark_where_chunk(tmp, pc->type, tmp->flags | in_where_spec_flags); in_where_spec_flags = tmp->flags & PCF_IN_WHERE_SPEC; tmp = chunk_get_next_ncnl(tmp); } } // Find the brace pair and set the parent if (chunk_is_token(pc, CT_FUNC_DEF)) { tmp = chunk_get_next_ncnl(paren_close); while ( tmp != nullptr && tmp->type != CT_BRACE_OPEN) { LOG_FMT(LFCN, "%s(%d): (13) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); set_chunk_parent(tmp, CT_FUNC_DEF); if (!chunk_is_semicolon(tmp)) { chunk_flags_set(tmp, PCF_OLD_FCN_PARAMS); } tmp = chunk_get_next_ncnl(tmp); } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { LOG_FMT(LFCN, "%s(%d): (14) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); set_chunk_parent(tmp, CT_FUNC_DEF); tmp = chunk_skip_to_match(tmp); if (tmp != nullptr) { LOG_FMT(LFCN, "%s(%d): (15) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n", __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); set_chunk_parent(tmp, CT_FUNC_DEF); } } } } // mark_function static void mark_cpp_constructor(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *paren_open; chunk_t *tmp; chunk_t *after; chunk_t *var; bool is_destr = false; tmp = chunk_get_prev_ncnlni(pc); // Issue #2279 if (chunk_is_token(tmp, CT_INV) || chunk_is_token(tmp, CT_DESTRUCTOR)) { set_chunk_type(tmp, CT_DESTRUCTOR); set_chunk_parent(pc, CT_DESTRUCTOR); is_destr = true; } LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, FOUND %sSTRUCTOR for '%s'[%s] prev '%s'[%s]\n", __func__, __LINE__, pc->orig_line, pc->orig_col, is_destr ? "DE" : "CON", pc->text(), get_token_name(pc->type), tmp->text(), get_token_name(tmp->type)); paren_open = skip_template_next(chunk_get_next_ncnl(pc)); if (!chunk_is_str(paren_open, "(", 1)) { LOG_FMT(LWARN, "%s:%zu Expected '(', got: [%s]\n", cpd.filename.c_str(), paren_open->orig_line, paren_open->text()); return; } // Mark parameters fix_fcn_def_params(paren_open); after = flag_parens(paren_open, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CLASS_PROTO, false); LOG_FMT(LFTOR, "%s(%d): text() '%s'\n", __func__, __LINE__, after->text()); // Scan until the brace open, mark everything tmp = paren_open; bool hit_colon = false; while ( tmp != nullptr && (tmp->type != CT_BRACE_OPEN || tmp->level != paren_open->level) && !chunk_is_semicolon(tmp)) { LOG_FMT(LFTOR, "%s(%d): tmp is '%s', orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, tmp->text(), tmp->orig_line, tmp->orig_col); chunk_flags_set(tmp, PCF_IN_CONST_ARGS); tmp = chunk_get_next_ncnl(tmp); if (chunk_is_str(tmp, ":", 1) && tmp->level == paren_open->level) { set_chunk_type(tmp, CT_CONSTR_COLON); hit_colon = true; } if ( hit_colon && (chunk_is_paren_open(tmp) || chunk_is_opening_brace(tmp)) && tmp->level == paren_open->level) { var = skip_template_prev(chunk_get_prev_ncnlni(tmp)); // Issue #2279 if (chunk_is_token(var, CT_TYPE) || chunk_is_token(var, CT_WORD)) { set_chunk_type(var, CT_FUNC_CTOR_VAR); flag_parens(tmp, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CTOR_VAR, false); } } } if (chunk_is_token(tmp, CT_BRACE_OPEN)) { set_paren_parent(paren_open, CT_FUNC_CLASS_DEF); set_paren_parent(tmp, CT_FUNC_CLASS_DEF); LOG_FMT(LFCN, "%s(%d): Marked '%s' as FUNC_CLASS_DEF on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); } else { set_chunk_parent(tmp, CT_FUNC_CLASS_PROTO); set_chunk_type(pc, CT_FUNC_CLASS_PROTO); LOG_FMT(LFCN, "%s(%d): Marked '%s' as FUNC_CLASS_PROTO on orig_line %zu, orig_col %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); } } // mark_cpp_constructor static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags) { /* TODO: should have options to control spacing around the ':' as well as newline ability for the * constraint clauses (should it break up a 'where A : B where C : D' on the same line? wrap? etc.) */ if (chunk_is_token(pc, CT_WHERE)) { set_chunk_type(pc, CT_WHERE_SPEC); set_chunk_parent(pc, parent_type); flags |= PCF_IN_WHERE_SPEC; LOG_FMT(LFTOR, "%s: where-spec on line %zu\n", __func__, pc->orig_line); } else if (flags.test(PCF_IN_WHERE_SPEC)) { if (chunk_is_str(pc, ":", 1)) { set_chunk_type(pc, CT_WHERE_COLON); LOG_FMT(LFTOR, "%s: where-spec colon on line %zu\n", __func__, pc->orig_line); } else if ((chunk_is_token(pc, CT_STRUCT)) || (chunk_is_token(pc, CT_CLASS))) { /* class/struct inside of a where-clause confuses parser for indentation; set it as a word so it looks like the rest */ set_chunk_type(pc, CT_WORD); } } if (flags.test(PCF_IN_WHERE_SPEC)) { chunk_flags_set(pc, PCF_IN_WHERE_SPEC); } return(flags); } static void mark_class_ctor(chunk_t *start) { LOG_FUNC_ENTRY(); LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, start is '%s', parent_type is %s\n", __func__, __LINE__, start->orig_line, start->orig_col, start->text(), get_token_name(get_chunk_parent_type(start))); log_pcf_flags(LFTOR, start->flags); chunk_t *pclass = chunk_get_next_ncnl(start, scope_e::PREPROC); LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); log_pcf_flags(LFTOR, pclass->flags); if (language_is_set(LANG_CPP)) { pclass = skip_attribute_next(pclass); LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); } if (get_chunk_parent_type(start) == CT_TEMPLATE) { // look after the class name chunk_t *openingTemplate = chunk_get_next_ncnl(pclass); LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, openingTemplate is '%s', type is %s\n", __func__, __LINE__, openingTemplate->orig_line, openingTemplate->orig_col, openingTemplate->text(), get_token_name(openingTemplate->type)); if (chunk_is_token(openingTemplate, CT_ANGLE_OPEN)) { chunk_t *closingTemplate = chunk_skip_to_match(openingTemplate); LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, closingTemplate is '%s', type is %s\n", __func__, __LINE__, closingTemplate->orig_line, closingTemplate->orig_col, closingTemplate->text(), get_token_name(closingTemplate->type)); chunk_t *thirdToken = chunk_get_next_ncnl(closingTemplate); LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, thirdToken is '%s', type is %s\n", __func__, __LINE__, thirdToken->orig_line, thirdToken->orig_col, thirdToken->text(), get_token_name(thirdToken->type)); if (chunk_is_token(thirdToken, CT_DC_MEMBER)) { pclass = chunk_get_next_ncnl(thirdToken); LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, pclass is '%s', type is %s\n", __func__, __LINE__, pclass->orig_line, pclass->orig_col, pclass->text(), get_token_name(pclass->type)); } } } pclass = skip_attribute_next(pclass); LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); if (chunk_is_token(pclass, CT_DECLSPEC)) // Issue 1289 { pclass = chunk_get_next_ncnl(pclass); LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); if (chunk_is_token(pclass, CT_PAREN_OPEN)) { pclass = chunk_get_next_ncnl(chunk_skip_to_match(pclass)); LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); } } if ( pclass == nullptr || (pclass->type != CT_TYPE && pclass->type != CT_WORD)) { return; } chunk_t *next = chunk_get_next_ncnl(pclass, scope_e::PREPROC); while ( chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_DC_MEMBER)) { pclass = next; LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); next = chunk_get_next_ncnl(next, scope_e::PREPROC); } chunk_t *pc = chunk_get_next_ncnl(pclass, scope_e::PREPROC); size_t level = pclass->brace_level + 1; if (pc == nullptr) { LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu. Bailed on NULL\n", __func__, __LINE__, pclass->text(), pclass->orig_line); return; } // Add the class name ChunkStack cs; cs.Push_Back(pclass); LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu (next is '%s')\n", __func__, __LINE__, pclass->text(), pclass->orig_line, pc->text()); // detect D template class: "class foo(x) { ... }" if (language_is_set(LANG_D) && chunk_is_token(next, CT_PAREN_OPEN)) // Coverity CID 76004 { set_chunk_parent(next, CT_TEMPLATE); next = get_d_template_types(cs, next); if (chunk_is_token(next, CT_PAREN_CLOSE)) { set_chunk_parent(next, CT_TEMPLATE); } } // Find the open brace, abort on semicolon pcf_flags_t flags = PCF_NONE; while (pc != nullptr && pc->type != CT_BRACE_OPEN) { LOG_FMT(LFTOR, " [%s]", pc->text()); flags = mark_where_chunk(pc, start->type, flags); if (!flags.test(PCF_IN_WHERE_SPEC) && chunk_is_str(pc, ":", 1)) { set_chunk_type(pc, CT_CLASS_COLON); flags |= PCF_IN_CLASS_BASE; LOG_FMT(LFTOR, "%s(%d): class colon on line %zu\n", __func__, __LINE__, pc->orig_line); } if (chunk_is_semicolon(pc)) { LOG_FMT(LFTOR, "%s(%d): bailed on semicolon on line %zu\n", __func__, __LINE__, pc->orig_line); return; } chunk_flags_set(pc, flags); pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); } if (pc == nullptr) { LOG_FMT(LFTOR, "%s(%d): bailed on NULL\n", __func__, __LINE__); return; } set_paren_parent(pc, start->type); chunk_flags_set(pc, PCF_IN_CLASS); pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", __func__, __LINE__, pclass->text()); while (pc != nullptr) { LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); chunk_flags_set(pc, PCF_IN_CLASS); if ( pc->brace_level > level || pc->level > pc->brace_level || pc->flags.test(PCF_IN_PREPROC)) { pc = chunk_get_next_ncnl(pc); continue; } if (chunk_is_token(pc, CT_BRACE_CLOSE) && pc->brace_level < level) { LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, Hit brace close\n", __func__, __LINE__, pc->orig_line); pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (chunk_is_token(pc, CT_SEMICOLON)) { set_chunk_parent(pc, start->type); } return; } next = chunk_get_next_ncnl(pc, scope_e::PREPROC); if (chunkstack_match(cs, pc)) { LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); // Issue #1333 Formatter removes semicolon after variable initializer at class level(C#) // if previous chunk is 'new' operator it is variable initializer not a CLASS_FUNC_DEF. chunk_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC); // Issue #2279 LOG_FMT(LFTOR, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); // Issue #1003, next->type should not be CT_FPAREN_OPEN if ( prev != nullptr && (prev->type != CT_NEW)) { bool is_func_class_def = false; if (chunk_is_token(next, CT_PAREN_OPEN)) { is_func_class_def = true; } else if (chunk_is_token(next, CT_ANGLE_OPEN)) // Issue # 1737 { chunk_t *closeAngle = chunk_skip_to_match(next); chunk_t *afterTemplate = chunk_get_next(closeAngle); if (chunk_is_token(afterTemplate, CT_PAREN_OPEN)) { is_func_class_def = true; } } else { LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); make_type(pc); } if (is_func_class_def) { set_chunk_type(pc, CT_FUNC_CLASS_DEF); LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s, Marked CTor/DTor\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); mark_cpp_constructor(pc); } } } pc = next; } } // mark_class_ctor static chunk_t *skip_align(chunk_t *start) { chunk_t *pc = start; if (chunk_is_token(pc, CT_ALIGN)) { pc = chunk_get_next_ncnl(pc); if (chunk_is_token(pc, CT_PAREN_OPEN)) { pc = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level); pc = chunk_get_next_ncnl(pc); if (chunk_is_token(pc, CT_COLON)) { pc = chunk_get_next_ncnl(pc); } } } return(pc); } chunk_t *skip_parent_types(chunk_t *colon) { auto pc = chunk_get_next_ncnlnp(colon); while (pc) { // Skip access specifier if (chunk_is_token(pc, CT_ACCESS)) { pc = chunk_get_next_ncnlnp(pc); continue; } // Check for a type name if (!(chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))) { LOG_FMT(LPCU, "%s is confused; expected a word at %zu:%zu " "following type list at %zu:%zu\n", __func__, colon->orig_line, colon->orig_col, pc->orig_line, pc->orig_col); return(colon); } // Get next token auto next = skip_template_next(chunk_get_next_ncnlnp(pc)); if (chunk_is_token(next, CT_DC_MEMBER) || chunk_is_token(next, CT_COMMA)) { pc = chunk_get_next_ncnlnp(next); } else if (next) { LOG_FMT(LPCU, "%s -> %zu:%zu ('%s')\n", __func__, next->orig_line, next->orig_col, next->text()); return(next); } else { break; } } LOG_FMT(LPCU, "%s: did not find end of type list (start was %zu:%zu)\n", __func__, colon->orig_line, colon->orig_col); return(colon); } // skip_parent_types static void mark_struct_union_body(chunk_t *start) { LOG_FUNC_ENTRY(); chunk_t *pc = start; while ( pc != nullptr && pc->level >= start->level && !(pc->level == start->level && chunk_is_token(pc, CT_BRACE_CLOSE))) { if ( chunk_is_token(pc, CT_BRACE_OPEN) || chunk_is_token(pc, CT_BRACE_CLOSE) || chunk_is_token(pc, CT_SEMICOLON)) { pc = chunk_get_next_ncnl(pc); if (pc == nullptr) { break; } } if (chunk_is_token(pc, CT_ALIGN)) { pc = skip_align(pc); // "align(x)" or "align(x):" if (pc == nullptr) { break; } } else { pc = fix_variable_definition(pc); if (pc == nullptr) { break; } } } } // mark_struct_union_body void mark_comments(void) { LOG_FUNC_ENTRY(); cpd.unc_stage = unc_stage_e::MARK_COMMENTS; bool prev_nl = true; chunk_t *cur = chunk_get_head(); while (cur != nullptr) { chunk_t *next = chunk_get_next_nvb(cur); bool next_nl = (next == nullptr) || chunk_is_newline(next); if (chunk_is_comment(cur)) { 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 mark_define_expressions(void) { LOG_FUNC_ENTRY(); bool in_define = false; bool first = true; chunk_t *pc = chunk_get_head(); chunk_t *prev = pc; while (pc != nullptr) { if (!in_define) { if ( chunk_is_token(pc, CT_PP_DEFINE) || chunk_is_token(pc, CT_PP_IF) || chunk_is_token(pc, CT_PP_ELSE)) { in_define = true; first = true; } } else { if (!pc->flags.test(PCF_IN_PREPROC) || chunk_is_token(pc, CT_PREPROC)) { in_define = false; } else { if ( pc->type != CT_MACRO && ( first || chunk_is_token(prev, CT_PAREN_OPEN) || chunk_is_token(prev, CT_ARITH) || chunk_is_token(prev, CT_CARET) || chunk_is_token(prev, CT_ASSIGN) || chunk_is_token(prev, CT_COMPARE) || chunk_is_token(prev, CT_RETURN) || chunk_is_token(prev, CT_GOTO) || chunk_is_token(prev, CT_CONTINUE) || chunk_is_token(prev, CT_FPAREN_OPEN) || chunk_is_token(prev, CT_SPAREN_OPEN) || chunk_is_token(prev, CT_BRACE_OPEN) || chunk_is_semicolon(prev) || chunk_is_token(prev, CT_COMMA) || chunk_is_token(prev, CT_COLON) || chunk_is_token(prev, CT_QUESTION))) { chunk_flags_set(pc, PCF_EXPR_START); first = false; } } } prev = pc; pc = chunk_get_next(pc); } } // mark_define_expressions static void handle_cpp_template(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *tmp = chunk_get_next_ncnl(pc); if (tmp->type != CT_ANGLE_OPEN) { return; } set_chunk_parent(tmp, CT_TEMPLATE); size_t level = tmp->level; while ((tmp = chunk_get_next(tmp)) != nullptr) { 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; } } if (tmp != nullptr) { tmp = chunk_get_next_ncnl(tmp); 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 = chunk_get_next_type(tmp, CT_SEMICOLON, tmp->level); if (tmp != nullptr) { set_chunk_parent(tmp, CT_TEMPLATE); } } } } // handle_cpp_template static void handle_cpp_lambda(chunk_t *sq_o) { LOG_FUNC_ENTRY(); chunk_t *ret = nullptr; // abort if type of the previous token is not contained in this whitelist chunk_t *prev = chunk_get_prev_ncnlni(sq_o); // Issue #2279 if ( prev == nullptr || ( prev->type != CT_ASSIGN && prev->type != CT_COMMA && prev->type != CT_PAREN_OPEN // allow Js like self invoking lambda syntax: ([](){})(); && prev->type != CT_FPAREN_OPEN && prev->type != CT_SQUARE_OPEN && prev->type != CT_BRACE_OPEN && prev->type != CT_SEMICOLON && prev->type != CT_RETURN)) { return; } chunk_t *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) { return; } } chunk_t *pa_o = chunk_get_next_ncnl(sq_c); // check to see if there is a lambda-specifier in the pa_o chunk; // assuming chunk is CT_EXECUTION_CONTEXT, ignore lambda-specifier while (pa_o->type == CT_EXECUTION_CONTEXT) { // set pa_o to next chunk after this specifier pa_o = chunk_get_next_ncnl(pa_o); } if (pa_o == nullptr) { return; } chunk_t *pa_c = nullptr; // 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 == nullptr) { return; } } // Check for 'mutable' keyword: '[]() mutable {}' or []() mutable -> ret {} chunk_t *br_o = pa_c ? chunk_get_next_ncnl(pa_c) : pa_o; if (chunk_is_str(br_o, "mutable", 7)) { br_o = chunk_get_next_ncnl(br_o); } //TODO: also check for exception and attribute between [] ... {} // skip possible arrow syntax: '-> ret' if (chunk_is_str(br_o, "->", 2)) { ret = br_o; // REVISIT: really should check the stuff we are skipping br_o = chunk_get_next_type(br_o, CT_BRACE_OPEN, br_o->level); } if ( br_o == nullptr || br_o->type != CT_BRACE_OPEN) { return; } // and now find the '}' chunk_t *br_c = chunk_skip_to_match(br_o); if (br_c == nullptr) { return; } // This looks like a lambda expression if (chunk_is_token(sq_o, CT_TSQUARE)) { // split into two chunks chunk_t 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<int>(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 != nullptr) { set_chunk_type(pa_o, CT_FPAREN_OPEN); set_chunk_parent(pa_o, CT_CPP_LAMBDA); set_chunk_type(pa_c, CT_FPAREN_CLOSE); set_chunk_parent(pa_c, CT_CPP_LAMBDA); } set_chunk_parent(br_o, CT_CPP_LAMBDA); set_chunk_parent(br_c, CT_CPP_LAMBDA); if (ret != nullptr) { set_chunk_type(ret, CT_CPP_LAMBDA_RET); ret = chunk_get_next_ncnl(ret); while (ret != br_o) { make_type(ret); ret = chunk_get_next_ncnl(ret); } } if (pa_c != nullptr) { fix_fcn_def_params(pa_o); } //handle self calling lambda paren chunk_t *call_pa_o = chunk_get_next_ncnl(br_c); if (chunk_is_token(call_pa_o, CT_PAREN_OPEN)) { chunk_t *call_pa_c = chunk_skip_to_match(call_pa_o); if (call_pa_c != nullptr) { 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); } } } // handle_cpp_lambda static chunk_t *get_d_template_types(ChunkStack &cs, chunk_t *open_paren) { LOG_FUNC_ENTRY(); chunk_t *tmp = open_paren; bool maybe_type = true; while ( ((tmp = chunk_get_next_ncnl(tmp)) != nullptr) && tmp->level > open_paren->level) { if (chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD)) { if (maybe_type) { make_type(tmp); cs.Push_Back(tmp); } maybe_type = false; } else if (chunk_is_token(tmp, CT_COMMA)) { maybe_type = true; } } return(tmp); } static bool chunkstack_match(ChunkStack &cs, chunk_t *pc) { for (size_t idx = 0; idx < cs.Len(); idx++) { chunk_t *tmp = cs.GetChunk(idx); if (pc->str.equals(tmp->str)) { return(true); } } return(false); } static void handle_d_template(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *name = chunk_get_next_ncnl(pc); chunk_t *po = chunk_get_next_ncnl(name); //if (!name || (name->type != CT_WORD && name->type != CT_WORD)) Coverity CID 76000 Same on both sides, 2016-03-16 if (!name || name->type != CT_WORD) { // TODO: log an error, expected NAME return; } if ( po == nullptr || po->type != 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_t *tmp = get_d_template_types(cs, po); if ( tmp == nullptr || tmp->type != CT_PAREN_CLOSE) { // TODO: log an error, expected ')' return; } set_chunk_parent(tmp, CT_TEMPLATE); tmp = chunk_get_next_ncnl(tmp); if (tmp->type != CT_BRACE_OPEN) { // TODO: log an error, expected '{' return; } set_chunk_parent(tmp, CT_TEMPLATE); po = tmp; tmp = po; while ( ((tmp = chunk_get_next_ncnl(tmp)) != nullptr) && tmp->level > po->level) { if (chunk_is_token(tmp, CT_WORD) && chunkstack_match(cs, tmp)) { set_chunk_type(tmp, CT_TYPE); } } // if (!chunk_is_token(tmp, CT_BRACE_CLOSE)) // { // // TODO: log an error, expected '}' // } set_chunk_parent(tmp, CT_TEMPLATE); } // handle_d_template static void mark_template_func(chunk_t *pc, chunk_t *pc_next) { LOG_FUNC_ENTRY(); // We know angle_close must be there... chunk_t *angle_close = chunk_get_next_type(pc_next, CT_ANGLE_CLOSE, pc->level); chunk_t *after = chunk_get_next_ncnl(angle_close); if (after != nullptr) { if (chunk_is_str(after, "(", 1)) { if (angle_close->flags.test(PCF_IN_FCN_CALL)) { LOG_FMT(LTEMPFUNC, "%s(%d): marking '%s' in line %zu as a FUNC_CALL\n", __func__, __LINE__, pc->text(), pc->orig_line); LOG_FMT(LFCN, "%s(%d): (16) 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); flag_parens(after, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false); } else { /* * Might be a function def. Must check what is before the template: * Func call: * BTree.Insert(std::pair<int, double>(*it, double(*it) + 1.0)); * a = Test<int>(j); * std::pair<int, double>(*it, double(*it) + 1.0)); */ LOG_FMT(LTEMPFUNC, "%s(%d): marking '%s' in line %zu as a FUNC_CALL 2\n", __func__, __LINE__, pc->text(), pc->orig_line); // its a function!!! LOG_FMT(LFCN, "%s(%d): (17) 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); mark_function(pc); } } else if (chunk_is_token(after, CT_WORD)) { // its a type! set_chunk_type(pc, CT_TYPE); chunk_flags_set(pc, PCF_VAR_TYPE); chunk_flags_set(after, PCF_VAR_DEF); } } } // mark_template_func static void mark_exec_sql(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *tmp; // Change CT_WORD to CT_SQL_WORD for (tmp = chunk_get_next(pc); tmp != nullptr; tmp = chunk_get_next(tmp)) { set_chunk_parent(tmp, pc->type); if (chunk_is_token(tmp, CT_WORD)) { set_chunk_type(tmp, CT_SQL_WORD); } if (chunk_is_token(tmp, CT_SEMICOLON)) { break; } } if ( pc->type != CT_SQL_BEGIN || tmp == nullptr || tmp->type != CT_SEMICOLON) { return; } for (tmp = chunk_get_next(tmp); tmp != nullptr && tmp->type != CT_SQL_END; tmp = chunk_get_next(tmp)) { tmp->level++; } } chunk_t *skip_template_next(chunk_t *ang_open) { if (chunk_is_token(ang_open, CT_ANGLE_OPEN)) { chunk_t *pc = chunk_get_next_type(ang_open, CT_ANGLE_CLOSE, ang_open->level); return(chunk_get_next_ncnl(pc)); } return(ang_open); } chunk_t *skip_template_prev(chunk_t *ang_close) { if (chunk_is_token(ang_close, CT_ANGLE_CLOSE)) { chunk_t *pc = chunk_get_prev_type(ang_close, CT_ANGLE_OPEN, ang_close->level); return(chunk_get_prev_ncnlni(pc)); // Issue #2279 } return(ang_close); } chunk_t *skip_tsquare_next(chunk_t *ary_def) { if (chunk_is_token(ary_def, CT_SQUARE_OPEN) || chunk_is_token(ary_def, CT_TSQUARE)) { return(chunk_get_next_nisq(ary_def)); } return(ary_def); } chunk_t *skip_attribute_next(chunk_t *attr) { chunk_t *pc = attr; while (chunk_is_token(pc, CT_ATTRIBUTE)) { pc = chunk_get_next_ncnl(pc); if (chunk_is_token(pc, CT_FPAREN_OPEN)) { pc = chunk_get_next_type(pc, CT_FPAREN_CLOSE, pc->level); pc = chunk_get_next_ncnl(pc); } } return(pc); } chunk_t *skip_attribute_prev(chunk_t *fp_close) { chunk_t *pc = fp_close; while (true) { if ( chunk_is_token(pc, CT_FPAREN_CLOSE) && get_chunk_parent_type(pc) == CT_ATTRIBUTE) { pc = chunk_get_prev_type(pc, CT_ATTRIBUTE, pc->level); } else if (chunk_is_not_token(pc, CT_ATTRIBUTE)) { break; } pc = chunk_get_prev_ncnlni(pc); // Issue #2279 } return(pc); } static void handle_oc_class(chunk_t *pc) { enum class angle_state_e : unsigned int { NONE = 0, OPEN = 1, // '<' found CLOSE = 2, // '>' found }; LOG_FUNC_ENTRY(); chunk_t *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 = chunk_get_next_ncnl(pc); 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 = chunk_get_next_nnl(tmp)) != nullptr) { 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, "<", 1)) { 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, ">", 1)) { 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, ">>", 2)) { 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 = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level); if ( tmp != nullptr && 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, "-", 1) || chunk_is_str(tmp, "+", 1)) { as = angle_state_e::CLOSE; if (chunk_is_newline(chunk_get_prev(tmp))) { 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 = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level); if (tmp != nullptr) { set_chunk_parent(tmp, CT_OC_CLASS); } } } // handle_oc_class static void handle_oc_block_literal(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *prev = chunk_get_prev_ncnlni(pc); // Issue #2279 chunk_t *next = chunk_get_next_ncnl(pc); if ( pc == nullptr || prev == nullptr || next == nullptr) { 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_t *apo = nullptr; // arg paren open chunk_t *bbo = nullptr; // block brace open chunk_t *bbc; // block brace close LOG_FMT(LOCBLK, "%s(%d): + scan", __func__, __LINE__); chunk_t *tmp; for (tmp = next; tmp; tmp = chunk_get_next_ncnl(tmp)) { /* handle '< protocol >' */ if (chunk_is_str(tmp, "<", 1)) { chunk_t *ao = tmp; chunk_t *ac = chunk_get_next_str(ao, ">", 1, ao->level); if (ac) { 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 = chunk_get_next(ao); tmp != ac; tmp = chunk_get_next(tmp)) { tmp->level += 1; set_chunk_parent(tmp, CT_OC_PROTO_LIST); } } tmp = chunk_get_next_ncnl(ac); } 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 == nullptr || bbc == nullptr) { 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_t *lbp; // last before paren - end of return type, if any if (apo) { chunk_t *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 = chunk_get_prev_ncnlni(apo); // Issue #2279 } else { lbp = chunk_get_prev_ncnlni(bbo); // 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 = chunk_get_prev_ncnlni(lbp); // 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_t *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_t *tpo = chunk_get_prev_ncnlni(pc); // type paren open Issue #2279 if (chunk_is_paren_open(tpo)) { /* * block type: 'RTYPE (^LABEL)(ARGS)' * LABEL is optional. */ chunk_t *tpc = chunk_skip_to_match(tpo); // type close paren (after '^') chunk_t *nam = chunk_get_prev_ncnlni(tpc); // name (if any) or '^' Issue #2279 chunk_t *apo = chunk_get_next_ncnl(tpc); // arg open paren chunk_t *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_t *aft = chunk_get_next_ncnl(apc); c_token_t pt; if (chunk_is_str(nam, "^", 1)) { 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, chunk_get_prev_ncnlni(tpo), pt); // Issue #2279 } } } // handle_oc_block_type static chunk_t *handle_oc_md_type(chunk_t *paren_open, c_token_t ptype, pcf_flags_t flags, bool &did_it) { chunk_t *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_t *cur = chunk_get_next_ncnl(paren_open); cur != paren_close; cur = chunk_get_next_ncnl(cur)) { 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(chunk_get_next_ncnl(paren_close)); } static void handle_oc_message_decl(chunk_t *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_t *tmp = pc; while ((tmp = chunk_get_next(tmp)) != nullptr) { 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; } c_token_t pt = (tmp->type == 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(chunk_get_next_ncnl(pc), 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_token(tmp, CT_WORD)) { LOG_FMT(LOCMSGD, " -- missing method name\n"); return; } // expect the method name/label chunk_t *label = tmp; set_chunk_type(tmp, pt); set_chunk_parent(tmp, pt); pc = chunk_get_next_ncnl(tmp); 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 = chunk_get_next_ncnl(pc); } // a colon must be next if (!chunk_is_str(pc, ":", 1)) { break; } set_chunk_type(pc, CT_OC_COLON); set_chunk_parent(pc, pt); pc = chunk_get_next_ncnl(pc); // 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 = chunk_get_next_ncnl(pc); } } 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_t *os) { LOG_FUNC_ENTRY(); chunk_t *cs = chunk_get_next(os); while (cs != nullptr && cs->level > os->level) { cs = chunk_get_next(cs); } if (cs == nullptr || cs->type != 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_t *tmp = chunk_get_next_ncnl(cs); if (chunk_is_semicolon(tmp)) { set_chunk_parent(tmp, CT_OC_MSG); } // expect a word first thing or [...] tmp = chunk_get_next_ncnl(os); if ( chunk_is_token(tmp, CT_SQUARE_OPEN) || chunk_is_token(tmp, CT_PAREN_OPEN) || (chunk_is_token(tmp, CT_OC_AT))) { chunk_t *tt = chunk_get_next_ncnl(tmp); if ((chunk_is_token(tmp, CT_OC_AT)) && tt) { 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 ( tmp->type != CT_WORD && tmp->type != CT_TYPE && tmp->type != CT_THIS && tmp->type != CT_STAR && tmp->type != 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 (chunk_is_star(tmp)) // Issue #2722 { set_chunk_type(tmp, CT_PTR_TYPE); tmp = chunk_get_next_ncnl(tmp); } chunk_t *tt = chunk_get_next_ncnl(tmp); 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 = chunk_get_prev_ncnlni(set_paren_parent(tt, CT_FUNC_CALL)); // 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 = chunk_get_next_ncnl(tmp); if (chunk_is_str(tmp, "<", 1)) { chunk_t *ao = tmp; chunk_t *ac = chunk_get_next_str(ao, ">", 1, ao->level); if (ac) { 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 = chunk_get_next(ao); tmp != ac; tmp = chunk_get_next(tmp)) { tmp->level += 1; set_chunk_parent(tmp, CT_OC_PROTO_LIST); } } tmp = chunk_get_next_ncnl(ac); } // handle 'object.property' and 'collection[index]' else { while (tmp) { if (chunk_is_token(tmp, CT_MEMBER)) // move past [object.prop1.prop2 { chunk_t *typ = chunk_get_next_ncnl(tmp); if (chunk_is_token(typ, CT_WORD) || chunk_is_token(typ, CT_TYPE)) { tmp = chunk_get_next_ncnl(typ); } else { break; } } else if (chunk_is_token(tmp, CT_SQUARE_OPEN)) // move past [collection[index] { chunk_t *tcs = chunk_get_next_ncnl(tmp); while (tcs != nullptr && tcs->level > tmp->level) { tcs = chunk_get_next_ncnl(tcs); } if (chunk_is_token(tcs, CT_SQUARE_CLOSE)) { tmp = chunk_get_next_ncnl(tcs); } else { break; } } else { break; } } } // [(self.foo.bar) method] if (chunk_is_paren_open(tmp)) { tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp)); } if (chunk_is_token(tmp, CT_WORD) || chunk_is_token(tmp, CT_TYPE)) { set_chunk_type(tmp, CT_OC_MSG_FUNC); } chunk_t *prev = nullptr; for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp)) { 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_t *pp = chunk_get_prev(prev); if ( pp != nullptr && pp->type != CT_OC_COLON && pp->type != CT_ARITH && pp->type != 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_t *os) { os = chunk_get_next(os); while (os != nullptr) { c_token_t origType = os->type; set_chunk_type(os, CT_OC_AVAILABLE_VALUE); if (origType == CT_PAREN_CLOSE) { break; } os = chunk_get_next(os); } } static void handle_oc_property_decl(chunk_t *os) { log_rule_B("mod_sort_oc_properties"); if (options::mod_sort_oc_properties()) { typedef std::vector<chunk_t *> ChunkGroup; chunk_t *next = chunk_get_next(os); chunk_t *open_paren = nullptr; std::vector<ChunkGroup> class_chunks; // class std::vector<ChunkGroup> thread_chunks; // atomic, nonatomic std::vector<ChunkGroup> readwrite_chunks; // readwrite, readonly std::vector<ChunkGroup> ref_chunks; // retain, copy, assign, weak, strong, unsafe_unretained std::vector<ChunkGroup> getter_chunks; // getter std::vector<ChunkGroup> setter_chunks; // setter std::vector<ChunkGroup> nullability_chunks; // nonnull, nullable, null_unspecified, null_resettable std::vector<ChunkGroup> other_chunks; // any words other than above if (chunk_is_token(next, CT_PAREN_OPEN)) { open_paren = next; next = chunk_get_next(next); /* * 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 != nullptr && next->type != CT_PAREN_CLOSE) { if (chunk_is_token(next, CT_OC_PROPERTY_ATTR)) { if ( chunk_is_str(next, "atomic", 6) || chunk_is_str(next, "nonatomic", 9)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); thread_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "readonly", 8) || chunk_is_str(next, "readwrite", 9)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); readwrite_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "assign", 6) || chunk_is_str(next, "retain", 6) || chunk_is_str(next, "copy", 4) || chunk_is_str(next, "strong", 6) || chunk_is_str(next, "weak", 4) || chunk_is_str(next, "unsafe_unretained", 17)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); ref_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "getter", 6)) { ChunkGroup chunkGroup; do { chunkGroup.push_back(next); next = chunk_get_next(next); } while ( next && next->type != CT_COMMA && next->type != CT_PAREN_CLOSE); next = next->prev; // coverity CID 160946 if (next == nullptr) { break; } getter_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "setter", 6)) { ChunkGroup chunkGroup; do { chunkGroup.push_back(next); next = chunk_get_next(next); } while ( next && next->type != CT_COMMA && next->type != CT_PAREN_CLOSE); next = chunk_get_prev(next); if (next == nullptr) { break; } setter_chunks.push_back(chunkGroup); } else if ( chunk_is_str(next, "nullable", 8) || chunk_is_str(next, "nonnull", 7) || chunk_is_str(next, "null_resettable", 15) || chunk_is_str(next, "null_unspecified", 16)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); nullability_chunks.push_back(chunkGroup); } else if (chunk_is_str(next, "class", 5)) { 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", 5)) { ChunkGroup chunkGroup; chunkGroup.push_back(next); class_chunks.push_back(chunkGroup); } else { ChunkGroup chunkGroup; chunkGroup.push_back(next); other_chunks.push_back(chunkGroup); } } next = chunk_get_next(next); } 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<int, std::vector<ChunkGroup> > sorted_chunk_map; sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(class_w, class_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(thread_w, thread_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(readwrite_w, readwrite_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(ref_w, ref_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(getter_w, getter_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(setter_w, setter_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(nullability_w, nullability_chunks)); sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(std::numeric_limits<int>::min(), other_chunks)); chunk_t *curr_chunk = open_paren; for (multimap<int, std::vector<ChunkGroup> >::reverse_iterator it = sorted_chunk_map.rbegin(); it != sorted_chunk_map.rend(); ++it) { std::vector<ChunkGroup> 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 = chunk_get_next(curr_chunk); } } // add the parenthesis chunk_t 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.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->next; } } // Remove the extra comma's that we did not move while (curr_chunk && curr_chunk->type != CT_PAREN_CLOSE) { chunk_t *rm_chunk = curr_chunk; curr_chunk = chunk_get_next(curr_chunk); chunk_del(rm_chunk); } } } chunk_t *tmp = chunk_get_next_ncnl(os); if (chunk_is_paren_open(tmp)) { tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp)); } fix_variable_definition(tmp); } // handle_oc_property_decl static void handle_cs_square_stmt(chunk_t *os) { LOG_FUNC_ENTRY(); chunk_t *cs = chunk_get_next(os); while (cs != nullptr && cs->level > os->level) { cs = chunk_get_next(cs); } if (cs == nullptr || cs->type != CT_SQUARE_CLOSE) { return; } set_chunk_parent(os, CT_CS_SQ_STMT); set_chunk_parent(cs, CT_CS_SQ_STMT); chunk_t *tmp; for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp)) { set_chunk_parent(tmp, CT_CS_SQ_STMT); if (chunk_is_token(tmp, CT_COLON)) { set_chunk_type(tmp, CT_CS_SQ_COLON); } } tmp = chunk_get_next_ncnl(cs); if (tmp != nullptr) { chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START); } } static void handle_cs_property(chunk_t *bro) { LOG_FUNC_ENTRY(); set_paren_parent(bro, CT_CS_PROPERTY); bool did_prop = false; chunk_t *pc = bro; while ((pc = chunk_get_prev_ncnlni(pc)) != nullptr) // Issue #2279 { if (pc->level == bro->level) { //prevent scanning back past 'new' in expressions like new List<int> {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_t *pc) { chunk_t *prev; for (prev = chunk_get_prev(pc); chunk_is_token(prev, CT_COMMA); prev = chunk_get_prev(prev)) { // empty } if (chunk_is_token(prev, CT_SQUARE_OPEN)) { while (pc != prev) { set_chunk_parent(pc, CT_TYPE); pc = chunk_get_prev(pc); } set_chunk_parent(prev, CT_TYPE); } } static void handle_wrap(chunk_t *pc) { LOG_FUNC_ENTRY(); chunk_t *opp = chunk_get_next(pc); chunk_t *name = chunk_get_next(opp); chunk_t *clp = chunk_get_next(name); log_rule_B("sp_func_call_paren"); log_rule_B("sp_cpp_cast_paren"); iarf_e pav = (pc->type == 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 = (pc->type == 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, (pc->type == 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_t *pc) { LOG_FUNC_ENTRY(); chunk_t *opp = chunk_get_next_ncnl(pc); chunk_t *name = chunk_get_next_ncnl(opp); chunk_t *tmp = chunk_get_next_ncnl(chunk_get_next_ncnl(name)); chunk_t *clp = chunk_skip_to_match(opp); chunk_t *cma = chunk_get_next_ncnl(clp); if ( !opp || !name || !clp || !cma || !tmp || (name->type != CT_WORD && name->type != CT_TYPE) || opp->type != 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 = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 { if ( !chunk_is_type(tmp) && tmp->type != CT_OPERATOR && tmp->type != CT_WORD && tmp->type != 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_t *pc) { LOG_FUNC_ENTRY(); bool did_colon = false; chunk_t *tmp = pc; while ((tmp = chunk_get_next(tmp)) != nullptr) { 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; } } } }
diff --git a/combine.cpp-2020-04-16-A b/combine.cpp-2020-04-16-R index bfdbb81..d54bc27 100644 --- a/combine.cpp-2020-04-16-A +++ b/combine.cpp-2020-04-16-R @@ -115,6 +115,48 @@ static void process_returns(void); static chunk_t *process_return(chunk_t *pc); +/** + * TODO: add doc cmt + * + */ +static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags); + + +/** + * We're on a 'class' or 'struct'. + * Scan for CT_FUNCTION with a string that matches pclass->str + */ +static void mark_class_ctor(chunk_t *pclass); + + +static void mark_cpp_constructor(chunk_t *pc); + + +/** + * Just hit an assign. Go backwards until we hit an open brace/paren/square or + * semicolon (TODO: other limiter?) and mark as a LValue. + */ +static void mark_lvalue(chunk_t *pc); + + +/** + * We are on a word followed by a angle open which is part of a template. + * If the angle close is followed by a open paren, then we are on a template + * function def or a template function call: + * Vector2<float>(...) [: ...[, ...]] { ... } + * Or we could be on a variable def if it's followed by a word: + * Renderer<rgb32> rend; + */ +static void mark_template_func(chunk_t *pc, chunk_t *pc_next); + + +/** + * Just mark every CT_WORD until a semicolon as CT_SQL_WORD. + * Adjust the levels if pc is CT_SQL_BEGIN + */ +static void mark_exec_sql(chunk_t *pc); + + /** * Process an ObjC 'class' * pc is the chunk after '@implementation' or '@interface' or '@protocol'. @@ -303,6 +345,42 @@ static bool is_oc_block(chunk_t *pc); static void handle_java_assert(chunk_t *pc); +/** + * Parse off the types in the D template args, adds to cs + * returns the close_paren + */ +static chunk_t *get_d_template_types(ChunkStack &cs, chunk_t *open_paren); + + +static bool chunkstack_match(ChunkStack &cs, chunk_t *pc); + + +void make_type(chunk_t *pc) +{ + LOG_FUNC_ENTRY(); + + if (pc != nullptr) + { + if (chunk_is_token(pc, CT_WORD)) + { + set_chunk_type(pc, CT_TYPE); + } + else if ( ( chunk_is_star(pc) + || chunk_is_msref(pc) + || chunk_is_nullable(pc)) + && chunk_is_type(pc->prev)) // Issue # 2640 + { + set_chunk_type(pc, CT_PTR_TYPE); + } + else if ( chunk_is_addr(pc) + && !chunk_is_token(pc->prev, CT_SQUARE_OPEN)) // Issue # 2166 + { + set_chunk_type(pc, CT_BYREF); + } + } +} + + void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_t clr_flags, scope_e nav) { LOG_FUNC_ENTRY(); @@ -326,6 +404,28 @@ void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_ } +chunk_t *set_paren_parent(chunk_t *start, c_token_t parent) +{ + LOG_FUNC_ENTRY(); + chunk_t *end; + + end = chunk_skip_to_match(start, scope_e::PREPROC); + + if (end != nullptr) + { + LOG_FMT(LFLPAREN, "%s(%d): %zu:%zu '%s' and %zu:%zu '%s' type is %s, parent_type is %s", + __func__, __LINE__, start->orig_line, start->orig_col, start->text(), + end->orig_line, end->orig_col, end->text(), + get_token_name(start->type), get_token_name(parent)); + log_func_stack_inline(LFLPAREN); + set_chunk_parent(start, parent); + set_chunk_parent(end, parent); + } + LOG_FMT(LFLPAREN, "%s(%d):\n", __func__, __LINE__); + return(chunk_get_next_ncnl(end, scope_e::PREPROC)); +} + + static void flag_asm(chunk_t *pc) { LOG_FUNC_ENTRY(); @@ -397,6 +497,79 @@ static void flag_asm(chunk_t *pc) } // flag_asm +static bool chunk_ends_type(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + chunk_t *pc = start; + bool ret = false; + size_t cnt = 0; + bool last_expr = false; + bool last_lval = false; + + for ( ; pc != nullptr; pc = chunk_get_prev_ncnlni(pc)) // Issue #2279 + { + LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s', orig_line %zu, orig_col %zu\n ", + __func__, __LINE__, get_token_name(pc->type), pc->text(), + pc->orig_line, pc->orig_col); + log_pcf_flags(LFTYPE, pc->flags); + + if ( chunk_is_token(pc, CT_WORD) + || chunk_is_token(pc, CT_TYPE) + || chunk_is_token(pc, CT_PTR_TYPE) + || chunk_is_token(pc, CT_STAR) + || chunk_is_token(pc, CT_STRUCT) + || chunk_is_token(pc, CT_DC_MEMBER) + || chunk_is_token(pc, CT_PP) + || chunk_is_token(pc, CT_QUALIFIER) + || ( language_is_set(LANG_CPP | LANG_OC) // Issue #2727 + && get_chunk_parent_type(pc) == CT_TEMPLATE + && ( chunk_is_token(pc, CT_ANGLE_OPEN) + || chunk_is_token(pc, CT_ANGLE_CLOSE))) + || ( language_is_set(LANG_CS) + && (chunk_is_token(pc, CT_MEMBER)))) + { + cnt++; + last_expr = pc->flags.test(PCF_EXPR_START) + && !pc->flags.test(PCF_IN_FCN_CALL); + last_lval = pc->flags.test(PCF_LVALUE); + continue; + } + + if ( ( chunk_is_semicolon(pc) + && !pc->flags.test(PCF_IN_FOR)) + || chunk_is_token(pc, CT_TYPEDEF) + || chunk_is_token(pc, CT_BRACE_OPEN) + || chunk_is_token(pc, CT_BRACE_CLOSE) + || chunk_is_token(pc, CT_VBRACE_CLOSE) + || chunk_is_token(pc, CT_FPAREN_CLOSE) + || chunk_is_forin(pc) + || chunk_is_token(pc, CT_MACRO) + || chunk_is_token(pc, CT_PP_IF) + || chunk_is_token(pc, CT_PP_ELSE) + || chunk_is_token(pc, CT_PP_ENDIF) + || ( ( chunk_is_token(pc, CT_COMMA) + && !pc->flags.test(PCF_IN_FCN_CALL)) + && last_expr) + || ( chunk_is_token(pc, CT_SPAREN_OPEN) + && last_lval)) + { + ret = cnt > 0; + } + break; + } + + if (pc == nullptr) + { + // first token + ret = true; + } + LOG_FMT(LFTYPE, "%s(%d): first token verdict: %s\n", + __func__, __LINE__, ret ? "yes" : "no"); + + return(ret); +} // chunk_ends_type + + void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next) { LOG_FUNC_ENTRY(); @@ -2130,1066 +2303,3421 @@ void fix_symbols(void) } // fix_symbols -static void process_returns(void) +static void mark_lvalue(chunk_t *pc) { LOG_FUNC_ENTRY(); - chunk_t *pc; + chunk_t *prev; - pc = chunk_get_head(); + if (pc->flags.test(PCF_IN_PREPROC)) + { + return; + } - while (pc != nullptr) + for (prev = chunk_get_prev_ncnlni(pc); // Issue #2279 + prev != nullptr; + prev = chunk_get_prev_ncnlni(prev)) // Issue #2279 { - if (pc->type != CT_RETURN) + if ( prev->level < pc->level + || chunk_is_token(prev, CT_ASSIGN) + || chunk_is_token(prev, CT_COMMA) + || chunk_is_token(prev, CT_BOOL) + || chunk_is_semicolon(prev) + || chunk_is_str(prev, "(", 1) + || chunk_is_str(prev, "{", 1) + || chunk_is_str(prev, "[", 1) + || prev->flags.test(PCF_IN_PREPROC)) { - pc = chunk_get_next_type(pc, CT_RETURN, -1); - continue; + break; + } + chunk_flags_set(prev, PCF_LVALUE); + + if (prev->level == pc->level && chunk_is_str(prev, "&", 1)) + { + make_type(prev); } - pc = process_return(pc); } } -static chunk_t *process_return(chunk_t *pc) +static void mark_function_return_type(chunk_t *fname, chunk_t *start, c_token_t parent_type) { LOG_FUNC_ENTRY(); - chunk_t *next; - chunk_t *temp; - chunk_t *semi; - chunk_t *cpar; - chunk_t chunk; - - // grab next and bail if it is a semicolon - next = chunk_ppa_get_next_ncnl(pc); - - if ( next == nullptr || 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()); - } + chunk_t *pc = start; - if (chunk_is_token(next, CT_PAREN_OPEN)) + if (pc != nullptr) { - // See if the return is fully paren'd - cpar = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level); - - if (cpar == nullptr) - { - return(nullptr); - } - semi = chunk_ppa_get_next_ncnl(cpar); + // Step backwards from pc and mark the parent of the return type + LOG_FMT(LFCNR, "%s(%d): (backwards) return type for '%s' @ orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, fname->text(), fname->orig_line, fname->orig_col); - if (semi == nullptr) - { - return(nullptr); - } + chunk_t *first = pc; - if (chunk_is_token(semi, CT_NEWLINE) || chunk_is_semicolon(semi)) + while (pc != nullptr) { - log_rule_B("mod_paren_on_return"); + LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', type is %s, ", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type)); + log_pcf_flags(LFCNR, pc->flags); - if (options::mod_paren_on_return() == IARF_REMOVE) + if (chunk_is_token(pc, CT_ANGLE_CLOSE)) { - LOG_FMT(LRETURN, "%s(%d): removing parens on orig_line %zu\n", - __func__, __LINE__, pc->orig_line); + pc = skip_template_prev(pc); - // lower the level of everything - for (temp = next; temp != cpar; temp = chunk_get_next(temp)) + if (pc == nullptr || chunk_is_token(pc, CT_TEMPLATE)) { - 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--; + //either expression is not complete or this is smth like 'template<T> void func()' + // - we are not interested in 'template<T>' part + break; } - - // delete the parenthesis - chunk_del(next); - chunk_del(cpar); - - // back up following chunks - temp = semi; - - while (temp != nullptr && temp->type != CT_NEWLINE) + else { - temp->column = temp->column - 2; - temp->orig_col = temp->orig_col - 2; - temp->orig_col_end = temp->orig_col_end - 2; - temp = chunk_get_next(temp); + //this is smth like 'vector<int> func()' and 'pc' is currently on 'vector' - just proceed } } - else + + if ( ( !chunk_is_type(pc) + && pc->type != CT_OPERATOR + && pc->type != CT_WORD + && pc->type != CT_ADDR) + || pc->flags.test(PCF_IN_PREPROC)) { - LOG_FMT(LRETURN, "%s(%d): keeping parens on orig_line %zu\n", - __func__, __LINE__, pc->orig_line); + break; + } - // mark & keep them - set_chunk_parent(next, CT_RETURN); - set_chunk_parent(cpar, CT_RETURN); + if (!chunk_is_ptr_operator(pc)) + { + first = pc; } - return(semi); + pc = chunk_get_prev_ncnlni(pc); // Issue #2279 } - } - // We don't have a fully paren'd return. Should we add some? - log_rule_B("mod_paren_on_return"); + LOG_FMT(LFCNR, "%s(%d): marking returns...", __func__, __LINE__); - if (!(options::mod_paren_on_return() & IARF_ADD)) - { - return(next); - } + // Changing words to types into tuple return types in CS. + bool is_return_tuple = false; - // 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 (chunk_is_token(pc, CT_PAREN_CLOSE) && !pc->flags.test(PCF_IN_PREPROC)) + { + first = chunk_skip_to_match_rev(pc); + is_return_tuple = true; + } + pc = first; - if (pc->flags.test(PCF_IN_PREPROC)) - { - while ((semi = semi->next) != nullptr) + while (pc != nullptr) { - if (!semi->flags.test(PCF_IN_PREPROC)) + LOG_FMT(LFCNR, " text() '%s', type is %s", pc->text(), get_token_name(pc->type)); + + if (parent_type != CT_NONE) { - break; + set_chunk_parent(pc, parent_type); } + chunk_t *prev = chunk_get_prev_ncnlni(pc); // Issue #2279 - if (semi->level < pc->level) + if ( !is_return_tuple + || pc->type != CT_WORD + || (prev != nullptr && prev->type != CT_TYPE)) { - return(semi); + make_type(pc); } - if (chunk_is_semicolon(semi) && pc->level == semi->level) + if (pc == start) { break; } - } - } - else - { - while ((semi = chunk_get_next(semi)) != nullptr) - { - if (semi->level < pc->level) + pc = chunk_get_next_ncnl(pc); + + //template angles should keep parent type CT_TEMPLATE + if (chunk_is_token(pc, CT_ANGLE_OPEN)) { - return(semi); + pc = chunk_get_next_type(pc, CT_ANGLE_CLOSE, pc->level); + + if (pc == start) + { + break; + } + pc = chunk_get_next_ncnl(pc); } + } + LOG_FMT(LFCNR, "\n"); - if (chunk_is_semicolon(semi) && pc->level == semi->level) + // Back up and mark parent type on friend declarations + if (parent_type != CT_NONE && first && first->flags.test(PCF_IN_CLASS)) + { + pc = chunk_get_prev_ncnlni(first); // Issue #2279 + + if (chunk_is_token(pc, CT_FRIEND)) { - break; + LOG_FMT(LFCNR, "%s(%d): marking friend\n", __func__, __LINE__); + set_chunk_parent(pc, parent_type); + // A friend might be preceded by a template specification, as in: + // template <...> friend type func(...); + // If so, we need to mark that also + pc = chunk_get_prev_ncnlni(pc); // Issue #2279 + + if (chunk_is_token(pc, CT_ANGLE_CLOSE)) + { + pc = skip_template_prev(pc); + + if (chunk_is_token(pc, CT_TEMPLATE)) + { + LOG_FMT(LFCNR, "%s(%d): marking friend template\n", + __func__, __LINE__); + set_chunk_parent(pc, parent_type); + } + } } } } +} // mark_function_return_type - 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.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); +static bool mark_function_type(chunk_t *pc) +{ + LOG_FUNC_ENTRY(); + LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s' @ orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, get_token_name(pc->type), pc->text(), + pc->orig_line, pc->orig_col); - LOG_FMT(LRETURN, "%s(%d): added parens on orig_line %zu\n", - __func__, __LINE__, pc->orig_line); + size_t star_count = 0; + size_t word_count = 0; + chunk_t *ptrcnk = nullptr; + chunk_t *tmp; + chunk_t *apo; + chunk_t *apc; + chunk_t *aft; + bool anon = false; + c_token_t pt, ptp; - for (temp = next; temp != cpar; temp = chunk_get_next(temp)) - { - temp->level++; - } - } - return(semi); -} // process_return + // Scan backwards across the name, which can only be a word and single star + chunk_t *varcnk = chunk_get_prev_ncnlni(pc); // Issue #2279 + varcnk = chunk_get_prev_ssq(varcnk); -static bool is_ucase_str(const char *str, size_t len) -{ - while (len-- > 0) + if (varcnk != nullptr && !chunk_is_word(varcnk)) { - if (unc_toupper(*str) != *str) + if ( language_is_set(LANG_OC) + && chunk_is_str(varcnk, "^", 1) + && chunk_is_paren_open(chunk_get_prev_ncnlni(varcnk))) // Issue #2279 { - return(false); + // anonymous ObjC block type -- RTYPE (^)(ARGS) + anon = true; + } + else + { + LOG_FMT(LFTYPE, "%s(%d): not a word: text() '%s', type is %s, @ orig_line is %zu:, orig_col is %zu\n", + __func__, __LINE__, varcnk->text(), get_token_name(varcnk->type), + varcnk->orig_line, varcnk->orig_col); + goto nogo_exit; } - str++; } - return(true); -} + apo = chunk_get_next_ncnl(pc); + if (apo == nullptr) + { + return(false); + } + apc = chunk_skip_to_match(apo); -static bool is_oc_block(chunk_t *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))); -} + if ( apc != nullptr + && ( !chunk_is_paren_open(apo) + || ((apc = chunk_skip_to_match(apo)) == nullptr))) + { + LOG_FMT(LFTYPE, "%s(%d): not followed by parens\n", __func__, __LINE__); + goto nogo_exit; + } + aft = chunk_get_next_ncnl(apc); + if (chunk_is_token(aft, CT_BRACE_OPEN)) + { + pt = CT_FUNC_DEF; + } + else if (chunk_is_token(aft, CT_SEMICOLON) || chunk_is_token(aft, CT_ASSIGN)) + { + pt = CT_FUNC_PROTO; + } + else + { + LOG_FMT(LFTYPE, "%s(%d): not followed by '{' or ';'\n", __func__, __LINE__); + goto nogo_exit; + } + ptp = pc->flags.test(PCF_IN_TYPEDEF) ? CT_FUNC_TYPE : CT_FUNC_VAR; -static void fix_casts(chunk_t *start) -{ - LOG_FUNC_ENTRY(); - chunk_t *pc; - chunk_t *prev; - chunk_t *first; - chunk_t *after; - chunk_t *last = nullptr; - chunk_t *paren_close; - const char *verb = "likely"; - const char *detail = ""; - size_t count = 0; - int word_count = 0; - bool nope; - bool doubtful_cast = false; + tmp = pc; + while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 + { + tmp = chunk_get_prev_ssq(tmp); - LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n", - __func__, __LINE__, start->text(), start->orig_line, start->orig_col); + LOG_FMT(LFTYPE, " -- type is %s, %s on orig_line %zu, orig_col is %zu", + get_token_name(tmp->type), tmp->text(), + tmp->orig_line, tmp->orig_col); - prev = chunk_get_prev_ncnlni(start); // Issue #2279 + if ( chunk_is_star(tmp) + || chunk_is_token(tmp, CT_PTR_TYPE) + || chunk_is_token(tmp, CT_CARET)) + { + star_count++; + ptrcnk = tmp; + LOG_FMT(LFTYPE, " -- PTR_TYPE\n"); + } + else if ( chunk_is_word(tmp) + || chunk_is_token(tmp, CT_WORD) + || chunk_is_token(tmp, CT_TYPE)) + { + word_count++; + LOG_FMT(LFTYPE, " -- TYPE(%s)\n", tmp->text()); + } + else if (chunk_is_token(tmp, CT_DC_MEMBER)) + { + word_count = 0; + LOG_FMT(LFTYPE, " -- :: reset word_count\n"); + } + else if (chunk_is_str(tmp, "(", 1)) + { + LOG_FMT(LFTYPE, " -- open paren (break)\n"); + break; + } + else + { + LOG_FMT(LFTYPE, " -- unexpected token: type is %s, text() '%s', on orig_line %zu, orig_col %zu\n", + get_token_name(tmp->type), tmp->text(), + tmp->orig_line, tmp->orig_col); + goto nogo_exit; + } + } - if (prev == nullptr) + // Fixes #issue 1577 + // Allow word count 2 incase of function pointer declaration. + // Ex: bool (__stdcall* funcptr)(int, int); + if ( star_count > 1 + || (word_count > 1 && !(word_count == 2 && ptp == CT_FUNC_VAR)) + || ((star_count + word_count) == 0)) { - return; + LOG_FMT(LFTYPE, "%s(%d): bad counts word: %zu, star: %zu\n", + __func__, __LINE__, word_count, star_count); + goto nogo_exit; } - if (chunk_is_token(prev, CT_PP_DEFINED)) + // make sure what appears before the first open paren can be a return type + if (!chunk_ends_type(chunk_get_prev_ncnlni(tmp))) // Issue #2279 { - LOG_FMT(LCASTS, "%s(%d): -- not a cast - after defined\n", - __func__, __LINE__); - return; + goto nogo_exit; } - if (chunk_is_token(prev, CT_ANGLE_CLOSE)) + if (ptrcnk) { - LOG_FMT(LCASTS, "%s(%d): -- not a cast - after > (template)\n", - __func__, __LINE__); - return; + set_chunk_type(ptrcnk, CT_PTR_TYPE); } - // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren - pc = chunk_get_next_ncnl(start); - first = pc; - while ( pc != nullptr - && ( chunk_is_type(pc) - || chunk_is_token(pc, CT_WORD) - || chunk_is_token(pc, CT_QUALIFIER) - || chunk_is_token(pc, CT_DC_MEMBER) - || chunk_is_token(pc, CT_PP) - || chunk_is_token(pc, CT_STAR) - || chunk_is_token(pc, CT_QUESTION) - || chunk_is_token(pc, CT_CARET) - || chunk_is_token(pc, CT_TSQUARE) - || ( ( chunk_is_token(pc, CT_ANGLE_OPEN) - || chunk_is_token(pc, CT_ANGLE_CLOSE)) - && language_is_set(LANG_OC | LANG_JAVA)) - || ( ( chunk_is_token(pc, CT_QUESTION) - || chunk_is_token(pc, CT_COMMA) - || chunk_is_token(pc, CT_MEMBER)) - && language_is_set(LANG_JAVA)) - || chunk_is_token(pc, CT_AMP))) + if (!anon) { - LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", - __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); - - if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER))) + if (pc->flags.test(PCF_IN_TYPEDEF)) { - word_count++; + set_chunk_type(varcnk, CT_TYPE); } - else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP)) + else { - // might be negativ, such as with: - // a = val + (CFoo::bar_t)7; - word_count--; + set_chunk_type(varcnk, CT_FUNC_VAR); + chunk_flags_set(varcnk, PCF_VAR_1ST_DEF); } - last = pc; - pc = chunk_get_next_ncnl(pc); - count++; } + set_chunk_type(pc, CT_TPAREN_CLOSE); + set_chunk_parent(pc, ptp); - if ( pc == nullptr - || pc->type != CT_PAREN_CLOSE - || chunk_is_token(prev, CT_OC_CLASS)) + set_chunk_type(apo, CT_FPAREN_OPEN); + set_chunk_parent(apo, pt); + set_chunk_type(apc, CT_FPAREN_CLOSE); + set_chunk_parent(apc, pt); + fix_fcn_def_params(apo); + + if (chunk_is_semicolon(aft)) { - LOG_FMT(LCASTS, "%s(%d): -- not a cast, hit type is %s\n", - __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type)); - return; + set_chunk_parent(aft, aft->flags.test(PCF_IN_TYPEDEF) ? CT_TYPEDEF : CT_FUNC_VAR); + } + else if (chunk_is_token(aft, CT_BRACE_OPEN)) + { + flag_parens(aft, PCF_NONE, CT_NONE, pt, false); + } + // Step backwards to the previous open paren and mark everything a + tmp = pc; + + while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 + { + LOG_FMT(LFTYPE, " ++ type is %s, text() '%s', on orig_line %zu, orig_col %zu\n", + get_token_name(tmp->type), tmp->text(), + tmp->orig_line, tmp->orig_col); + + if (*tmp->str.c_str() == '(') + { + if (!pc->flags.test(PCF_IN_TYPEDEF)) + { + chunk_flags_set(tmp, PCF_VAR_1ST_DEF); + } + set_chunk_type(tmp, CT_TPAREN_OPEN); + set_chunk_parent(tmp, ptp); + + tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279 + + if ( chunk_is_token(tmp, CT_FUNCTION) + || chunk_is_token(tmp, CT_FUNC_CALL) + || chunk_is_token(tmp, CT_FUNC_CALL_USER) + || chunk_is_token(tmp, CT_FUNC_DEF) + || chunk_is_token(tmp, CT_FUNC_PROTO)) + { + set_chunk_type(tmp, CT_TYPE); + chunk_flags_clr(tmp, PCF_VAR_1ST_DEF); + } + mark_function_return_type(varcnk, tmp, ptp); + break; + } } + return(true); - if (word_count > 1) +nogo_exit: + tmp = chunk_get_next_ncnl(pc); + + if (chunk_is_paren_open(tmp)) { - LOG_FMT(LCASTS, "%s(%d): -- too many words: %d\n", - __func__, __LINE__, word_count); - return; + LOG_FMT(LFTYPE, "%s(%d): setting FUNC_CALL on orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, tmp->orig_line, tmp->orig_col); + flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false); } - paren_close = pc; + return(false); +} // mark_function_type - // If last is a type or star/caret, we have a cast for sure - if ( chunk_is_token(last, CT_STAR) - || chunk_is_token(last, CT_CARET) - || chunk_is_token(last, CT_PTR_TYPE) - || chunk_is_token(last, CT_TYPE) - || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA))) + +static void process_returns(void) +{ + LOG_FUNC_ENTRY(); + chunk_t *pc; + + pc = chunk_get_head(); + + while (pc != nullptr) { - verb = "for sure"; + if (pc->type != CT_RETURN) + { + pc = chunk_get_next_type(pc, CT_RETURN, -1); + continue; + } + pc = process_return(pc); } - else if (count == 1) +} + + +static chunk_t *process_return(chunk_t *pc) +{ + LOG_FUNC_ENTRY(); + chunk_t *next; + chunk_t *temp; + chunk_t *semi; + chunk_t *cpar; + chunk_t chunk; + + // grab next and bail if it is a semicolon + next = chunk_ppa_get_next_ncnl(pc); + + if ( next == nullptr || chunk_is_semicolon(next) + || chunk_is_token(next, CT_NEWLINE)) { - /* - * We are on a potential cast of the form "(word)". - * We don't know if the word is a type. So lets guess based on some - * simple rules: - * - if all caps, likely a type - * - if it ends in _t, likely a type - * - if it's objective-c and the type is id, likely valid - */ - verb = "guessed"; + return(next); + } + log_rule_B("nl_return_expr"); - if ( (last->len() > 3) - && (last->str[last->len() - 2] == '_') - && (last->str[last->len() - 1] == 't')) + 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 = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level); + + if (cpar == nullptr) + { + return(nullptr); + } + semi = chunk_ppa_get_next_ncnl(cpar); + + if (semi == nullptr) + { + return(nullptr); + } + + 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 = chunk_get_next(temp)) + { + 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 != nullptr && temp->type != CT_NEWLINE) + { + temp->column = temp->column - 2; + temp->orig_col = temp->orig_col - 2; + temp->orig_col_end = temp->orig_col_end - 2; + temp = chunk_get_next(temp); + } + } + 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->next) != nullptr) + { + 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 = chunk_get_next(semi)) != nullptr) + { + 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.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 = chunk_get_next(temp)) + { + temp->level++; + } + } + return(semi); +} // process_return + + +static bool is_ucase_str(const char *str, size_t len) +{ + while (len-- > 0) + { + if (unc_toupper(*str) != *str) + { + return(false); + } + str++; + } + return(true); +} + + +static bool is_oc_block(chunk_t *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))); +} + + +static void fix_casts(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + chunk_t *pc; + chunk_t *prev; + chunk_t *first; + chunk_t *after; + chunk_t *last = nullptr; + chunk_t *paren_close; + const char *verb = "likely"; + const char *detail = ""; + size_t count = 0; + int word_count = 0; + bool nope; + bool doubtful_cast = false; + + + LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, start->text(), start->orig_line, start->orig_col); + + prev = chunk_get_prev_ncnlni(start); // Issue #2279 + + if (prev == nullptr) + { + return; + } + + if (chunk_is_token(prev, CT_PP_DEFINED)) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast - after defined\n", + __func__, __LINE__); + return; + } + + if (chunk_is_token(prev, CT_ANGLE_CLOSE)) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast - after > (template)\n", + __func__, __LINE__); + return; + } + // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren + pc = chunk_get_next_ncnl(start); + first = pc; + + while ( pc != nullptr + && ( chunk_is_type(pc) + || chunk_is_token(pc, CT_WORD) + || chunk_is_token(pc, CT_QUALIFIER) + || chunk_is_token(pc, CT_DC_MEMBER) + || chunk_is_token(pc, CT_PP) + || chunk_is_token(pc, CT_STAR) + || chunk_is_token(pc, CT_QUESTION) + || chunk_is_token(pc, CT_CARET) + || chunk_is_token(pc, CT_TSQUARE) + || ( ( chunk_is_token(pc, CT_ANGLE_OPEN) + || chunk_is_token(pc, CT_ANGLE_CLOSE)) + && language_is_set(LANG_OC | LANG_JAVA)) + || ( ( chunk_is_token(pc, CT_QUESTION) + || chunk_is_token(pc, CT_COMMA) + || chunk_is_token(pc, CT_MEMBER)) + && language_is_set(LANG_JAVA)) + || chunk_is_token(pc, CT_AMP))) + { + LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); + + if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER))) + { + word_count++; + } + else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP)) + { + // might be negativ, such as with: + // a = val + (CFoo::bar_t)7; + word_count--; + } + last = pc; + pc = chunk_get_next_ncnl(pc); + count++; + } + + if ( pc == nullptr + || pc->type != CT_PAREN_CLOSE + || chunk_is_token(prev, CT_OC_CLASS)) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast, hit type is %s\n", + __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type)); + return; + } + + if (word_count > 1) + { + LOG_FMT(LCASTS, "%s(%d): -- too many words: %d\n", + __func__, __LINE__, word_count); + return; + } + paren_close = pc; + + // If last is a type or star/caret, we have a cast for sure + if ( chunk_is_token(last, CT_STAR) + || chunk_is_token(last, CT_CARET) + || chunk_is_token(last, CT_PTR_TYPE) + || chunk_is_token(last, CT_TYPE) + || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA))) + { + verb = "for sure"; + } + else if (count == 1) + { + /* + * We are on a potential cast of the form "(word)". + * We don't know if the word is a type. So lets guess based on some + * simple rules: + * - if all caps, likely a type + * - if it ends in _t, likely a type + * - if it's objective-c and the type is id, likely valid + */ + verb = "guessed"; + + if ( (last->len() > 3) + && (last->str[last->len() - 2] == '_') + && (last->str[last->len() - 1] == 't')) + { + detail = " -- '_t'"; + } + else if (is_ucase_str(last->text(), last->len())) + { + detail = " -- upper case"; + } + else if (language_is_set(LANG_OC) && chunk_is_str(last, "id", 2)) + { + detail = " -- Objective-C id"; + } + else + { + // If we can't tell for sure whether this is a cast, decide against it + detail = " -- mixed case"; + doubtful_cast = true; + } + /* + * If the next item is a * or &, the next item after that can't be a + * number or string. + * + * If the next item is a +, the next item has to be a number. + * + * If the next item is a -, the next item can't be a string. + * + * For this to be a cast, the close paren must be followed by: + * - constant (number or string) + * - paren open + * - word + * + * Find the next non-open paren item. + */ + pc = chunk_get_next_ncnl(paren_close); + after = pc; + + do + { + after = chunk_get_next_ncnl(after); + } while (chunk_is_token(after, CT_PAREN_OPEN)); + + if (after == nullptr) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast - hit NULL\n", + __func__, __LINE__); + return; + } + nope = false; + + if (chunk_is_ptr_operator(pc)) + { + // star (*) and address (&) are ambiguous + if ( chunk_is_token(after, CT_NUMBER_FP) + || chunk_is_token(after, CT_NUMBER) + || chunk_is_token(after, CT_STRING) + || doubtful_cast) + { + nope = true; + } + } + else if (chunk_is_token(pc, CT_MINUS)) + { + // (UINT8)-1 or (foo)-1 or (FOO)-'a' + if (chunk_is_token(after, CT_STRING) || doubtful_cast) + { + nope = true; + } + } + else if (chunk_is_token(pc, CT_PLUS)) + { + // (UINT8)+1 or (foo)+1 + if ( (after->type != CT_NUMBER && after->type != CT_NUMBER_FP) + || doubtful_cast) + { + nope = true; + } + } + else if ( pc->type != CT_NUMBER_FP + && pc->type != CT_NUMBER + && pc->type != CT_WORD + && pc->type != CT_THIS + && pc->type != CT_TYPE + && pc->type != CT_PAREN_OPEN + && pc->type != CT_STRING + && pc->type != CT_DECLTYPE + && pc->type != CT_SIZEOF + && get_chunk_parent_type(pc) != CT_SIZEOF + && pc->type != CT_FUNC_CALL + && pc->type != CT_FUNC_CALL_USER + && pc->type != CT_FUNCTION + && pc->type != CT_BRACE_OPEN + && (!( chunk_is_token(pc, CT_SQUARE_OPEN) + && language_is_set(LANG_OC)))) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by text() '%s', type is %s\n", + __func__, __LINE__, pc->text(), get_token_name(pc->type)); + return; + } + + if (nope) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast - text() '%s' followed by type %s\n", + __func__, __LINE__, pc->text(), get_token_name(after->type)); + return; + } + } + // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't + pc = chunk_get_next_ncnl(paren_close); + + if (pc == nullptr) + { + return; + } + + if ( chunk_is_semicolon(pc) + || chunk_is_token(pc, CT_COMMA) + || chunk_is_token(pc, CT_BOOL) // Issue #2151 + || chunk_is_paren_close(pc)) + { + LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by type %s\n", + __func__, __LINE__, get_token_name(pc->type)); + return; + } + set_chunk_parent(start, CT_C_CAST); + set_chunk_parent(paren_close, CT_C_CAST); + + LOG_FMT(LCASTS, "%s(%d): -- %s c-cast: (", + __func__, __LINE__, verb); + + for (pc = first; + pc != nullptr && pc != paren_close; + pc = chunk_get_next_ncnl(pc)) + { + set_chunk_parent(pc, CT_C_CAST); + make_type(pc); + LOG_FMT(LCASTS, " %s", pc->text()); + } + + LOG_FMT(LCASTS, " )%s\n", detail); + + // Mark the next item as an expression start + pc = chunk_get_next_ncnl(paren_close); + + if (pc != nullptr) + { + chunk_flags_set(pc, PCF_EXPR_START); + + if (chunk_is_opening_brace(pc)) + { + set_paren_parent(pc, get_chunk_parent_type(start)); + } + } +} // fix_casts + + +static void fix_type_cast(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + chunk_t *pc; + + pc = chunk_get_next_ncnl(start); + + if (pc == nullptr || pc->type != CT_ANGLE_OPEN) + { + return; + } + + while ( ((pc = chunk_get_next_ncnl(pc)) != nullptr) + && pc->level >= start->level) + { + if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE)) + { + pc = chunk_get_next_ncnl(pc); + + if (pc == nullptr) + { + return; + } + + if (chunk_is_str(pc, "(", 1)) + { + set_paren_parent(pc, CT_TYPE_CAST); + } + return; + } + make_type(pc); + } +} + + +static void fix_enum_struct_union(chunk_t *pc) +{ + LOG_FUNC_ENTRY(); + chunk_t *next; + chunk_t *prev = nullptr; + pcf_flags_t flags = PCF_VAR_1ST_DEF; + auto const in_fcn_paren = pc->flags & PCF_IN_FCN_DEF; + + // Make sure this wasn't a cast + if (get_chunk_parent_type(pc) == CT_C_CAST) + { + return; + } + // the next item is either a type or open brace + next = chunk_get_next_ncnl(pc); + + // the enum-key might be enum, enum class or enum struct (TODO) + if (chunk_is_token(next, CT_ENUM_CLASS)) + { + next = chunk_get_next_ncnl(next); // get the next one + } + + if (language_is_set(LANG_CPP)) + { + next = skip_attribute_next(next); // get the next one + } + + // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace + if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON)) + { + // i.e. "enum xyz : unsigned int { ... };" + // i.e. "enum class xyz : unsigned int { ... };" + // i.e. "enum : unsigned int { ... };" + // xyz is a type + + // save the type if it exists + if (!chunk_is_token(next, CT_COLON)) + { + set_chunk_parent(next, pc->type); + prev = next; + next = chunk_get_next_ncnl(next); + } + + if (next == nullptr) + { + return; + } + set_chunk_parent(next, pc->type); + auto const is_struct_or_class = + (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS)); + + // next up is either a colon, open brace, or open parenthesis (pawn) + if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN)) + { + next = set_paren_parent(next, CT_ENUM); + } + else if (chunk_is_token(next, CT_COLON)) + { + if (chunk_is_token(pc, CT_ENUM)) + { + // enum TYPE : INT_TYPE { ... }; + next = chunk_get_next_ncnl(next); + + if (next != nullptr) + { + make_type(next); + next = chunk_get_next_ncnl(next); + + // enum TYPE : unsigned int { ... }; + if (chunk_is_token(next, CT_TYPE)) + { + // get the next part of the type + next = chunk_get_next_ncnl(next); + } + } + } + else if (is_struct_or_class) + { + next = skip_parent_types(next); + } + } + else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN)) + { + // Fix #1267 structure attributes + // struct __attribute__(align(x)) struct_name; + // skip to matching parenclose and make next token as type. + next = chunk_skip_to_match(next); + next = chunk_get_next_ncnl(next); + set_chunk_type(next, CT_TYPE); + set_chunk_parent(next, pc->type); + } + + if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration + { + set_chunk_parent(next, pc->type); + flag_series(pc, prev, PCF_INCOMPLETE); + return; + } + } + + if (chunk_is_token(next, CT_BRACE_OPEN)) + { + auto const flag = [pc] { + switch (pc->type) + { + case CT_ENUM: + return(PCF_IN_ENUM); + + case CT_STRUCT: + return(PCF_IN_STRUCT); + + case CT_CLASS: + return(PCF_IN_CLASS); + + default: + return(PCF_NONE); + } + }(); + + flag_parens(next, flag, CT_NONE, CT_NONE, false); + + if ( chunk_is_token(pc, CT_UNION) + || chunk_is_token(pc, CT_STRUCT) + || chunk_is_token(pc, CT_CLASS)) + { + mark_struct_union_body(next); + } + // Skip to the closing brace + set_chunk_parent(next, pc->type); + next = chunk_get_next_type(next, CT_BRACE_CLOSE, pc->level); + flags |= PCF_VAR_INLINE; + + if (next != nullptr) + { + set_chunk_parent(next, pc->type); + next = chunk_get_next_ncnl(next); + } + prev = nullptr; + } + // reset var name parent type + else if (next && prev) + { + set_chunk_parent(prev, CT_NONE); + } + + if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE)) + { + return; + } + + if (!chunk_is_semicolon(next)) + { + // Pawn does not require a semicolon after an enum + if (language_is_set(LANG_PAWN)) + { + return; + } + + /* + * D does not require a semicolon after an enum, but we add one to make + * other code happy. + */ + if (language_is_set(LANG_D)) + { + next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next)); // Issue #2279 + } + } + + // We are either pointing to a ';' or a variable + while ( next != nullptr + && !chunk_is_semicolon(next) + && next->type != CT_ASSIGN + && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any()) + { + if (next->level == pc->level) + { + if (chunk_is_token(next, CT_WORD)) + { + chunk_flags_set(next, flags); + flags &= ~PCF_VAR_1ST; // clear the first flag for the next items + LOG_FMT(LCASTS, "%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()); + } + + if ( chunk_is_token(next, CT_STAR) + || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET))) + { + set_chunk_type(next, CT_PTR_TYPE); + } + + // If we hit a comma in a function param, we are done + if ( (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE)) + && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL))) + { + return; + } + } + next = chunk_get_next_ncnl(next); + } + + if ( next != nullptr + && chunk_is_token(next, CT_SEMICOLON)) + { + set_chunk_parent(next, pc->type); + } +} // fix_enum_struct_union + + +static void fix_typedef(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + + if (start == nullptr) + { + return; + } + LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n", + __func__, __LINE__, start->orig_line, start->orig_col); + + chunk_t *the_type = nullptr; + chunk_t *last_op = nullptr; + + /* + * Mark everything in the typedef and scan for ")(", which makes it a + * function type + */ + for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC) + ; next != nullptr && next->level >= start->level + ; next = chunk_get_next_ncnl(next, scope_e::PREPROC)) + { + chunk_flags_set(next, PCF_IN_TYPEDEF); + + if (start->level == next->level) + { + if (chunk_is_semicolon(next)) + { + set_chunk_parent(next, CT_TYPEDEF); + break; + } + + if (chunk_is_token(next, CT_ATTRIBUTE)) + { + break; + } + + if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN)) + { + set_chunk_parent(next, CT_TYPEDEF); + break; + } + make_type(next); + + if (chunk_is_token(next, CT_TYPE)) + { + the_type = next; + } + chunk_flags_clr(next, PCF_VAR_1ST_DEF); + + if (*next->str.c_str() == '(') + { + last_op = next; + } + } + } + + // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def + if ( last_op != nullptr + && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM)) + { + flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false); + fix_fcn_def_params(last_op); + + the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC); // Issue #2279 + + if (the_type == nullptr) + { + return; + } + chunk_t *open_paren = nullptr; + + if (chunk_is_paren_close(the_type)) + { + open_paren = chunk_skip_to_match_rev(the_type); + mark_function_type(the_type); + the_type = chunk_get_prev_ncnlni(the_type, scope_e::PREPROC); // Issue #2279 + + if (the_type == nullptr) + { + return; + } + } + else + { + // must be: "typedef <return type>func(params);" + set_chunk_type(the_type, CT_FUNC_TYPE); + } + set_chunk_parent(the_type, CT_TYPEDEF); + + LOG_FMT(LTYPEDEF, "%s(%d): fcn typedef text() '%s', on orig_line %zu\n", + __func__, __LINE__, the_type->text(), the_type->orig_line); + + // If we are aligning on the open parenthesis, grab that instead + log_rule_B("align_typedef_func"); + + if (open_paren != nullptr && options::align_typedef_func() == 1) + { + the_type = open_paren; + } + log_rule_B("align_typedef_func"); + + if (options::align_typedef_func() != 0) + { + LOG_FMT(LTYPEDEF, "%s(%d): -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n", + __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col); + chunk_flags_set(the_type, PCF_ANCHOR); + } + // already did everything we need to do + return; + } + /* + * Skip over enum/struct/union stuff, as we know it isn't a return type + * for a function type + */ + chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC); + + if (after == nullptr) + { + return; + } + + if ( after->type != CT_ENUM + && after->type != CT_STRUCT + && after->type != CT_UNION) + { + if (the_type != nullptr) + { + // We have just a regular typedef + LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n", + __func__, __LINE__, the_type->text(), the_type->orig_line); + chunk_flags_set(the_type, PCF_ANCHOR); + } + return; + } + // We have a struct/union/enum, next should be either a type or { + chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC); + + if (next == nullptr) + { + return; + } + + if (chunk_is_token(next, CT_TYPE)) + { + next = chunk_get_next_ncnl(next, scope_e::PREPROC); + + if (next == nullptr) + { + return; + } + } + + if (chunk_is_token(next, CT_BRACE_OPEN)) + { + // Skip to the closing brace + chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC); + + if (br_c != nullptr) + { + const c_token_t tag = after->type; + set_chunk_parent(next, tag); + set_chunk_parent(br_c, tag); + + if (tag == CT_ENUM) + { + flag_series(after, br_c, PCF_IN_ENUM); + } + else if (tag == CT_STRUCT) + { + flag_series(after, br_c, PCF_IN_STRUCT); + } + } + } + + if (the_type != nullptr) + { + LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n", + __func__, __LINE__, get_token_name(after->type), the_type->text(), + the_type->orig_line); + chunk_flags_set(the_type, PCF_ANCHOR); + } +} // fix_typedef + + +static void mark_variable_stack(ChunkStack &cs, log_sev_t sev) +{ + UNUSED(sev); + LOG_FUNC_ENTRY(); + + // throw out the last word and mark the rest + chunk_t *var_name = cs.Pop_Back(); + + if (var_name && var_name->prev->type == CT_DC_MEMBER) + { + cs.Push_Back(var_name); + } + + if (var_name != nullptr) + { + LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n", + __func__, __LINE__, var_name->orig_line, var_name->orig_col); + + size_t word_cnt = 0; + chunk_t *word_type; + + while ((word_type = cs.Pop_Back()) != nullptr) + { + if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE)) + { + LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", + __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text()); + set_chunk_type(word_type, CT_TYPE); + chunk_flags_set(word_type, PCF_VAR_TYPE); + } + word_cnt++; + } + + if (chunk_is_token(var_name, CT_WORD)) + { + if (word_cnt > 0) + { + LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n", + __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); + chunk_flags_set(var_name, PCF_VAR_DEF); + } + else + { + LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", + __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); + set_chunk_type(var_name, CT_TYPE); + chunk_flags_set(var_name, PCF_VAR_TYPE); + } + } + } +} // mark_variable_stack + + +static void fix_fcn_def_params(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + + if (start == nullptr) + { + return; + } + LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n", + __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level); + + while (start != nullptr && !chunk_is_paren_open(start)) + { + start = chunk_get_next_ncnl(start); + } + + if (start == nullptr)// Coverity CID 76003, 1100782 + { + return; + } + // ensure start chunk holds a single '(' character + assert((start->len() == 1) && (start->str[0] == '(')); + + ChunkStack cs; + size_t level = start->level + 1; + chunk_t *pc = start; + + while ((pc = chunk_get_next_ncnl(pc)) != nullptr) + { + if ( ((start->len() == 1) && (start->str[0] == ')')) + || pc->level < level) + { + LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line); + break; + } + LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n", + __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at", + pc->text(), pc->orig_line, pc->level); + + if (pc->level > level) + { + continue; + } + + if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc)) + { + set_chunk_type(pc, CT_PTR_TYPE); + cs.Push_Back(pc); + } + else if ( chunk_is_token(pc, CT_AMP) + || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2))) + { + set_chunk_type(pc, CT_BYREF); + cs.Push_Back(pc); + } + else if (chunk_is_token(pc, CT_TYPE_WRAP)) + { + cs.Push_Back(pc); + } + else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)) + { + cs.Push_Back(pc); + } + else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN)) + { + mark_variable_stack(cs, LFCNP); + + if (chunk_is_token(pc, CT_ASSIGN)) + { + // Mark assignment for default param spacing + set_chunk_parent(pc, CT_FUNC_PROTO); + } + } + } + mark_variable_stack(cs, LFCNP); +} // fix_fcn_def_params + + +static chunk_t *skip_to_next_statement(chunk_t *pc) +{ + while ( pc != nullptr + && !chunk_is_semicolon(pc) + && pc->type != CT_BRACE_OPEN + && pc->type != CT_BRACE_CLOSE) + { + pc = chunk_get_next_ncnl(pc); + } + return(pc); +} + + +static chunk_t *fix_variable_definition(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + chunk_t *pc = start; + chunk_t *end; + chunk_t *tmp_pc; + ChunkStack cs; + int idx; + int ref_idx; + + LOG_FMT(LFVD, "%s(%d): start at pc->orig_line is %zu, pc->orig_col is %zu\n", + __func__, __LINE__, pc->orig_line, pc->orig_col); + + // Scan for words and types and stars oh my! + while ( chunk_is_token(pc, CT_TYPE) + || chunk_is_token(pc, CT_WORD) + || chunk_is_token(pc, CT_QUALIFIER) + || chunk_is_token(pc, CT_TYPENAME) + || chunk_is_token(pc, CT_DC_MEMBER) + || chunk_is_token(pc, CT_MEMBER) + || chunk_is_ptr_operator(pc)) + { + LOG_FMT(LFVD, "%s(%d): 1:pc->text() '%s', type is %s\n", + __func__, __LINE__, pc->text(), get_token_name(pc->type)); + cs.Push_Back(pc); + pc = chunk_get_next_ncnl(pc); + + if (pc == nullptr) + { + LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__); + return(nullptr); + } + LOG_FMT(LFVD, "%s(%d): 2:pc->text() '%s', type is %s\n", + __func__, __LINE__, pc->text(), get_token_name(pc->type)); + + // Skip templates and attributes + pc = skip_template_next(pc); + + if (pc == nullptr) + { + LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__); + return(nullptr); + } + LOG_FMT(LFVD, "%s(%d): 3:pc->text() '%s', type is %s\n", + __func__, __LINE__, pc->text(), get_token_name(pc->type)); + + pc = skip_attribute_next(pc); + + if (pc == nullptr) + { + LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__); + return(nullptr); + } + LOG_FMT(LFVD, "%s(%d): 4:pc->text() '%s', type is %s\n", + __func__, __LINE__, pc->text(), get_token_name(pc->type)); + + if (language_is_set(LANG_JAVA)) + { + pc = skip_tsquare_next(pc); + LOG_FMT(LFVD, "%s(%d): 5:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type)); + } + } + end = pc; + + if (end == nullptr) + { + LOG_FMT(LFVD, "%s(%d): end is nullptr\n", __func__, __LINE__); + return(nullptr); + } + LOG_FMT(LFVD, "%s(%d): end->type is %s\n", __func__, __LINE__, get_token_name(end->type)); + + if ( cs.Len() == 1 + && chunk_is_token(end, CT_BRACE_OPEN) + && get_chunk_parent_type(end) == CT_BRACED_INIT_LIST) + { + set_chunk_type(cs.Get(0)->m_pc, CT_TYPE); + } + + // Function defs are handled elsewhere + if ( (cs.Len() <= 1) + || chunk_is_token(end, CT_FUNC_DEF) + || chunk_is_token(end, CT_FUNC_PROTO) + || chunk_is_token(end, CT_FUNC_CLASS_DEF) + || chunk_is_token(end, CT_FUNC_CLASS_PROTO) + || chunk_is_token(end, CT_OPERATOR)) + { + return(skip_to_next_statement(end)); + } + // ref_idx points to the alignable part of the variable definition + ref_idx = cs.Len() - 1; + + // Check for the '::' stuff: "char *Engine::name" + if ( (cs.Len() >= 3) + && ( (cs.Get(cs.Len() - 2)->m_pc->type == CT_MEMBER) + || (cs.Get(cs.Len() - 2)->m_pc->type == CT_DC_MEMBER))) + { + idx = cs.Len() - 2; + + while (idx > 0) + { + tmp_pc = cs.Get(idx)->m_pc; + + if ( tmp_pc->type != CT_DC_MEMBER + && tmp_pc->type != CT_MEMBER) + { + break; + } + + if (idx == 0) + { + fprintf(stderr, "%s(%d): idx is ZERO, cannot be decremented, at line %zu, column %zu\n", + __func__, __LINE__, tmp_pc->orig_line, tmp_pc->orig_col); + log_flush(true); + exit(EX_SOFTWARE); + } + idx--; + tmp_pc = cs.Get(idx)->m_pc; + + if ( tmp_pc->type != CT_WORD + && tmp_pc->type != CT_TYPE) + { + break; + } + make_type(tmp_pc); + idx--; + } + ref_idx = idx + 1; + } + tmp_pc = cs.Get(ref_idx)->m_pc; + LOG_FMT(LFVD, "%s(%d): ref_idx(%d) is '%s'\n", __func__, __LINE__, ref_idx, tmp_pc->text()); + + // No type part found! + if (ref_idx <= 0) + { + return(skip_to_next_statement(end)); + } + LOG_FMT(LFVD2, "%s(%d): orig_line is %zu, TYPE : ", __func__, __LINE__, start->orig_line); + + for (size_t idxForCs = 0; idxForCs < cs.Len() - 1; idxForCs++) + { + tmp_pc = cs.Get(idxForCs)->m_pc; + make_type(tmp_pc); + chunk_flags_set(tmp_pc, PCF_VAR_TYPE); + LOG_FMT(LFVD2, " text() is '%s', type is %s", tmp_pc->text(), get_token_name(tmp_pc->type)); + } + + LOG_FMT(LFVD2, "\n"); + + // OK we have two or more items, mark types up to the end. + LOG_FMT(LFVD, "%s(%d): pc->orig_line is %zu, pc->orig_col is %zu\n", + __func__, __LINE__, pc->orig_line, pc->orig_col); + mark_variable_definition(cs.Get(cs.Len() - 1)->m_pc); + + if (chunk_is_token(end, CT_COMMA)) + { + return(chunk_get_next_ncnl(end)); + } + return(skip_to_next_statement(end)); +} // fix_variable_definition + + +static chunk_t *skip_expression(chunk_t *start) +{ + chunk_t *pc = start; + + while (pc != nullptr && pc->level >= start->level) + { + if ( pc->level == start->level + && (chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA))) + { + return(pc); + } + pc = chunk_get_next_ncnl(pc); + } + return(pc); +} + + +bool go_on(chunk_t *pc, chunk_t *start) +{ + if (pc == nullptr || pc->level != start->level) + { + return(false); + } + + if (pc->flags.test(PCF_IN_FOR)) + { + return((!chunk_is_semicolon(pc)) && (!(chunk_is_token(pc, CT_COLON)))); + } + return(!chunk_is_semicolon(pc)); +} // go_on + + +static chunk_t *mark_variable_definition(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + + if (start == nullptr) + { + return(nullptr); + } + chunk_t *pc = start; + pcf_flags_t flags = PCF_VAR_1ST_DEF; + + LOG_FMT(LVARDEF, "%s(%d): orig_line %zu, orig_col %zu, text() '%s', type is %s\n", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), + get_token_name(pc->type)); + + // Issue #596 + bool bit_field_colon_is_present = false; + + while (go_on(pc, start)) + { + if ( chunk_is_token(pc, CT_WORD) + || chunk_is_token(pc, CT_FUNC_CTOR_VAR)) + { + auto const orig_flags = pc->flags; + + if (!pc->flags.test(PCF_IN_ENUM)) + { + chunk_flags_set(pc, flags); + } + flags &= ~PCF_VAR_1ST; + LOG_FMT(LVARDEF, "%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()); + + LOG_FMT(LVARDEF, + "%s(%d): orig_line is %zu, marked text() '%s'[%s] " + "in orig_col %zu, flags: %s -> %s\n", + __func__, __LINE__, pc->orig_line, pc->text(), + get_token_name(pc->type), pc->orig_col, + pcf_flags_str(orig_flags).c_str(), + pcf_flags_str(pc->flags).c_str()); + } + else if ( !bit_field_colon_is_present // Issue #2689 + && ( chunk_is_star(pc) + || chunk_is_msref(pc))) + { + set_chunk_type(pc, CT_PTR_TYPE); + } + else if (chunk_is_addr(pc)) + { + set_chunk_type(pc, CT_BYREF); + } + else if ( chunk_is_token(pc, CT_SQUARE_OPEN) + || chunk_is_token(pc, CT_ASSIGN)) + { + pc = skip_expression(pc); + continue; + } + else if (chunk_is_token(pc, CT_COLON)) + { + bit_field_colon_is_present = true; // Issue #2689 + } + pc = chunk_get_next_ncnl(pc); + } + return(pc); +} // mark_variable_definition + + +static bool can_be_full_param(chunk_t *start, chunk_t *end) +{ + LOG_FUNC_ENTRY(); + + LOG_FMT(LFPARAM, "%s:", __func__); + + int word_count = 0; + int type_count = 0; + chunk_t *pc; + + for (pc = start; + pc != nullptr && pc != end; + pc = chunk_get_next_ncnl(pc, scope_e::PREPROC)) + { + LOG_FMT(LFPARAM, " [%s]", pc->text()); + + if ( chunk_is_token(pc, CT_QUALIFIER) + || chunk_is_token(pc, CT_STRUCT) + || chunk_is_token(pc, CT_ENUM) + || chunk_is_token(pc, CT_UNION) + || chunk_is_token(pc, CT_TYPENAME)) + { + LOG_FMT(LFPARAM, " <== %s! (yes)\n", get_token_name(pc->type)); + return(true); + } + + if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)) + { + ++word_count; + + if (chunk_is_token(pc, CT_TYPE)) + { + ++type_count; + } + } + else if (chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_DC_MEMBER)) + { + if (word_count > 0) + { + --word_count; + } + } + else if (pc != start && chunk_is_ptr_operator(pc)) + { + // chunk is OK + } + else if (chunk_is_token(pc, CT_ASSIGN)) + { + // chunk is OK (default values) + break; + } + else if (chunk_is_token(pc, CT_ANGLE_OPEN)) + { + LOG_FMT(LFPARAM, " <== template\n"); + return(true); + } + else if (chunk_is_token(pc, CT_ELLIPSIS)) + { + LOG_FMT(LFPARAM, " <== elipses\n"); + return(true); + } + else if (word_count == 0 && chunk_is_token(pc, CT_PAREN_OPEN)) + { + // Check for old-school func proto param '(type)' + chunk_t *tmp1 = chunk_skip_to_match(pc, scope_e::PREPROC); + + if (tmp1 == nullptr) + { + return(false); + } + chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC); + + if (tmp2 == nullptr) + { + return(false); + } + + if (chunk_is_token(tmp2, CT_COMMA) || chunk_is_paren_close(tmp2)) + { + do + { + pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); + + if (pc == nullptr) + { + return(false); + } + LOG_FMT(LFPARAM, " [%s]", pc->text()); + } while (pc != tmp1); + + // reset some vars to allow [] after parens + word_count = 1; + type_count = 1; + } + else + { + LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type)); + return(false); + } + } + else if ( (word_count == 1 || (word_count == type_count)) + && chunk_is_token(pc, CT_PAREN_OPEN)) + { + // Check for func proto param 'void (*name)' or 'void (*name)(params)' or 'void (^name)(params)' + // <name> can be optional + chunk_t *tmp1 = chunk_get_next_ncnl(pc, scope_e::PREPROC); + + if (tmp1 == nullptr) + { + return(false); + } + chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC); + + if (tmp2 == nullptr) + { + return(false); + } + chunk_t *tmp3 = (chunk_is_str(tmp2, ")", 1)) ? tmp2 : chunk_get_next_ncnl(tmp2, scope_e::PREPROC); + + if (tmp3 == nullptr) + { + return(false); + } + + if ( !chunk_is_str(tmp3, ")", 1) + || !(chunk_is_str(tmp1, "*", 1) || chunk_is_str(tmp1, "^", 1)) // Issue #2656 + || !(tmp2->type == CT_WORD || chunk_is_str(tmp2, ")", 1))) + { + LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type)); + return(false); + } + LOG_FMT(LFPARAM, " <skip fcn type>"); + tmp1 = chunk_get_next_ncnl(tmp3, scope_e::PREPROC); + + if (tmp1 == nullptr) + { + return(false); + } + + if (chunk_is_str(tmp1, "(", 1)) + { + tmp3 = chunk_skip_to_match(tmp1, scope_e::PREPROC); + } + pc = tmp3; + + // reset some vars to allow [] after parens + word_count = 1; + type_count = 1; + } + else if (chunk_is_token(pc, CT_TSQUARE)) + { + // ignore it + } + else if (word_count == 1 && chunk_is_token(pc, CT_SQUARE_OPEN)) + { + // skip over any array stuff + pc = chunk_skip_to_match(pc, scope_e::PREPROC); + } + else if (word_count == 2 && chunk_is_token(pc, CT_SQUARE_OPEN)) + { + // Bug #671: is it such as: bool foo[FOO_MAX] + pc = chunk_skip_to_match(pc, scope_e::PREPROC); + } + else if ( word_count == 1 + && language_is_set(LANG_CPP) + && chunk_is_str(pc, "&&", 2)) + { + // ignore possible 'move' operator + } + else + { + LOG_FMT(LFPARAM, " <== [%s] no way! tc=%d wc=%d\n", + get_token_name(pc->type), type_count, word_count); + return(false); + } + } + + chunk_t *last = chunk_get_prev_ncnlni(pc); // Issue #2279 + + if (chunk_is_ptr_operator(last)) + { + LOG_FMT(LFPARAM, " <== [%s] sure!\n", get_token_name(pc->type)); + return(true); + } + + if (word_count < 2 && type_count < 1 && start->brace_level > 0) + { + LOG_FMT(LFPARAM, " !MVP!"); + // Oh, joy, we are in Most Vexing Parse territory + auto const brace = + chunk_get_prev_type(start, CT_BRACE_OPEN, start->brace_level - 1); + + if (brace) + { + LOG_FMT(LFPARAM, " (matching %s brace at %zu:%zu)", + get_token_name(get_chunk_parent_type(brace)), + brace->orig_line, brace->orig_col); + } + + if ( brace + && ( get_chunk_parent_type(brace) == CT_CLASS + || get_chunk_parent_type(brace) == CT_STRUCT)) + { + // A Most Vexing Parse variable declaration cannot occur in the body + // of a struct/class, so we probably have a function prototype + LOG_FMT(LFPARAM, " <== [%s] Likely!\n", + (pc == nullptr ? "nullptr" : get_token_name(pc->type))); + return(true); + } + } + bool ret = ( word_count >= 2 + || (word_count == 1 && type_count == 1)); + + LOG_FMT(LFPARAM, " <== [%s] %s!\n", + (pc == nullptr ? "nullptr" : get_token_name(pc->type)), + ret ? "Yup" : "Unlikely"); + return(ret); +} // can_be_full_param + + +static void mark_function(chunk_t *pc) +{ + LOG_FUNC_ENTRY(); + + if (pc == nullptr) + { + return; + } + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); + chunk_t *prev = chunk_get_prev_ncnlni(pc); // Issue #2279 + chunk_t *next = chunk_get_next_ncnlnp(pc); + + if (next == nullptr) + { + return; + } + chunk_t *tmp; + chunk_t *semi = nullptr; + chunk_t *paren_open; + chunk_t *paren_close; + + // Find out what is before the operator + if (get_chunk_parent_type(pc) == CT_OPERATOR) + { + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); + log_pcf_flags(LGUY, pc->flags); + chunk_t *pc_op = chunk_get_prev_type(pc, CT_OPERATOR, pc->level); + + if ( pc_op != nullptr + && pc_op->flags.test(PCF_EXPR_START)) + { + LOG_FMT(LFCN, "%s(%d): (4) 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 (language_is_set(LANG_CPP)) + { + tmp = pc; + + while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr) // Issue #2279 + { + if ( chunk_is_token(tmp, CT_BRACE_CLOSE) + || chunk_is_token(tmp, CT_BRACE_OPEN) // Issue 575 + || chunk_is_token(tmp, CT_SEMICOLON)) + { + break; + } + + if ( chunk_is_paren_open(tmp) + && !pc->flags.test(PCF_IN_PREPROC)) // Issue #2703 + { + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); + LOG_FMT(LFCN, "%s(%d): (5) 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); + break; + } + + if (chunk_is_token(tmp, CT_ASSIGN)) + { + LOG_FMT(LFCN, "%s(%d): (6) 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); + break; + } + + if (chunk_is_token(tmp, CT_TEMPLATE)) + { + LOG_FMT(LFCN, "%s(%d): (7) 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); + break; + } + + if (chunk_is_token(tmp, CT_BRACE_OPEN)) + { + if (get_chunk_parent_type(tmp) == CT_FUNC_DEF) + { + LOG_FMT(LFCN, "%s(%d): (8) 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 ( get_chunk_parent_type(tmp) == CT_CLASS + || get_chunk_parent_type(tmp) == CT_STRUCT) + { + LOG_FMT(LFCN, "%s(%d): (9) 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); + } + break; + } + } + + if ( tmp != nullptr + && pc->type != CT_FUNC_CALL) + { + // Mark the return type + while ( (tmp = chunk_get_next_ncnl(tmp)) != pc + && tmp != nullptr) + { + make_type(tmp); // Mark the return type + } + } + } + } + + if (chunk_is_ptr_operator(next)) + { + next = chunk_get_next_ncnlnp(next); + + if (next == nullptr) + { + return; + } + } + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s, type is %s, parent_type is %s\n", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), + get_token_name(pc->type), get_token_name(get_chunk_parent_type(pc))); + LOG_FMT(LFCN, " level is %zu, brace_level is %zu, next->text() '%s', next->type is %s, next->level is %zu\n", + pc->level, pc->brace_level, + next->text(), get_token_name(next->type), next->level); + + if (pc->flags.test(PCF_IN_CONST_ARGS)) + { + set_chunk_type(pc, CT_FUNC_CTOR_VAR); + LOG_FMT(LFCN, "%s(%d): 1) Marked [%s] as FUNC_CTOR_VAR on line %zu col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + next = skip_template_next(next); + + if (next == nullptr) + { + return; + } + flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, true); + return; + } + // Skip over any template and attribute madness + next = skip_template_next(next); + + if (next == nullptr) + { + return; + } + next = skip_attribute_next(next); + + if (next == nullptr) + { + return; + } + // Find the open and close parenthesis + paren_open = chunk_get_next_str(pc, "(", 1, pc->level); + paren_close = chunk_get_next_str(paren_open, ")", 1, pc->level); + + if ( paren_open == nullptr + || paren_close == nullptr) + { + LOG_FMT(LFCN, "%s(%d): No parens found for [%s] on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + return; + } + /* + * This part detects either chained function calls or a function ptr definition. + * MYTYPE (*func)(void); + * mWriter( "class Clst_"c )( somestr.getText() )( " : Cluster {"c ).newline; + * + * For it to be a function variable def, there must be a '*' followed by a + * single word. + * + * Otherwise, it must be chained function calls. + */ + tmp = chunk_get_next_ncnl(paren_close); + + if ( tmp != nullptr + && chunk_is_str(tmp, "(", 1)) + { + chunk_t *tmp1; + chunk_t *tmp2; + chunk_t *tmp3; + + // skip over any leading class/namespace in: "T(F::*A)();" + tmp1 = chunk_get_next_ncnl(next); + + while (tmp1 != nullptr) + { + tmp2 = chunk_get_next_ncnl(tmp1); + + if (!chunk_is_word(tmp1) || !chunk_is_token(tmp2, CT_DC_MEMBER)) + { + break; + } + tmp1 = chunk_get_next_ncnl(tmp2); + } + tmp2 = chunk_get_next_ncnl(tmp1); + + if (chunk_is_str(tmp2, ")", 1)) + { + tmp3 = tmp2; + tmp2 = nullptr; + } + else + { + tmp3 = chunk_get_next_ncnl(tmp2); + } + tmp3 = chunk_get_next_ssq(tmp3); + + if ( chunk_is_str(tmp3, ")", 1) + && ( chunk_is_star(tmp1) + || chunk_is_msref(tmp1) + || (language_is_set(LANG_OC) && chunk_is_token(tmp1, CT_CARET))) + && (tmp2 == nullptr || chunk_is_token(tmp2, CT_WORD))) + { + if (tmp2) + { + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function variable '%s', changing '%s' into a type\n", + __func__, __LINE__, pc->orig_line, pc->orig_col, tmp2->text(), pc->text()); + set_chunk_type(tmp2, CT_FUNC_VAR); + flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_VAR, false); + + LOG_FMT(LFCN, "%s(%d): paren open @ orig_line %zu, orig_col %zu\n", + __func__, __LINE__, paren_open->orig_line, paren_open->orig_col); + } + else + { + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function type, changing '%s' into a type\n", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); + + if (tmp2) + { + set_chunk_type(tmp2, CT_FUNC_TYPE); + } + flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_TYPE, false); + } + set_chunk_type(pc, CT_TYPE); + set_chunk_type(tmp1, CT_PTR_TYPE); + chunk_flags_clr(pc, PCF_VAR_1ST_DEF); + + if (tmp2 != nullptr) + { + chunk_flags_set(tmp2, PCF_VAR_1ST_DEF); + } + flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_PROTO, false); + fix_fcn_def_params(tmp); + return; + } + LOG_FMT(LFCN, "%s(%d): chained function calls? text() is '%s', orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + } + + // Assume it is a function call if not already labeled + if (chunk_is_token(pc, CT_FUNCTION)) + { + LOG_FMT(LFCN, "%s(%d): examine: text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); + // look for an assigment. Issue #575 + chunk_t *temp = chunk_get_next_type(pc, CT_ASSIGN, pc->level); + + if (temp != nullptr) + { + LOG_FMT(LFCN, "%s(%d): assigment found, orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, temp->orig_line, temp->orig_col, temp->text()); + LOG_FMT(LFCN, "%s(%d): (10) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'", + __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text()); + set_chunk_type(pc, CT_FUNC_CALL); + } + else + { + LOG_FMT(LFCN, "%s(%d): (11) SET TO %s: orig_line is %zu, orig_col is %zu, text() '%s'", + __func__, __LINE__, (get_chunk_parent_type(pc) == CT_OPERATOR) ? "CT_FUNC_DEF" : "CT_FUNC_CALL", + pc->orig_line, pc->orig_col, pc->text()); + set_chunk_type(pc, (get_chunk_parent_type(pc) == CT_OPERATOR) ? CT_FUNC_DEF : CT_FUNC_CALL); + } + } + LOG_FMT(LFCN, "%s(%d): Check for C++ function def, text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); + + if (prev != nullptr) + { + LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); + } + + // Check for C++ function def + if ( chunk_is_token(pc, CT_FUNC_CLASS_DEF) + || ( prev != nullptr + && ( chunk_is_token(prev, CT_INV) + || chunk_is_token(prev, CT_DC_MEMBER)))) + { + chunk_t *destr = nullptr; + + if (chunk_is_token(prev, CT_INV)) + { + // TODO: do we care that this is the destructor? + set_chunk_type(prev, CT_DESTRUCTOR); + set_chunk_type(pc, CT_FUNC_CLASS_DEF); + + set_chunk_parent(pc, CT_DESTRUCTOR); + + destr = prev; + // Point to the item previous to the class name + prev = chunk_get_prev_ncnlnp(prev); + } + + if (chunk_is_token(prev, CT_DC_MEMBER)) + { + prev = chunk_get_prev_ncnlnp(prev); + LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, + get_token_name(prev->type)); + prev = skip_template_prev(prev); + LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, + get_token_name(prev->type)); + prev = skip_attribute_prev(prev); + LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, + get_token_name(prev->type)); + + if (chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE)) + { + if (pc->str.equals(prev->str)) + { + LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, + get_token_name(prev->type)); + set_chunk_type(pc, CT_FUNC_CLASS_DEF); + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu - FOUND %sSTRUCTOR for '%s', type is %s\n", + __func__, __LINE__, + prev->orig_line, prev->orig_col, + (destr != nullptr) ? "DE" : "CON", + prev->text(), get_token_name(prev->type)); + + mark_cpp_constructor(pc); + return; + } + // Point to the item previous to the class name + prev = chunk_get_prev_ncnlnp(prev); + } + } + } + + /* + * Determine if this is a function call or a function def/proto + * We check for level==1 to allow the case that a function prototype is + * wrapped in a macro: "MACRO(void foo(void));" + */ + if ( chunk_is_token(pc, CT_FUNC_CALL) + && ( pc->level == pc->brace_level + || pc->level == 1) + && !pc->flags.test(PCF_IN_ARRAY_ASSIGN)) + { + bool isa_def = false; + bool hit_star = false; + LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, + get_token_name(pc->type)); + + if (prev == nullptr) + { + LOG_FMT(LFCN, "%s(%d): Checking func call: prev is NULL\n", + __func__, __LINE__); + } + else + { + LOG_FMT(LFCN, "%s(%d): Checking func call: prev->text() '%s', prev->type is %s\n", + __func__, __LINE__, prev->text(), get_token_name(prev->type)); + } + // if (!chunk_ends_type(prev)) + // { + // goto bad_ret_type; + // } + + /* + * REVISIT: + * a function def can only occur at brace level, but not inside an + * assignment, structure, enum, or union. + * The close paren must be followed by an open brace, with an optional + * qualifier (const) in between. + * There can be all sorts of template stuff and/or '[]' in the type. + * This hack mostly checks that. + * + * Examples: + * foo->bar(maid); -- fcn call + * FOO * bar(); -- fcn proto or class variable + * FOO foo(); -- fcn proto or class variable + * FOO foo(1); -- class variable + * a = FOO * bar(); -- fcn call + * a.y = foo() * bar(); -- fcn call + * static const char * const fizz(); -- fcn def + */ + while (prev != nullptr) + { + LOG_FMT(LFCN, "%s(%d): next step with: prev->orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text()); + + if (get_chunk_parent_type(pc) == CT_FIXED) + { + isa_def = true; + } + + if (prev->flags.test(PCF_IN_PREPROC)) + { + prev = chunk_get_prev_ncnlnp(prev); + continue; + } + + // Some code slips an attribute between the type and function + if ( chunk_is_token(prev, CT_FPAREN_CLOSE) + && get_chunk_parent_type(prev) == CT_ATTRIBUTE) + { + prev = skip_attribute_prev(prev); + continue; + } + + // skip const(TYPE) + if ( chunk_is_token(prev, CT_PAREN_CLOSE) + && get_chunk_parent_type(prev) == CT_D_CAST) + { + LOG_FMT(LFCN, "%s(%d): --> For sure a prototype or definition\n", + __func__, __LINE__); + isa_def = true; + break; + } + + if (get_chunk_parent_type(prev) == CT_DECLSPEC) // Issue 1289 + { + prev = chunk_skip_to_match_rev(prev); + prev = chunk_get_prev(prev); + + if (chunk_is_token(prev, CT_DECLSPEC)) + { + prev = chunk_get_prev(prev); + } + } + + // if it was determined that this could be a function definition + // but one of the preceding tokens is a CT_MEMBER than this is not a + // fcn def, issue #1466 + if ( isa_def + && chunk_is_token(prev, CT_MEMBER)) + { + isa_def = false; + } + + // get first chunk before: A::B::pc | this.B.pc | this->B->pc + if ( chunk_is_token(prev, CT_DC_MEMBER) + || chunk_is_token(prev, CT_MEMBER)) + { + while ( chunk_is_token(prev, CT_DC_MEMBER) + || chunk_is_token(prev, CT_MEMBER)) + { + prev = chunk_get_prev_ncnlnp(prev); + + if ( prev == nullptr + || ( prev->type != CT_WORD + && prev->type != CT_TYPE + && prev->type != CT_THIS)) + { + LOG_FMT(LFCN, "%s(%d): --? skipped MEMBER and landed on %s\n", + __func__, __LINE__, (prev == nullptr) ? "<null>" : get_token_name(prev->type)); + break; + } + LOG_FMT(LFCN, "%s(%d): <skip> '%s'\n", + __func__, __LINE__, prev->text()); + + // Issue #1112 + // clarification: this will skip the CT_WORD, CT_TYPE or CT_THIS landing on either + // another CT_DC_MEMBER or CT_MEMBER or a token that indicates the context of the + // token in question; therefore, exit loop when not a CT_DC_MEMBER or CT_MEMBER + prev = chunk_get_prev_ncnlnp(prev); + + if (prev == nullptr) + { + LOG_FMT(LFCN, "%s(%d): prev is nullptr\n", + __func__, __LINE__); + } + else + { + LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text()); + } + } + + if (prev == nullptr) + { + break; + } + } + + // If we are on a TYPE or WORD, then this could be a proto or def + if ( chunk_is_token(prev, CT_TYPE) + || chunk_is_token(prev, CT_WORD)) + { + if (!hit_star) + { + LOG_FMT(LFCN, "%s(%d): --> For sure a prototype or definition\n", + __func__, __LINE__); + isa_def = true; + break; + } + chunk_t *prev_prev = chunk_get_prev_ncnlnp(prev); + + if (!chunk_is_token(prev_prev, CT_QUESTION)) // Issue #1753 + { + LOG_FMT(LFCN, "%s(%d): --> maybe a proto/def\n", + __func__, __LINE__); + + LOG_FMT(LFCN, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s, parent_type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, + get_token_name(prev->type), get_token_name(get_chunk_parent_type(prev))); + log_pcf_flags(LFCN, pc->flags); + isa_def = true; + } + } + + if (chunk_is_ptr_operator(prev)) + { + hit_star = true; + } + + if ( prev->type != CT_OPERATOR + && prev->type != CT_TSQUARE + && prev->type != CT_ANGLE_CLOSE + && prev->type != CT_QUALIFIER + && prev->type != CT_TYPE + && prev->type != CT_WORD + && !chunk_is_ptr_operator(prev)) + { + LOG_FMT(LFCN, "%s(%d): --> Stopping on prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); + + // certain tokens are unlikely to precede a prototype or definition + if ( chunk_is_token(prev, CT_ARITH) + || chunk_is_token(prev, CT_ASSIGN) + || chunk_is_token(prev, CT_COMMA) + || (chunk_is_token(prev, CT_STRING) && get_chunk_parent_type(prev) != CT_EXTERN) // fixes issue 1259 + || chunk_is_token(prev, CT_STRING_MULTI) + || chunk_is_token(prev, CT_NUMBER) + || chunk_is_token(prev, CT_NUMBER_FP) + || chunk_is_token(prev, CT_FPAREN_OPEN)) // issue #1464 + { + isa_def = false; + } + break; + } + + // Skip over template and attribute stuff + if (chunk_is_token(prev, CT_ANGLE_CLOSE)) + { + prev = skip_template_prev(prev); + } + else + { + prev = chunk_get_prev_ncnlnp(prev); + } + } + //LOG_FMT(LFCN, " -- stopped on %s [%s]\n", + // prev->text(), get_token_name(prev->type)); + + // Fixes issue #1634 + if (chunk_is_paren_close(prev)) + { + chunk_t *preproc = chunk_get_next_ncnl(prev); + + if (chunk_is_token(preproc, CT_PREPROC)) + { + size_t pp_level = preproc->pp_level; + + if (chunk_is_token(chunk_get_next_ncnl(preproc), CT_PP_ELSE)) + { + do + { + preproc = chunk_get_prev_ncnlni(preproc); // Issue #2279 + + if (chunk_is_token(preproc, CT_PP_IF)) + { + preproc = chunk_get_prev_ncnlni(preproc); // Issue #2279 + + if (preproc->pp_level == pp_level) + { + prev = chunk_get_prev_ncnlnp(preproc); + break; + } + } + } while (preproc != nullptr); + } + } + } + + if ( isa_def + && prev != nullptr + && ( ( chunk_is_paren_close(prev) + && get_chunk_parent_type(prev) != CT_D_CAST + && get_chunk_parent_type(prev) != CT_MACRO_OPEN // Issue #2726 + && get_chunk_parent_type(prev) != CT_MACRO_CLOSE) + || prev->type == CT_ASSIGN + || prev->type == CT_RETURN)) + { + LOG_FMT(LFCN, "%s(%d): -- overriding DEF due to prev is '%s', type is %s\n", + __func__, __LINE__, prev->text(), get_token_name(prev->type)); + isa_def = false; + } + + // Fixes issue #1266, identification of a tuple return type in CS. + if ( !isa_def + && chunk_is_token(prev, CT_PAREN_CLOSE) + && chunk_get_next_ncnl(prev) == pc) + { + tmp = chunk_skip_to_match_rev(prev); + + while ( tmp != nullptr // Issue #2315 + && tmp != prev) + { + if (chunk_is_token(tmp, CT_COMMA) && tmp->level == prev->level + 1) + { + LOG_FMT(LFCN, "%s(%d): -- overriding call due to tuple return type -- prev is '%s', type is %s\n", + __func__, __LINE__, prev->text(), get_token_name(prev->type)); + isa_def = true; + break; + } + tmp = chunk_get_next_ncnl(tmp); + } + } + + if (isa_def) { - detail = " -- '_t'"; + LOG_FMT(LFCN, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); + LOG_FMT(LFCN, "%s(%d): (12) 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); + + if (prev == nullptr) + { + prev = chunk_get_head(); + } + + for ( tmp = prev; (tmp != nullptr) + && tmp != pc; tmp = chunk_get_next_ncnlnp(tmp)) + { + LOG_FMT(LFCN, "%s(%d): text() is '%s', type is %s\n", + __func__, __LINE__, tmp->text(), get_token_name(tmp->type)); + make_type(tmp); + } } - else if (is_ucase_str(last->text(), last->len())) + } + + if (pc->type != CT_FUNC_DEF) + { + LOG_FMT(LFCN, "%s(%d): Detected type %s, text() is '%s', on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, get_token_name(pc->type), + pc->text(), pc->orig_line, pc->orig_col); + + tmp = flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false); + + if ( chunk_is_token(tmp, CT_BRACE_OPEN) + && get_chunk_parent_type(tmp) != CT_DOUBLE_BRACE) { - detail = " -- upper case"; + set_paren_parent(tmp, pc->type); } - else if (language_is_set(LANG_OC) && chunk_is_str(last, "id", 2)) + return; + } + /* + * We have a function definition or prototype + * Look for a semicolon or a brace open after the close parenthesis to figure + * out whether this is a prototype or definition + */ + + // See if this is a prototype or implementation + + // FIXME: this doesn't take the old K&R parameter definitions into account + + // Scan tokens until we hit a brace open (def) or semicolon (proto) + tmp = paren_close; + + while ((tmp = chunk_get_next_ncnl(tmp)) != nullptr) + { + // Only care about brace or semicolon on the same level + if (tmp->level < pc->level) { - detail = " -- Objective-C id"; + // No semicolon - guess that it is a prototype + chunk_flags_clr(pc, PCF_VAR_1ST_DEF); + set_chunk_type(pc, CT_FUNC_PROTO); + break; } - else + else if (tmp->level == pc->level) { - // If we can't tell for sure whether this is a cast, decide against it - detail = " -- mixed case"; - doubtful_cast = true; + if (chunk_is_token(tmp, CT_BRACE_OPEN)) + { + // its a function def for sure + break; + } + else if (chunk_is_semicolon(tmp)) + { + // Set the parent for the semicolon for later + semi = tmp; + chunk_flags_clr(pc, PCF_VAR_1ST_DEF); + set_chunk_type(pc, CT_FUNC_PROTO); + LOG_FMT(LFCN, "%s(%d): 2) Marked text() is '%s', as FUNC_PROTO on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + break; + } + else if (chunk_is_token(pc, CT_COMMA)) + { + set_chunk_type(pc, CT_FUNC_CTOR_VAR); + LOG_FMT(LFCN, "%s(%d): 2) Marked text() is '%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + break; + } } + } + + /* + * C++ syntax is wacky. We need to check to see if a prototype is really a + * variable definition with parameters passed into the constructor. + * Unfortunately, without being able to accurately determine if an + * identifier is a type (which would require us to more or less be a full + * compiler), the only mostly reliable way to do so is to guess that it is + * a constructor variable if inside a function body and scan the 'parameter + * list' for items that are not allowed in a prototype. We search backwards + * and checking the parent of the containing open braces. If the parent is a + * class or namespace, then it probably is a prototype. + */ + if ( language_is_set(LANG_CPP) + && chunk_is_token(pc, CT_FUNC_PROTO) + && get_chunk_parent_type(pc) != CT_OPERATOR) + { + LOG_FMT(LFPARAM, "%s(%d):", __func__, __LINE__); + LOG_FMT(LFPARAM, " checking '%s' for constructor variable %s %s\n", + pc->text(), + get_token_name(paren_open->type), + get_token_name(paren_close->type)); + /* - * If the next item is a * or &, the next item after that can't be a - * number or string. - * - * If the next item is a +, the next item has to be a number. - * - * If the next item is a -, the next item can't be a string. - * - * For this to be a cast, the close paren must be followed by: - * - constant (number or string) - * - paren open - * - word - * - * Find the next non-open paren item. + * Check the token at the start of the statement. If it's 'extern', we + * definitely have a function prototype. */ - pc = chunk_get_next_ncnl(paren_close); - after = pc; - - do - { - after = chunk_get_next_ncnl(after); - } while (chunk_is_token(after, CT_PAREN_OPEN)); + tmp = pc; - if (after == nullptr) + while ( tmp != nullptr + && !tmp->flags.test(PCF_STMT_START)) { - LOG_FMT(LCASTS, "%s(%d): -- not a cast - hit NULL\n", - __func__, __LINE__); - return; + tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279 } - nope = false; + const bool is_extern = (tmp && tmp->str.equals("extern")); - if (chunk_is_ptr_operator(pc)) + /* + * Scan the parameters looking for: + * - constant strings + * - numbers + * - non-type fields + * - function calls + */ + chunk_t *ref = chunk_get_next_ncnl(paren_open); + chunk_t *tmp2; + bool is_param = true; + tmp = ref; + + while (tmp != paren_close) { - // star (*) and address (&) are ambiguous - if ( chunk_is_token(after, CT_NUMBER_FP) - || chunk_is_token(after, CT_NUMBER) - || chunk_is_token(after, CT_STRING) - || doubtful_cast) + tmp2 = chunk_get_next_ncnl(tmp); + + if ( chunk_is_token(tmp, CT_COMMA) + && (tmp->level == (paren_open->level + 1))) { - nope = true; + if (!can_be_full_param(ref, tmp)) + { + is_param = false; + break; + } + ref = tmp2; } + tmp = tmp2; } - else if (chunk_is_token(pc, CT_MINUS)) + + if ( !is_extern + && is_param && ref != tmp) { - // (UINT8)-1 or (foo)-1 or (FOO)-'a' - if (chunk_is_token(after, CT_STRING) || doubtful_cast) + if (!can_be_full_param(ref, tmp)) { - nope = true; + is_param = false; } } - else if (chunk_is_token(pc, CT_PLUS)) + + if ( !is_extern + && !is_param) { - // (UINT8)+1 or (foo)+1 - if ( (after->type != CT_NUMBER && after->type != CT_NUMBER_FP) - || doubtful_cast) - { - nope = true; - } + set_chunk_type(pc, CT_FUNC_CTOR_VAR); + LOG_FMT(LFCN, "%s(%d): 3) Marked text() '%s' as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); } - else if ( pc->type != CT_NUMBER_FP - && pc->type != CT_NUMBER - && pc->type != CT_WORD - && pc->type != CT_THIS - && pc->type != CT_TYPE - && pc->type != CT_PAREN_OPEN - && pc->type != CT_STRING - && pc->type != CT_DECLTYPE - && pc->type != CT_SIZEOF - && get_chunk_parent_type(pc) != CT_SIZEOF - && pc->type != CT_FUNC_CALL - && pc->type != CT_FUNC_CALL_USER - && pc->type != CT_FUNCTION - && pc->type != CT_BRACE_OPEN - && (!( chunk_is_token(pc, CT_SQUARE_OPEN) - && language_is_set(LANG_OC)))) + else if (pc->brace_level > 0) { - LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by text() '%s', type is %s\n", - __func__, __LINE__, pc->text(), get_token_name(pc->type)); - return; - } + chunk_t *br_open = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1); - if (nope) - { - LOG_FMT(LCASTS, "%s(%d): -- not a cast - text() '%s' followed by type %s\n", - __func__, __LINE__, pc->text(), get_token_name(after->type)); - return; + if ( br_open != nullptr + && get_chunk_parent_type(br_open) != CT_EXTERN + && get_chunk_parent_type(br_open) != CT_NAMESPACE) + { + // Do a check to see if the level is right + prev = chunk_get_prev_ncnlni(pc); // Issue #2279 + + if ( !chunk_is_str(prev, "*", 1) + && !chunk_is_str(prev, "&", 1)) + { + chunk_t *p_op = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1); + + if ( p_op != nullptr + && get_chunk_parent_type(p_op) != CT_CLASS + && get_chunk_parent_type(p_op) != CT_STRUCT + && get_chunk_parent_type(p_op) != CT_NAMESPACE) + { + set_chunk_type(pc, CT_FUNC_CTOR_VAR); + LOG_FMT(LFCN, "%s(%d): 4) Marked text() is'%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + } + } + } } } - // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't - pc = chunk_get_next_ncnl(paren_close); - if (pc == nullptr) + if (semi != nullptr) { - return; + set_chunk_parent(semi, pc->type); } - if ( chunk_is_semicolon(pc) - || chunk_is_token(pc, CT_COMMA) - || chunk_is_token(pc, CT_BOOL) // Issue #2151 - || chunk_is_paren_close(pc)) + // Issue # 1403, 2152 + if (chunk_is_token(paren_open->prev, CT_FUNC_CTOR_VAR)) { - LOG_FMT(LCASTS, "%s(%d): -- not a cast - followed by type %s\n", - __func__, __LINE__, get_token_name(pc->type)); - return; + flag_parens(paren_open, PCF_IN_FCN_CTOR, CT_FPAREN_OPEN, pc->type, false); } - set_chunk_parent(start, CT_C_CAST); - set_chunk_parent(paren_close, CT_C_CAST); - - LOG_FMT(LCASTS, "%s(%d): -- %s c-cast: (", - __func__, __LINE__, verb); - - for (pc = first; - pc != nullptr && pc != paren_close; - pc = chunk_get_next_ncnl(pc)) + else { - set_chunk_parent(pc, CT_C_CAST); - make_type(pc); - LOG_FMT(LCASTS, " %s", pc->text()); + flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, false); } + //flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, true); - LOG_FMT(LCASTS, " )%s\n", detail); - - // Mark the next item as an expression start - pc = chunk_get_next_ncnl(paren_close); + if (chunk_is_token(pc, CT_FUNC_CTOR_VAR)) + { + chunk_flags_set(pc, PCF_VAR_1ST_DEF); + return; + } - if (pc != nullptr) + if (chunk_is_token(next, CT_TSQUARE)) { - chunk_flags_set(pc, PCF_EXPR_START); + next = chunk_get_next_ncnl(next); - if (chunk_is_opening_brace(pc)) + if (next == nullptr) { - set_paren_parent(pc, get_chunk_parent_type(start)); + return; } } -} // fix_casts + // Mark parameters and return type + fix_fcn_def_params(next); + mark_function_return_type(pc, chunk_get_prev_ncnlni(pc), pc->type); // Issue #2279 + /* mark C# where chunk */ + if ( language_is_set(LANG_CS) + && ( (chunk_is_token(pc, CT_FUNC_DEF)) + || (chunk_is_token(pc, CT_FUNC_PROTO)))) + { + tmp = chunk_get_next_ncnl(paren_close); + pcf_flags_t in_where_spec_flags = PCF_NONE; -static void fix_type_cast(chunk_t *start) -{ - LOG_FUNC_ENTRY(); - chunk_t *pc; - - pc = chunk_get_next_ncnl(start); + while ( (tmp != nullptr) + && (tmp->type != CT_BRACE_OPEN) + && (tmp->type != CT_SEMICOLON)) + { + mark_where_chunk(tmp, pc->type, tmp->flags | in_where_spec_flags); + in_where_spec_flags = tmp->flags & PCF_IN_WHERE_SPEC; - if (pc == nullptr || pc->type != CT_ANGLE_OPEN) - { - return; + tmp = chunk_get_next_ncnl(tmp); + } } - while ( ((pc = chunk_get_next_ncnl(pc)) != nullptr) - && pc->level >= start->level) + // Find the brace pair and set the parent + if (chunk_is_token(pc, CT_FUNC_DEF)) { - if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE)) + tmp = chunk_get_next_ncnl(paren_close); + + while ( tmp != nullptr + && tmp->type != CT_BRACE_OPEN) { - pc = chunk_get_next_ncnl(pc); + LOG_FMT(LFCN, "%s(%d): (13) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); + set_chunk_parent(tmp, CT_FUNC_DEF); - if (pc == nullptr) + if (!chunk_is_semicolon(tmp)) { - return; + chunk_flags_set(tmp, PCF_OLD_FCN_PARAMS); } + tmp = chunk_get_next_ncnl(tmp); + } - if (chunk_is_str(pc, "(", 1)) + if (chunk_is_token(tmp, CT_BRACE_OPEN)) + { + LOG_FMT(LFCN, "%s(%d): (14) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); + set_chunk_parent(tmp, CT_FUNC_DEF); + tmp = chunk_skip_to_match(tmp); + + if (tmp != nullptr) { - set_paren_parent(pc, CT_TYPE_CAST); + LOG_FMT(LFCN, "%s(%d): (15) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n", + __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text()); + set_chunk_parent(tmp, CT_FUNC_DEF); } - return; } - make_type(pc); } -} +} // mark_function -static void fix_enum_struct_union(chunk_t *pc) +static void mark_cpp_constructor(chunk_t *pc) { LOG_FUNC_ENTRY(); - chunk_t *next; - chunk_t *prev = nullptr; - pcf_flags_t flags = PCF_VAR_1ST_DEF; - auto const in_fcn_paren = pc->flags & PCF_IN_FCN_DEF; + chunk_t *paren_open; + chunk_t *tmp; + chunk_t *after; + chunk_t *var; + bool is_destr = false; - // Make sure this wasn't a cast - if (get_chunk_parent_type(pc) == CT_C_CAST) - { - return; - } - // the next item is either a type or open brace - next = chunk_get_next_ncnl(pc); + tmp = chunk_get_prev_ncnlni(pc); // Issue #2279 - // the enum-key might be enum, enum class or enum struct (TODO) - if (chunk_is_token(next, CT_ENUM_CLASS)) + if (chunk_is_token(tmp, CT_INV) || chunk_is_token(tmp, CT_DESTRUCTOR)) { - next = chunk_get_next_ncnl(next); // get the next one + set_chunk_type(tmp, CT_DESTRUCTOR); + set_chunk_parent(pc, CT_DESTRUCTOR); + is_destr = true; } + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, FOUND %sSTRUCTOR for '%s'[%s] prev '%s'[%s]\n", + __func__, __LINE__, pc->orig_line, pc->orig_col, + is_destr ? "DE" : "CON", + pc->text(), get_token_name(pc->type), + tmp->text(), get_token_name(tmp->type)); - if (language_is_set(LANG_CPP)) - { - next = skip_attribute_next(next); // get the next one - } + paren_open = skip_template_next(chunk_get_next_ncnl(pc)); - // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace - if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON)) + if (!chunk_is_str(paren_open, "(", 1)) { - // i.e. "enum xyz : unsigned int { ... };" - // i.e. "enum class xyz : unsigned int { ... };" - // i.e. "enum : unsigned int { ... };" - // xyz is a type + LOG_FMT(LWARN, "%s:%zu Expected '(', got: [%s]\n", + cpd.filename.c_str(), paren_open->orig_line, + paren_open->text()); + return; + } + // Mark parameters + fix_fcn_def_params(paren_open); + after = flag_parens(paren_open, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CLASS_PROTO, false); - // save the type if it exists - if (!chunk_is_token(next, CT_COLON)) - { - set_chunk_parent(next, pc->type); - prev = next; - next = chunk_get_next_ncnl(next); - } + LOG_FMT(LFTOR, "%s(%d): text() '%s'\n", __func__, __LINE__, after->text()); - if (next == nullptr) - { - return; - } - set_chunk_parent(next, pc->type); - auto const is_struct_or_class = - (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS)); + // Scan until the brace open, mark everything + tmp = paren_open; + bool hit_colon = false; - // next up is either a colon, open brace, or open parenthesis (pawn) - if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN)) + while ( tmp != nullptr + && (tmp->type != CT_BRACE_OPEN || tmp->level != paren_open->level) + && !chunk_is_semicolon(tmp)) + { + LOG_FMT(LFTOR, "%s(%d): tmp is '%s', orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, tmp->text(), tmp->orig_line, tmp->orig_col); + chunk_flags_set(tmp, PCF_IN_CONST_ARGS); + tmp = chunk_get_next_ncnl(tmp); + + if (chunk_is_str(tmp, ":", 1) && tmp->level == paren_open->level) { - next = set_paren_parent(next, CT_ENUM); + set_chunk_type(tmp, CT_CONSTR_COLON); + hit_colon = true; } - else if (chunk_is_token(next, CT_COLON)) - { - if (chunk_is_token(pc, CT_ENUM)) - { - // enum TYPE : INT_TYPE { ... }; - next = chunk_get_next_ncnl(next); - if (next != nullptr) - { - make_type(next); - next = chunk_get_next_ncnl(next); + if ( hit_colon + && (chunk_is_paren_open(tmp) || chunk_is_opening_brace(tmp)) + && tmp->level == paren_open->level) + { + var = skip_template_prev(chunk_get_prev_ncnlni(tmp)); // Issue #2279 - // enum TYPE : unsigned int { ... }; - if (chunk_is_token(next, CT_TYPE)) - { - // get the next part of the type - next = chunk_get_next_ncnl(next); - } - } - } - else if (is_struct_or_class) + if (chunk_is_token(var, CT_TYPE) || chunk_is_token(var, CT_WORD)) { - next = skip_parent_types(next); + set_chunk_type(var, CT_FUNC_CTOR_VAR); + flag_parens(tmp, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CTOR_VAR, false); } } - else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN)) + } + + if (chunk_is_token(tmp, CT_BRACE_OPEN)) + { + set_paren_parent(paren_open, CT_FUNC_CLASS_DEF); + set_paren_parent(tmp, CT_FUNC_CLASS_DEF); + LOG_FMT(LFCN, "%s(%d): Marked '%s' as FUNC_CLASS_DEF on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + } + else + { + set_chunk_parent(tmp, CT_FUNC_CLASS_PROTO); + set_chunk_type(pc, CT_FUNC_CLASS_PROTO); + LOG_FMT(LFCN, "%s(%d): Marked '%s' as FUNC_CLASS_PROTO on orig_line %zu, orig_col %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + } +} // mark_cpp_constructor + + +static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags) +{ + /* TODO: should have options to control spacing around the ':' as well as newline ability for the + * constraint clauses (should it break up a 'where A : B where C : D' on the same line? wrap? etc.) */ + + if (chunk_is_token(pc, CT_WHERE)) + { + set_chunk_type(pc, CT_WHERE_SPEC); + set_chunk_parent(pc, parent_type); + flags |= PCF_IN_WHERE_SPEC; + LOG_FMT(LFTOR, "%s: where-spec on line %zu\n", + __func__, pc->orig_line); + } + else if (flags.test(PCF_IN_WHERE_SPEC)) + { + if (chunk_is_str(pc, ":", 1)) { - // Fix #1267 structure attributes - // struct __attribute__(align(x)) struct_name; - // skip to matching parenclose and make next token as type. - next = chunk_skip_to_match(next); - next = chunk_get_next_ncnl(next); - set_chunk_type(next, CT_TYPE); - set_chunk_parent(next, pc->type); + set_chunk_type(pc, CT_WHERE_COLON); + LOG_FMT(LFTOR, "%s: where-spec colon on line %zu\n", + __func__, pc->orig_line); } - - if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration + else if ((chunk_is_token(pc, CT_STRUCT)) || (chunk_is_token(pc, CT_CLASS))) { - set_chunk_parent(next, pc->type); - flag_series(pc, prev, PCF_INCOMPLETE); - return; + /* class/struct inside of a where-clause confuses parser for indentation; set it as a word so it looks like the rest */ + set_chunk_type(pc, CT_WORD); } } - if (chunk_is_token(next, CT_BRACE_OPEN)) + if (flags.test(PCF_IN_WHERE_SPEC)) { - auto const flag = [pc] { - switch (pc->type) - { - case CT_ENUM: - return(PCF_IN_ENUM); + chunk_flags_set(pc, PCF_IN_WHERE_SPEC); + } + return(flags); +} - case CT_STRUCT: - return(PCF_IN_STRUCT); - case CT_CLASS: - return(PCF_IN_CLASS); +static void mark_class_ctor(chunk_t *start) +{ + LOG_FUNC_ENTRY(); - default: - return(PCF_NONE); - } - }(); + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, start is '%s', parent_type is %s\n", + __func__, __LINE__, start->orig_line, start->orig_col, start->text(), + get_token_name(get_chunk_parent_type(start))); + log_pcf_flags(LFTOR, start->flags); - flag_parens(next, flag, CT_NONE, CT_NONE, false); + chunk_t *pclass = chunk_get_next_ncnl(start, scope_e::PREPROC); - if ( chunk_is_token(pc, CT_UNION) - || chunk_is_token(pc, CT_STRUCT) - || chunk_is_token(pc, CT_CLASS)) + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); + log_pcf_flags(LFTOR, pclass->flags); + + if (language_is_set(LANG_CPP)) + { + pclass = skip_attribute_next(pclass); + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); + } + + if (get_chunk_parent_type(start) == CT_TEMPLATE) + { + // look after the class name + chunk_t *openingTemplate = chunk_get_next_ncnl(pclass); + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, openingTemplate is '%s', type is %s\n", + __func__, __LINE__, openingTemplate->orig_line, openingTemplate->orig_col, + openingTemplate->text(), get_token_name(openingTemplate->type)); + + if (chunk_is_token(openingTemplate, CT_ANGLE_OPEN)) { - mark_struct_union_body(next); + chunk_t *closingTemplate = chunk_skip_to_match(openingTemplate); + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, closingTemplate is '%s', type is %s\n", + __func__, __LINE__, closingTemplate->orig_line, closingTemplate->orig_col, + closingTemplate->text(), get_token_name(closingTemplate->type)); + chunk_t *thirdToken = chunk_get_next_ncnl(closingTemplate); + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, thirdToken is '%s', type is %s\n", + __func__, __LINE__, thirdToken->orig_line, thirdToken->orig_col, + thirdToken->text(), get_token_name(thirdToken->type)); + + if (chunk_is_token(thirdToken, CT_DC_MEMBER)) + { + pclass = chunk_get_next_ncnl(thirdToken); + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, pclass is '%s', type is %s\n", + __func__, __LINE__, pclass->orig_line, pclass->orig_col, + pclass->text(), get_token_name(pclass->type)); + } } - // Skip to the closing brace - set_chunk_parent(next, pc->type); - next = chunk_get_next_type(next, CT_BRACE_CLOSE, pc->level); - flags |= PCF_VAR_INLINE; + } + pclass = skip_attribute_next(pclass); + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); - if (next != nullptr) + if (chunk_is_token(pclass, CT_DECLSPEC)) // Issue 1289 + { + pclass = chunk_get_next_ncnl(pclass); + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); + + if (chunk_is_token(pclass, CT_PAREN_OPEN)) { - set_chunk_parent(next, pc->type); - next = chunk_get_next_ncnl(next); + pclass = chunk_get_next_ncnl(chunk_skip_to_match(pclass)); + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); } - prev = nullptr; } - // reset var name parent type - else if (next && prev) + + if ( pclass == nullptr + || (pclass->type != CT_TYPE && pclass->type != CT_WORD)) { - set_chunk_parent(prev, CT_NONE); + return; } + chunk_t *next = chunk_get_next_ncnl(pclass, scope_e::PREPROC); - if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE)) + while ( chunk_is_token(next, CT_TYPE) + || chunk_is_token(next, CT_WORD) + || chunk_is_token(next, CT_DC_MEMBER)) + { + pclass = next; + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); + next = chunk_get_next_ncnl(next, scope_e::PREPROC); + } + chunk_t *pc = chunk_get_next_ncnl(pclass, scope_e::PREPROC); + size_t level = pclass->brace_level + 1; + + if (pc == nullptr) { + LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu. Bailed on NULL\n", + __func__, __LINE__, pclass->text(), pclass->orig_line); return; } + // Add the class name + ChunkStack cs; - if (!chunk_is_semicolon(next)) + cs.Push_Back(pclass); + + LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu (next is '%s')\n", + __func__, __LINE__, pclass->text(), pclass->orig_line, pc->text()); + + // detect D template class: "class foo(x) { ... }" + if (language_is_set(LANG_D) && chunk_is_token(next, CT_PAREN_OPEN)) // Coverity CID 76004 { - // Pawn does not require a semicolon after an enum - if (language_is_set(LANG_PAWN)) - { - return; - } + set_chunk_parent(next, CT_TEMPLATE); - /* - * D does not require a semicolon after an enum, but we add one to make - * other code happy. - */ - if (language_is_set(LANG_D)) + next = get_d_template_types(cs, next); + + if (chunk_is_token(next, CT_PAREN_CLOSE)) { - next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next)); // Issue #2279 + set_chunk_parent(next, CT_TEMPLATE); } } + // Find the open brace, abort on semicolon + pcf_flags_t flags = PCF_NONE; - // We are either pointing to a ';' or a variable - while ( next != nullptr - && !chunk_is_semicolon(next) - && next->type != CT_ASSIGN - && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any()) + while (pc != nullptr && pc->type != CT_BRACE_OPEN) { - if (next->level == pc->level) - { - if (chunk_is_token(next, CT_WORD)) - { - chunk_flags_set(next, flags); - flags &= ~PCF_VAR_1ST; // clear the first flag for the next items - LOG_FMT(LCASTS, "%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()); - } + LOG_FMT(LFTOR, " [%s]", pc->text()); - if ( chunk_is_token(next, CT_STAR) - || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET))) - { - set_chunk_type(next, CT_PTR_TYPE); - } + flags = mark_where_chunk(pc, start->type, flags); - // If we hit a comma in a function param, we are done - if ( (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE)) - && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL))) - { - return; - } + if (!flags.test(PCF_IN_WHERE_SPEC) && chunk_is_str(pc, ":", 1)) + { + set_chunk_type(pc, CT_CLASS_COLON); + flags |= PCF_IN_CLASS_BASE; + LOG_FMT(LFTOR, "%s(%d): class colon on line %zu\n", + __func__, __LINE__, pc->orig_line); } - next = chunk_get_next_ncnl(next); - } - if ( next != nullptr - && chunk_is_token(next, CT_SEMICOLON)) - { - set_chunk_parent(next, pc->type); + if (chunk_is_semicolon(pc)) + { + LOG_FMT(LFTOR, "%s(%d): bailed on semicolon on line %zu\n", + __func__, __LINE__, pc->orig_line); + return; + } + chunk_flags_set(pc, flags); + pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); } -} // fix_enum_struct_union - -static void fix_typedef(chunk_t *start) -{ - LOG_FUNC_ENTRY(); - - if (start == nullptr) + if (pc == nullptr) { + LOG_FMT(LFTOR, "%s(%d): bailed on NULL\n", __func__, __LINE__); return; } - LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n", - __func__, __LINE__, start->orig_line, start->orig_col); + set_paren_parent(pc, start->type); + chunk_flags_set(pc, PCF_IN_CLASS); - chunk_t *the_type = nullptr; - chunk_t *last_op = nullptr; + pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); + LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n", + __func__, __LINE__, pclass->text()); - /* - * Mark everything in the typedef and scan for ")(", which makes it a - * function type - */ - for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC) - ; next != nullptr && next->level >= start->level - ; next = chunk_get_next_ncnl(next, scope_e::PREPROC)) + while (pc != nullptr) { - chunk_flags_set(next, PCF_IN_TYPEDEF); + LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + chunk_flags_set(pc, PCF_IN_CLASS); - if (start->level == next->level) + if ( pc->brace_level > level + || pc->level > pc->brace_level + || pc->flags.test(PCF_IN_PREPROC)) { - if (chunk_is_semicolon(next)) - { - set_chunk_parent(next, CT_TYPEDEF); - break; - } + pc = chunk_get_next_ncnl(pc); + continue; + } - if (chunk_is_token(next, CT_ATTRIBUTE)) - { - break; - } + if (chunk_is_token(pc, CT_BRACE_CLOSE) && pc->brace_level < level) + { + LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, Hit brace close\n", + __func__, __LINE__, pc->orig_line); + pc = chunk_get_next_ncnl(pc, scope_e::PREPROC); - if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN)) + if (chunk_is_token(pc, CT_SEMICOLON)) { - set_chunk_parent(next, CT_TYPEDEF); - break; + set_chunk_parent(pc, start->type); } - make_type(next); + return; + } + next = chunk_get_next_ncnl(pc, scope_e::PREPROC); - if (chunk_is_token(next, CT_TYPE)) + if (chunkstack_match(cs, pc)) + { + LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); + // Issue #1333 Formatter removes semicolon after variable initializer at class level(C#) + // if previous chunk is 'new' operator it is variable initializer not a CLASS_FUNC_DEF. + chunk_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC); // Issue #2279 + LOG_FMT(LFTOR, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type)); + + // Issue #1003, next->type should not be CT_FPAREN_OPEN + if ( prev != nullptr + && (prev->type != CT_NEW)) { - the_type = next; + bool is_func_class_def = false; + + if (chunk_is_token(next, CT_PAREN_OPEN)) + { + is_func_class_def = true; + } + else if (chunk_is_token(next, CT_ANGLE_OPEN)) // Issue # 1737 + { + chunk_t *closeAngle = chunk_skip_to_match(next); + chunk_t *afterTemplate = chunk_get_next(closeAngle); + + if (chunk_is_token(afterTemplate, CT_PAREN_OPEN)) + { + is_func_class_def = true; + } + } + else + { + LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); + make_type(pc); + } + + if (is_func_class_def) + { + set_chunk_type(pc, CT_FUNC_CLASS_DEF); + LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s, Marked CTor/DTor\n", + __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type)); + mark_cpp_constructor(pc); + } } - chunk_flags_clr(next, PCF_VAR_1ST_DEF); + } + pc = next; + } +} // mark_class_ctor - if (*next->str.c_str() == '(') + +static chunk_t *skip_align(chunk_t *start) +{ + chunk_t *pc = start; + + if (chunk_is_token(pc, CT_ALIGN)) + { + pc = chunk_get_next_ncnl(pc); + + if (chunk_is_token(pc, CT_PAREN_OPEN)) + { + pc = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level); + pc = chunk_get_next_ncnl(pc); + + if (chunk_is_token(pc, CT_COLON)) { - last_op = next; + pc = chunk_get_next_ncnl(pc); } } } + return(pc); +} - // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def - if ( last_op != nullptr - && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM)) + +chunk_t *skip_parent_types(chunk_t *colon) +{ + auto pc = chunk_get_next_ncnlnp(colon); + + while (pc) { - flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false); - fix_fcn_def_params(last_op); + // Skip access specifier + if (chunk_is_token(pc, CT_ACCESS)) + { + pc = chunk_get_next_ncnlnp(pc); + continue; + } + + // Check for a type name + if (!(chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))) + { + LOG_FMT(LPCU, + "%s is confused; expected a word at %zu:%zu " + "following type list at %zu:%zu\n", __func__, + colon->orig_line, colon->orig_col, + pc->orig_line, pc->orig_col); + return(colon); + } + // Get next token + auto next = skip_template_next(chunk_get_next_ncnlnp(pc)); + + if (chunk_is_token(next, CT_DC_MEMBER) || chunk_is_token(next, CT_COMMA)) + { + pc = chunk_get_next_ncnlnp(next); + } + else if (next) + { + LOG_FMT(LPCU, "%s -> %zu:%zu ('%s')\n", __func__, + next->orig_line, next->orig_col, next->text()); + return(next); + } + else + { + break; + } + } + LOG_FMT(LPCU, "%s: did not find end of type list (start was %zu:%zu)\n", + __func__, colon->orig_line, colon->orig_col); + return(colon); +} // skip_parent_types - the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC); // Issue #2279 - if (the_type == nullptr) +static void mark_struct_union_body(chunk_t *start) +{ + LOG_FUNC_ENTRY(); + chunk_t *pc = start; + + while ( pc != nullptr + && pc->level >= start->level + && !(pc->level == start->level && chunk_is_token(pc, CT_BRACE_CLOSE))) + { + if ( chunk_is_token(pc, CT_BRACE_OPEN) + || chunk_is_token(pc, CT_BRACE_CLOSE) + || chunk_is_token(pc, CT_SEMICOLON)) { - return; + pc = chunk_get_next_ncnl(pc); + + if (pc == nullptr) + { + break; + } } - chunk_t *open_paren = nullptr; - if (chunk_is_paren_close(the_type)) + if (chunk_is_token(pc, CT_ALIGN)) { - open_paren = chunk_skip_to_match_rev(the_type); - mark_function_type(the_type); - the_type = chunk_get_prev_ncnlni(the_type, scope_e::PREPROC); // Issue #2279 + pc = skip_align(pc); // "align(x)" or "align(x):" - if (the_type == nullptr) + if (pc == nullptr) { - return; + break; } } else { - // must be: "typedef <return type>func(params);" - set_chunk_type(the_type, CT_FUNC_TYPE); + pc = fix_variable_definition(pc); + + if (pc == nullptr) + { + break; + } } - set_chunk_parent(the_type, CT_TYPEDEF); + } +} // mark_struct_union_body - LOG_FMT(LTYPEDEF, "%s(%d): fcn typedef text() '%s', on orig_line %zu\n", - __func__, __LINE__, the_type->text(), the_type->orig_line); - // If we are aligning on the open parenthesis, grab that instead - log_rule_B("align_typedef_func"); +void mark_comments(void) +{ + LOG_FUNC_ENTRY(); - if (open_paren != nullptr && options::align_typedef_func() == 1) - { - the_type = open_paren; - } - log_rule_B("align_typedef_func"); + cpd.unc_stage = unc_stage_e::MARK_COMMENTS; - if (options::align_typedef_func() != 0) - { - LOG_FMT(LTYPEDEF, "%s(%d): -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n", - __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col); - chunk_flags_set(the_type, PCF_ANCHOR); - } - // already did everything we need to do - return; - } - /* - * Skip over enum/struct/union stuff, as we know it isn't a return type - * for a function type - */ - chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC); + bool prev_nl = true; + chunk_t *cur = chunk_get_head(); - if (after == nullptr) + while (cur != nullptr) { - return; - } + chunk_t *next = chunk_get_next_nvb(cur); + bool next_nl = (next == nullptr) || chunk_is_newline(next); - if ( after->type != CT_ENUM - && after->type != CT_STRUCT - && after->type != CT_UNION) - { - if (the_type != nullptr) + if (chunk_is_comment(cur)) { - // We have just a regular typedef - LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n", - __func__, __LINE__, the_type->text(), the_type->orig_line); - chunk_flags_set(the_type, PCF_ANCHOR); + 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); + } } - return; + prev_nl = chunk_is_newline(cur); + cur = next; } - // We have a struct/union/enum, next should be either a type or { - chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC); +} - if (next == nullptr) - { - return; - } - if (chunk_is_token(next, CT_TYPE)) - { - next = chunk_get_next_ncnl(next, scope_e::PREPROC); +static void mark_define_expressions(void) +{ + LOG_FUNC_ENTRY(); - if (next == nullptr) - { - return; - } - } + bool in_define = false; + bool first = true; + chunk_t *pc = chunk_get_head(); + chunk_t *prev = pc; - if (chunk_is_token(next, CT_BRACE_OPEN)) + while (pc != nullptr) { - // Skip to the closing brace - chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC); - - if (br_c != nullptr) + if (!in_define) { - const c_token_t tag = after->type; - set_chunk_parent(next, tag); - set_chunk_parent(br_c, tag); - - if (tag == CT_ENUM) + if ( chunk_is_token(pc, CT_PP_DEFINE) + || chunk_is_token(pc, CT_PP_IF) + || chunk_is_token(pc, CT_PP_ELSE)) { - flag_series(after, br_c, PCF_IN_ENUM); + in_define = true; + first = true; } - else if (tag == CT_STRUCT) + } + else + { + if (!pc->flags.test(PCF_IN_PREPROC) || chunk_is_token(pc, CT_PREPROC)) { - flag_series(after, br_c, PCF_IN_STRUCT); + in_define = false; + } + else + { + if ( pc->type != CT_MACRO + && ( first + || chunk_is_token(prev, CT_PAREN_OPEN) + || chunk_is_token(prev, CT_ARITH) + || chunk_is_token(prev, CT_CARET) + || chunk_is_token(prev, CT_ASSIGN) + || chunk_is_token(prev, CT_COMPARE) + || chunk_is_token(prev, CT_RETURN) + || chunk_is_token(prev, CT_GOTO) + || chunk_is_token(prev, CT_CONTINUE) + || chunk_is_token(prev, CT_FPAREN_OPEN) + || chunk_is_token(prev, CT_SPAREN_OPEN) + || chunk_is_token(prev, CT_BRACE_OPEN) + || chunk_is_semicolon(prev) + || chunk_is_token(prev, CT_COMMA) + || chunk_is_token(prev, CT_COLON) + || chunk_is_token(prev, CT_QUESTION))) + { + chunk_flags_set(pc, PCF_EXPR_START); + first = false; + } } } + prev = pc; + pc = chunk_get_next(pc); } - - if (the_type != nullptr) - { - LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n", - __func__, __LINE__, get_token_name(after->type), the_type->text(), - the_type->orig_line); - chunk_flags_set(the_type, PCF_ANCHOR); - } -} // fix_typedef - - -//static void mark_variable_stack(ChunkStack &cs, log_sev_t sev) -//{ -// UNUSED(sev); -// LOG_FUNC_ENTRY(); -// -// // throw out the last word and mark the rest -// chunk_t *var_name = cs.Pop_Back(); -// -// if (var_name && var_name->prev->type == CT_DC_MEMBER) -// { -// cs.Push_Back(var_name); -// } -// -// if (var_name != nullptr) -// { -// LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n", -// __func__, __LINE__, var_name->orig_line, var_name->orig_col); -// -// size_t word_cnt = 0; -// chunk_t *word_type; -// -// while ((word_type = cs.Pop_Back()) != nullptr) -// { -// if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE)) -// { -// LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", -// __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text()); -// set_chunk_type(word_type, CT_TYPE); -// chunk_flags_set(word_type, PCF_VAR_TYPE); -// } -// word_cnt++; -// } -// -// if (chunk_is_token(var_name, CT_WORD)) -// { -// if (word_cnt > 0) -// { -// LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n", -// __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); -// chunk_flags_set(var_name, PCF_VAR_DEF); -// } -// else -// { -// LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n", -// __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text()); -// set_chunk_type(var_name, CT_TYPE); -// chunk_flags_set(var_name, PCF_VAR_TYPE); -// } -// } -// } -//} // mark_variable_stack - - -//static void fix_fcn_def_params(chunk_t *start) -//{ -// LOG_FUNC_ENTRY(); -// -// if (start == nullptr) -// { -// return; -// } -// LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n", -// __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level); -// -// while (start != nullptr && !chunk_is_paren_open(start)) -// { -// start = chunk_get_next_ncnl(start); -// } -// -// if (start == nullptr)// Coverity CID 76003, 1100782 -// { -// return; -// } -// // ensure start chunk holds a single '(' character -// assert((start->len() == 1) && (start->str[0] == '(')); -// -// ChunkStack cs; -// size_t level = start->level + 1; -// chunk_t *pc = start; -// -// while ((pc = chunk_get_next_ncnl(pc)) != nullptr) -// { -// if ( ((start->len() == 1) && (start->str[0] == ')')) -// || pc->level < level) -// { -// LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n", -// __func__, __LINE__, pc->text(), pc->orig_line); -// break; -// } -// LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n", -// __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at", -// pc->text(), pc->orig_line, pc->level); -// -// if (pc->level > level) -// { -// continue; -// } -// -// if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc)) -// { -// set_chunk_type(pc, CT_PTR_TYPE); -// cs.Push_Back(pc); -// } -// else if ( chunk_is_token(pc, CT_AMP) -// || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2))) -// { -// set_chunk_type(pc, CT_BYREF); -// cs.Push_Back(pc); -// } -// else if (chunk_is_token(pc, CT_TYPE_WRAP)) -// { -// cs.Push_Back(pc); -// } -// else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)) -// { -// cs.Push_Back(pc); -// } -// else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN)) -// { -// mark_variable_stack(cs, LFCNP); -// -// if (chunk_is_token(pc, CT_ASSIGN)) -// { -// // Mark assignment for default param spacing -// set_chunk_parent(pc, CT_FUNC_PROTO); -// } -// } -// } -// mark_variable_stack(cs, LFCNP); -//} // fix_fcn_def_params +} // mark_define_expressions static void handle_cpp_template(chunk_t *pc) @@ -3401,6 +5929,49 @@ static void handle_cpp_lambda(chunk_t *sq_o) } // handle_cpp_lambda +static chunk_t *get_d_template_types(ChunkStack &cs, chunk_t *open_paren) +{ + LOG_FUNC_ENTRY(); + chunk_t *tmp = open_paren; + bool maybe_type = true; + + while ( ((tmp = chunk_get_next_ncnl(tmp)) != nullptr) + && tmp->level > open_paren->level) + { + if (chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD)) + { + if (maybe_type) + { + make_type(tmp); + cs.Push_Back(tmp); + } + maybe_type = false; + } + else if (chunk_is_token(tmp, CT_COMMA)) + { + maybe_type = true; + } + } + return(tmp); +} + + +static bool chunkstack_match(ChunkStack &cs, chunk_t *pc) +{ + for (size_t idx = 0; idx < cs.Len(); idx++) + { + chunk_t *tmp = cs.GetChunk(idx); + + if (pc->str.equals(tmp->str)) + { + return(true); + } + } + + return(false); +} + + static void handle_d_template(chunk_t *pc) { LOG_FUNC_ENTRY(); @@ -3464,6 +6035,94 @@ static void handle_d_template(chunk_t *pc) } // handle_d_template +static void mark_template_func(chunk_t *pc, chunk_t *pc_next) +{ + LOG_FUNC_ENTRY(); + + // We know angle_close must be there... + chunk_t *angle_close = chunk_get_next_type(pc_next, CT_ANGLE_CLOSE, pc->level); + chunk_t *after = chunk_get_next_ncnl(angle_close); + + if (after != nullptr) + { + if (chunk_is_str(after, "(", 1)) + { + if (angle_close->flags.test(PCF_IN_FCN_CALL)) + { + LOG_FMT(LTEMPFUNC, "%s(%d): marking '%s' in line %zu as a FUNC_CALL\n", + __func__, __LINE__, pc->text(), pc->orig_line); + LOG_FMT(LFCN, "%s(%d): (16) 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); + flag_parens(after, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false); + } + else + { + /* + * Might be a function def. Must check what is before the template: + * Func call: + * BTree.Insert(std::pair<int, double>(*it, double(*it) + 1.0)); + * a = Test<int>(j); + * std::pair<int, double>(*it, double(*it) + 1.0)); + */ + + LOG_FMT(LTEMPFUNC, "%s(%d): marking '%s' in line %zu as a FUNC_CALL 2\n", + __func__, __LINE__, pc->text(), pc->orig_line); + // its a function!!! + LOG_FMT(LFCN, "%s(%d): (17) 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); + mark_function(pc); + } + } + else if (chunk_is_token(after, CT_WORD)) + { + // its a type! + set_chunk_type(pc, CT_TYPE); + chunk_flags_set(pc, PCF_VAR_TYPE); + chunk_flags_set(after, PCF_VAR_DEF); + } + } +} // mark_template_func + + +static void mark_exec_sql(chunk_t *pc) +{ + LOG_FUNC_ENTRY(); + chunk_t *tmp; + + // Change CT_WORD to CT_SQL_WORD + for (tmp = chunk_get_next(pc); tmp != nullptr; tmp = chunk_get_next(tmp)) + { + set_chunk_parent(tmp, pc->type); + + if (chunk_is_token(tmp, CT_WORD)) + { + set_chunk_type(tmp, CT_SQL_WORD); + } + + if (chunk_is_token(tmp, CT_SEMICOLON)) + { + break; + } + } + + if ( pc->type != CT_SQL_BEGIN + || tmp == nullptr + || tmp->type != CT_SEMICOLON) + { + return; + } + + for (tmp = chunk_get_next(tmp); + tmp != nullptr && tmp->type != CT_SQL_END; + tmp = chunk_get_next(tmp)) + { + tmp->level++; + } +} + + chunk_t *skip_template_next(chunk_t *ang_open) { if (chunk_is_token(ang_open, CT_ANGLE_OPEN)) @@ -3475,6 +6134,66 @@ chunk_t *skip_template_next(chunk_t *ang_open) } +chunk_t *skip_template_prev(chunk_t *ang_close) +{ + if (chunk_is_token(ang_close, CT_ANGLE_CLOSE)) + { + chunk_t *pc = chunk_get_prev_type(ang_close, CT_ANGLE_OPEN, ang_close->level); + return(chunk_get_prev_ncnlni(pc)); // Issue #2279 + } + return(ang_close); +} + + +chunk_t *skip_tsquare_next(chunk_t *ary_def) +{ + if (chunk_is_token(ary_def, CT_SQUARE_OPEN) || chunk_is_token(ary_def, CT_TSQUARE)) + { + return(chunk_get_next_nisq(ary_def)); + } + return(ary_def); +} + + +chunk_t *skip_attribute_next(chunk_t *attr) +{ + chunk_t *pc = attr; + + while (chunk_is_token(pc, CT_ATTRIBUTE)) + { + pc = chunk_get_next_ncnl(pc); + + if (chunk_is_token(pc, CT_FPAREN_OPEN)) + { + pc = chunk_get_next_type(pc, CT_FPAREN_CLOSE, pc->level); + pc = chunk_get_next_ncnl(pc); + } + } + return(pc); +} + + +chunk_t *skip_attribute_prev(chunk_t *fp_close) +{ + chunk_t *pc = fp_close; + + while (true) + { + if ( chunk_is_token(pc, CT_FPAREN_CLOSE) + && get_chunk_parent_type(pc) == CT_ATTRIBUTE) + { + pc = chunk_get_prev_type(pc, CT_ATTRIBUTE, pc->level); + } + else if (chunk_is_not_token(pc, CT_ATTRIBUTE)) + { + break; + } + pc = chunk_get_prev_ncnlni(pc); // Issue #2279 + } + return(pc); +} + + static void handle_oc_class(chunk_t *pc) { enum class angle_state_e : unsigned int