blob: f9e73941ce5e9666ee10dc59fc3791ccb3b61ef5 [file] [log] [blame]
/**
* @file braces.cpp
* Adds or removes braces.
*
* @author Ben Gardner
* @license GPL v2+
*/
#include "uncrustify_types.h"
#include "chunk_list.h"
#include "prototypes.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include "unc_ctype.h"
static void convert_brace(chunk_t *br);
static void convert_vbrace(chunk_t *br);
static void convert_vbrace_to_brace(void);
static void examine_braces(void);
static void examine_brace(chunk_t *bopen);
static void move_case_break(void);
static void mod_case_brace(void);
static void mod_full_brace_if_chain(void);
static bool can_remove_braces(chunk_t *bopen);
static bool should_add_braces(chunk_t *vbopen);
void do_braces(void)
{
LOG_FUNC_ENTRY();
if (cpd.settings[UO_mod_full_brace_if_chain].b)
{
mod_full_brace_if_chain();
}
if (((cpd.settings[UO_mod_full_brace_if].a |
cpd.settings[UO_mod_full_brace_do].a |
cpd.settings[UO_mod_full_brace_for].a |
cpd.settings[UO_mod_full_brace_using].a |
cpd.settings[UO_mod_full_brace_while].a) & AV_REMOVE) != 0)
{
examine_braces();
}
/* convert vbraces if needed */
if (((cpd.settings[UO_mod_full_brace_if].a |
cpd.settings[UO_mod_full_brace_do].a |
cpd.settings[UO_mod_full_brace_for].a |
cpd.settings[UO_mod_full_brace_function].a |
cpd.settings[UO_mod_full_brace_using].a |
cpd.settings[UO_mod_full_brace_while].a) & AV_ADD) != 0)
{
convert_vbrace_to_brace();
}
/* Mark one-liners */
chunk_t *pc;
chunk_t *br_open;
chunk_t *tmp;
c_token_t brc_type;
pc = chunk_get_head();
while ((pc = chunk_get_next_ncnl(pc)) != NULL)
{
if ((pc->type != CT_BRACE_OPEN) &&
(pc->type != CT_VBRACE_OPEN))
{
continue;
}
br_open = pc;
brc_type = c_token_t(pc->type + 1);
/* Detect empty bodies */
tmp = chunk_get_next_ncnl(pc);
if ((tmp != NULL) && (tmp->type == brc_type))
{
br_open->flags |= PCF_EMPTY_BODY;
tmp->flags |= PCF_EMPTY_BODY;
}
/* Scan for the brace close or a newline */
tmp = br_open;
while ((tmp = chunk_get_next_nc(tmp)) != NULL)
{
if (chunk_is_newline(tmp))
{
break;
}
if ((tmp->type == brc_type) && (br_open->level == tmp->level))
{
flag_series(br_open, tmp, PCF_ONE_LINER);
break;
}
}
}
if (cpd.settings[UO_mod_case_brace].a != AV_IGNORE)
{
mod_case_brace();
}
if (cpd.settings[UO_mod_move_case_break].b)
{
move_case_break();
}
}
/**
* Go backwards to honor brace newline removal limits
*/
static void examine_braces(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *prev;
pc = chunk_get_tail();
while (pc != NULL)
{
prev = chunk_get_prev_type(pc, CT_BRACE_OPEN, -1);
if ((pc->type == CT_BRACE_OPEN) &&
((pc->flags & PCF_IN_PREPROC) == 0))
{
if ((((pc->parent_type == CT_IF) ||
(pc->parent_type == CT_ELSE) ||
(pc->parent_type == CT_ELSEIF)) &&
((cpd.settings[UO_mod_full_brace_if].a) == AV_REMOVE)) ||
((pc->parent_type == CT_DO) &&
((cpd.settings[UO_mod_full_brace_do].a) == AV_REMOVE)) ||
((pc->parent_type == CT_FOR) &&
((cpd.settings[UO_mod_full_brace_for].a) == AV_REMOVE)) ||
((pc->parent_type == CT_USING_STMT) &&
((cpd.settings[UO_mod_full_brace_using].a) == AV_REMOVE)) ||
((pc->parent_type == CT_WHILE) &&
((cpd.settings[UO_mod_full_brace_while].a) == AV_REMOVE)))
{
examine_brace(pc);
}
}
pc = prev;
}
}
/**
* Checks to see if the virtual braces should be converted to real braces.
* - over a certain length
*
* @param vbopen Virtual Brace Open chunk
* @return true (convert to real braces) or false (leave alone)
*/
static bool should_add_braces(chunk_t *vbopen)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
int nl_max = cpd.settings[UO_mod_full_brace_nl].n;
int nl_count = 0;
if (nl_max == 0)
{
return(false);
}
LOG_FMT(LBRDEL, "%s: start on %d : ", __func__, vbopen->orig_line);
for (pc = chunk_get_next_nc(vbopen, CNAV_PREPROC);
(pc != NULL) && (pc->level > vbopen->level);
pc = chunk_get_next_nc(pc, CNAV_PREPROC))
{
if (chunk_is_newline(pc))
{
nl_count += pc->nl_count;
}
}
if ((pc != NULL) && (nl_count > nl_max) && (vbopen->pp_level == pc->pp_level))
{
LOG_FMT(LBRDEL, " exceeded %d newlines\n", nl_max);
return(true);
}
return(false);
}
/**
* Checks to see if the braces can be removed.
* - less than a certain length
* - doesn't mess up if/else stuff
*/
static bool can_remove_braces(chunk_t *bopen)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *prev = NULL;
int semi_count = 0;
int level = bopen->level + 1;
bool hit_semi = false;
bool was_fcn = false;
int nl_max = cpd.settings[UO_mod_full_brace_nl].n;
int nl_count = 0;
int if_count = 0;
int br_count = 0;
/* Cannot remove braces inside a preprocessor */
if (bopen->flags & PCF_IN_PREPROC)
{
return(false);
}
pc = chunk_get_next_ncnl(bopen, CNAV_PREPROC);
if ((pc != NULL) && (pc->type == CT_BRACE_CLOSE))
{
/* Can't remove empty statement */
return(false);
}
LOG_FMT(LBRDEL, "%s: start on %d : ", __func__, bopen->orig_line);
pc = chunk_get_next_nc(bopen, CNAV_ALL);
while ((pc != NULL) && (pc->level >= level))
{
if (pc->flags & PCF_IN_PREPROC)
{
/* Cannot remove braces that contain a preprocessor */
return(false);
}
if (chunk_is_newline(pc))
{
nl_count += pc->nl_count;
if ((nl_max > 0) && (nl_count > nl_max))
{
LOG_FMT(LBRDEL, " exceeded %d newlines\n", nl_max);
return(false);
}
}
else
{
if (pc->type == CT_BRACE_OPEN)
{
br_count++;
}
else if (pc->type == CT_BRACE_CLOSE)
{
br_count--;
}
else if ((pc->type == CT_IF) || (pc->type == CT_ELSEIF))
{
if (br_count == 0)
{
if_count++;
}
}
if (pc->level == level)
{
if ((semi_count > 0) && hit_semi)
{
/* should have bailed due to close brace level drop */
LOG_FMT(LBRDEL, " no close brace\n");
return(false);
}
LOG_FMT(LBRDEL, " [%s %d-%d]", pc->str.c_str(), pc->orig_line, semi_count);
if (pc->type == CT_ELSE)
{
LOG_FMT(LBRDEL, " bailed on %s on line %d\n",
pc->str.c_str(), pc->orig_line);
return(false);
}
was_fcn = (prev != NULL) && (prev->type == CT_FPAREN_CLOSE);
if (chunk_is_semicolon(pc) ||
(pc->type == CT_IF) ||
(pc->type == CT_ELSEIF) ||
(pc->type == CT_FOR) ||
(pc->type == CT_DO) ||
(pc->type == CT_WHILE) ||
(pc->type == CT_USING_STMT) ||
((pc->type == CT_BRACE_OPEN) && was_fcn))
{
hit_semi |= chunk_is_semicolon(pc);
if (++semi_count > 1)
{
LOG_FMT(LBRDEL, " bailed on %d because of %s on line %d\n",
bopen->orig_line, pc->str.c_str(), pc->orig_line);
return(false);
}
}
}
}
prev = pc;
pc = chunk_get_next_nc(pc);
}
if (pc == NULL)
{
LOG_FMT(LBRDEL, " NULL\n");
return(false);
}
if ((pc->type == CT_BRACE_CLOSE) && (pc->parent_type == CT_IF))
{
chunk_t *next = chunk_get_next_ncnl(pc, CNAV_PREPROC);
prev = chunk_get_prev_ncnl(pc, CNAV_PREPROC);
if ((next != NULL) && (next->type == CT_ELSE) &&
((prev->type == CT_BRACE_CLOSE) || (prev->type == CT_VBRACE_CLOSE)) &&
(prev->parent_type == CT_IF))
{
LOG_FMT(LBRDEL, " - bailed on '%s'[%s] on line %d due to 'if' and 'else' sequence\n",
get_token_name(pc->type), get_token_name(pc->parent_type),
pc->orig_line);
return(false);
}
}
LOG_FMT(LBRDEL, " - end on '%s' on line %d. if_count=%d semi_count=%d\n",
get_token_name(pc->type), pc->orig_line, if_count, semi_count);
return((pc->type == CT_BRACE_CLOSE) && (pc->pp_level == bopen->pp_level));
}
/**
* Step forward and count the number of semi colons at the current level.
* Abort if more than 1 or if we enter a preprocessor
*/
static void examine_brace(chunk_t *bopen)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *next;
chunk_t *prev = NULL;
int semi_count = 0;
int level = bopen->level + 1;
bool hit_semi = false;
bool was_fcn = false;
int nl_max = cpd.settings[UO_mod_full_brace_nl].n;
int nl_count = 0;
int if_count = 0;
int br_count = 0;
LOG_FMT(LBRDEL, "%s: start on %d : ", __func__, bopen->orig_line);
pc = chunk_get_next_nc(bopen);
while ((pc != NULL) && (pc->level >= level))
{
if ((pc->flags & PCF_IN_PREPROC) != 0)
{
LOG_FMT(LBRDEL, " PREPROC\n");
return;
}
if (chunk_is_newline(pc))
{
nl_count += pc->nl_count;
if ((nl_max > 0) && (nl_count > nl_max))
{
LOG_FMT(LBRDEL, " exceeded %d newlines\n", nl_max);
return;
}
}
else
{
if (pc->type == CT_BRACE_OPEN)
{
br_count++;
}
else if (pc->type == CT_BRACE_CLOSE)
{
br_count--;
if (br_count == 0)
{
next = chunk_get_next_ncnl(pc, CNAV_PREPROC);
if ((next == NULL) || (next->type != CT_BRACE_CLOSE))
{
LOG_FMT(LBRDEL, " junk after close brace\n");
return;
}
}
}
else if ((pc->type == CT_IF) || (pc->type == CT_ELSEIF))
{
if (br_count == 0)
{
if_count++;
}
}
if (pc->level == level)
{
if ((semi_count > 0) && hit_semi)
{
/* should have bailed due to close brace level drop */
LOG_FMT(LBRDEL, " no close brace\n");
return;
}
LOG_FMT(LBRDEL, " [%s %d-%d]", pc->str.c_str(), pc->orig_line, semi_count);
if (pc->type == CT_ELSE)
{
LOG_FMT(LBRDEL, " bailed on %s on line %d\n",
pc->str.c_str(), pc->orig_line);
return;
}
was_fcn = (prev != NULL) && (prev->type == CT_FPAREN_CLOSE);
if (chunk_is_semicolon(pc) ||
(pc->type == CT_IF) ||
(pc->type == CT_ELSEIF) ||
(pc->type == CT_FOR) ||
(pc->type == CT_DO) ||
(pc->type == CT_WHILE) ||
(pc->type == CT_SWITCH) ||
(pc->type == CT_USING_STMT) ||
((pc->type == CT_BRACE_OPEN) && was_fcn))
{
hit_semi |= chunk_is_semicolon(pc);
if (++semi_count > 1)
{
LOG_FMT(LBRDEL, " bailed on %d because of %s on line %d\n",
bopen->orig_line, pc->str.c_str(), pc->orig_line);
return;
}
}
}
}
prev = pc;
pc = chunk_get_next_nc(pc);
}
if (pc == NULL)
{
LOG_FMT(LBRDEL, " NULL\n");
return;
}
LOG_FMT(LBRDEL, " - end on '%s' on line %d. if_count=%d semi_count=%d\n",
get_token_name(pc->type), pc->orig_line, if_count, semi_count);
if (pc->type == CT_BRACE_CLOSE)
{
next = chunk_get_next_ncnl(pc);
while ((next != NULL) && (next->type == CT_VBRACE_CLOSE))
{
next = chunk_get_next_ncnl(next);
}
LOG_FMT(LBRDEL, " next is '%s'\n", get_token_name(next->type));
if ((if_count > 0) &&
((next->type == CT_ELSE) || (next->type == CT_ELSEIF)))
{
LOG_FMT(LBRDEL, " bailed on because 'else' is next and %d ifs\n", if_count);
return;
}
if (semi_count > 0)
{
if (bopen->parent_type == CT_ELSE)
{
next = chunk_get_next_ncnl(bopen);
if (next->type == CT_IF)
{
prev = chunk_get_prev_ncnl(bopen);
LOG_FMT(LBRDEL, " else-if removing braces on line %d and %d\n",
bopen->orig_line, pc->orig_line);
chunk_del(bopen);
chunk_del(pc);
newline_del_between(prev, next);
if (cpd.settings[UO_nl_else_if].a & AV_ADD)
{
newline_add_between(prev, next);
}
return;
}
}
/* we have a pair of braces with only 1 statement inside */
convert_brace(bopen);
convert_brace(pc);
LOG_FMT(LBRDEL, " removing braces on line %d and %d\n",
bopen->orig_line, pc->orig_line);
}
else
{
LOG_FMT(LBRDEL, " empty statement\n");
}
}
else
{
LOG_FMT(LBRDEL, " not a close brace? - '%s'\n", pc->str.c_str());
}
}
/**
* Converts a single brace into a virtual brace
*/
static void convert_brace(chunk_t *br)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
if (!br || (br->flags & PCF_KEEP_BRACE))
{
return;
}
else if (br->type == CT_BRACE_OPEN)
{
set_chunk_type(br, CT_VBRACE_OPEN);
br->str.clear();
tmp = chunk_get_prev(br);
}
else if (br->type == CT_BRACE_CLOSE)
{
set_chunk_type(br, CT_VBRACE_CLOSE);
br->str.clear();
tmp = chunk_get_next(br);
}
else
{
return;
}
if (chunk_is_newline(tmp))
{
if (tmp->nl_count > 1)
{
tmp->nl_count--;
}
else
{
if (chunk_safe_to_del_nl(tmp))
{
chunk_del(tmp);
}
}
}
}
/**
* Converts a single virtual brace into a brace
*/
static void convert_vbrace(chunk_t *vbr)
{
LOG_FUNC_ENTRY();
if (vbr == NULL)
{
return;
}
else if (vbr->type == CT_VBRACE_OPEN)
{
set_chunk_type(vbr, CT_BRACE_OPEN);
vbr->str = "{";
/* If the next chunk is a preprocessor, then move the open brace after the
* preprocessor.
*/
chunk_t *tmp = chunk_get_next(vbr);
if ((tmp != NULL) && (tmp->type == CT_PREPROC))
{
tmp = chunk_get_next(vbr, CNAV_PREPROC);
chunk_move_after(vbr, tmp);
newline_add_after(vbr);
}
}
else if (vbr->type == CT_VBRACE_CLOSE)
{
set_chunk_type(vbr, CT_BRACE_CLOSE);
vbr->str = "}";
/* If the next chunk is a comment, followed by a newline, then
* move the brace after the newline and add another newline after
* the close brace.
*/
chunk_t *tmp = chunk_get_next(vbr);
if (chunk_is_comment(tmp))
{
tmp = chunk_get_next(tmp);
if (chunk_is_newline(tmp))
{
chunk_move_after(vbr, tmp);
newline_add_after(vbr);
}
}
}
}
static void convert_vbrace_to_brace(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *tmp;
chunk_t *vbc;
bool in_preproc;
/* Find every vbrace open */
for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next_ncnl(pc))
{
if (pc->type != CT_VBRACE_OPEN)
{
continue;
}
in_preproc = (pc->flags & PCF_IN_PREPROC) != 0;
if ((((pc->parent_type == CT_IF) ||
(pc->parent_type == CT_ELSE) ||
(pc->parent_type == CT_ELSEIF)) &&
((cpd.settings[UO_mod_full_brace_if].a & AV_ADD) != 0) &&
!cpd.settings[UO_mod_full_brace_if_chain].b)
||
((pc->parent_type == CT_FOR) &&
((cpd.settings[UO_mod_full_brace_for].a & AV_ADD) != 0))
||
((pc->parent_type == CT_DO) &&
((cpd.settings[UO_mod_full_brace_do].a & AV_ADD) != 0))
||
((pc->parent_type == CT_WHILE) &&
((cpd.settings[UO_mod_full_brace_while].a & AV_ADD) != 0))
||
((pc->parent_type == CT_USING_STMT) &&
((cpd.settings[UO_mod_full_brace_using].a & AV_ADD) != 0))
||
((pc->parent_type == CT_FUNC_DEF) &&
((cpd.settings[UO_mod_full_brace_function].a & AV_ADD) != 0)))
{
/* Find the matching vbrace close */
vbc = NULL;
tmp = pc;
while ((tmp = chunk_get_next(tmp)) != NULL)
{
if (in_preproc && ((tmp->flags & PCF_IN_PREPROC) == 0))
{
/* Can't leave a preprocessor */
break;
}
if ((pc->brace_level == tmp->brace_level) &&
(tmp->type == CT_VBRACE_CLOSE) &&
(pc->parent_type == tmp->parent_type) &&
((tmp->flags & PCF_IN_PREPROC) == (pc->flags & PCF_IN_PREPROC)))
{
vbc = tmp;
break;
}
}
if (vbc == NULL)
{
continue;
}
convert_vbrace(pc);
convert_vbrace(vbc);
}
}
}
/**
* Adds a comment after the ref chunk
* Returns the added chunk or NULL
*/
chunk_t *insert_comment_after(chunk_t *ref, c_token_t cmt_type,
const unc_text& cmt_text)
{
LOG_FUNC_ENTRY();
chunk_t new_cmt;
new_cmt = *ref;
new_cmt.prev = NULL;
new_cmt.next = NULL;
new_cmt.flags = (ref->flags & PCF_COPY_FLAGS);
new_cmt.type = cmt_type;
new_cmt.str.clear();
if (cmt_type == CT_COMMENT_CPP)
{
new_cmt.str.append("// ");
new_cmt.str.append(cmt_text);
}
else
{
new_cmt.str.append("/* ");
new_cmt.str.append(cmt_text);
new_cmt.str.append(" */");
}
/* TODO: expand comment type to cover other comment styles? */
new_cmt.column = ref->column + ref->len() + 1;
new_cmt.orig_col = new_cmt.column;
return(chunk_add_after(&new_cmt, ref));
}
/**
* Collect the text into txt that contains the full tag name.
* Mainly for collecting namespace 'a.b.c' or function 'foo::bar()' names.
*/
static void append_tag_name(unc_text& txt, chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *tmp = pc;
/* step backwards over all a::b stuff */
while ((tmp = chunk_get_prev_ncnl(tmp)) != NULL)
{
if ((tmp->type != CT_DC_MEMBER) && (tmp->type != CT_MEMBER))
{
break;
}
tmp = chunk_get_prev_ncnl(tmp);
pc = tmp;
if (!chunk_is_word(tmp))
{
break;
}
}
txt += pc->str;
while ((pc = chunk_get_next_ncnl(pc)) != NULL)
{
if ((pc->type != CT_DC_MEMBER) && (pc->type != CT_MEMBER))
{
break;
}
txt += pc->str;
pc = chunk_get_next_ncnl(pc);
if (pc)
{
txt += pc->str;
}
}
}
/*
* See also it's preprocessor counterpart
* add_long_preprocessor_conditional_block_comment
* in defines.cpp
*/
void add_long_closebrace_comment(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *tmp;
chunk_t *br_open;
chunk_t *br_close;
chunk_t *fcn_pc = NULL;
chunk_t *sw_pc = NULL;
chunk_t *ns_pc = NULL;
unc_text xstr;
int nl_count;
for (pc = chunk_get_head(); pc; pc = chunk_get_next_ncnl(pc))
{
if ((pc->type == CT_FUNC_DEF) ||
(pc->type == CT_OC_MSG_DECL))
{
fcn_pc = pc;
}
else if (pc->type == CT_SWITCH)
{
/* kinda pointless, since it always has the text "switch" */
sw_pc = pc;
}
else if (pc->type == CT_NAMESPACE)
{
ns_pc = pc;
}
if ((pc->type != CT_BRACE_OPEN) || ((pc->flags & PCF_IN_PREPROC) != 0))
{
continue;
}
br_open = pc;
nl_count = 0;
tmp = pc;
while ((tmp = chunk_get_next(tmp)) != NULL)
{
if (chunk_is_newline(tmp))
{
nl_count += tmp->nl_count;
}
else if ((tmp->level == br_open->level) &&
(tmp->type == CT_BRACE_CLOSE))
{
br_close = tmp;
//LOG_FMT(LSYS, "found brace pair on lines %d and %d, nl_count=%d\n",
// br_open->orig_line, br_close->orig_line, nl_count);
/* Found the matching close brace - make sure a newline is next */
tmp = chunk_get_next(tmp);
if ((tmp == NULL) || chunk_is_newline(tmp))
{
int nl_min = 0;
chunk_t *tag_pc = NULL;
if (br_open->parent_type == CT_SWITCH)
{
nl_min = cpd.settings[UO_mod_add_long_switch_closebrace_comment].n;
tag_pc = sw_pc;
xstr = sw_pc->str;
}
else if ((br_open->parent_type == CT_FUNC_DEF) ||
(br_open->parent_type == CT_OC_MSG_DECL))
{
nl_min = cpd.settings[UO_mod_add_long_function_closebrace_comment].n;
tag_pc = fcn_pc;
xstr.clear();
append_tag_name(xstr, tag_pc);
}
else if (br_open->parent_type == CT_NAMESPACE)
{
nl_min = cpd.settings[UO_mod_add_long_namespace_closebrace_comment].n;
tag_pc = ns_pc;
/* obtain the next chunck, normally this is the name of the namespace
and append it to generate "namespace xyz" */
xstr = ns_pc->str;
xstr.append(" ");
append_tag_name(xstr, chunk_get_next(ns_pc));
}
if ((nl_min > 0) && (nl_count >= nl_min) && (tag_pc != NULL))
{
/* determine the added comment style */
c_token_t style = (cpd.lang_flags & (LANG_CPP | LANG_CS)) ?
CT_COMMENT_CPP : CT_COMMENT;
/* Add a comment after the close brace */
insert_comment_after(br_close, style, xstr);
}
}
break;
}
}
}
}
static void move_case_break(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *prev = NULL;
for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next_ncnl(pc))
{
if ((pc->type == CT_BREAK) &&
(prev != NULL) &&
(prev->type == CT_BRACE_CLOSE) &&
(prev->parent_type == CT_CASE))
{
if (chunk_is_newline(chunk_get_prev(pc)) &&
chunk_is_newline(chunk_get_prev(prev)))
{
chunk_swap_lines(prev, pc);
}
}
prev = pc;
}
}
/**
* Remove the case brace, if allowable.
*/
static chunk_t *mod_case_brace_remove(chunk_t *br_open)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *br_close;
chunk_t *next = chunk_get_next_ncnl(br_open, CNAV_PREPROC);
LOG_FMT(LMCB, "%s: line %d", __func__, br_open->orig_line);
/* Find the matching brace close */
br_close = chunk_get_next_type(br_open, CT_BRACE_CLOSE, br_open->level, CNAV_PREPROC);
if (br_close == NULL)
{
LOG_FMT(LMCB, " - no close\n");
return(next);
}
/* Make sure 'break', 'return', 'goto', 'case' or '}' is after the close brace */
pc = chunk_get_next_ncnl(br_close, CNAV_PREPROC);
if ((pc == NULL) ||
((pc->type != CT_BREAK) &&
(pc->type != CT_RETURN) &&
(pc->type != CT_CASE) &&
(pc->type != CT_GOTO) &&
(pc->type != CT_BRACE_CLOSE)))
{
LOG_FMT(LMCB, " - after '%s'\n",
(pc == NULL) ? "<null>" : get_token_name(pc->type));
return(next);
}
/* scan to make sure there are no definitions at brace level between braces */
for (pc = br_open; pc != br_close; pc = chunk_get_next_ncnl(pc, CNAV_PREPROC))
{
if ((pc->level == (br_open->level + 1)) && (pc->flags & PCF_VAR_DEF))
{
LOG_FMT(LMCB, " - vardef on line %d: '%s'\n", pc->orig_line, pc->str.c_str());
return(next);
}
}
LOG_FMT(LMCB, " - removing braces on lines %d and %d\n",
br_open->orig_line, br_close->orig_line);
for (pc = br_open; pc != br_close; pc = chunk_get_next_ncnl(pc, CNAV_PREPROC))
{
pc->brace_level--;
pc->level--;
}
next = chunk_get_prev(br_open, CNAV_PREPROC);
chunk_del(br_open);
chunk_del(br_close);
return(chunk_get_next(next, CNAV_PREPROC));
}
/**
* Add the case brace, if allowable.
*/
static chunk_t *mod_case_brace_add(chunk_t *cl_colon)
{
LOG_FUNC_ENTRY();
chunk_t *pc = cl_colon;
chunk_t *last = NULL;
chunk_t *next = chunk_get_next_ncnl(cl_colon, CNAV_PREPROC);
chunk_t *br_open;
chunk_t *br_close;
chunk_t chunk;
LOG_FMT(LMCB, "%s: line %d", __func__, pc->orig_line);
while ((pc = chunk_get_next_ncnl(pc, CNAV_PREPROC)) != NULL)
{
if (pc->level < cl_colon->level)
{
LOG_FMT(LMCB, " - level drop\n");
return(next);
}
if ((pc->level == cl_colon->level) &&
((pc->type == CT_CASE) ||
(pc->type == CT_BREAK)))
{
last = pc;
//if (pc->type == CT_BREAK)
//{
// /* Step past the semicolon */
// last = chunk_get_next_ncnl(chunk_get_next_ncnl(last));
//}
break;
}
}
if (last == NULL)
{
LOG_FMT(LMCB, " - NULL last\n");
return(next);
}
LOG_FMT(LMCB, " - adding before '%s' on line %d\n", last->str.c_str(), last->orig_line);
chunk.type = CT_BRACE_OPEN;
chunk.orig_line = cl_colon->orig_line;
chunk.parent_type = CT_CASE;
chunk.level = cl_colon->level;
chunk.brace_level = cl_colon->brace_level;
chunk.flags = pc->flags & PCF_COPY_FLAGS;
chunk.str = "{";
br_open = chunk_add_after(&chunk, cl_colon);
chunk.type = CT_BRACE_CLOSE;
chunk.orig_line = last->orig_line;
chunk.str = "}";
br_close = chunk_add_before(&chunk, last);
newline_add_before(last);
for (pc = chunk_get_next(br_open, CNAV_PREPROC);
pc != br_close;
pc = chunk_get_next(pc, CNAV_PREPROC))
{
pc->level++;
pc->brace_level++;
}
return(br_open);
}
static void mod_case_brace(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc = chunk_get_head();
chunk_t *next;
while (pc != NULL)
{
next = chunk_get_next_ncnl(pc, CNAV_PREPROC);
if (next == NULL)
{
return;
}
if ((cpd.settings[UO_mod_case_brace].a == AV_REMOVE) &&
(pc->type == CT_BRACE_OPEN) &&
(pc->parent_type == CT_CASE))
{
pc = mod_case_brace_remove(pc);
}
else if ((cpd.settings[UO_mod_case_brace].a & AV_ADD) &&
(pc->type == CT_CASE_COLON) &&
(next->type != CT_BRACE_OPEN) &&
(next->type != CT_BRACE_CLOSE) &&
(next->type != CT_CASE))
{
pc = mod_case_brace_add(pc);
}
else
{
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
}
}
}
/**
* Traverse the if chain and see if all can be removed
*/
static void process_if_chain(chunk_t *br_start)
{
LOG_FUNC_ENTRY();
chunk_t *braces[256];
chunk_t *br_close;
int br_cnt = 0;
chunk_t *pc;
bool must_have_braces = false;
bool tmp;
pc = br_start;
LOG_FMT(LBRCH, "%s: if starts on line %d\n", __func__, br_start->orig_line);
while (pc != NULL)
{
if (pc->type == CT_BRACE_OPEN)
{
tmp = can_remove_braces(pc);
LOG_FMT(LBRCH, " [%d] line %d - can%s remove %s\n",
br_cnt, pc->orig_line, tmp ? "" : "not",
get_token_name(pc->type));
if (!tmp)
{
must_have_braces = true;
}
}
else
{
tmp = should_add_braces(pc);
if (tmp)
{
must_have_braces = true;
}
LOG_FMT(LBRCH, " [%d] line %d - %s %s\n",
br_cnt, pc->orig_line, tmp ? "should add" : "ignore",
get_token_name(pc->type));
}
braces[br_cnt++] = pc;
br_close = chunk_skip_to_match(pc, CNAV_PREPROC);
if (br_close == NULL)
{
break;
}
braces[br_cnt++] = br_close;
pc = chunk_get_next_ncnl(br_close, CNAV_PREPROC);
if ((pc == NULL) || (pc->type != CT_ELSE))
{
break;
}
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
if ((pc != NULL) && (pc->type == CT_ELSEIF))
{
while ((pc != NULL) && (pc->type != CT_VBRACE_OPEN) && (pc->type != CT_BRACE_OPEN))
{
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
}
}
if (pc == NULL)
{
break;
}
if ((pc->type != CT_BRACE_OPEN) && (pc->type != CT_VBRACE_OPEN))
{
break;
}
}
if (must_have_braces)
{
LOG_FMT(LBRCH, "%s: add braces on lines[%d]:", __func__, br_cnt);
while (--br_cnt >= 0)
{
braces[br_cnt]->flags |= PCF_KEEP_BRACE;
if ((braces[br_cnt]->type == CT_VBRACE_OPEN) ||
(braces[br_cnt]->type == CT_VBRACE_CLOSE))
{
LOG_FMT(LBRCH, " %d", braces[br_cnt]->orig_line);
convert_vbrace(braces[br_cnt]);
}
else
{
LOG_FMT(LBRCH, " {%d}", braces[br_cnt]->orig_line);
}
braces[br_cnt] = NULL;
}
LOG_FMT(LBRCH, "\n");
}
else
{
LOG_FMT(LBRCH, "%s: remove braces on lines[%d]:", __func__, br_cnt);
while (--br_cnt >= 0)
{
if ((braces[br_cnt]->type == CT_BRACE_OPEN) ||
(braces[br_cnt]->type == CT_BRACE_CLOSE))
{
LOG_FMT(LBRCH, " {%d}", braces[br_cnt]->orig_line);
convert_brace(braces[br_cnt]);
}
else
{
LOG_FMT(LBRCH, " %d", braces[br_cnt]->orig_line);
}
braces[br_cnt] = NULL;
}
LOG_FMT(LBRCH, "\n");
}
}
static void mod_full_brace_if_chain(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next(pc))
{
if (((pc->type == CT_BRACE_OPEN) || (pc->type == CT_VBRACE_OPEN)) &&
(pc->parent_type == CT_IF))
{
process_if_chain(pc);
}
}
}