diff options
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.75.0/src/combine_fix_mark.cpp')
-rw-r--r-- | debian/uncrustify-trinity/uncrustify-trinity-0.75.0/src/combine_fix_mark.cpp | 2617 |
1 files changed, 2617 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.75.0/src/combine_fix_mark.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.75.0/src/combine_fix_mark.cpp new file mode 100644 index 00000000..7c6ce813 --- /dev/null +++ b/debian/uncrustify-trinity/uncrustify-trinity-0.75.0/src/combine_fix_mark.cpp @@ -0,0 +1,2617 @@ +/** + * @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 <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 *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<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) + && 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()) ? "<null chunk>" : 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 = 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<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 + + +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 |