blob: d0f0d439bbb11d3f9434507d567f292e56d319fb [file] [log] [blame]
/**
* @file parens.cpp
* Adds or removes parens.
*
* @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 add_parens_between(chunk_t *first, chunk_t *last);
static void check_bool_parens(chunk_t *popen, chunk_t *pclose, int nest);
void do_parens(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *pclose;
if (cpd.settings[UO_mod_full_paren_if_bool].b)
{
pc = chunk_get_head();
while ((pc = chunk_get_next_ncnl(pc)) != NULL)
{
if ((pc->type != CT_SPAREN_OPEN) ||
((pc->parent_type != CT_IF) &&
(pc->parent_type != CT_ELSEIF) &&
(pc->parent_type != CT_SWITCH)))
{
continue;
}
/* Grab the close sparen */
pclose = chunk_get_next_type(pc, CT_SPAREN_CLOSE, pc->level, CNAV_PREPROC);
if (pclose != NULL)
{
check_bool_parens(pc, pclose, 0);
pc = pclose;
}
}
}
}
/**
* Add an open paren after first and add a close paren before the last
*/
static void add_parens_between(chunk_t *first, chunk_t *last)
{
LOG_FUNC_ENTRY();
chunk_t pc;
chunk_t *first_n;
chunk_t *last_p;
chunk_t *tmp;
LOG_FMT(LPARADD, "%s: line %d between %s [lvl=%d] and %s [lvl=%d]\n",
__func__, first->orig_line,
first->str.c_str(), first->level,
last->str.c_str(), last->level);
/* Don't do anything if we have a bad sequence, ie "&& )" */
first_n = chunk_get_next_ncnl(first);
if (first_n == last)
{
return;
}
pc.type = CT_PAREN_OPEN;
pc.str = "(";
pc.flags = first_n->flags & PCF_COPY_FLAGS;
pc.level = first_n->level;
pc.pp_level = first_n->pp_level;
pc.brace_level = first_n->brace_level;
chunk_add_before(&pc, first_n);
last_p = chunk_get_prev_ncnl(last, CNAV_PREPROC);
pc.type = CT_PAREN_CLOSE;
pc.str = ")";
pc.flags = last_p->flags & PCF_COPY_FLAGS;
pc.level = last_p->level;
pc.pp_level = last_p->pp_level;
pc.brace_level = last_p->brace_level;
chunk_add_after(&pc, last_p);
for (tmp = first_n;
tmp != last_p;
tmp = chunk_get_next_ncnl(tmp))
{
tmp->level++;
}
last_p->level++;
}
/**
* Scans between two parens and adds additional parens if needed.
* This function is recursive. If it hits another open paren, it'll call itself
* with the new bounds.
*
* Adds optional parens in an IF or SWITCH conditional statement.
*
* This basically just checks for a CT_COMPARE that isn't surrounded by parens.
* The edges for the compare are the open, close and any CT_BOOL tokens.
*
* This only handleds VERY simple patterns:
* (!a && b) => (!a && b) -- no change
* (a && b == 1) => (a && (b == 1))
* (a == 1 || b > 2) => ((a == 1) || (b > 2))
*
* FIXME: we really should bail if we transition between a preprocessor and
* a non-preprocessor
*/
static void check_bool_parens(chunk_t *popen, chunk_t *pclose, int nest)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *ref = popen;
chunk_t *next;
bool hit_compare = false;
LOG_FMT(LPARADD, "%s(%d): popen on %d, col %d, pclose on %d, col %d, level=%d\n",
__func__, nest,
popen->orig_line, popen->orig_col,
pclose->orig_line, pclose->orig_col,
popen->level);
pc = popen;
while (((pc = chunk_get_next_ncnl(pc)) != NULL) && (pc != pclose))
{
if (pc->flags & PCF_IN_PREPROC)
{
LOG_FMT(LPARADD2, " -- bail on PP %s [%s] at line %d col %d, level %d\n",
get_token_name(pc->type),
pc->str.c_str(), pc->orig_line, pc->orig_col, pc->level);
return;
}
if ((pc->type == CT_BOOL) ||
(pc->type == CT_QUESTION) ||
(pc->type == CT_COND_COLON) ||
(pc->type == CT_COMMA))
{
LOG_FMT(LPARADD2, " -- %s [%s] at line %d col %d, level %d\n",
get_token_name(pc->type),
pc->str.c_str(), pc->orig_line, pc->orig_col, pc->level);
if (hit_compare)
{
hit_compare = false;
add_parens_between(ref, pc);
}
ref = pc;
}
else if (pc->type == CT_COMPARE)
{
LOG_FMT(LPARADD2, " -- compare [%s] at line %d col %d, level %d\n",
pc->str.c_str(), pc->orig_line, pc->orig_col, pc->level);
hit_compare = true;
}
else if (chunk_is_paren_open(pc))
{
next = chunk_skip_to_match(pc);
if (next != NULL)
{
check_bool_parens(pc, next, nest + 1);
pc = next;
}
}
else if ((pc->type == CT_BRACE_OPEN) ||
(pc->type == CT_SQUARE_OPEN) ||
(pc->type == CT_ANGLE_OPEN))
{
/* Skip [], {}, and <> */
pc = chunk_skip_to_match(pc);
}
}
if (hit_compare && (ref != popen))
{
add_parens_between(ref, pclose);
}
}