/** * Splits the parameters at every comma that is at the fparen level. * * @param start the offending token */ static void split_fcn_params_full(chunk_t *start) { LOG_FMT(LSPLIT, "%s", __func__); chunk_t *fpo; chunk_t *pc; /* Find the opening fparen */ fpo = start; while (((fpo = chunk_get_prev(fpo)) != NULL) && (fpo->type != CT_FPAREN_OPEN)) { /* do nothing */ } /* Now break after every comma */ pc = fpo; while ((pc = chunk_get_next_ncnl(pc)) != NULL) { if (pc->level <= fpo->level) { break; } if ((pc->level == (fpo->level + 1)) && (pc->type == CT_COMMA)) { split_before_chunk(chunk_get_next(pc)); } } }
static void split_fcn_params_full(chunk_t *start) { LOG_FUNC_ENTRY(); LOG_FMT(LSPLIT, "%s(%d): %s\n", __func__, __LINE__, start->text()); // Find the opening function parenthesis chunk_t *fpo = start; LOG_FMT(LSPLIT, " %s(%d): Find the opening function parenthesis\n", __func__, __LINE__); while ((fpo = chunk_get_prev(fpo)) != nullptr) { LOG_FMT(LSPLIT, "%s(%d): %s, orig_col is %zu, level is %zu\n", __func__, __LINE__, fpo->text(), fpo->orig_col, fpo->level); if (fpo->type == CT_FPAREN_OPEN && (fpo->level == start->level - 1)) { break; // opening parenthesis found. Issue #1020 } } // Now break after every comma chunk_t *pc = fpo; while ((pc = chunk_get_next_ncnl(pc)) != nullptr) { if (pc->level <= fpo->level) { break; } if ((pc->level == (fpo->level + 1)) && pc->type == CT_COMMA) { split_before_chunk(chunk_get_next(pc)); } } }
/** * Figures out where to split a function def/proto/call * * For fcn protos and defs. Also fcn calls where level == brace_level: * - find the open fparen * + if it doesn't have a newline right after it * * see if all parameters will fit individually after the paren * * if not, throw a newline after the open paren & return * - scan backwards to the open fparen or comma * + if there isn't a newline after that item, add one & return * + otherwise, add a newline before the start token * * @param start the offending token * @return the token that should have a newline * inserted before it */ static void split_fcn_params(chunk_t *start) { LOG_FMT(LSPLIT, " %s: ", __func__); chunk_t *next; chunk_t *prev; chunk_t *fpo; chunk_t *pc; /* Find the opening fparen */ fpo = start; while (((fpo = chunk_get_prev(fpo)) != NULL) && (fpo->type != CT_FPAREN_OPEN)) { /* do nothing */ } pc = chunk_get_next(fpo); if (!chunk_is_newline(pc)) { int min_col = pc->column; int max_width = 0; int cur_width = 0; int last_col = -1; LOG_FMT(LSPLIT, " mincol=%d, max_width=%d ", min_col, cpd.settings[UO_code_width].n - min_col); while (pc != NULL) { if (chunk_is_newline(pc)) { last_col = -1; } else { if (last_col < 0) { last_col = pc->column; } cur_width += (pc->column - last_col) + pc->len(); last_col = pc->column + pc->len(); if ((pc->type == CT_COMMA) || (pc->type == CT_FPAREN_CLOSE)) { cur_width--; LOG_FMT(LSPLIT, " width=%d ", cur_width); if (cur_width > max_width) { max_width = cur_width; if ((max_width + min_col) > cpd.settings[UO_code_width].n) { break; } } cur_width = 0; last_col = -1; if (pc->type == CT_FPAREN_CLOSE) { break; } } } pc = chunk_get_next(pc); } // don't split function w/o parameters next = chunk_get_next(fpo); if (((max_width + min_col) > cpd.settings[UO_code_width].n) && next->type != CT_FPAREN_CLOSE) { LOG_FMT(LSPLIT, " - A param won't fit, nl after open paren."); split_before_chunk(chunk_get_next(fpo)); return; } } /* back up until the prev is a comma */ prev = pc; while ((prev = chunk_get_prev(prev)) != NULL) { if (chunk_is_newline(prev) || (prev->type == CT_COMMA)) { break; } if (prev->type == CT_FPAREN_OPEN) { /* Don't split "()" */ pc = chunk_get_next(prev); if (pc->type != c_token_t(prev->type + 1)) { break; } } } if (prev != NULL) { LOG_FMT(LSPLIT, " -- ended on [%s] -- ", get_token_name(prev->type)); } if (prev != NULL) { split_before_chunk(chunk_get_next(prev)); } }
/** * A for statement is too long. * Step backwards and forwards to find the semicolons * Try splitting at the semicolons first. * If that doesn't work, then look for a comma at paren level. * If that doesn't work, then look for an assignment at paren level. * If that doesn't work, then give up. */ static void split_for_stmt(chunk_t *start) { int count = 0; int max_cnt = cpd.settings[UO_ls_for_split_full].b ? 2 : 1; chunk_t *st[2]; chunk_t *pc; chunk_t *open_paren = NULL; int nl_cnt = 0; LOG_FMT(LSPLIT, "%s: starting on %s, line %d\n", __func__, start->str.c_str(), start->orig_line); /* Find the open paren so we know the level and count newlines */ pc = start; while ((pc = chunk_get_prev(pc)) != NULL) { if (pc->type == CT_SPAREN_OPEN) { open_paren = pc; break; } if (pc->nl_count > 0) { nl_cnt += pc->nl_count; } } if (open_paren == NULL) { LOG_FMT(LSYS, "No open paren\n"); return; } /* see if we started on the semicolon */ pc = start; if ((pc->type == CT_SEMICOLON) && (pc->parent_type == CT_FOR)) { st[count++] = pc; } /* first scan backwards for the semicolons */ while ((count < max_cnt) && ((pc = chunk_get_prev(pc)) != NULL) && (pc->flags & PCF_IN_SPAREN)) { if ((pc->type == CT_SEMICOLON) && (pc->parent_type == CT_FOR)) { st[count++] = pc; } } /* And now scan forward */ pc = start; while ((count < max_cnt) && ((pc = chunk_get_next(pc)) != NULL) && (pc->flags & PCF_IN_SPAREN)) { if ((pc->type == CT_SEMICOLON) && (pc->parent_type == CT_FOR)) { st[count++] = pc; } } while (--count >= 0) { LOG_FMT(LSPLIT, "%s: split before %s\n", __func__, st[count]->str.c_str()); split_before_chunk(chunk_get_next(st[count])); } if (!is_past_width(start) || (nl_cnt > 0)) { return; } /* Still past width, check for commas at paren level */ pc = open_paren; while ((pc = chunk_get_next(pc)) != start) { if ((pc->type == CT_COMMA) && (pc->level == (open_paren->level + 1))) { split_before_chunk(chunk_get_next(pc)); if (!is_past_width(pc)) { return; } } } /* Still past width, check for a assignments at paren level */ pc = open_paren; while ((pc = chunk_get_next(pc)) != start) { if ((pc->type == CT_ASSIGN) && (pc->level == (open_paren->level + 1))) { split_before_chunk(chunk_get_next(pc)); if (!is_past_width(pc)) { return; } } } /* Oh, well. We tried. */ }
/** * Scan backwards to find the most appropriate spot to split the line * and insert a newline. * * See if this needs special function handling. * Scan backwards and find the best token for the split. * * @param start The first chunk that exceeded the limit */ static void split_line(chunk_t *start) { LOG_FMT(LSPLIT, "%s: line %d, col %d token:%s[%s] (IN_FUNC=%d) ", __func__, start->orig_line, start->column, start->str.c_str(), get_token_name(start->type), (start->flags & (PCF_IN_FCN_DEF | PCF_IN_FCN_CALL)) != 0); /** * break at maximum line length if indent_continue is absolute */ if (cpd.settings[UO_indent_continue].n < 0) { } /* Check to see if we are in a for statement */ else if ((start->flags & PCF_IN_FOR) != 0) { LOG_FMT(LSPLIT, " ** FOR SPLIT **\n"); split_for_stmt(start); if (!is_past_width(start)) { return; } LOG_FMT(LSPLIT, "%s: for split didn't work\n", __func__); } /* If this is in a function call or prototype, split on commas or right * after the open paren */ else if (((start->flags & PCF_IN_FCN_DEF) != 0) || ((start->level == (start->brace_level + 1)) && ((start->flags & PCF_IN_FCN_CALL) != 0))) { LOG_FMT(LSPLIT, " ** FUNC SPLIT **\n"); if (cpd.settings[UO_ls_func_split_full].b) { split_fcn_params_full(start); if (!is_past_width(start)) { return; } } split_fcn_params(start); if (!is_past_width(start)) { return; } LOG_FMT(LSPLIT, "%s: func split didn't work\n", __func__); } /** * Try to find the best spot to split the line */ cw_entry ent; memset(&ent, 0, sizeof(ent)); chunk_t *pc = start; chunk_t *prev; while (((pc = chunk_get_prev(pc)) != NULL) && !chunk_is_newline(pc)) { if (pc->type != CT_SPACE) { try_split_here(ent, pc); // break at maximum line length if indent_continue is absolute if ((ent.pc != NULL) && (cpd.settings[UO_indent_continue].n < 0)) break; } } if (ent.pc == NULL) { LOG_FMT(LSPLIT, "%s: TRY_SPLIT yielded NO SOLUTION for line %d at %s [%s]\n", __func__, start->orig_line, start->str.c_str(), get_token_name(start->type)); } else { LOG_FMT(LSPLIT, "%s: TRY_SPLIT yielded '%s' [%s] on line %d\n", __func__, ent.pc->str.c_str(), get_token_name(ent.pc->type), ent.pc->orig_line); } /* Break before the token instead of after it according to the pos_xxx rules */ if ((chunk_is_token(ent.pc, CT_ARITH) && (cpd.settings[UO_pos_arith].tp & TP_LEAD)) || (chunk_is_token(ent.pc, CT_ASSIGN) && (cpd.settings[UO_pos_assign].tp & TP_LEAD)) || (chunk_is_token(ent.pc, CT_COMPARE) && (cpd.settings[UO_pos_compare].tp & TP_LEAD)) || ((chunk_is_token(ent.pc, CT_COND_COLON) || chunk_is_token(ent.pc, CT_QUESTION)) && (cpd.settings[UO_pos_conditional].tp & TP_LEAD)) || (chunk_is_token(ent.pc, CT_BOOL) && (cpd.settings[UO_pos_bool].tp & TP_LEAD))) { pc = ent.pc; } else { pc = chunk_get_next(ent.pc); } if (pc == NULL) { pc = start; /* Don't break before a close, comma, or colon */ if ((start->type == CT_PAREN_CLOSE) || (start->type == CT_PAREN_OPEN) || (start->type == CT_FPAREN_CLOSE) || (start->type == CT_FPAREN_OPEN) || (start->type == CT_SPAREN_CLOSE) || (start->type == CT_SPAREN_OPEN) || (start->type == CT_ANGLE_CLOSE) || (start->type == CT_BRACE_CLOSE) || (start->type == CT_COMMA) || (start->type == CT_SEMICOLON) || (start->type == CT_VSEMICOLON) || (start->len() == 0)) { LOG_FMT(LSPLIT, " ** NO GO **\n"); /*TODO: Add in logic to handle 'hard' limits by backing up a token */ return; } } /* add a newline before pc */ prev = chunk_get_prev(pc); if ((prev != NULL) && !chunk_is_newline(pc) && !chunk_is_newline(prev)) { int plen = (pc->len() < 5) ? pc->len() : 5; int slen = (start->len() < 5) ? start->len() : 5; LOG_FMT(LSPLIT, " '%.*s' [%s], started on token '%.*s' [%s]\n", plen, pc->str.c_str(), get_token_name(pc->type), slen, start->str.c_str(), get_token_name(start->type)); split_before_chunk(pc); } }
/* * The for statement split algorithm works as follows: * 1. Step backwards and forwards to find the semicolons * 2. Try splitting at the semicolons first. * 3. If that doesn't work, then look for a comma at paren level. * 4. If that doesn't work, then look for an assignment at paren level. * 5. If that doesn't work, then give up. */ static void split_for_stmt(chunk_t *start) { LOG_FUNC_ENTRY(); // how many semicolons (1 or 2) do we need to find size_t max_cnt = cpd.settings[UO_ls_for_split_full].b ? 2 : 1; chunk_t *open_paren = nullptr; size_t nl_cnt = 0; LOG_FMT(LSPLIT, "%s: starting on %s, line %zu\n", __func__, start->text(), start->orig_line); // Find the open paren so we know the level and count newlines chunk_t *pc = start; while ((pc = chunk_get_prev(pc)) != nullptr) { if (pc->type == CT_SPAREN_OPEN) { open_paren = pc; break; } if (pc->nl_count > 0) { nl_cnt += pc->nl_count; } } if (open_paren == nullptr) { LOG_FMT(LSPLIT, "No open paren\n"); return; } // see if we started on the semicolon int count = 0; chunk_t *st[2]; pc = start; if (pc->type == CT_SEMICOLON && pc->parent_type == CT_FOR) { st[count++] = pc; } // first scan backwards for the semicolons while ( (count < static_cast<int>(max_cnt)) && ((pc = chunk_get_prev(pc)) != nullptr) && (pc->flags & PCF_IN_SPAREN)) { if (pc->type == CT_SEMICOLON && pc->parent_type == CT_FOR) { st[count++] = pc; } } // And now scan forward pc = start; while ( (count < static_cast<int>(max_cnt)) && ((pc = chunk_get_next(pc)) != nullptr) && (pc->flags & PCF_IN_SPAREN)) { if (pc->type == CT_SEMICOLON && pc->parent_type == CT_FOR) { st[count++] = pc; } } while (--count >= 0) { // TODO: st[0] may be uninitialized here LOG_FMT(LSPLIT, "%s(%d): split before %s\n", __func__, __LINE__, st[count]->text()); split_before_chunk(chunk_get_next(st[count])); } if (!is_past_width(start) || nl_cnt > 0) { return; } // Still past width, check for commas at parenthese level pc = open_paren; while ((pc = chunk_get_next(pc)) != start) { if (pc->type == CT_COMMA && (pc->level == (open_paren->level + 1))) { split_before_chunk(chunk_get_next(pc)); if (!is_past_width(pc)) { return; } } } // Still past width, check for a assignments at parenthese level pc = open_paren; while ((pc = chunk_get_next(pc)) != start) { if (pc->type == CT_ASSIGN && (pc->level == (open_paren->level + 1))) { split_before_chunk(chunk_get_next(pc)); if (!is_past_width(pc)) { return; } } } // Oh, well. We tried. } // split_for_stmt
static bool split_line(chunk_t *start) { LOG_FUNC_ENTRY(); LOG_FMT(LSPLIT, "%s(%d): start->text() '%s', orig_line is %zu, orig_col is %zu, type is %s\n", __func__, __LINE__, start->text(), start->orig_line, start->orig_col, get_token_name(start->type)); LOG_FMT(LSPLIT, " start->flags "); log_pcf_flags(LSPLIT, start->flags); LOG_FMT(LSPLIT, " start->parent_type %s, (PCF_IN_FCN_DEF is %s), (PCF_IN_FCN_CALL is %s)\n", get_token_name(start->parent_type), ((start->flags & (PCF_IN_FCN_DEF)) != 0) ? "TRUE" : "FALSE", ((start->flags & (PCF_IN_FCN_CALL)) != 0) ? "TRUE" : "FALSE"); // break at maximum line length if ls_code_width is true if (start->flags & PCF_ONE_LINER) { LOG_FMT(LSPLIT, "%s(%d): ** ONCE LINER SPLIT **\n", __func__, __LINE__); undo_one_liner(start); newlines_cleanup_braces(false); // Issue #1352 cpd.changes++; return(false); } LOG_FMT(LSPLIT, "%s(%d):\n", __func__, __LINE__); if (cpd.settings[UO_ls_code_width].b) { } // Check to see if we are in a for statement else if (start->flags & PCF_IN_FOR) { LOG_FMT(LSPLIT, " ** FOR SPLIT **\n"); split_for_stmt(start); if (!is_past_width(start)) { return(true); } LOG_FMT(LSPLIT, "%s(%d): for split didn't work\n", __func__, __LINE__); } /* * If this is in a function call or prototype, split on commas or right * after the open parenthesis */ else if ( (start->flags & PCF_IN_FCN_DEF) || start->parent_type == CT_FUNC_PROTO // Issue #1169 || ( (start->level == (start->brace_level + 1)) && (start->flags & PCF_IN_FCN_CALL))) { LOG_FMT(LSPLIT, " ** FUNC SPLIT **\n"); if (cpd.settings[UO_ls_func_split_full].b) { split_fcn_params_full(start); if (!is_past_width(start)) { return(true); } } split_fcn_params(start); return(true); } /* * If this is in a template, split on commas, Issue #1170 */ else if (start->flags & PCF_IN_TEMPLATE) { LOG_FMT(LSPLIT, " ** TEMPLATE SPLIT **\n"); split_template(start); return(true); } LOG_FMT(LSPLIT, "%s(%d):\n", __func__, __LINE__); // Try to find the best spot to split the line cw_entry ent; memset(&ent, 0, sizeof(ent)); chunk_t *pc = start; chunk_t *prev; while (((pc = chunk_get_prev(pc)) != nullptr) && !chunk_is_newline(pc)) { LOG_FMT(LSPLIT, "%s(%d): at %s, orig_line=%zu, orig_col=%zu\n", __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col); if (pc->type != CT_SPACE) { try_split_here(ent, pc); // break at maximum line length if (ent.pc != nullptr && (cpd.settings[UO_ls_code_width].b)) { break; } } } if (ent.pc == nullptr) { LOG_FMT(LSPLIT, "\n%s(%d): TRY_SPLIT yielded NO SOLUTION for orig_line %zu at '%s' [%s]\n", __func__, __LINE__, start->orig_line, start->text(), get_token_name(start->type)); } else { LOG_FMT(LSPLIT, "\n%s(%d): TRY_SPLIT yielded '%s' [%s] on orig_line %zu\n", __func__, __LINE__, ent.pc->text(), get_token_name(ent.pc->type), ent.pc->orig_line); LOG_FMT(LSPLIT, "%s(%d): ent at '%s', orig_col is %zu\n", __func__, __LINE__, ent.pc->text(), ent.pc->orig_col); } // Break before the token instead of after it according to the pos_xxx rules if (ent.pc == nullptr) { pc = nullptr; } else { if ( ( ( chunk_is_token(ent.pc, CT_ARITH) || chunk_is_token(ent.pc, CT_CARET)) && (cpd.settings[UO_pos_arith].tp & TP_LEAD)) || ( chunk_is_token(ent.pc, CT_ASSIGN) && (cpd.settings[UO_pos_assign].tp & TP_LEAD)) || ( chunk_is_token(ent.pc, CT_COMPARE) && (cpd.settings[UO_pos_compare].tp & TP_LEAD)) || ( ( chunk_is_token(ent.pc, CT_COND_COLON) || chunk_is_token(ent.pc, CT_QUESTION)) && (cpd.settings[UO_pos_conditional].tp & TP_LEAD)) || ( chunk_is_token(ent.pc, CT_BOOL) && (cpd.settings[UO_pos_bool].tp & TP_LEAD))) { pc = ent.pc; } else { pc = chunk_get_next(ent.pc); } LOG_FMT(LSPLIT, "%s(%d): at '%s', orig_col is %zu\n", __func__, __LINE__, pc->text(), pc->orig_col); } if (pc == nullptr) { pc = start; // Don't break before a close, comma, or colon if ( start->type == CT_PAREN_CLOSE || start->type == CT_PAREN_OPEN || start->type == CT_FPAREN_CLOSE || start->type == CT_FPAREN_OPEN || start->type == CT_SPAREN_CLOSE || start->type == CT_SPAREN_OPEN || start->type == CT_ANGLE_CLOSE || start->type == CT_BRACE_CLOSE || start->type == CT_COMMA || start->type == CT_SEMICOLON || start->type == CT_VSEMICOLON || start->len() == 0) { LOG_FMT(LSPLIT, " ** NO GO **\n"); // TODO: Add in logic to handle 'hard' limits by backing up a token return(true); } } // add a newline before pc prev = chunk_get_prev(pc); if ( prev != nullptr && !chunk_is_newline(pc) && !chunk_is_newline(prev)) { //int plen = (pc->len() < 5) ? pc->len() : 5; //int slen = (start->len() < 5) ? start->len() : 5; //LOG_FMT(LSPLIT, " '%.*s' [%s], started on token '%.*s' [%s]\n", // plen, pc->text(), get_token_name(pc->type), // slen, start->text(), get_token_name(start->type)); LOG_FMT(LSPLIT, "%s(%d): text() '%s', type [%s], started on token '%s', type [%s]\n", __func__, __LINE__, pc->text(), get_token_name(pc->type), start->text(), get_token_name(start->type)); split_before_chunk(pc); } return(true); } // split_line