/** * 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); /* 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; } /* Check to see if we are in a for statement */ 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); } } 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; } /* 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
/** * 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) { LOG_FUNC_ENTRY(); 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 %lu\n", __func__, start->text(), 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(LSPLIT, "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]->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 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. */ } // 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