/** * @file combine_fix_mark.cpp * * @author Guy Maurel * @license GPL v2+ * extract fom combine.cpp */ #include "combine_fix_mark.h" #include "combine_skip.h" #include "combine_tools.h" #include "flag_parens.h" #include "log_rules.h" constexpr static auto LCURRENT = LCOMBINE; void fix_casts(Chunk *start) { LOG_FUNC_ENTRY(); Chunk *pc; Chunk *prev; Chunk *first; Chunk *after; Chunk *last = nullptr; Chunk *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 = start->GetPrevNcNnlNi(); // Issue #2279 if (prev->IsNullChunk()) { 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 = start->GetNextNcNnl(); first = pc; while ( pc->IsNotNullChunk() && ( 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 | LANG_CS | LANG_VALA | LANG_CPP)) || ( ( chunk_is_token(pc, CT_QUESTION) || chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_MEMBER)) && language_is_set(LANG_JAVA | LANG_CS | LANG_VALA)) || ( chunk_is_token(pc, CT_COMMA) && language_is_set(LANG_CPP)) || 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 = pc->GetNextNcNnl(); count++; } if ( pc->IsNullChunk() || chunk_is_not_token(pc, 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->IsNullChunk() ? "Null chunk" : 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 | LANG_CS | LANG_VALA | LANG_CPP))) { 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")) { 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 = paren_close->GetNextNcNnl(); after = pc; do { after = after->GetNextNcNnl(); } while (chunk_is_token(after, CT_PAREN_OPEN)); if (after->IsNullChunk()) { LOG_FMT(LCASTS, "%s(%d): -- not a cast - hit null chunk\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 ( ( chunk_is_not_token(after, CT_NUMBER) && chunk_is_not_token(after, CT_NUMBER_FP)) || doubtful_cast) { nope = true; } } else if ( chunk_is_not_token(pc, CT_NUMBER_FP) && chunk_is_not_token(pc, CT_NUMBER) && chunk_is_not_token(pc, CT_WORD) && chunk_is_not_token(pc, CT_THIS) && chunk_is_not_token(pc, CT_TYPE) && chunk_is_not_token(pc, CT_PAREN_OPEN) && chunk_is_not_token(pc, CT_STRING) && chunk_is_not_token(pc, CT_DECLTYPE) && chunk_is_not_token(pc, CT_SIZEOF) && get_chunk_parent_type(pc) != CT_SIZEOF && chunk_is_not_token(pc, CT_FUNC_CALL) && chunk_is_not_token(pc, CT_FUNC_CALL_USER) && chunk_is_not_token(pc, CT_FUNCTION) && chunk_is_not_token(pc, 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 = paren_close->GetNextNcNnl(); if (pc->IsNullChunk()) { 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->IsNotNullChunk() && pc != paren_close; pc = pc->GetNextNcNnl()) { 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 = paren_close->GetNextNcNnl(); if (pc->IsNotNullChunk()) { chunk_flags_set(pc, PCF_EXPR_START); if (chunk_is_opening_brace(pc)) { set_paren_parent(pc, get_chunk_parent_type(start)); } } } // fix_casts void fix_fcn_def_params(Chunk *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->IsNotNullChunk() && !chunk_is_paren_open(start)) { start = start->GetNextNcNnl(); } if (start->IsNullChunk()) // 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 *pc = start->GetNextNcNnl(); while (pc->IsNotNullChunk()) { 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) { pc = pc->GetNextNcNnl(); continue; } if ( pc->IsStar() || 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, "&&"))) { 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); } } pc = pc->GetNextNcNnl(); } mark_variable_stack(cs, LFCNP); } // fix_fcn_def_params void fix_type_cast(Chunk *start) { LOG_FUNC_ENTRY(); if (start == nullptr) { return; } Chunk *pc = start->GetNextNcNnl(); if ( pc->IsNullChunk() || chunk_is_not_token(pc, CT_ANGLE_OPEN)) { return; } pc = pc->GetNextNcNnl(); while ( pc->IsNotNullChunk() && pc->level >= start->level) { if ( pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE)) { pc = pc->GetNextNcNnl(); if (pc->IsNullChunk()) { return; } if (chunk_is_str(pc, "(")) { set_paren_parent(pc, CT_TYPE_CAST); } return; } make_type(pc); pc = pc->GetNextNcNnl(); } } // fix_type_cast void fix_typedef(Chunk *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 *the_type = Chunk::NullChunkPtr; Chunk *last_op = Chunk::NullChunkPtr; /* * Mark everything in the typedef and scan for ")(", which makes it a * function type */ for (Chunk *next = start->GetNextNcNnl(E_Scope::PREPROC) ; next->IsNotNullChunk() && next->level >= start->level ; next = next->GetNextNcNnl(E_Scope::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->IsNotNullChunk() && !( 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 = last_op->GetPrevNcNnlNi(E_Scope::PREPROC); // Issue #2279 if (the_type->IsNullChunk()) { return; } Chunk *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 = the_type->GetPrevNcNnlNi(E_Scope::PREPROC); // Issue #2279 if (the_type->IsNullChunk()) { return; } } else { // must be: "typedef 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 *after = start->GetNextNcNnl(E_Scope::PREPROC); if (after->IsNullChunk()) { return; } if ( chunk_is_not_token(after, CT_ENUM) && chunk_is_not_token(after, CT_STRUCT) && chunk_is_not_token(after, CT_UNION)) { if ( the_type != nullptr && the_type->IsNotNullChunk()) { // 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 *next = after->GetNextNcNnl(E_Scope::PREPROC); if (next->IsNullChunk()) { return; } if (chunk_is_token(next, CT_TYPE)) { next = next->GetNextNcNnl(E_Scope::PREPROC); if (next->IsNullChunk()) { return; } } if (chunk_is_token(next, CT_BRACE_OPEN)) { // Skip to the closing brace Chunk *br_c = next->GetNextType(CT_BRACE_CLOSE, next->level, E_Scope::PREPROC); if (br_c->IsNotNullChunk()) { const E_Token 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 && the_type->IsNotNullChunk()) { 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 Chunk *fix_variable_definition(Chunk *start) { LOG_FUNC_ENTRY(); Chunk *pc = start; Chunk *end; Chunk *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_token(pc, CT_PP) // Issue #3169 || 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 = pc->GetNextNcNnl(); if (pc->IsNullChunk()) { LOG_FMT(LFVD, "%s(%d): pc is null chunk\n", __func__, __LINE__); return(Chunk::NullChunkPtr); } 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->IsNullChunk()) { LOG_FMT(LFVD, "%s(%d): pc is null chunk\n", __func__, __LINE__); return(Chunk::NullChunkPtr); } 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->IsNullChunk()) { LOG_FMT(LFVD, "%s(%d): pc is null chunk\n", __func__, __LINE__); return(Chunk::NullChunkPtr); } 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); if (pc->IsNotNullChunk()) { LOG_FMT(LFVD, "%s(%d): 5:pc->Text() '%s', type is %s\n", __func__, __LINE__, pc->Text(), get_token_name(pc->type)); } else { pc = Chunk::NullChunkPtr; } } } end = pc; if (end->IsNullChunk()) { LOG_FMT(LFVD, "%s(%d): end is null chunk\n", __func__, __LINE__); return(nullptr); } LOG_FMT(LFVD, "%s(%d): end->type is %s\n", __func__, __LINE__, get_token_name(end->type)); if (chunk_is_token(end, CT_FUNC_CTOR_VAR)) // Issue #3010 { return(end); } 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 ( chunk_is_not_token(tmp_pc, CT_DC_MEMBER) && chunk_is_not_token(tmp_pc, CT_MEMBER)) { break; } idx--; tmp_pc = cs.Get(idx)->m_pc; if ( chunk_is_not_token(tmp_pc, CT_WORD) && chunk_is_not_token(tmp_pc, 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(end->GetNextNcNnl()); } return(skip_to_next_statement(end)); } // fix_variable_definition void mark_cpp_constructor(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *paren_open; Chunk *tmp; Chunk *after; Chunk *var; bool is_destr = false; tmp = pc->GetPrevNcNnlNi(); // 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(pc->GetNextNcNnl()); if (!chunk_is_str(paren_open, "(")) { 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->IsNotNullChunk() && ( chunk_is_not_token(tmp, 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 = tmp->GetNextNcNnl(); if ( chunk_is_str(tmp, ":") && 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(tmp->GetPrevNcNnlNi()); // 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); } tmp = pc->GetPrevNcNnlNi(); // Issue #2907 if (chunk_is_token(tmp, CT_DESTRUCTOR)) { set_chunk_parent(tmp, pc->type); tmp = tmp->GetPrevNcNnlNi(); } while (chunk_is_token(tmp, CT_QUALIFIER)) { set_chunk_parent(tmp, pc->type); tmp = tmp->GetPrevNcNnlNi(); } } // mark_cpp_constructor void mark_cpp_lambda(Chunk *square_open) { if ( chunk_is_token(square_open, CT_SQUARE_OPEN) && get_chunk_parent_type(square_open) == CT_CPP_LAMBDA) { auto *brace_close = square_open->GetNextType(CT_BRACE_CLOSE, square_open->level); if (get_chunk_parent_type(brace_close) == CT_CPP_LAMBDA) { for (auto *pc = square_open; pc != brace_close; pc = pc->GetNextNcNnl()) { chunk_flags_set(pc, PCF_IN_LAMBDA); } } } } // mark_cpp_lambda void mark_define_expressions(void) { LOG_FUNC_ENTRY(); bool in_define = false; bool first = true; Chunk *pc = Chunk::GetHead(); Chunk *prev = pc; while (pc->IsNotNullChunk()) { 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 ( chunk_is_not_token(pc, CT_MACRO) && ( first || chunk_is_token(prev, CT_PAREN_OPEN) || chunk_is_token(prev, CT_ARITH) || chunk_is_token(prev, CT_SHIFT) || 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 = pc->GetNext(); } } // mark_define_expressions void mark_exec_sql(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *tmp; // Change CT_WORD to CT_SQL_WORD for (tmp = pc->GetNext(); tmp != nullptr && tmp->IsNotNullChunk(); tmp = tmp->GetNext()) { 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 ( chunk_is_not_token(pc, CT_SQL_BEGIN) || tmp->IsNullChunk() || chunk_is_not_token(tmp, CT_SEMICOLON)) { return; } for (tmp = tmp->GetNext(); tmp->IsNotNullChunk() && chunk_is_not_token(tmp, CT_SQL_END); tmp = tmp->GetNext()) { tmp->level++; } } // mark_exec_sql void mark_function_return_type(Chunk *fname, Chunk *start, E_Token parent_type) { LOG_FUNC_ENTRY(); Chunk *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 *first = pc; while (pc->IsNotNullChunk()) { 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->IsNullChunk() || chunk_is_token(pc, CT_TEMPLATE)) { //either expression is not complete or this is smth like 'template void func()' // - we are not interested in 'template' part break; } else { //this is smth like 'vector func()' and 'pc' is currently on 'vector' - just proceed } } if ( ( !chunk_is_type(pc) && chunk_is_not_token(pc, CT_OPERATOR) && chunk_is_not_token(pc, CT_WORD) && chunk_is_not_token(pc, CT_ADDR)) || pc->flags.test(PCF_IN_PREPROC)) { break; } if (!chunk_is_ptr_operator(pc)) { first = pc; } pc = pc->GetPrevNcNnlNi(); // 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->IsNotNullChunk()) { 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 *prev = pc->GetPrevNcNnlNi(); // Issue #2279 if ( !is_return_tuple || chunk_is_not_token(pc, CT_WORD) || ( prev->IsNullChunk() && chunk_is_not_token(prev, CT_TYPE))) { make_type(pc); } if (pc == start) { break; } pc = pc->GetNextNcNnl(); //template angles should keep parent type CT_TEMPLATE if (chunk_is_token(pc, CT_ANGLE_OPEN)) { pc = pc->GetNextType(CT_ANGLE_CLOSE, pc->level); if (pc == start) { break; } pc = pc->GetNextNcNnl(); } } 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 = first->GetPrevNcNnlNi(); // 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 = pc->GetPrevNcNnlNi(); // 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 void mark_function(Chunk *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 *prev = pc->GetPrevNcNnlNi(); // Issue #2279 Chunk *next = pc->GetNextNppOrNcNnl(); if (next->IsNullChunk()) { return; } Chunk *tmp; Chunk *semi = nullptr; Chunk *paren_open; Chunk *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 *pc_op = pc->GetPrevType(CT_OPERATOR, pc->level); if ( pc_op->IsNotNullChunk() && 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 = tmp->GetPrevNcNnlNi())->IsNotNullChunk()) // 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 && chunk_is_not_token(pc, CT_FUNC_CALL)) { // Mark the return type tmp = tmp->GetNextNcNnl(); while ( tmp != pc && tmp->IsNotNullChunk()) { make_type(tmp); // Mark the return type tmp = tmp->GetNextNcNnl(); } } } } if ( chunk_is_ptr_operator(next) || chunk_is_newline(next)) { next = next->GetNextNppOrNcNnl(); if (next->IsNullChunk()) { 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 = pc->GetNextString("(", 1, pc->level); paren_close = paren_open->GetNextString(")", 1, pc->level); if ( paren_open->IsNullChunk() || paren_close->IsNullChunk()) { 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 = paren_close->GetNextNcNnl(); if ( tmp->IsNotNullChunk() && chunk_is_str(tmp, "(")) { Chunk *tmp1; Chunk *tmp2; Chunk *tmp3; // skip over any leading class/namespace in: "T(F::*A)();" tmp1 = next->GetNextNcNnl(); while (tmp1->IsNotNullChunk()) { tmp2 = tmp1->GetNextNcNnl(); if ( !chunk_is_word(tmp1) || chunk_is_not_token(tmp2, CT_DC_MEMBER)) { break; } tmp1 = tmp2->GetNextNcNnl(); } tmp2 = tmp1->GetNextNcNnl(); if (chunk_is_str(tmp2, ")")) { tmp3 = tmp2; tmp2 = Chunk::NullChunkPtr; } else { tmp3 = tmp2->GetNextNcNnl(); } tmp3 = chunk_get_next_ssq(tmp3); if ( chunk_is_str(tmp3, ")") && ( tmp1->IsStar() || chunk_is_msref(tmp1) || ( language_is_set(LANG_OC) && chunk_is_token(tmp1, CT_CARET))) && ( tmp2->IsNullChunk() || chunk_is_token(tmp2, CT_WORD))) { if (tmp2->IsNotNullChunk()) { 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->IsNotNullChunk()) { 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 *temp = pc->GetNextType(CT_ASSIGN, pc->level); if (temp->IsNotNullChunk()) { 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)); } else { prev = Chunk::NullChunkPtr; } // Check for C++ function def if ( chunk_is_token(pc, CT_FUNC_CLASS_DEF) || ( prev->IsNotNullChunk() && ( chunk_is_token(prev, CT_INV) || chunk_is_token(prev, CT_DC_MEMBER)))) { Chunk *destr = Chunk::NullChunkPtr; 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 = prev->GetPrevNcNnlNpp(); } if (chunk_is_token(prev, CT_DC_MEMBER)) { prev = prev->GetPrevNcNnlNpp(); if (prev->IsNotNullChunk()) { 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->IsNotNullChunk()) ? "DE" : "CON", prev->Text(), get_token_name(prev->type)); mark_cpp_constructor(pc); return; } // Point to the item previous to the class name prev = prev->GetPrevNcNnlNpp(); } } } /* * 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->IsNullChunk()) { LOG_FMT(LFCN, "%s(%d): Checking func call: prev is null chunk\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->IsNotNullChunk()) { 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 = prev->GetPrevNcNnlNpp(); 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); if (prev != nullptr) { prev = prev->GetPrev(); } if (chunk_is_token(prev, CT_DECLSPEC)) { if ( prev != nullptr && prev->IsNotNullChunk()) { prev = prev->GetPrev(); } } } // 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 = prev->GetPrevNcNnlNpp(); if ( prev->IsNullChunk() || ( chunk_is_not_token(prev, CT_WORD) && chunk_is_not_token(prev, CT_TYPE) && chunk_is_not_token(prev, CT_THIS))) { LOG_FMT(LFCN, "%s(%d): --? skipped MEMBER and landed on %s\n", __func__, __LINE__, (prev->IsNullChunk()) ? "" : get_token_name(prev->type)); break; } LOG_FMT(LFCN, "%s(%d): '%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 = prev->GetPrevNcNnlNpp(); if (prev->IsNullChunk()) { LOG_FMT(LFCN, "%s(%d): prev is null chunk\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->IsNullChunk()) { 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 *prev_prev = prev->GetPrevNcNnlNpp(); 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 ( chunk_is_not_token(prev, CT_OPERATOR) && chunk_is_not_token(prev, CT_TSQUARE) && chunk_is_not_token(prev, CT_ANGLE_CLOSE) && chunk_is_not_token(prev, CT_QUALIFIER) && chunk_is_not_token(prev, CT_TYPE) && chunk_is_not_token(prev, 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_SHIFT) || 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 = prev->GetPrevNcNnlNpp(); } } //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 *preproc = prev->GetNextNcNnl(); if (chunk_is_token(preproc, CT_PREPROC)) { size_t pp_level = preproc->pp_level; if (chunk_is_token(preproc->GetNextNcNnl(), CT_PP_ELSE)) { do { preproc = preproc->GetPrevNcNnlNi(); // Issue #2279 if (chunk_is_token(preproc, CT_PP_IF)) { preproc = preproc->GetPrevNcNnlNi(); // Issue #2279 if (preproc->pp_level == pp_level) { prev = preproc->GetPrevNcNnlNpp(); break; } } } while (preproc->IsNotNullChunk()); } } } if ( isa_def && prev != nullptr && prev->IsNotNullChunk() && ( ( 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) || chunk_is_token(prev, CT_ASSIGN) || chunk_is_token(prev, 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) && prev->GetNextNcNnl() == pc) { tmp = chunk_skip_to_match_rev(prev); if (tmp == nullptr) { tmp = Chunk::NullChunkPtr; } while ( tmp->IsNotNullChunk() // 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 = tmp->GetNextNcNnl(); } } 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->IsNullChunk()) { prev = Chunk::GetHead(); } for (tmp = prev; tmp->IsNotNullChunk() && tmp != pc; tmp = tmp->GetNextNcNnlNpp()) { 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 (chunk_is_not_token(pc, 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->GetNextNcNnl(); while (tmp->IsNotNullChunk()) { // 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; } } tmp = tmp->GetNextNcNnl(); } /* * 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->IsNotNullChunk() && !tmp->flags.test(PCF_STMT_START)) { tmp = tmp->GetPrevNcNnlNi(); // Issue #2279 } const bool is_extern = ( tmp->IsNotNullChunk() && tmp->str.equals("extern")); /* * Scan the parameters looking for: * - constant strings * - numbers * - non-type fields * - function calls */ Chunk *ref = paren_open->GetNextNcNnl(); Chunk *tmp2; bool is_param = true; tmp = ref; while (tmp != paren_close) { tmp2 = tmp->GetNextNcNnl(); 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 *br_open = pc->GetPrevType(CT_BRACE_OPEN, pc->brace_level - 1); if ( br_open->IsNotNullChunk() && 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 = pc->GetPrevNcNnlNi(); // Issue #2279 if ( !chunk_is_str(prev, "*") && !chunk_is_str(prev, "&")) { Chunk *p_op = pc->GetPrevType(CT_BRACE_OPEN, pc->brace_level - 1); if ( p_op->IsNotNullChunk() && 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 = next->GetNextNcNnl(); if (next->IsNullChunk()) { return; } } // Mark parameters and return type fix_fcn_def_params(next); mark_function_return_type(pc, pc->GetPrevNcNnlNi(), 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 = paren_close->GetNextNcNnl(); pcf_flags_t in_where_spec_flags = PCF_NONE; while ( tmp->IsNotNullChunk() && chunk_is_not_token(tmp, CT_BRACE_OPEN) && chunk_is_not_token(tmp, 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 = tmp->GetNextNcNnl(); } } // Find the brace pair and set the parent if (chunk_is_token(pc, CT_FUNC_DEF)) { tmp = paren_close->GetNextNcNnl(); while ( tmp->IsNotNullChunk() && chunk_is_not_token(tmp, 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 = tmp->GetNextNcNnl(); } 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 bool mark_function_type(Chunk *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 *ptrcnk = nullptr; Chunk *tmp; Chunk *apo; Chunk *apc; Chunk *aft; bool anon = false; E_Token pt, ptp; // Scan backwards across the name, which can only be a word and single star Chunk *varcnk = pc->GetPrevNcNnlNi(); // Issue #2279 varcnk = chunk_get_prev_ssq(varcnk); if ( varcnk->IsNotNullChunk() && !chunk_is_word(varcnk)) { if ( language_is_set(LANG_OC) && chunk_is_str(varcnk, "^") && chunk_is_paren_open(varcnk->GetPrevNcNnlNi())) // 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 = pc->GetNextNcNnl(); if (apo->IsNullChunk()) { 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 = apc->GetNextNcNnl(); 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 = tmp->GetPrevNcNnlNi())->IsNotNullChunk()) // 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 ( tmp->IsStar() || 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, "(")) { 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(tmp->GetPrevNcNnlNi())) // 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_FUNC_TYPE); // Issue #3402 } 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 = tmp->GetPrevNcNnlNi())->IsNotNullChunk()) // 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 = tmp->GetPrevNcNnlNi(); // 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 = pc->GetNextNcNnl(); 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 void mark_lvalue(Chunk *pc) { LOG_FUNC_ENTRY(); Chunk *prev; if (pc->flags.test(PCF_IN_PREPROC)) { return; } for (prev = pc->GetPrevNcNnlNi(); // Issue #2279 prev->IsNotNullChunk(); prev = prev->GetPrevNcNnlNi()) // Issue #2279 { if ( prev->level < pc->level || chunk_is_token(prev, CT_ACCESS_COLON) || chunk_is_token(prev, CT_ASSIGN) || chunk_is_token(prev, CT_BOOL) || chunk_is_token(prev, CT_COMMA) || chunk_is_cpp_inheritance_access_specifier(prev) || chunk_is_semicolon(prev) || chunk_is_str(prev, "(") || chunk_is_str(prev, "{") || chunk_is_str(prev, "[") || prev->flags.test(PCF_IN_PREPROC) || get_chunk_parent_type(prev) == CT_NAMESPACE || get_chunk_parent_type(prev) == CT_TEMPLATE) { break; } chunk_flags_set(prev, PCF_LVALUE); if ( prev->level == pc->level && chunk_is_str(prev, "&")) { make_type(prev); } } } // mark_lvalue void mark_struct_union_body(Chunk *start) { LOG_FUNC_ENTRY(); Chunk *pc = start; while ( pc->IsNotNullChunk() && 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 = pc->GetNextNcNnl(); if (pc->IsNullChunk()) { break; } } if (chunk_is_token(pc, CT_ALIGN)) { pc = skip_align(pc); // "align(x)" or "align(x):" if (pc->IsNullChunk()) { break; } } else if (chunk_is_token(pc, CT_AMP)) { pc = skip_expression(pc); } else { pc = fix_variable_definition(pc); if (pc->IsNullChunk()) { break; } } } } // mark_struct_union_body void mark_template_func(Chunk *pc, Chunk *pc_next) { LOG_FUNC_ENTRY(); // We know angle_close must be there... Chunk *angle_close = pc_next->GetNextType(CT_ANGLE_CLOSE, pc->level); Chunk *after = angle_close->GetNextNcNnl(); if (after->IsNotNullChunk()) { if (chunk_is_str(after, "(")) { 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(*it, double(*it) + 1.0)); * a = Test(j); * std::pair(*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 Chunk *mark_variable_definition(Chunk *start) { LOG_FUNC_ENTRY(); if (start == nullptr) { return(nullptr); } Chunk *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]\n" " 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 && ( pc->IsStar() || 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 = pc->GetNextNcNnl(); } return(pc); } // mark_variable_definition 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 *var_name = cs.Pop_Back(); if ( var_name != nullptr && var_name->GetPrev()->IsNotNullChunk() && var_name->GetPrev()->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 *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 pcf_flags_t mark_where_chunk(Chunk *pc, E_Token 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, ":")) { 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); } // mark_where_chunk