blob: f5834ba9afeb57c2df69298547b81c73448b4c37 [file] [log] [blame]
/**
* @file combine.cpp
* Labels the chunks as needed.
*
* @author Ben Gardner
* @license GPL v2+
*/
#include "uncrustify_types.h"
#include "chunk_list.h"
#include "ChunkStack.h"
#include "prototypes.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include "unc_ctype.h"
#include <cassert>
static void fix_fcn_def_params(chunk_t *pc);
static void fix_typedef(chunk_t *pc);
static void fix_enum_struct_union(chunk_t *pc);
static void fix_casts(chunk_t *pc);
static void fix_type_cast(chunk_t *pc);
static chunk_t *fix_var_def(chunk_t *pc);
static void mark_function(chunk_t *pc);
static void mark_function_return_type(chunk_t *fname, chunk_t *pc, c_token_t parent_type);
static bool mark_function_type(chunk_t *pc);
static void mark_struct_union_body(chunk_t *start);
static chunk_t *mark_variable_definition(chunk_t *start);
static void mark_define_expressions(void);
static void process_returns(void);
static chunk_t *process_return(chunk_t *pc);
static void mark_class_ctor(chunk_t *pclass);
static void mark_namespace(chunk_t *pns);
static void mark_cpp_constructor(chunk_t *pc);
static void mark_lvalue(chunk_t *pc);
static void mark_template_func(chunk_t *pc, chunk_t *pc_next);
static void mark_exec_sql(chunk_t *pc);
static void handle_oc_class(chunk_t *pc);
static void handle_oc_block_literal(chunk_t *pc);
static void handle_oc_block_type(chunk_t *pc);
static void handle_oc_message_decl(chunk_t *pc);
static void handle_oc_message_send(chunk_t *pc);
static void handle_cs_square_stmt(chunk_t *pc);
static void handle_cs_property(chunk_t *pc);
static void handle_cs_array_type(chunk_t *pc);
static void handle_cpp_template(chunk_t *pc);
static void handle_cpp_lambda(chunk_t *pc);
static void handle_d_template(chunk_t *pc);
static void handle_wrap(chunk_t *pc);
static void handle_proto_wrap(chunk_t *pc);
static bool is_oc_block(chunk_t *pc);
static void handle_java_assert(chunk_t *pc);
static chunk_t *get_d_template_types(ChunkStack& cs, chunk_t *open_paren);
static bool chunkstack_match(ChunkStack& cs, chunk_t *pc);
void make_type(chunk_t *pc)
{
LOG_FUNC_ENTRY();
if (pc != NULL)
{
if (pc->type == CT_WORD)
{
set_chunk_type(pc, CT_TYPE);
}
else if (chunk_is_star(pc))
{
set_chunk_type(pc, CT_PTR_TYPE);
}
else if (chunk_is_addr(pc))
{
set_chunk_type(pc, CT_BYREF);
}
}
}
void flag_series(chunk_t *start, chunk_t *end, UINT64 set_flags, UINT64 clr_flags, chunk_nav_t nav)
{
LOG_FUNC_ENTRY();
while (start && (start != end))
{
start->flags = (start->flags & ~clr_flags) | set_flags;
start = chunk_get_next(start, nav);
}
if (end)
{
end->flags = (end->flags & ~clr_flags) | set_flags;
}
}
/**
* Flags everything from the open paren to the close paren.
*
* @param po Pointer to the open parenthesis
* @return The token after the close paren
*/
static chunk_t *flag_parens(chunk_t *po, UINT64 flags,
c_token_t opentype, c_token_t parenttype,
bool parent_all)
{
LOG_FUNC_ENTRY();
chunk_t *paren_close;
chunk_t *pc;
paren_close = chunk_skip_to_match(po, CNAV_PREPROC);
if (paren_close == NULL)
{
LOG_FMT(LERR, "flag_parens: no match for [%s] at [%d:%d]",
po->str.c_str(), po->orig_line, po->orig_col);
log_func_stack_inline(LERR);
return(NULL);
}
LOG_FMT(LFLPAREN, "flag_parens: %d:%d [%s] and %d:%d [%s] type=%s ptype=%s",
po->orig_line, po->orig_col, po->text(),
paren_close->orig_line, paren_close->orig_col, paren_close->text(),
get_token_name(opentype), get_token_name(parenttype));
log_func_stack_inline(LSETTYP);
if (po != paren_close)
{
if ((flags != 0) ||
(parent_all && (parenttype != CT_NONE)))
{
for (pc = chunk_get_next(po, CNAV_PREPROC);
pc != paren_close;
pc = chunk_get_next(pc, CNAV_PREPROC))
{
pc->flags |= flags;
if (parent_all)
{
set_chunk_parent(pc, parenttype);
}
}
}
if (opentype != CT_NONE)
{
set_chunk_type(po, opentype);
set_chunk_type(paren_close, (c_token_t)(opentype + 1));
}
if (parenttype != CT_NONE)
{
set_chunk_parent(po, parenttype);
set_chunk_parent(paren_close, parenttype);
}
}
return(chunk_get_next_ncnl(paren_close, CNAV_PREPROC));
}
/**
* Sets the parent of the open paren/brace/square/angle and the closing.
* Note - it is assumed that pc really does point to an open item and the
* close must be open + 1.
*
* @param start The open paren
* @param parent The type to assign as the parent
* @reutrn The chunk after the close paren
*/
chunk_t *set_paren_parent(chunk_t *start, c_token_t parent)
{
LOG_FUNC_ENTRY();
chunk_t *end;
end = chunk_skip_to_match(start, CNAV_PREPROC);
if (end != NULL)
{
LOG_FMT(LFLPAREN, "set_paren_parent: %d:%d [%s] and %d:%d [%s] type=%s ptype=%s",
start->orig_line, start->orig_col, start->text(),
end->orig_line, end->orig_col, end->text(),
get_token_name(start->type), get_token_name(parent));
log_func_stack_inline(LFLPAREN);
set_chunk_parent(start, parent);
set_chunk_parent(end, parent);
}
return(chunk_get_next_ncnl(end, CNAV_PREPROC));
}
/* Scan backwards to see if we might be on a type declaration */
static bool chunk_ends_type(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *pc = start;
bool ret = false;
int cnt = 0;
bool last_lval = false;
for (/* nada */; pc != NULL; pc = chunk_get_prev_ncnl(pc))
{
LOG_FMT(LFTYPE, "%s: [%s] %s flags %" PRIx64 " on line %d, col %d\n",
__func__, get_token_name(pc->type), pc->str.c_str(),
pc->flags, pc->orig_line, pc->orig_col);
if ((pc->type == CT_WORD) ||
(pc->type == CT_TYPE) ||
(pc->type == CT_PTR_TYPE) ||
(pc->type == CT_STRUCT) ||
(pc->type == CT_DC_MEMBER) ||
(pc->type == CT_QUALIFIER))
{
cnt++;
last_lval = (pc->flags & PCF_LVALUE) != 0;
continue;
}
if (chunk_is_semicolon(pc) ||
(pc->type == CT_TYPEDEF) ||
(pc->type == CT_BRACE_OPEN) ||
(pc->type == CT_BRACE_CLOSE) ||
chunk_is_forin(pc) ||
((pc->type == CT_SPAREN_OPEN) && last_lval))
{
ret = cnt > 0;
}
break;
}
if (pc == NULL)
{
/* first token */
ret = true;
}
LOG_FMT(LFTYPE, "%s verdict: %s\n", __func__, ret ? "yes" : "no");
return(ret);
}
/* skip to the final word/type in a :: chain
* pc is either a word or a ::
*/
static chunk_t *skip_dc_member(chunk_t *start)
{
LOG_FUNC_ENTRY();
if (!start)
{
return NULL;
}
chunk_t *pc = start;
chunk_t *next = (pc->type == CT_DC_MEMBER) ? pc : chunk_get_next_ncnl(pc);
while (next && (next->type == CT_DC_MEMBER))
{
pc = chunk_get_next_ncnl(next);
next = chunk_get_next_ncnl(pc);
}
return pc;
}
/**
* This is called on every chunk.
* First on all non-preprocessor chunks and then on each preprocessor chunk.
* It does all the detection and classifying.
*/
void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
// LOG_FMT(LSYS, " %3d > ['%s' %s] ['%s' %s] ['%s' %s]\n",
// pc->orig_line,
// prev->str.c_str(), get_token_name(prev->type),
// pc->str.c_str(), get_token_name(pc->type),
// next->str.c_str(), get_token_name(next->type));
if ((pc->type == CT_OC_AT) && next)
{
if ((next->type == CT_PAREN_OPEN) ||
(next->type == CT_BRACE_OPEN) ||
(next->type == CT_SQUARE_OPEN))
{
flag_parens(next, PCF_OC_BOXED, next->type, CT_OC_AT, false);
}
else
{
set_chunk_parent(next, CT_OC_AT);
}
}
/* D stuff */
if ((cpd.lang_flags & LANG_D) &&
(pc->type == CT_QUALIFIER) &&
chunk_is_str(pc, "const", 5) &&
(next->type == CT_PAREN_OPEN))
{
set_chunk_type(pc, CT_D_CAST);
set_paren_parent(next, pc->type);
}
if ((next->type == CT_PAREN_OPEN) &&
((pc->type == CT_D_CAST) ||
(pc->type == CT_DELEGATE) ||
(pc->type == CT_ALIGN)))
{
/* mark the parenthesis parent */
tmp = set_paren_parent(next, pc->type);
/* For a D cast - convert the next item */
if ((pc->type == CT_D_CAST) && (tmp != NULL))
{
if (tmp->type == CT_STAR)
{
set_chunk_type(tmp, CT_DEREF);
}
else if (tmp->type == CT_AMP)
{
set_chunk_type(tmp, CT_ADDR);
}
else if (tmp->type == CT_MINUS)
{
set_chunk_type(tmp, CT_NEG);
}
else if (tmp->type == CT_PLUS)
{
set_chunk_type(tmp, CT_POS);
}
}
/* For a delegate, mark previous words as types and the item after the
* close paren as a variable def
*/
if (pc->type == CT_DELEGATE)
{
if (tmp != NULL)
{
set_chunk_parent(tmp, CT_DELEGATE);
if (tmp->level == tmp->brace_level)
{
tmp->flags |= PCF_VAR_1ST_DEF;
}
}
for (tmp = chunk_get_prev_ncnl(pc); tmp != NULL; tmp = chunk_get_prev_ncnl(tmp))
{
if (chunk_is_semicolon(tmp) ||
(tmp->type == CT_BRACE_OPEN) ||
(tmp->type == CT_VBRACE_OPEN))
{
break;
}
make_type(tmp);
}
}
if ((pc->type == CT_ALIGN) && (tmp != NULL))
{
if (tmp->type == CT_BRACE_OPEN)
{
set_paren_parent(tmp, pc->type);
}
else if (tmp->type == CT_COLON)
{
set_chunk_parent(tmp, pc->type);
}
}
} /* paren open + cast/align/delegate */
if (pc->type == CT_INVARIANT)
{
if (next->type == CT_PAREN_OPEN)
{
set_chunk_parent(next, pc->type);
tmp = chunk_get_next(next);
while (tmp != NULL)
{
if (tmp->type == CT_PAREN_CLOSE)
{
set_chunk_parent(tmp, pc->type);
break;
}
make_type(tmp);
tmp = chunk_get_next(tmp);
}
}
else
{
set_chunk_type(pc, CT_QUALIFIER);
}
}
if ((prev->type == CT_BRACE_OPEN) &&
(prev->parent_type != CT_CS_PROPERTY) &&
((pc->type == CT_GETSET) || (pc->type == CT_GETSET_EMPTY)))
{
flag_parens(prev, 0, CT_NONE, CT_GETSET, false);
}
/* Objective C stuff */
if (cpd.lang_flags & LANG_OC)
{
/* Check for message declarations */
if (pc->flags & PCF_STMT_START)
{
if ((chunk_is_str(pc, "-", 1) || chunk_is_str(pc, "+", 1)) &&
chunk_is_str(next, "(", 1))
{
handle_oc_message_decl(pc);
}
}
if (pc->flags & PCF_EXPR_START)
{
if (pc->type == CT_SQUARE_OPEN)
{
handle_oc_message_send(pc);
}
if (pc->type == CT_CARET)
{
handle_oc_block_literal(pc);
}
}
}
/* C# stuff */
if (cpd.lang_flags & LANG_CS)
{
/* '[assembly: xxx]' stuff */
if ((pc->flags & PCF_EXPR_START) &&
(pc->type == CT_SQUARE_OPEN))
{
handle_cs_square_stmt(pc);
}
if ((next != NULL) && (next->type == CT_BRACE_OPEN) &&
(next->parent_type == CT_NONE) &&
((pc->type == CT_SQUARE_CLOSE) ||
(pc->type == CT_ANGLE_CLOSE) ||
(pc->type == CT_WORD)))
{
handle_cs_property(next);
}
if ((pc->type == CT_SQUARE_CLOSE) &&
(next != NULL) && (next->type == CT_WORD))
{
handle_cs_array_type(pc);
}
if (((pc->type == CT_LAMBDA || pc->type == CT_DELEGATE)) && (next->type == CT_BRACE_OPEN))
{
set_paren_parent(next, pc->type);
}
}
/* C++11 Lambda stuff */
if (prev && (cpd.lang_flags & LANG_CPP) &&
((pc->type == CT_SQUARE_OPEN) || (pc->type == CT_TSQUARE)) &&
!CharTable::IsKw1(prev->str[0]))
{
handle_cpp_lambda(pc);
}
/* FIXME: which language does this apply to? */
if ((pc->type == CT_ASSIGN) && (next->type == CT_SQUARE_OPEN))
{
set_paren_parent(next, CT_ASSIGN);
/* Mark one-liner assignment */
tmp = next;
while ((tmp = chunk_get_next_nc(tmp)) != NULL)
{
if (chunk_is_newline(tmp))
{
break;
}
if ((tmp->type == CT_SQUARE_CLOSE) && (next->level == tmp->level))
{
tmp->flags |= PCF_ONE_LINER;
next->flags |= PCF_ONE_LINER;
break;
}
}
}
if (pc->type == CT_ASSERT)
{
handle_java_assert(pc);
}
if (pc->type == CT_ANNOTATION)
{
tmp = chunk_get_next_ncnl(pc);
if (chunk_is_paren_open(tmp))
{
set_paren_parent(tmp, CT_ANNOTATION);
}
}
/* A [] in C# and D only follows a type */
if ((pc->type == CT_TSQUARE) &&
((cpd.lang_flags & (LANG_D | LANG_CS | LANG_VALA)) != 0))
{
if ((prev != NULL) && (prev->type == CT_WORD))
{
set_chunk_type(prev, CT_TYPE);
}
if ((next != NULL) && (next->type == CT_WORD))
{
next->flags |= PCF_VAR_1ST_DEF;
}
}
if ((pc->type == CT_SQL_EXEC) ||
(pc->type == CT_SQL_BEGIN) ||
(pc->type == CT_SQL_END))
{
mark_exec_sql(pc);
}
if (pc->type == CT_PROTO_WRAP)
{
handle_proto_wrap(pc);
}
/* Handle the typedef */
if (pc->type == CT_TYPEDEF)
{
fix_typedef(pc);
}
if ((pc->type == CT_ENUM) ||
(pc->type == CT_STRUCT) ||
(pc->type == CT_UNION))
{
if (prev->type != CT_TYPEDEF)
{
fix_enum_struct_union(pc);
}
}
if (pc->type == CT_EXTERN)
{
if (chunk_is_paren_open(next))
{
tmp = flag_parens(next, 0, CT_NONE, CT_EXTERN, true);
if (tmp && (tmp->type == CT_BRACE_OPEN))
{
set_paren_parent(tmp, CT_EXTERN);
}
}
else
{
/* next likely is a string (see tokenize_cleanup.cpp) */
set_chunk_parent(next, CT_EXTERN);
tmp = chunk_get_next_ncnl(next);
if (tmp && (tmp->type == CT_BRACE_OPEN))
{
set_paren_parent(tmp, CT_EXTERN);
}
}
}
if (pc->type == CT_TEMPLATE)
{
if (cpd.lang_flags & LANG_D)
{
handle_d_template(pc);
}
else
{
handle_cpp_template(pc);
}
}
if ((pc->type == CT_WORD) &&
(next->type == CT_ANGLE_OPEN) &&
(next->parent_type == CT_TEMPLATE))
{
mark_template_func(pc, next);
}
if ((pc->type == CT_SQUARE_CLOSE) &&
(next->type == CT_PAREN_OPEN))
{
flag_parens(next, 0, CT_FPAREN_OPEN, CT_NONE, false);
}
if (pc->type == CT_TYPE_CAST)
{
fix_type_cast(pc);
}
if ((pc->parent_type == CT_ASSIGN) &&
((pc->type == CT_BRACE_OPEN) ||
(pc->type == CT_SQUARE_OPEN)))
{
/* Mark everything in here as in assign */
flag_parens(pc, PCF_IN_ARRAY_ASSIGN, pc->type, CT_NONE, false);
}
if (pc->type == CT_D_TEMPLATE)
{
set_paren_parent(next, pc->type);
}
/**
* A word before an open paren is a function call or definition.
* CT_WORD => CT_FUNC_CALL or CT_FUNC_DEF
*/
if (next->type == CT_PAREN_OPEN)
{
tmp = chunk_get_next_ncnl(next);
if ((cpd.lang_flags & LANG_OC) && chunk_is_token(tmp, CT_CARET))
{
handle_oc_block_type(tmp);
// This is the case where a block literal is passed as the first argument of a C-style method invocation.
if ((tmp->type == CT_OC_BLOCK_CARET) && (pc->type == CT_WORD))
{
set_chunk_type(pc, CT_FUNC_CALL);
}
}
else if ((pc->type == CT_WORD) || (pc->type == CT_OPERATOR_VAL))
{
set_chunk_type(pc, CT_FUNCTION);
}
else if (pc->type == CT_TYPE)
{
/**
* If we are on a type, then we are either on a C++ style cast, a
* function or we are on a function type.
* The only way to tell for sure is to find the close paren and see
* if it is followed by an open paren.
* "int(5.6)"
* "int()"
* "int(foo)(void)"
*
* FIXME: this check can be done better...
*/
tmp = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);
tmp = chunk_get_next(tmp);
if ((tmp != NULL) && (tmp->type == CT_PAREN_OPEN))
{
/* we have "TYPE(...)(" */
set_chunk_type(pc, CT_FUNCTION);
}
else
{
if ((pc->parent_type == CT_NONE) &&
((pc->flags & PCF_IN_TYPEDEF) == 0))
{
tmp = chunk_get_next_ncnl(next);
if ((tmp != NULL) && (tmp->type == CT_PAREN_CLOSE))
{
/* we have TYPE() */
set_chunk_type(pc, CT_FUNCTION);
}
else
{
/* we have TYPE(...) */
set_chunk_type(pc, CT_CPP_CAST);
set_paren_parent(next, CT_CPP_CAST);
}
}
}
}
else if (pc->type == CT_ATTRIBUTE)
{
flag_parens(next, 0, CT_FPAREN_OPEN, CT_ATTRIBUTE, false);
}
}
if ((cpd.lang_flags & LANG_PAWN) != 0)
{
if ((pc->type == CT_FUNCTION) && (pc->brace_level > 0))
{
set_chunk_type(pc, CT_FUNC_CALL);
}
if ((pc->type == CT_STATE) &&
(next != NULL) &&
(next->type == CT_PAREN_OPEN))
{
set_paren_parent(next, pc->type);
}
}
else
{
if ((pc->type == CT_FUNCTION) &&
((pc->parent_type == CT_OC_BLOCK_EXPR) || !is_oc_block(pc)))
{
mark_function(pc);
}
}
/* Detect C99 member stuff */
if ((pc->type == CT_MEMBER) &&
((prev->type == CT_COMMA) ||
(prev->type == CT_BRACE_OPEN)))
{
set_chunk_type(pc, CT_C99_MEMBER);
set_chunk_parent(next, CT_C99_MEMBER);
}
/* Mark function parens and braces */
if ((pc->type == CT_FUNC_DEF) ||
(pc->type == CT_FUNC_CALL) ||
(pc->type == CT_FUNC_CALL_USER) ||
(pc->type == CT_FUNC_PROTO))
{
tmp = next;
if (tmp->type == CT_SQUARE_OPEN)
{
tmp = set_paren_parent(tmp, pc->type);
}
else if ((tmp->type == CT_TSQUARE) ||
(tmp->parent_type == CT_OPERATOR))
{
tmp = chunk_get_next_ncnl(tmp);
}
if (chunk_is_paren_open(tmp))
{
tmp = flag_parens(tmp, 0, CT_FPAREN_OPEN, pc->type, false);
if (tmp != NULL)
{
if (tmp->type == CT_BRACE_OPEN)
{
if ((tmp->parent_type != CT_DOUBLE_BRACE) &&
((pc->flags & PCF_IN_CONST_ARGS) == 0))
{
set_paren_parent(tmp, pc->type);
}
}
else if (chunk_is_semicolon(tmp) && (pc->type == CT_FUNC_PROTO))
{
set_chunk_parent(tmp, pc->type);
}
}
}
}
/* Mark the parameters in catch() */
if ((pc->type == CT_CATCH) && (next->type == CT_SPAREN_OPEN))
{
fix_fcn_def_params(next);
}
if ((pc->type == CT_THROW) && (prev->type == CT_FPAREN_CLOSE))
{
set_chunk_parent(pc, prev->parent_type);
if (next->type == CT_PAREN_OPEN)
{
set_paren_parent(next, CT_THROW);
}
}
/* Mark the braces in: "for_each_entry(xxx) { }" */
if ((pc->type == CT_BRACE_OPEN) &&
(pc->parent_type != CT_DOUBLE_BRACE) &&
(prev->type == CT_FPAREN_CLOSE) &&
((prev->parent_type == CT_FUNC_CALL) ||
(prev->parent_type == CT_FUNC_CALL_USER)) &&
((pc->flags & PCF_IN_CONST_ARGS) == 0))
{
set_paren_parent(pc, CT_FUNC_CALL);
}
/* Check for a close paren followed by an open paren, which means that
* we are on a function type declaration (C/C++ only?).
* Note that typedefs are already taken care of.
*/
if ((next != NULL) &&
((pc->flags & (PCF_IN_TYPEDEF | PCF_IN_TEMPLATE)) == 0) &&
(pc->parent_type != CT_CPP_CAST) &&
(pc->parent_type != CT_C_CAST) &&
((pc->flags & PCF_IN_PREPROC) == 0) &&
(!is_oc_block(pc)) &&
(pc->parent_type != CT_OC_MSG_DECL) &&
(pc->parent_type != CT_OC_MSG_SPEC) &&
chunk_is_str(pc, ")", 1) &&
chunk_is_str(next, "(", 1))
{
if ((cpd.lang_flags & LANG_D) != 0)
{
flag_parens(next, 0, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
}
else
{
mark_function_type(pc);
}
}
if (((pc->type == CT_CLASS) ||
(pc->type == CT_STRUCT)) &&
(pc->level == pc->brace_level))
{
if ((pc->type != CT_STRUCT) || ((cpd.lang_flags & LANG_C) == 0))
{
mark_class_ctor(pc);
}
}
if (pc->type == CT_OC_CLASS)
{
handle_oc_class(pc);
}
if (pc->type == CT_NAMESPACE)
{
mark_namespace(pc);
}
/*TODO: Check for stuff that can only occur at the start of an statement */
if ((cpd.lang_flags & LANG_D) == 0)
{
/**
* Check a paren pair to see if it is a cast.
* Note that SPAREN and FPAREN have already been marked.
*/
if ((pc->type == CT_PAREN_OPEN) &&
((pc->parent_type == CT_NONE) ||
(pc->parent_type == CT_OC_MSG) ||
(pc->parent_type == CT_OC_BLOCK_EXPR)) &&
((next->type == CT_WORD) ||
(next->type == CT_TYPE) ||
(next->type == CT_STRUCT) ||
(next->type == CT_QUALIFIER) ||
(next->type == CT_MEMBER) ||
(next->type == CT_DC_MEMBER) ||
(next->type == CT_ENUM) ||
(next->type == CT_UNION)) &&
(prev->type != CT_SIZEOF) &&
(prev->parent_type != CT_OPERATOR) &&
((pc->flags & PCF_IN_TYPEDEF) == 0))
{
fix_casts(pc);
}
}
/* Check for stuff that can only occur at the start of an expression */
if ((pc->flags & PCF_EXPR_START) != 0)
{
/* Change STAR, MINUS, and PLUS in the easy cases */
if (pc->type == CT_STAR)
{
set_chunk_type(pc, (prev->type == CT_ANGLE_CLOSE) ? CT_PTR_TYPE : CT_DEREF);
}
if (pc->type == CT_MINUS)
{
set_chunk_type(pc, CT_NEG);
}
if (pc->type == CT_PLUS)
{
set_chunk_type(pc, CT_POS);
}
if (pc->type == CT_INCDEC_AFTER)
{
set_chunk_type(pc, CT_INCDEC_BEFORE);
//fprintf(stderr, "%s: %d> changed INCDEC_AFTER to INCDEC_BEFORE\n", __func__, pc->orig_line);
}
if (pc->type == CT_AMP)
{
//fprintf(stderr, "Changed AMP to ADDR on line %d\n", pc->orig_line);
set_chunk_type(pc, CT_ADDR);
}
if (pc->type == CT_CARET)
{
if (cpd.lang_flags & LANG_OC)
{
/* This is likely the start of a block literal */
handle_oc_block_literal(pc);
}
}
}
/* Detect a variable definition that starts with struct/enum/union/class */
if (((pc->flags & PCF_IN_TYPEDEF) == 0) &&
(prev->parent_type != CT_CPP_CAST) &&
((prev->flags & PCF_IN_FCN_DEF) == 0) &&
((pc->type == CT_STRUCT) ||
(pc->type == CT_UNION) ||
(pc->type == CT_CLASS) ||
(pc->type == CT_ENUM)))
{
tmp = skip_dc_member(next);
if (tmp && ((tmp->type == CT_TYPE) || (tmp->type == CT_WORD)))
{
set_chunk_parent(tmp, pc->type);
set_chunk_type(tmp, CT_TYPE);
tmp = chunk_get_next_ncnl(tmp);
}
if ((tmp != NULL) && (tmp->type == CT_BRACE_OPEN))
{
tmp = chunk_skip_to_match(tmp);
tmp = chunk_get_next_ncnl(tmp);
}
if ((tmp != NULL) && (chunk_is_star(tmp) || chunk_is_addr(tmp) || (tmp->type == CT_WORD)))
{
mark_variable_definition(tmp);
}
}
if (pc->type == CT_OC_PROPERTY)
{
tmp = chunk_get_next_ncnl(pc);
if (chunk_is_paren_open(tmp))
{
tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp));
}
fix_var_def(tmp);
}
/**
* Change the paren pair after a function/macrofunc.
* CT_PAREN_OPEN => CT_FPAREN_OPEN
*/
if (pc->type == CT_MACRO_FUNC)
{
flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_MACRO_FUNC, false);
}
if ((pc->type == CT_MACRO_OPEN) ||
(pc->type == CT_MACRO_ELSE) ||
(pc->type == CT_MACRO_CLOSE))
{
if (next->type == CT_PAREN_OPEN)
{
flag_parens(next, 0, CT_FPAREN_OPEN, pc->type, false);
}
}
if ((pc->type == CT_DELETE) && (next->type == CT_TSQUARE))
{
set_chunk_parent(next, CT_DELETE);
}
/* Change CT_STAR to CT_PTR_TYPE or CT_ARITH or CT_DEREF */
if (pc->type == CT_STAR)
{
if (chunk_is_paren_close(next) || (next->type == CT_COMMA))
{
set_chunk_type(pc, CT_PTR_TYPE);
}
else if ((cpd.lang_flags & LANG_OC) && (next->type == CT_STAR))
{
/* Change pointer-to-pointer types in OC_MSG_DECLs
* from ARITH <===> DEREF to PTR_TYPE <===> PTR_TYPE */
set_chunk_type(pc, CT_PTR_TYPE);
set_chunk_parent(pc, prev->parent_type);
set_chunk_type(next, CT_PTR_TYPE);
set_chunk_parent(next, pc->parent_type);
}
else if ((prev->type == CT_SIZEOF) || (prev->type == CT_DELETE))
{
set_chunk_type(pc, CT_DEREF);
}
else if (((prev->type == CT_WORD) && chunk_ends_type(prev)) ||
(prev->type == CT_DC_MEMBER) || (prev->type == CT_PTR_TYPE))
{
set_chunk_type(pc, CT_PTR_TYPE);
}
else if (next->type == CT_SQUARE_OPEN)
{
set_chunk_type(pc, CT_PTR_TYPE);
}
else
{
/* most PCF_PUNCTUATOR chunks except a paren close would make this
* a deref. A paren close may end a cast or may be part of a macro fcn.
*/
set_chunk_type(pc,
((prev->flags & PCF_PUNCTUATOR) &&
(!chunk_is_paren_close(prev) ||
(prev->parent_type == CT_MACRO_FUNC)) &&
(prev->type != CT_SQUARE_CLOSE) &&
(prev->type != CT_DC_MEMBER)) ? CT_DEREF : CT_ARITH);
}
}
if (pc->type == CT_AMP)
{
if (prev->type == CT_DELETE)
{
set_chunk_type(pc, CT_ADDR);
}
else if (prev->type == CT_TYPE)
{
set_chunk_type(pc, CT_BYREF);
}
else
{
set_chunk_type(pc, CT_ARITH);
if (prev->type == CT_WORD)
{
tmp = chunk_get_prev_ncnl(prev);
if ((tmp != NULL) &&
(chunk_is_semicolon(tmp) ||
(tmp->type == CT_BRACE_OPEN) ||
(tmp->type == CT_QUALIFIER)))
{
set_chunk_type(prev, CT_TYPE);
set_chunk_type(pc, CT_ADDR);
next->flags |= PCF_VAR_1ST;
}
}
}
}
if ((pc->type == CT_MINUS) ||
(pc->type == CT_PLUS))
{
if ((prev->type == CT_POS) || (prev->type == CT_NEG))
{
set_chunk_type(pc, (pc->type == CT_MINUS) ? CT_NEG : CT_POS);
}
else if (prev->type == CT_OC_CLASS)
{
set_chunk_type(pc, (pc->type == CT_MINUS) ? CT_NEG : CT_POS);
}
else
{
set_chunk_type(pc, CT_ARITH);
}
}
}
/**
* Combines two tokens into {{ and }} if inside parens and nothing is between
* either pair.
*/
static void check_double_brace_init(chunk_t *bo1)
{
LOG_FUNC_ENTRY();
LOG_FMT(LJDBI, "%s: %d:%d", __func__, bo1->orig_line, bo1->orig_col);
chunk_t *pc = chunk_get_prev_ncnl(bo1);
if (chunk_is_paren_close(pc))
{
chunk_t *bo2 = chunk_get_next(bo1);
if (chunk_is_token(bo2, CT_BRACE_OPEN))
{
/* found a potential double brace */
chunk_t *bc2 = chunk_skip_to_match(bo2);
chunk_t *bc1 = chunk_get_next(bc2);
if (chunk_is_token(bc1, CT_BRACE_CLOSE))
{
LOG_FMT(LJDBI, " - end %d:%d\n", bc2->orig_line, bc2->orig_col);
/* delete bo2 and bc1 */
bo1->str += bo2->str;
bo1->orig_col_end = bo2->orig_col_end;
chunk_del(bo2);
set_chunk_parent(bo1, CT_DOUBLE_BRACE);
bc2->str += bc1->str;
bc2->orig_col_end = bc1->orig_col_end;
chunk_del(bc1);
set_chunk_parent(bc2, CT_DOUBLE_BRACE);
return;
}
}
}
LOG_FMT(LJDBI, " - no\n");
}
/**
* Change CT_INCDEC_AFTER + WORD to CT_INCDEC_BEFORE
* Change number/word + CT_ADDR to CT_ARITH
* Change number/word + CT_STAR to CT_ARITH
* Change number/word + CT_NEG to CT_ARITH
* Change word + ( to a CT_FUNCTION
* Change struct/union/enum + CT_WORD => CT_TYPE
* Force parens on return.
*
* TODO: This could be done earlier.
*
* Patterns detected:
* STRUCT/ENUM/UNION + WORD :: WORD => TYPE
* WORD + '(' :: WORD => FUNCTION
*/
void fix_symbols(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *next;
chunk_t *prev;
chunk_t dummy;
mark_define_expressions();
bool is_java = cpd.lang_flags & LANG_JAVA;
for (pc = chunk_get_head(); pc != NULL; pc = chunk_get_next_ncnl(pc))
{
if ((pc->type == CT_FUNC_WRAP) ||
(pc->type == CT_TYPE_WRAP))
{
handle_wrap(pc);
}
if (pc->type == CT_ASSIGN)
{
mark_lvalue(pc);
}
if (is_java && (pc->type == CT_BRACE_OPEN))
{
check_double_brace_init(pc);
}
}
pc = chunk_get_head();
if (chunk_is_newline(pc) || chunk_is_comment(pc))
{
pc = chunk_get_next_ncnl(pc);
}
while (pc != NULL)
{
prev = chunk_get_prev_ncnl(pc, CNAV_PREPROC);
if (prev == NULL)
{
prev = &dummy;
}
next = chunk_get_next_ncnl(pc, CNAV_PREPROC);
if (next == NULL)
{
next = &dummy;
}
do_symbol_check(prev, pc, next);
pc = chunk_get_next_ncnl(pc);
}
pawn_add_virtual_semicolons();
process_returns();
/**
* 2nd pass - handle variable definitions
* REVISIT: We need function params marked to do this (?)
*/
pc = chunk_get_head();
int square_level = -1;
while (pc != NULL)
{
/* Can't have a variable definition inside [ ] */
if (square_level < 0)
{
if (pc->type == CT_SQUARE_OPEN)
{
square_level = pc->level;
}
}
else
{
if (pc->level <= square_level)
{
square_level = -1;
}
}
/**
* A variable definition is possible after at the start of a statement
* that starts with: QUALIFIER, TYPE, or WORD
*/
if ((square_level < 0) &&
((pc->flags & PCF_STMT_START) != 0) &&
((pc->type == CT_QUALIFIER) ||
(pc->type == CT_TYPE) ||
(pc->type == CT_WORD)) &&
(pc->parent_type != CT_ENUM) &&
((pc->flags & PCF_IN_ENUM) == 0))
{
pc = fix_var_def(pc);
}
else
{
pc = chunk_get_next_ncnl(pc);
}
}
}
/* Just hit an assign. Go backwards until we hit an open brace/paren/square or
* semicolon (TODO: other limiter?) and mark as a LValue.
*/
static void mark_lvalue(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *prev;
if ((pc->flags & PCF_IN_PREPROC) != 0)
{
return;
}
for (prev = chunk_get_prev_ncnl(pc);
prev != NULL;
prev = chunk_get_prev_ncnl(prev))
{
if ((prev->level < pc->level) ||
(prev->type == CT_ASSIGN) ||
(prev->type == CT_COMMA) ||
(prev->type == CT_BOOL) ||
chunk_is_semicolon(prev) ||
chunk_is_str(prev, "(", 1) ||
chunk_is_str(prev, "{", 1) ||
chunk_is_str(prev, "[", 1) ||
(prev->flags & PCF_IN_PREPROC))
{
break;
}
prev->flags |= PCF_LVALUE;
if ((prev->level == pc->level) && chunk_is_str(prev, "&", 1))
{
make_type(prev);
}
}
}
/**
* Changes the return type to type and set the parent.
*
* @param pc the last chunk of the return type
* @param parent_type CT_NONE (no change) or the new parent type
*/
static void mark_function_return_type(chunk_t *fname, chunk_t *start, c_token_t parent_type)
{
LOG_FUNC_ENTRY();
chunk_t *pc = start;
if (pc)
{
/* Step backwards from pc and mark the parent of the return type */
LOG_FMT(LFCNR, "%s: (backwards) return type for '%s' @ %d:%d", __func__,
fname->text(), fname->orig_line, fname->orig_col);
chunk_t *first = pc;
while (pc)
{
if ((!chunk_is_type(pc) &&
(pc->type != CT_OPERATOR) &&
(pc->type != CT_WORD) &&
(pc->type != CT_ADDR)) ||
((pc->flags & PCF_IN_PREPROC) != 0))
{
break;
}
if (!chunk_is_addr(pc) && !chunk_is_star(pc))
{
first = pc;
}
pc = chunk_get_prev_ncnl(pc);
}
pc = first;
while (pc)
{
LOG_FMT(LFCNR, " [%s|%s]", pc->text(), get_token_name(pc->type));
if (parent_type != CT_NONE)
{
set_chunk_parent(pc, parent_type);
}
make_type(pc);
if (pc == start)
{
break;
}
pc = chunk_get_next_ncnl(pc);
}
LOG_FMT(LFCNR, "\n");
}
}
/**
* Process a function type that is not in a typedef.
* pc points to the first close paren.
*
* void (*func)(params);
* const char * (*func)(params);
* const char * (^func)(params); -- Objective C
*
* @param pc Points to the first closing paren
* @return whether a function type was processed
*/
static bool mark_function_type(chunk_t *pc)
{
LOG_FUNC_ENTRY();
LOG_FMT(LFTYPE, "%s: [%s] %s @ %d:%d\n",
__func__, get_token_name(pc->type), pc->str.c_str(),
pc->orig_line, pc->orig_col);
int star_count = 0;
int word_count = 0;
chunk_t *ptrcnk = NULL;
chunk_t *varcnk = NULL;
chunk_t *tmp;
chunk_t *apo;
chunk_t *apc;
chunk_t *aft;
bool anon = false;
c_token_t pt, ptp;
/* Scan backwards across the name, which can only be a word and single star */
varcnk = chunk_get_prev_ncnl(pc);
if (!chunk_is_word(varcnk))
{
if ((cpd.lang_flags & LANG_OC) && chunk_is_str(varcnk, "^", 1) &&
chunk_is_paren_open(chunk_get_prev_ncnl(varcnk)))
{
/* anonymous ObjC block type -- RTYPE (^)(ARGS) */
anon = true;
}
else
{
LOG_FMT(LFTYPE, "%s: not a word '%s' [%s] @ %d:%d\n",
__func__, varcnk->text(), get_token_name(varcnk->type),
varcnk->orig_line, varcnk->orig_col);
goto nogo_exit;
}
}
apo = chunk_get_next_ncnl(pc);
apc = chunk_skip_to_match(apo);
if (!chunk_is_paren_open(apo) || ((apc = chunk_skip_to_match(apo)) == NULL))
{
LOG_FMT(LFTYPE, "%s: not followed by parens\n", __func__);
goto nogo_exit;
}
aft = chunk_get_next_ncnl(apc);
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: not followed by '{' or ';'\n", __func__);
goto nogo_exit;
}
ptp = (pc->flags & PCF_IN_TYPEDEF) ? CT_FUNC_TYPE : CT_FUNC_VAR;
tmp = pc;
while ((tmp = chunk_get_prev_ncnl(tmp)) != NULL)
{
LOG_FMT(LFTYPE, " -- [%s] %s on line %d, col %d",
get_token_name(tmp->type), tmp->str.c_str(),
tmp->orig_line, tmp->orig_col);
if (chunk_is_star(tmp) || 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) ||
(tmp->type == CT_WORD) ||
(tmp->type == CT_TYPE))
{
word_count++;
LOG_FMT(LFTYPE, " -- TYPE(%s)\n", tmp->text());
}
else if (tmp->type == CT_DC_MEMBER)
{
word_count = 0;
LOG_FMT(LFTYPE, " -- :: reset word_count\n");
}
else if (chunk_is_str(tmp, "(", 1))
{
LOG_FMT(LFTYPE, " -- open paren (break)\n");
break;
}
else
{
LOG_FMT(LFTYPE, " -- unexpected token [%s] %s on line %d, col %d\n",
get_token_name(tmp->type), tmp->str.c_str(),
tmp->orig_line, tmp->orig_col);
goto nogo_exit;
}
}
if ((star_count > 1) ||
(word_count > 1) ||
((star_count + word_count) == 0))
{
LOG_FMT(LFTYPE, "%s: bad counts word:%d, star:%d\n", __func__,
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(chunk_get_prev_ncnl(tmp)))
{
goto nogo_exit;
}
if (ptrcnk)
{
set_chunk_type(ptrcnk, CT_PTR_TYPE);
}
if (!anon)
{
if (pc->flags & PCF_IN_TYPEDEF)
{
set_chunk_type(varcnk, CT_TYPE);
}
else
{
set_chunk_type(varcnk, CT_FUNC_VAR);
varcnk->flags |= 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 & PCF_IN_TYPEDEF) ? CT_TYPEDEF : CT_FUNC_VAR);
}
else if (chunk_is_token(aft, CT_BRACE_OPEN))
{
flag_parens(aft, 0, CT_NONE, pt, false);
}
/* Step backwards to the previous open paren and mark everything a
*/
tmp = pc;
while ((tmp = chunk_get_prev_ncnl(tmp)) != NULL)
{
LOG_FMT(LFTYPE, " ++ [%s] %s on line %d, col %d\n",
get_token_name(tmp->type), tmp->str.c_str(),
tmp->orig_line, tmp->orig_col);
if (*tmp->str == '(')
{
if ((pc->flags & PCF_IN_TYPEDEF) == 0)
{
tmp->flags |= PCF_VAR_1ST_DEF;
}
set_chunk_type(tmp, CT_TPAREN_OPEN);
set_chunk_parent(tmp, ptp);
tmp = chunk_get_prev_ncnl(tmp);
if (tmp != NULL)
{
if ((tmp->type == CT_FUNCTION) ||
(tmp->type == CT_FUNC_CALL) ||
(tmp->type == CT_FUNC_CALL_USER) ||
(tmp->type == CT_FUNC_DEF) ||
(tmp->type == CT_FUNC_PROTO))
{
set_chunk_type(tmp, CT_TYPE);
tmp->flags &= ~PCF_VAR_1ST_DEF;
}
}
mark_function_return_type(varcnk, tmp, ptp);
break;
}
}
return true;
nogo_exit:
tmp = chunk_get_next_ncnl(pc);
if (chunk_is_paren_open(tmp))
{
LOG_FMT(LFTYPE, "%s:%d setting FUNC_CALL on %d:%d\n", __func__, __LINE__,
tmp->orig_line, tmp->orig_col);
flag_parens(tmp, 0, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
}
return false;
}
static void process_returns(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
pc = chunk_get_head();
while (pc != NULL)
{
if ((pc->type != CT_RETURN) || (pc->flags & PCF_IN_PREPROC))
{
pc = chunk_get_next_type(pc, CT_RETURN, -1);
continue;
}
pc = process_return(pc);
}
}
/**
* Processes a return statement, labeling the parens and marking the parent.
* May remove or add parens around the return statement
*
* @param pc Pointer to the return chunk
*/
static chunk_t *process_return(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *next;
chunk_t *temp;
chunk_t *semi;
chunk_t *cpar;
chunk_t chunk;
/* grab next and bail if it is a semicolon */
next = chunk_get_next_ncnl(pc);
if ((next == NULL) || chunk_is_semicolon(next))
{
return(next);
}
if (cpd.settings[UO_nl_return_expr].a != AV_IGNORE)
{
newline_iarf(pc, cpd.settings[UO_nl_return_expr].a);
}
if (next->type == CT_PAREN_OPEN)
{
/* See if the return is fully paren'd */
cpar = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);
semi = chunk_get_next_ncnl(cpar);
if (chunk_is_semicolon(semi))
{
if (cpd.settings[UO_mod_paren_on_return].a == AV_REMOVE)
{
LOG_FMT(LRETURN, "%s: removing parens on line %d\n",
__func__, pc->orig_line);
/* lower the level of everything */
for (temp = next; temp != cpar; temp = chunk_get_next(temp))
{
temp->level--;
}
/* delete the parens */
chunk_del(next);
chunk_del(cpar);
/* back up the semicolon */
semi->column--;
semi->orig_col--;
semi->orig_col_end--;
}
else
{
LOG_FMT(LRETURN, "%s: keeping parens on line %d\n",
__func__, pc->orig_line);
/* mark & keep them */
set_chunk_parent(next, CT_RETURN);
set_chunk_parent(cpar, CT_RETURN);
}
return(semi);
}
}
/* We don't have a fully paren'd return. Should we add some? */
if ((cpd.settings[UO_mod_paren_on_return].a & AV_ADD) == 0)
{
return(next);
}
/* find the next semicolon on the same level */
semi = next;
while ((semi = chunk_get_next(semi)) != NULL)
{
if ((chunk_is_semicolon(semi) && (pc->level == semi->level)) ||
(semi->level < pc->level))
{
break;
}
}
if (chunk_is_semicolon(semi) && (pc->level == semi->level))
{
/* add the parens */
chunk.type = CT_PAREN_OPEN;
chunk.str = "(";
chunk.level = pc->level;
chunk.brace_level = pc->brace_level;
chunk.orig_line = pc->orig_line;
chunk.parent_type = CT_RETURN;
chunk.flags = pc->flags & PCF_COPY_FLAGS;
chunk_add_before(&chunk, next);
chunk.type = CT_PAREN_CLOSE;
chunk.str = ")";
chunk.orig_line = semi->orig_line;
cpar = chunk_add_before(&chunk, semi);
LOG_FMT(LRETURN, "%s: added parens on line %d\n",
__func__, pc->orig_line);
for (temp = next; temp != cpar; temp = chunk_get_next(temp))
{
temp->level++;
}
}
return(semi);
}
static bool is_ucase_str(const char *str, int len)
{
while (len-- > 0)
{
if (unc_toupper(*str) != *str)
{
return(false);
}
str++;
}
return(true);
}
static bool is_oc_block(chunk_t *pc)
{
return((pc != NULL) &&
((pc->parent_type == CT_OC_BLOCK_TYPE) ||
(pc->parent_type == CT_OC_BLOCK_EXPR) ||
(pc->parent_type == CT_OC_BLOCK_ARG) ||
(pc->parent_type == CT_OC_BLOCK) ||
(pc->type == CT_OC_BLOCK_CARET) ||
(pc->next && pc->next->type == CT_OC_BLOCK_CARET) ||
(pc->prev && pc->prev->type == CT_OC_BLOCK_CARET)));
}
/**
* Checks to see if the current paren is part of a cast.
* We already verified that this doesn't follow function, TYPE, IF, FOR,
* SWITCH, or WHILE and is followed by WORD, TYPE, STRUCT, ENUM, or UNION.
*
* @param start Pointer to the open paren
*/
static void fix_casts(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *prev;
chunk_t *first;
chunk_t *after;
chunk_t *last = NULL;
chunk_t *paren_close;
const char *verb = "likely";
const char *detail = "";
int count = 0;
int word_count = 0;
int word_consec = 0;
bool nope;
bool doubtful_cast = false;
LOG_FMT(LCASTS, "%s:line %d, col %d:", __func__, start->orig_line, start->orig_col);
prev = chunk_get_prev_ncnl(start);
if ((prev != NULL) && (prev->type == CT_PP_DEFINED))
{
LOG_FMT(LCASTS, " -- not a cast - after defined\n");
return;
}
/* Make sure there is only WORD, TYPE, and '*' before the close paren */
pc = chunk_get_next_ncnl(start);
first = pc;
while ((pc != NULL) && (chunk_is_type(pc) ||
(pc->type == CT_WORD) ||
(pc->type == CT_QUALIFIER) ||
(pc->type == CT_DC_MEMBER) ||
(pc->type == CT_STAR) ||
(pc->type == CT_TSQUARE) ||
(pc->type == CT_AMP)))
{
LOG_FMT(LCASTS, " [%s]", get_token_name(pc->type));
if (pc->type == CT_WORD)
{
word_count++;
word_consec++;
}
else if (pc->type == CT_DC_MEMBER)
{
word_count--;
}
else
{
word_consec = 0;
}
last = pc;
pc = chunk_get_next_ncnl(pc);
count++;
}
if ((pc == NULL) || (pc->type != CT_PAREN_CLOSE) || (prev->type == CT_OC_CLASS))
{
LOG_FMT(LCASTS, " -- not a cast, hit [%s]\n",
pc == NULL ? "NULL" : get_token_name(pc->type));
return;
}
if (word_count > 1)
{
LOG_FMT(LCASTS, " -- too many words: %d\n", word_count);
return;
}
paren_close = pc;
/* If last is a type or star, we have a cast for sure */
if ((last->type == CT_STAR) ||
(last->type == CT_PTR_TYPE) ||
(last->type == CT_TYPE))
{
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 ((cpd.lang_flags & LANG_OC) && chunk_is_str(last, "id", 2))
{
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 = chunk_get_next_ncnl(paren_close);
after = pc;
do
{
after = chunk_get_next_ncnl(after);
} while ((after != NULL) && (after->type == CT_PAREN_OPEN));
if (after == NULL)
{
LOG_FMT(LCASTS, " -- not a cast - hit NULL\n");
return;
}
nope = false;
if (chunk_is_star(pc) || chunk_is_addr(pc))
{
/* star (*) and addr (&) are ambiguous */
if ((after->type == CT_NUMBER_FP) ||
(after->type == CT_NUMBER) ||
(after->type == CT_STRING) ||
doubtful_cast)
{
nope = true;
}
}
else if (pc->type == CT_MINUS)
{
/* (UINT8)-1 or (foo)-1 or (FOO)-'a' */
if ((after->type == CT_STRING) || doubtful_cast)
{
nope = true;
}
}
else if (pc->type == CT_PLUS)
{
/* (UINT8)+1 or (foo)+1 */
if (((after->type != CT_NUMBER) &&
(after->type != CT_NUMBER_FP)) || doubtful_cast)
{
nope = true;
}
}
else if ((pc->type != CT_NUMBER_FP) &&
(pc->type != CT_NUMBER) &&
(pc->type != CT_WORD) &&
(pc->type != CT_TYPE) &&
(pc->type != CT_PAREN_OPEN) &&
(pc->type != CT_STRING) &&
(pc->type != CT_SIZEOF) &&
(pc->type != CT_FUNC_CALL) &&
(pc->type != CT_FUNC_CALL_USER) &&
(pc->type != CT_FUNCTION) &&
(pc->type != CT_BRACE_OPEN) &&
(!((pc->type == CT_SQUARE_OPEN) && (cpd.lang_flags & LANG_OC))))
{
LOG_FMT(LCASTS, " -- not a cast - followed by '%s' %s\n",
pc->str.c_str(), get_token_name(pc->type));
return;
}
if (nope)
{
LOG_FMT(LCASTS, " -- not a cast - '%s' followed by %s\n",
pc->str.c_str(), get_token_name(after->type));
return;
}
}
/* if the 'cast' is followed by a semicolon, comma or close paren, it isn't */
pc = chunk_get_next_ncnl(paren_close);
if (chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA) || chunk_is_paren_close(pc))
{
LOG_FMT(LCASTS, " -- not a cast - followed by %s\n", 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 c-cast: (", verb);
for (pc = first; pc != paren_close; pc = chunk_get_next_ncnl(pc))
{
set_chunk_parent(pc, CT_C_CAST);
make_type(pc);
LOG_FMT(LCASTS, " %s", pc->str.c_str());
}
LOG_FMT(LCASTS, " )%s\n", detail);
/* Mark the next item as an expression start */
pc = chunk_get_next_ncnl(paren_close);
if (pc != NULL)
{
pc->flags |= PCF_EXPR_START;
if (chunk_is_opening_brace(pc))
{
set_paren_parent(pc, start->parent_type);
}
}
}
/**
* CT_TYPE_CAST follows this pattern:
* dynamic_cast<...>(...)
*
* Mark everything between the <> as a type and set the paren parent
*/
static void fix_type_cast(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
pc = chunk_get_next_ncnl(start);
if ((pc == NULL) || (pc->type != CT_ANGLE_OPEN))
{
return;
}
while (((pc = chunk_get_next_ncnl(pc)) != NULL) &&
(pc->level >= start->level))
{
if ((pc->level == start->level) && (pc->type == CT_ANGLE_CLOSE))
{
pc = chunk_get_next_ncnl(pc);
if (chunk_is_str(pc, "(", 1))
{
set_paren_parent(pc, CT_TYPE_CAST);
}
return;
}
make_type(pc);
}
}
/**
* We are on an enum/struct/union tag that is NOT inside a typedef.
* If there is a {...} and words before the ';', then they are variables.
*
* tag { ... } [*] word [, [*]word] ;
* tag [word/type] { ... } [*] word [, [*]word] ;
* enum [word/type [: int_type]] { ... } [*] word [, [*]word] ;
* tag [word/type] [word]; -- this gets caught later.
* fcn(tag [word/type] [word])
* a = (tag [word/type] [*])&b;
*
* REVISIT: should this be consolidated with the typedef code?
*/
static void fix_enum_struct_union(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *next;
chunk_t *prev = NULL;
int flags = PCF_VAR_1ST_DEF;
int in_fcn_paren = pc->flags & PCF_IN_FCN_DEF;
/* Make sure this wasn't a cast */
if (pc->parent_type == CT_C_CAST)
{
return;
}
/* the next item is either a type or open brace */
next = chunk_get_next_ncnl(pc);
if (next && (next->type == CT_ENUM_CLASS))
{
next = chunk_get_next_ncnl(next);
}
if (next && (next->type == CT_TYPE))
{
set_chunk_parent(next, pc->type);
prev = next;
next = chunk_get_next_ncnl(next);
/* next up is either a colon, open brace, or open paren (pawn) */
if (!next)
{
return;
}
else if (((cpd.lang_flags & LANG_PAWN) != 0) &&
(next->type == CT_PAREN_OPEN))
{
next = set_paren_parent(next, CT_ENUM);
}
else if ((pc->type == CT_ENUM) && (next->type == CT_COLON))
{
/* enum TYPE : INT_TYPE { */
next = chunk_get_next_ncnl(next);
if (next)
{
make_type(next);
next = chunk_get_next_ncnl(next);
}
}
}
if (next && (next->type == CT_BRACE_OPEN))
{
flag_parens(next, (pc->type == CT_ENUM) ? PCF_IN_ENUM : PCF_IN_STRUCT,
CT_NONE, CT_NONE, false);
if ((pc->type == CT_UNION) || (pc->type == CT_STRUCT))
{
mark_struct_union_body(next);
}
/* Skip to the closing brace */
set_chunk_parent(next, pc->type);
next = chunk_get_next_type(next, CT_BRACE_CLOSE, pc->level);
flags |= PCF_VAR_INLINE;
if (next != NULL)
{
set_chunk_parent(next, pc->type);
next = chunk_get_next_ncnl(next);
}
prev = NULL;
}
/* reset var name parent type */
else if (next && prev)
{
set_chunk_parent(prev, CT_NONE);
}
if ((next == NULL) || (next->type == CT_PAREN_CLOSE))
{
return;
}
if (!chunk_is_semicolon(next))
{
/* Pawn does not require a semicolon after an enum */
if (cpd.lang_flags & LANG_PAWN)
{
return;
}
/* D does not require a semicolon after an enum, but we add one to make
* other code happy.
*/
if (cpd.lang_flags & LANG_D)
{
next = pawn_add_vsemi_after(chunk_get_prev_ncnl(next));
}
}
/* We are either pointing to a ';' or a variable */
while ((next != NULL) && !chunk_is_semicolon(next) &&
(next->type != CT_ASSIGN) &&
((in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)) == 0))
{
if (next->level == pc->level)
{
if (next->type == CT_WORD)
{
next->flags |= flags;
flags &= ~PCF_VAR_1ST; /* clear the first flag for the next items */
}
if (next->type == CT_STAR)
{
set_chunk_type(next, CT_PTR_TYPE);
}
/* If we hit a comma in a function param, we are done */
if (((next->type == CT_COMMA) ||
(next->type == CT_FPAREN_CLOSE)) &&
((next->flags & (PCF_IN_FCN_DEF | PCF_IN_FCN_CALL)) != 0))
{
return;
}
}
next = chunk_get_next_ncnl(next);
}
if (next && !prev && (next->type == CT_SEMICOLON))
{
set_chunk_parent(next, pc->type);
}
}
/**
* We are on a typedef.
* If the next word is not enum/union/struct, then the last word before the
* next ',' or ';' or '__attribute__' is a type.
*
* typedef [type...] [*] type [, [*]type] ;
* typedef <return type>([*]func)(params);
* typedef <return type>func(params);
* typedef <enum/struct/union> [type] [*] type [, [*]type] ;
* typedef <enum/struct/union> [type] { ... } [*] type [, [*]type] ;
*/
static void fix_typedef(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *next;
chunk_t *the_type = NULL;
chunk_t *open_paren;
chunk_t *last_op = NULL;
c_token_t tag;
LOG_FMT(LTYPEDEF, "%s: typedef @ %d:%d\n", __func__, start->orig_line, start->orig_col);
/* Mark everything in the typedef and scan for ")(", which makes it a
* function type
*/
next = start;
while (((next = chunk_get_next_ncnl(next, CNAV_PREPROC)) != NULL) &&
(next->level >= start->level))
{
next->flags |= PCF_IN_TYPEDEF;
if (start->level == next->level)
{
if (chunk_is_semicolon(next))
{
set_chunk_parent(next, CT_TYPEDEF);
break;
}
if (next->type == CT_ATTRIBUTE)
{
break;
}
if ((cpd.lang_flags & LANG_D) && (next->type == CT_ASSIGN))
{
set_chunk_parent(next, CT_TYPEDEF);
break;
}
make_type(next);
if (next->type == CT_TYPE)
{
the_type = next;
}
next->flags &= ~PCF_VAR_1ST_DEF;
if (*next->str == '(')
{
last_op = next;
}
}
}
/* avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def */
if (last_op && !((cpd.lang_flags & LANG_OC) &&
(last_op->parent_type == CT_ENUM)))
{
flag_parens(last_op, 0, CT_FPAREN_OPEN, CT_TYPEDEF, false);
fix_fcn_def_params(last_op);
open_paren = NULL;
the_type = chunk_get_prev_ncnl(last_op, CNAV_PREPROC);
if (chunk_is_paren_close(the_type))
{
open_paren = chunk_skip_to_match_rev(the_type);
mark_function_type(the_type);
the_type = chunk_get_prev_ncnl(the_type, CNAV_PREPROC);
}
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: fcn typedef [%s] on line %d\n", __func__,
the_type->text(), the_type->orig_line);
/* If we are aligning on the open paren, grab that instead */
if (open_paren && (cpd.settings[UO_align_typedef_func].n == 1))
{
the_type = open_paren;
}
if (cpd.settings[UO_align_typedef_func].n != 0)
{
LOG_FMT(LTYPEDEF, "%s: -- align anchor on [%s] @ %d:%d\n", __func__,
the_type->text(), the_type->orig_line, the_type->orig_col);
the_type->flags |= 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
*/
next = chunk_get_next_ncnl(start, CNAV_PREPROC);
if ((next->type != CT_ENUM) &&
(next->type != CT_STRUCT) &&
(next->type != CT_UNION))
{
if (the_type != NULL)
{
/* We have just a regular typedef */
LOG_FMT(LTYPEDEF, "%s: regular typedef [%s] on line %d\n", __func__,
the_type->str.c_str(), the_type->orig_line);
the_type->flags |= PCF_ANCHOR;
}
return;
}
/* We have a struct/union/enum type, set the parent */
tag = next->type;
/* the next item should be either a type or { */
next = chunk_get_next_ncnl(next, CNAV_PREPROC);
if (next->type == CT_TYPE)
{
next = chunk_get_next_ncnl(next, CNAV_PREPROC);
}
if (next->type == CT_BRACE_OPEN)
{
set_chunk_parent(next, tag);
/* Skip to the closing brace */
next = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, CNAV_PREPROC);
if (next != NULL)
{
set_chunk_parent(next, tag);
}
}
if (the_type != NULL)
{
LOG_FMT(LTYPEDEF, "%s: %s typedef [%s] on line %d\n",
__func__, get_token_name(tag), the_type->str.c_str(), the_type->orig_line);
the_type->flags |= PCF_ANCHOR;
}
}
/**
* Examines the whole file and changes CT_COLON to
* CT_Q_COLON, CT_LABEL_COLON, or CT_CASE_COLON.
* It also changes the CT_WORD before CT_LABEL_COLON into CT_LABEL.
*/
void combine_labels(void)
{
LOG_FUNC_ENTRY();
chunk_t *cur;
chunk_t *prev;
chunk_t *next;
chunk_t *tmp;
int question_count = 0;
bool hit_case = false;
bool hit_class = false;
prev = chunk_get_head();
cur = chunk_get_next_nc(prev);
next = chunk_get_next_nc(cur);
/* unlikely that the file will start with a label... */
while (next != NULL)
{
if (!(next->flags & PCF_IN_OC_MSG) && /* filter OC case of [self class] msg send */
((next->type == CT_CLASS) ||
(next->type == CT_OC_CLASS) ||
(next->type == CT_TEMPLATE)))
{
hit_class = true;
}
if (chunk_is_semicolon(next) || (next->type == CT_BRACE_OPEN))
{
hit_class = false;
}
if (next->type == CT_QUESTION)
{
question_count++;
}
else if (next->type == CT_CASE)
{
if (cur->type == CT_GOTO)
{
/* handle "goto case x;" */
set_chunk_type(next, CT_QUALIFIER);
}
else
{
hit_case = true;
}
}
else if ((next->type == CT_COLON) ||
((question_count > 0) && (next->type == CT_OC_COLON)))
{
if (cur->type == CT_DEFAULT)
{
set_chunk_type(cur, CT_CASE);
hit_case = true;
}
if (question_count > 0)
{
set_chunk_type(next, CT_COND_COLON);
question_count--;
}
else if (hit_case)
{
hit_case = false;
set_chunk_type(next, CT_CASE_COLON);
tmp = chunk_get_next_ncnl(next);
if ((tmp != NULL) && (tmp->type == CT_BRACE_OPEN))
{
set_chunk_parent(tmp, CT_CASE);
tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);
if (tmp != NULL)
{
set_chunk_parent(tmp, CT_CASE);
}
}
}
else
{
chunk_t *nextprev = chunk_get_prev_ncnl(next);
if ((cpd.lang_flags & LANG_PAWN) != 0)
{
if ((cur->type == CT_WORD) ||
(cur->type == CT_BRACE_CLOSE))
{
c_token_t new_type = CT_TAG;
tmp = chunk_get_next_nc(next);
if (chunk_is_newline(prev) && chunk_is_newline(tmp))
{
new_type = CT_LABEL;
set_chunk_type(next, CT_LABEL_COLON);
}
else
{
set_chunk_type(next, CT_TAG_COLON);
}
if (cur->type == CT_WORD)
{
set_chunk_type(cur, new_type);
}
}
}
else if (next->flags & PCF_IN_ARRAY_ASSIGN)
{
set_chunk_type(next, CT_D_ARRAY_COLON);
}
else if (next->flags & PCF_IN_FOR)
{
set_chunk_type(next, CT_FOR_COLON);
}
else if (next->flags & PCF_OC_BOXED)
{
set_chunk_type(next, CT_OC_DICT_COLON);
}
else if (cur->type == CT_WORD)
{
tmp = chunk_get_next_nc(next, CNAV_PREPROC);
if (next->flags & PCF_IN_FCN_CALL)
{
/* Must be a macro thingy, assume some sort of label */
set_chunk_type(next, CT_LABEL_COLON);
}
else if (next->flags & (PCF_IN_STRUCT | PCF_IN_TYPEDEF))
{
set_chunk_type(next, CT_BIT_COLON);
tmp = chunk_get_next(next);
while ((tmp = chunk_get_next(tmp)) != NULL)
{
if (tmp->type == CT_SEMICOLON)
{
break;
}
if (tmp->type == CT_COLON)
{
set_chunk_type(tmp, CT_BIT_COLON);
}
}
}
else if ((tmp == NULL) || (tmp->type != CT_NUMBER))
{
set_chunk_type(cur, CT_LABEL);
set_chunk_type(next, CT_LABEL_COLON);
}
}
else if (nextprev->type == CT_FPAREN_CLOSE)
{
/* it's a class colon */
set_chunk_type(next, CT_CLASS_COLON);
}
else if (next->level > next->brace_level)
{
/* ignore it, as it is inside a paren */
}
else if (cur->type == CT_TYPE)
{
set_chunk_type(next, CT_BIT_COLON);
}
else if ((cur->type == CT_ENUM) ||
(cur->type == CT_PRIVATE) ||
(cur->type == CT_QUALIFIER) ||
(cur->parent_type == CT_ALIGN))
{
/* ignore it - bit field, align or public/private, etc */
}
else if ((cur->type == CT_ANGLE_CLOSE) || hit_class)
{
/* ignore it - template thingy */
}
else if (cur->parent_type == CT_SQL_EXEC)
{
/* ignore it - SQL variable name */
}
else if (next->parent_type == CT_ASSERT)
{
/* ignore it - Java assert thing */
}
else
{
tmp = chunk_get_next_ncnl(next);
if ((tmp != NULL) && ((tmp->type == CT_BASE) ||
(tmp->type == CT_THIS)))
{
/* ignore it, as it is a C# base thingy */
}
else
{
LOG_FMT(LWARN, "%s:%d unexpected colon in col %d n-parent=%s c-parent=%s l=%d bl=%d\n",
cpd.filename, next->orig_line, next->orig_col,
get_token_name(next->parent_type),
get_token_name(cur->parent_type),
next->level, next->brace_level);
cpd.error_count++;
}
}
}
}
prev = cur;
cur = next;
next = chunk_get_next_nc(cur);
}
}
static void mark_variable_stack(ChunkStack& cs, log_sev_t sev)
{
LOG_FUNC_ENTRY();
chunk_t *var_name;
chunk_t *word_type;
int word_cnt = 0;
/* throw out the last word and mark the rest */
var_name = cs.Pop_Back();
if (var_name && var_name->prev->type == CT_DC_MEMBER)
{
cs.Push_Back(var_name);
}
if (var_name != NULL)
{
LOG_FMT(LFCNP, "%s: parameter on line %d :",
__func__, var_name->orig_line);
while ((word_type = cs.Pop_Back()) != NULL)
{
if ((word_type->type == CT_WORD) || (word_type->type == CT_TYPE))
{
LOG_FMT(LFCNP, " <%s>", word_type->str.c_str());
set_chunk_type(word_type, CT_TYPE);
word_type->flags |= PCF_VAR_TYPE;
}
word_cnt++;
}
if (var_name->type == CT_WORD)
{
if (word_cnt)
{
LOG_FMT(LFCNP, " [%s]\n", var_name->str.c_str());
var_name->flags |= PCF_VAR_DEF;
}
else
{
LOG_FMT(LFCNP, " <%s>\n", var_name->str.c_str());
set_chunk_type(var_name, CT_TYPE);
var_name->flags |= PCF_VAR_TYPE;
}
}
}
}
/**
* Simply change any STAR to PTR_TYPE and WORD to TYPE
*
* @param start points to the open paren
*/
static void fix_fcn_def_params(chunk_t *start)
{
LOG_FUNC_ENTRY();
LOG_FMT(LFCNP, "%s: %s [%s] on line %d, level %d\n",
__func__, start->str.c_str(), get_token_name(start->type), start->orig_line, start->level);
while ((start != NULL) && !chunk_is_paren_open(start))
{
start = chunk_get_next_ncnl(start);
}
assert((start->len() == 1) && (start->str[0] == '('));
ChunkStack cs;
int level = start->level + 1;
chunk_t *pc = start;
while ((pc = chunk_get_next_ncnl(pc)) != NULL)
{
if (((start->len() == 1) && (start->str[0] == ')')) ||
(pc->level < level))
{
LOG_FMT(LFCNP, "%s: bailed on %s on line %d\n", __func__, pc->str.c_str(), pc->orig_line);
break;
}
LOG_FMT(LFCNP, "%s: %s %s on line %d, level %d\n", __func__,
(pc->level > level) ? "skipping" : "looking at",
pc->str.c_str(), pc->orig_line, pc->level);
if (pc->level > level)
{
continue;
}
if (chunk_is_star(pc))
{
set_chunk_type(pc, CT_PTR_TYPE);
cs.Push_Back(pc);
}
else if ((pc->type == CT_AMP) ||
((cpd.lang_flags & LANG_CPP) && chunk_is_str(pc, "&&", 2)))
{
set_chunk_type(pc, CT_BYREF);
cs.Push_Back(pc);
}
else if (pc->type == CT_TYPE_WRAP)
{
cs.Push_Back(pc);
}
else if ((pc->type == CT_WORD) || (pc->type == CT_TYPE))
{
cs.Push_Back(pc);
}
else if ((pc->type == CT_COMMA) || (pc->type == CT_ASSIGN))
{
mark_variable_stack(cs, LFCNP);
if (pc->type == CT_ASSIGN)
{
/* Mark assignment for default param spacing */
set_chunk_parent(pc, CT_FUNC_PROTO);
}
}
}
mark_variable_stack(cs, LFCNP);
}
/**
* Skips to the start of the next statement.
*/
static chunk_t *skip_to_next_statement(chunk_t *pc)
{
while ((pc != NULL) && !chunk_is_semicolon(pc) &&
(pc->type != CT_BRACE_OPEN) &&
(pc->type != CT_BRACE_CLOSE))
{
pc = chunk_get_next_ncnl(pc);
}
return(pc);
}
/**
* We are on the start of a sequence that could be a var def
* - FPAREN_OPEN (parent == CT_FOR)
* - BRACE_OPEN
* - SEMICOLON
*
*/
static chunk_t *fix_var_def(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *pc = start;
chunk_t *end;
chunk_t *tmp_pc;
ChunkStack cs;
int idx, ref_idx;
LOG_FMT(LFVD, "%s: start[%d:%d]", __func__, pc->orig_line, pc->orig_col);
/* Scan for words and types and stars oh my! */
while ((pc != NULL) &&
((pc->type == CT_TYPE) ||
(pc->type == CT_WORD) ||
(pc->type == CT_QUALIFIER) ||
(pc->type == CT_DC_MEMBER) ||
(pc->type == CT_MEMBER) ||
chunk_is_addr(pc) ||
chunk_is_star(pc)))
{
LOG_FMT(LFVD, " %s[%s]", pc->str.c_str(), get_token_name(pc->type));
cs.Push_Back(pc);
pc = chunk_get_next_ncnl(pc);
/* Skip templates and attributes */
pc = skip_template_next(pc);
pc = skip_attribute_next(pc);
if (cpd.lang_flags & LANG_JAVA)
{
pc = skip_tsquare_next(pc);
}
}
end = pc;
LOG_FMT(LFVD, " end=[%s]\n", (end != NULL) ? get_token_name(end->type) : "NULL");
if (end == NULL)
{
return(NULL);
}
/* Function defs are handled elsewhere */
if ((cs.Len() <= 1) ||
(end->type == CT_FUNC_DEF) ||
(end->type == CT_FUNC_PROTO) ||
(end->type == CT_FUNC_CLASS_DEF) ||
(end->type == CT_FUNC_CLASS_PROTO) ||
(end->type == CT_OPERATOR))
{
return(skip_to_next_statement(end));
}
/* ref_idx points to the alignable part of the var def */
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 ((tmp_pc->type != CT_DC_MEMBER) &&
(tmp_pc->type != CT_MEMBER))
{
break;
}
idx--;
tmp_pc = cs.Get(idx)->m_pc;
if ((tmp_pc->type != CT_WORD) &&
(tmp_pc->type != CT_TYPE))
{
break;
}
make_type(tmp_pc);
idx--;
}
ref_idx = idx + 1;
}
tmp_pc = cs.Get(ref_idx)->m_pc;
LOG_FMT(LFVD, " ref_idx(%d) => %s\n", ref_idx, tmp_pc->str.c_str());
/* No type part found! */
if (ref_idx <= 0)
{
return(skip_to_next_statement(end));
}
LOG_FMT(LFVD2, "%s:%d TYPE : ", __func__, start->orig_line);
for (idx = 0; idx < cs.Len() - 1; idx++)
{
tmp_pc = cs.Get(idx)->m_pc;
make_type(tmp_pc);
tmp_pc->flags |= PCF_VAR_TYPE;
LOG_FMT(LFVD2, " %s[%s]", tmp_pc->str.c_str(), get_token_name(tmp_pc->type));
}
LOG_FMT(LFVD2, "\n");
/**
* OK we have two or more items, mark types up to the end.
*/
mark_variable_definition(cs.Get(cs.Len() - 1)->m_pc);
if (end->type == CT_COMMA)
{
return(chunk_get_next_ncnl(end));
}
return(skip_to_next_statement(end));
}
/**
* Skips everything until a comma or semicolon at the same level.
* Returns the semicolon, comma, or close brace/paren or NULL.
*/
static chunk_t *skip_expression(chunk_t *start)
{
chunk_t *pc = start;
while ((pc != NULL) && (pc->level >= start->level))
{
if ((pc->level == start->level) &&
(chunk_is_semicolon(pc) || (pc->type == CT_COMMA)))
{
return(pc);
}
pc = chunk_get_next_ncnl(pc);
}
return(pc);
}
/**
* We are on the first word of a variable definition.
* Mark all the variable names with PCF_VAR_1ST and PCF_VAR_DEF as appropriate.
* Also mark any '*' encountered as a CT_PTR_TYPE.
* Skip over []. Go until a ';' is hit.
*
* Example input:
* int a = 3, b, c = 2; ## called with 'a'
* foo_t f = {1, 2, 3}, g = {5, 6, 7}; ## called with 'f'
* struct {...} *a, *b; ## called with 'a' or '*'
* myclass a(4);
*/
static chunk_t *mark_variable_definition(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *pc = start;
int flags = PCF_VAR_1ST_DEF;
if (start == NULL)
{
return(NULL);
}
LOG_FMT(LVARDEF, "%s: line %d, col %d '%s' type %s\n",
__func__,
pc->orig_line, pc->orig_col, pc->str.c_str(),
get_token_name(pc->type));
pc = start;
while ((pc != NULL) && !chunk_is_semicolon(pc) &&
(pc->level == start->level))
{
if ((pc->type == CT_WORD) || (pc->type == CT_FUNC_CTOR_VAR))
{
UINT64 flg = pc->flags;
if ((pc->flags & PCF_IN_ENUM) == 0)
{
pc->flags |= flags;
}
flags &= ~PCF_VAR_1ST;
LOG_FMT(LVARDEF, "%s:%d marked '%s'[%s] in col %d flags: %#" PRIx64 " -> %#" PRIx64 "\n",
__func__, pc->orig_line, pc->str.c_str(),
get_token_name(pc->type), pc->orig_col, flg, pc->flags);
}
else if (chunk_is_star(pc))
{
set_chunk_type(pc, CT_PTR_TYPE);
}
else if (chunk_is_addr(pc))
{
set_chunk_type(pc, CT_BYREF);
}
else if ((pc->type == CT_SQUARE_OPEN) || (pc->type == CT_ASSIGN))
{
pc = skip_expression(pc);
continue;
}
pc = chunk_get_next_ncnl(pc);
}
return(pc);
}
/**
* Checks to see if a series of chunks could be a C++ parameter
* FOO foo(5, &val);
*
* WORD means CT_WORD or CT_TYPE
*
* "WORD WORD" ==> true
* "QUALIFIER ??" ==> true
* "TYPE" ==> true
* "WORD" ==> true
* "WORD.WORD" ==> true
* "WORD::WORD" ==> true
* "WORD * WORD" ==> true
* "WORD & WORD" ==> true
* "NUMBER" ==> false
* "STRING" ==> false
* "OPEN PAREN" ==> false
*
* @param start the first chunk to look at
* @param end the chunk after the last one to look at
*/
static bool can_be_full_param(chunk_t *start, chunk_t *end)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *last;
int word_cnt = 0;
int type_count = 0;
bool ret;
LOG_FMT(LFPARAM, "%s:", __func__);
for (pc = start; pc != end; pc = chunk_get_next_ncnl(pc, CNAV_PREPROC))
{
LOG_FMT(LFPARAM, " [%s]", pc->str.c_str());
if ((pc->type == CT_QUALIFIER) ||
(pc->type == CT_STRUCT) ||
(pc->type == CT_ENUM) ||
(pc->type == CT_UNION) ||
(pc->type == CT_TYPENAME))
{
LOG_FMT(LFPARAM, " <== %s! (yes)\n", get_token_name(pc->type));
return(true);
}
if ((pc->type == CT_WORD) ||
(pc->type == CT_TYPE))
{
word_cnt++;
if (pc->type == CT_TYPE)
{
type_count++;
}
}
else if ((pc->type == CT_MEMBER) ||
(pc->type == CT_DC_MEMBER))
{
if (word_cnt > 0)
{
word_cnt--;
}
}
else if ((pc != start) && (chunk_is_star(pc) ||
chunk_is_addr(pc)))
{
/* chunk is OK */
}
else if (pc->type == CT_ASSIGN)
{
/* chunk is OK (default values) */
break;
}
else if (pc->type == CT_ANGLE_OPEN)
{
LOG_FMT(LFPARAM, " <== template\n");
return(true);
}
else if (pc->type == CT_ELLIPSIS)
{
LOG_FMT(LFPARAM, " <== elipses\n");
return(true);
}
else if ((word_cnt == 0) && (pc->type == CT_PAREN_OPEN))
{
/* Check for old-school func proto param '(type)' */
chunk_t *tmp1 = chunk_skip_to_match(pc, CNAV_PREPROC);
chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, CNAV_PREPROC);
if (chunk_is_token(tmp2, CT_COMMA) || chunk_is_paren_close(tmp2))
{
do {
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
LOG_FMT(LFPARAM, " [%s]", pc->text());
} while (pc != tmp1);
/* reset some vars to allow [] after parens */
word_cnt = 1;
type_count = 1;
}
else
{
LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type));
return false;
}
}
else if (((word_cnt == 1) || (word_cnt == type_count)) &&
(pc->type == CT_PAREN_OPEN))
{
/* Check for func proto param 'void (*name)' or 'void (*name)(params)' */
chunk_t *tmp1 = chunk_get_next_ncnl(pc, CNAV_PREPROC);
chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, CNAV_PREPROC);
chunk_t *tmp3 = chunk_get_next_ncnl(tmp2, CNAV_PREPROC);
if (!chunk_is_str(tmp3, ")", 1) ||
!chunk_is_str(tmp1, "*", 1) ||
(tmp2->type != CT_WORD))
{
LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type));
return(false);
}
LOG_FMT(LFPARAM, " <skip fcn type>");
tmp1 = chunk_get_next_ncnl(tmp3, CNAV_PREPROC);
tmp2 = chunk_get_next_ncnl(tmp1, CNAV_PREPROC);
if (chunk_is_str(tmp1, "(", 1))
{
tmp3 = chunk_skip_to_match(tmp1, CNAV_PREPROC);
}
pc = tmp3;
/* reset some vars to allow [] after parens */
word_cnt = 1;
type_count = 1;
}
else if (pc->type == CT_TSQUARE)
{
/* ignore it */
}
else if ((word_cnt == 1) && (pc->type == CT_SQUARE_OPEN))
{
/* skip over any array stuff */
pc = chunk_skip_to_match(pc, CNAV_PREPROC);
}
else if ((word_cnt == 1) && (cpd.lang_flags & LANG_CPP) &&
chunk_is_str(pc, "&&", 2))
{
/* ignore possible 'move' operator */
}
else
{
LOG_FMT(LFPARAM, " <== [%s] no way! tc=%d wc=%d\n",
get_token_name(pc->type), type_count, word_cnt);
return(false);
}
}
last = chunk_get_prev_ncnl(pc);
if (chunk_is_star(last) || chunk_is_addr(last))
{
LOG_FMT(LFPARAM, " <== [%s] sure!\n", get_token_name(pc->type));
return(true);
}
ret = ((word_cnt >= 2) || ((word_cnt == 1) && (type_count == 1)));
LOG_FMT(LFPARAM, " <== [%s] %s!\n",
get_token_name(pc->type), ret ? "Yup" : "Unlikely");
return(ret);
}
/**
* We are on a function word. we need to:
* - find out if this is a call or prototype or implementation
* - mark return type
* - mark parameter types
* - mark brace pair
*
* REVISIT:
* This whole function is a mess.
* It needs to be reworked to eliminate duplicate logic and determine the
* function type more directly.
* 1. Skip to the close paren and see what is after.
* a. semicolon - function call or function proto
* b. open brace - function call (ie, list_for_each) or function def
* c. open paren - function type or chained function call
* d. qualifier - function def or proto, continue to semicolon or open brace
* 2. Examine the 'parameters' to see if it can be a proto/def
* 3. Examine what is before the function name to see if it is a proto or call
* Constructor/destructor detection should have already been done when the
* 'class' token was encountered (see mark_class_ctor).
*/
static void mark_function(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *prev;
chunk_t *next;
chunk_t *tmp;
chunk_t *semi = NULL;
chunk_t *paren_open;
chunk_t *paren_close;
chunk_t *pc_op = NULL;
prev = chunk_get_prev_ncnlnp(pc);
next = chunk_get_next_ncnlnp(pc);
/* Find out what is before the operator */
if (pc->parent_type == CT_OPERATOR)
{
pc_op = chunk_get_prev_type(pc, CT_OPERATOR, pc->level);
if ((pc_op != NULL) && (pc_op->flags & PCF_EXPR_START))
{
set_chunk_type(pc, CT_FUNC_CALL);
}
if (cpd.lang_flags & LANG_CPP)
{
tmp = pc;
while ((tmp = chunk_get_prev_ncnl(tmp)) != NULL)
{
if ((tmp->type == CT_BRACE_CLOSE) ||
(tmp->type == CT_SEMICOLON))
{
break;
}
if (chunk_is_paren_open(tmp))
{
set_chunk_type(pc, CT_FUNC_CALL);
break;
}
if (tmp->type == CT_ASSIGN)
{
set_chunk_type(pc, CT_FUNC_CALL);
break;
}
if (tmp->type == CT_TEMPLATE)
{
set_chunk_type(pc, CT_FUNC_DEF);
break;
}
if (tmp->type == CT_BRACE_OPEN)
{
if (tmp->parent_type == CT_FUNC_DEF)
{
set_chunk_type(pc, CT_FUNC_CALL);
}
if ((tmp->parent_type == CT_CLASS) ||
(tmp->parent_type == CT_STRUCT))
{
set_chunk_type(pc, CT_FUNC_DEF);
}
break;
}
}
if ((tmp != NULL) && (pc->type != CT_FUNC_CALL))
{
/* Mark the return type */
while ((tmp = chunk_get_next_ncnl(tmp)) != pc)
{
make_type(tmp);
}
}
}
}
if (chunk_is_star(next) || chunk_is_addr(next))
{
next = chunk_get_next_ncnlnp(next);
}
LOG_FMT(LFCN, "%s: %d] %s[%s] - parent=%s level=%d/%d, next=%s[%s] - level=%d\n",
__func__,
pc->orig_line, pc->str.c_str(),
get_token_name(pc->type), get_token_name(pc->parent_type),
pc->level, pc->brace_level,
next->str.c_str(), get_token_name(next->type), next->level);
if (pc->flags & PCF_IN_CONST_ARGS)
{
set_chunk_type(pc, CT_FUNC_CTOR_VAR);
LOG_FMT(LFCN, " 1) Marked [%s] as FUNC_CTOR_VAR on line %d col %d\n",
pc->str.c_str(), pc->orig_line, pc->orig_col);
next = skip_template_next(next);
flag_parens(next, 0, CT_FPAREN_OPEN, pc->type, true);
return;
}
/* Skip over any template and attribute madness */
next = skip_template_next(next);
next = skip_attribute_next(next);
/* Find the open and close paren */
paren_open = chunk_get_next_str(pc, "(", 1, pc->level);
paren_close = chunk_get_next_str(paren_open, ")", 1, pc->level);
if ((paren_open == NULL) || (paren_close == NULL))
{
LOG_FMT(LFCN, "No parens found for [%s] on line %d col %d\n",
pc->str.c_str(), 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 = chunk_get_next_ncnl(paren_close);
if (chunk_is_str(tmp, "(", 1))
{
chunk_t *tmp1, *tmp2, *tmp3;
/* skip over any leading class/namespace in: "T(F::*A)();" */
tmp1 = chunk_get_next_ncnl(next);
while (tmp1)
{
tmp2 = chunk_get_next_ncnl(tmp1);
if (!chunk_is_word(tmp1) || !chunk_is_token(tmp2, CT_DC_MEMBER))
{
break;
}
tmp1 = chunk_get_next_ncnl(tmp2);
}
tmp2 = chunk_get_next_ncnl(tmp1);
if (chunk_is_str(tmp2, ")", 1))
{
tmp3 = tmp2;
tmp2 = NULL;
}
else
{
tmp3 = chunk_get_next_ncnl(tmp2);
}
if (chunk_is_str(tmp3, ")", 1) &&
(chunk_is_star(tmp1) ||
((cpd.lang_flags & LANG_OC) && chunk_is_token(tmp1, CT_CARET)))
&&
((tmp2 == NULL) || (tmp2->type == CT_WORD)))
{
if (tmp2)
{
LOG_FMT(LFCN, "%s: [%d/%d] function variable [%s], changing [%s] into a type\n",
__func__, pc->orig_line, pc->orig_col, tmp2->text(), pc->text());
set_chunk_type(tmp2, CT_FUNC_VAR);
flag_parens(paren_open, 0, CT_PAREN_OPEN, CT_FUNC_VAR, false);
LOG_FMT(LFCN, "%s: paren open @ %d:%d\n",
__func__, paren_open->orig_line, paren_open->orig_col);
}
else
{
LOG_FMT(LFCN, "%s: [%d/%d] function type, changing [%s] into a type\n",
__func__, pc->orig_line, pc->orig_col, pc->str.c_str());
if (tmp2)
{
set_chunk_type(tmp2, CT_FUNC_TYPE);
}
flag_parens(paren_open, 0, CT_PAREN_OPEN, CT_FUNC_TYPE, false);
}
set_chunk_type(pc, CT_TYPE);
set_chunk_type(tmp1, CT_PTR_TYPE);
pc->flags &= ~PCF_VAR_1ST_DEF;
if (tmp2 != NULL)
{
tmp2->flags |= PCF_VAR_1ST_DEF;
}
flag_parens(tmp, 0, CT_FPAREN_OPEN, CT_FUNC_PROTO, false);
fix_fcn_def_params(tmp);
return;
}
LOG_FMT(LFCN, "%s: chained function calls? [%d.%d] [%s]\n",
__func__, pc->orig_line, pc->orig_col, pc->str.c_str());
}
/* Assume it is a function call if not already labeled */
if (pc->type == CT_FUNCTION)
{
set_chunk_type(pc, (pc->parent_type == CT_OPERATOR) ? CT_FUNC_DEF : CT_FUNC_CALL);
}
/* Check for C++ function def */
if ((pc->type == CT_FUNC_CLASS_DEF) ||
((prev != NULL) && ((prev->type == CT_DC_MEMBER) ||
(prev->type == CT_INV))))
{
chunk_t *destr = NULL;
if (prev->type == 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;
prev = chunk_get_prev_ncnlnp(prev);
}
if ((prev != NULL) && (prev->type == CT_DC_MEMBER))
{
prev = chunk_get_prev_ncnlnp(prev);
// LOG_FMT(LSYS, "%s: prev1 = %s (%s)\n", __func__,
// get_token_name(prev->type), prev->str.c_str());
prev = skip_template_prev(prev);
prev = skip_attribute_prev(prev);
// LOG_FMT(LSYS, "%s: prev2 = %s [%d](%s) pc = %s [%d](%s)\n", __func__,
// get_token_name(prev->type), prev->len, prev->str.c_str(),
// get_token_name(pc->type), pc->len, pc->str.c_str());
if ((prev != NULL) && ((prev->type == CT_WORD) || (prev->type == CT_TYPE)))
{
if (pc->str.equals(prev->str))
{
set_chunk_type(pc, CT_FUNC_CLASS_DEF);
LOG_FMT(LFCN, "%d:%d - FOUND %sSTRUCTOR for %s[%s]\n",
prev->orig_line, prev->orig_col,
(destr != NULL) ? "DE" : "CON",
prev->str.c_str(), get_token_name(prev->type));
mark_cpp_constructor(pc);
return;
}
else
{
/* Point to the item previous to the class name */
prev = chunk_get_prev_ncnlnp(prev);
}
}
}
}
/* 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 ((pc->type == CT_FUNC_CALL) &&
((pc->level == pc->brace_level) || (pc->level == 1)) &&
((pc->flags & PCF_IN_ARRAY_ASSIGN) == 0))
{
bool isa_def = false;
bool hit_star = false;
LOG_FMT(LFCN, " Checking func call: prev=%s", (prev == NULL) ? "<null>" : 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 crap 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 != NULL)
{
if (prev->flags & PCF_IN_PREPROC)
{
prev = chunk_get_prev_ncnlnp(prev);
continue;
}
/* Some code slips an attribute between the type and function */
if ((prev->type == CT_FPAREN_CLOSE) &&
(prev->parent_type == CT_ATTRIBUTE))
{
prev = skip_attribute_prev(prev);
continue;
}
/* skip const(TYPE) */
if ((prev->type == CT_PAREN_CLOSE) &&
(prev->parent_type == CT_D_CAST))
{
LOG_FMT(LFCN, " --> For sure a prototype or definition\n");
isa_def = true;
break;
}
/** Skip the word/type before the '.' or '::' */
if ((prev->type == CT_DC_MEMBER) ||
(prev->type == CT_MEMBER))
{
prev = chunk_get_prev_ncnlnp(prev);
if ((prev == NULL) ||
((prev->type != CT_WORD) &&
(prev->type != CT_TYPE) &&
(prev->type != CT_THIS)))
{
LOG_FMT(LFCN, " --? Skipped MEMBER and landed on %s\n",
(prev == NULL) ? "<null>" : get_token_name(prev->type));
set_chunk_type(pc, CT_FUNC_CALL);
isa_def = false;
break;
}
LOG_FMT(LFCN, " <skip %s>", prev->str.c_str());
prev = chunk_get_prev_ncnlnp(prev);
continue;
}
/* If we are on a TYPE or WORD, then we must be on a proto or def */
if ((prev->type == CT_TYPE) ||
(prev->type == CT_WORD))
{
if (!hit_star)
{
LOG_FMT(LFCN, " --> For sure a prototype or definition\n");
isa_def = true;
break;
}
LOG_FMT(LFCN, " --> maybe a proto/def\n");
isa_def = true;
}
if (chunk_is_addr(prev) ||
chunk_is_star(prev))
{
hit_star = true;
}
if ((prev->type != CT_OPERATOR) &&
(prev->type != CT_TSQUARE) &&
(prev->type != CT_ANGLE_CLOSE) &&
(prev->type != CT_QUALIFIER) &&
(prev->type != CT_TYPE) &&
(prev->type != CT_WORD) &&
!chunk_is_addr(prev) &&
!chunk_is_star(prev))
{
LOG_FMT(LFCN, " --> Stopping on %s [%s]\n",
prev->str.c_str(), get_token_name(prev->type));
/* certain tokens are unlikely to precede a proto or def */
if ((prev->type == CT_ARITH) ||
(prev->type == CT_ASSIGN) ||
(prev->type == CT_COMMA) ||
(prev->type == CT_STRING) ||
(prev->type == CT_STRING_MULTI) ||
(prev->type == CT_NUMBER) ||
(prev->type == CT_NUMBER_FP))
{
isa_def = false;
}
break;
}
/* Skip over template and attribute stuff */
if (prev->type == CT_ANGLE_CLOSE)
{
prev = skip_template_prev(prev);
}
else
{
prev = chunk_get_prev_ncnlnp(prev);
}
}
//LOG_FMT(LFCN, " -- stopped on %s [%s]\n",
// prev->str.c_str(), get_token_name(prev->type));
if (isa_def && (prev != NULL) &&
((chunk_is_paren_close(prev) && (prev->parent_type != CT_D_CAST)) ||
(prev->type == CT_ASSIGN) ||
(prev->type == CT_RETURN)))
{
LOG_FMT(LFCN, " -- overriding DEF due to %s [%s]\n",
prev->str.c_str(), get_token_name(prev->type));
isa_def = false;
}
if (isa_def)
{
set_chunk_type(pc, CT_FUNC_DEF);
LOG_FMT(LFCN, "%s: '%s' is FCN_DEF:", __func__, pc->str.c_str());
if (prev == NULL)
{
prev = chunk_get_head();
}
for (tmp = prev; tmp != pc; tmp = chunk_get_next_ncnl(tmp))
{
LOG_FMT(LFCN, " %s[%s]",
tmp->str.c_str(), get_token_name(tmp->type));
make_type(tmp);
}
LOG_FMT(LFCN, "\n");
}
}
if (pc->type != CT_FUNC_DEF)
{
LOG_FMT(LFCN, " Detected %s '%s' on line %d col %d\n",
get_token_name(pc->type),
pc->str.c_str(), pc->orig_line, pc->orig_col);
tmp = flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
if (tmp && (tmp->type == CT_BRACE_OPEN) && (tmp->parent_type != 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 paren 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;
while ((tmp = chunk_get_next_ncnl(tmp)) != NULL)
{
/* Only care about brace or semi on the same level */
if (tmp->level < pc->level)
{
/* No semicolon - guess that it is a prototype */
set_chunk_type(pc, CT_FUNC_PROTO);
break;
}
else if (tmp->level == pc->level)
{
if (tmp->type == CT_BRACE_OPEN)
{
/* its a function def for sure */
break;
}
else if (chunk_is_semicolon(tmp))
{
/* Set the parent for the semi for later */
semi = tmp;
set_chunk_type(pc, CT_FUNC_PROTO);
break;
}
else if (pc->type == CT_COMMA)
{
set_chunk_type(pc, CT_FUNC_CTOR_VAR);
LOG_FMT(LFCN, " 2) Marked [%s] as FUNC_CTOR_VAR on line %d col %d\n",
pc->str.c_str(), pc->orig_line, pc->orig_col);
break;
}
}
}
/**
* 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, 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 ((cpd.lang_flags & LANG_CPP) &&
(pc->type == CT_FUNC_PROTO) &&
(pc->parent_type != CT_OPERATOR))
{
LOG_FMT(LFPARAM, "%s :: checking '%s' for constructor variable %s %s\n",
__func__, pc->str.c_str(),
get_token_name(paren_open->type),
get_token_name(paren_close->type));
/* Scan the parameters looking for:
* - constant strings
* - numbers
* - non-type fields
* - function calls
*/
chunk_t *ref = chunk_get_next_ncnl(paren_open);
chunk_t *tmp2;
bool is_param = true;
tmp = ref;
while (tmp != paren_close)
{
tmp2 = chunk_get_next_ncnl(tmp);
if ((tmp->type == 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_param && (ref != tmp))
{
if (!can_be_full_param(ref, tmp))
{
is_param = false;
}
}
if (!is_param)
{
set_chunk_type(pc, CT_FUNC_CTOR_VAR);
LOG_FMT(LFCN, " 3) Marked [%s] as FUNC_CTOR_VAR on line %d col %d\n",
pc->str.c_str(), pc->orig_line, pc->orig_col);
}
else if (pc->brace_level > 0)
{
chunk_t *br_open = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1);
if ((br_open != NULL) &&
(br_open->parent_type != CT_EXTERN) &&
(br_open->parent_type != CT_NAMESPACE))
{
/* Do a check to see if the level is right */
prev = chunk_get_prev_ncnl(pc);
if (!chunk_is_str(prev, "*", 1) && !chunk_is_str(prev, "&", 1))
{
chunk_t *p_op = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1);
if ((p_op != NULL) &&
(p_op->parent_type != CT_CLASS) &&
(p_op->parent_type != CT_STRUCT) &&
(p_op->parent_type != CT_NAMESPACE))
{
set_chunk_type(pc, CT_FUNC_CTOR_VAR);
LOG_FMT(LFCN, " 4) Marked [%s] as FUNC_CTOR_VAR on line %d col %d\n",
pc->str.c_str(), pc->orig_line, pc->orig_col);
}
}
}
}
}
if (semi != NULL)
{
set_chunk_parent(semi, pc->type);
}
flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, false);
if (pc->type == CT_FUNC_CTOR_VAR)
{
pc->flags |= PCF_VAR_1ST_DEF;
return;
}
if (next->type == CT_TSQUARE)
{
next = chunk_get_next_ncnl(next);
}
/* Mark parameters and return type */
fix_fcn_def_params(next);
mark_function_return_type(pc, chunk_get_prev_ncnl(pc), pc->type);
/* Find the brace pair and set the parent */
if (pc->type == CT_FUNC_DEF)
{
tmp = chunk_get_next_ncnl(paren_close);
while ((tmp != NULL) &&
(tmp->type != CT_BRACE_OPEN))
{
//LOG_FMT(LSYS, "%s: set parent to FUNC_DEF on line %d: [%s]\n", __func__, tmp->orig_line, tmp->str.c_str());
set_chunk_parent(tmp, CT_FUNC_DEF);
if (!chunk_is_semicolon(tmp))
{
tmp->flags |= PCF_OLD_FCN_PARAMS;
}
tmp = chunk_get_next_ncnl(tmp);
}
if ((tmp != NULL) && (tmp->type == CT_BRACE_OPEN))
{
set_chunk_parent(tmp, CT_FUNC_DEF);
tmp = chunk_skip_to_match(tmp);
if (tmp != NULL)
{
set_chunk_parent(tmp, CT_FUNC_DEF);
}
}
}
}
static void mark_cpp_constructor(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *paren_open;
chunk_t *tmp;
chunk_t *after;
chunk_t *var;
bool is_destr = false;
tmp = chunk_get_prev_ncnl(pc);
if ((tmp->type == CT_INV) || (tmp->type == CT_DESTRUCTOR))
{
set_chunk_type(tmp, CT_DESTRUCTOR);
set_chunk_parent(pc, CT_DESTRUCTOR);
is_destr = true;
}
LOG_FMT(LFTOR, "%d:%d FOUND %sSTRUCTOR for %s[%s] prev=%s[%s]",
pc->orig_line, pc->orig_col,
is_destr ? "DE" : "CON",
pc->str.c_str(), get_token_name(pc->type),
tmp->str.c_str(), get_token_name(tmp->type));
paren_open = skip_template_next(chunk_get_next_ncnl(pc));
if (!chunk_is_str(paren_open, "(", 1))
{
LOG_FMT(LWARN, "%s:%d Expected '(', got: [%s]\n",
cpd.filename, paren_open->orig_line,
paren_open->str.c_str());
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]\n", after->str.c_str());
/* Scan until the brace open, mark everything */
tmp = paren_open;
bool hit_colon = false;
while ((tmp != NULL) && (tmp->type != CT_BRACE_OPEN) &&
!chunk_is_semicolon(tmp))
{
tmp->flags |= PCF_IN_CONST_ARGS;
tmp = chunk_get_next_ncnl(tmp);
if (chunk_is_str(tmp, ":", 1) && (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(chunk_get_prev_ncnl(tmp));
if ((var->type == CT_TYPE) || (var->type == 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 (tmp != NULL)
{
if (tmp->type == CT_BRACE_OPEN)
{
set_paren_parent(paren_open, CT_FUNC_CLASS_DEF);
set_paren_parent(tmp, CT_FUNC_CLASS_DEF);
}
else
{
set_chunk_parent(tmp, CT_FUNC_CLASS_PROTO);
set_chunk_type(pc, CT_FUNC_CLASS_PROTO);
}
}
}
/**
* We're on a 'class' or 'struct'.
* Scan for CT_FUNCTION with a string that matches pclass->str
*/
static void mark_class_ctor(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *next;
chunk_t *pclass;
ChunkStack cs;
pclass = chunk_get_next_ncnl(start, CNAV_PREPROC);
if ((pclass == NULL) ||
((pclass->type != CT_TYPE) &&
(pclass->type != CT_WORD)))
{
return;
}
next = chunk_get_next_ncnl(pclass, CNAV_PREPROC);
while ((next != NULL) &&
((next->type == CT_TYPE) ||
(next->type == CT_WORD) ||
(next->type == CT_DC_MEMBER)))
{
pclass = next;
next = chunk_get_next_ncnl(next, CNAV_PREPROC);
}
chunk_t *pc = chunk_get_next_ncnl(pclass, CNAV_PREPROC);
int level = pclass->brace_level + 1;
if (pc == NULL)
{
LOG_FMT(LFTOR, "%s: Called on %s on line %d. Bailed on NULL\n",
__func__, pclass->str.c_str(), pclass->orig_line);
return;
}
/* Add the class name */
cs.Push_Back(pclass);
LOG_FMT(LFTOR, "%s: Called on %s on line %d (next='%s')\n",
__func__, pclass->str.c_str(), pclass->orig_line, pc->str.c_str());
/* detect D template class: "class foo(x) { ... }" */
if ((cpd.lang_flags & LANG_D) && (next->type == CT_PAREN_OPEN))
{
set_chunk_parent(next, CT_TEMPLATE);
next = get_d_template_types(cs, next);
if (next && (next->type == CT_PAREN_CLOSE))
{
set_chunk_parent(next, CT_TEMPLATE);
}
}
/* Find the open brace, abort on semicolon */
int flags = 0;
while ((pc != NULL) && (pc->type != CT_BRACE_OPEN))
{
LOG_FMT(LFTOR, " [%s]", pc->str.c_str());
if (chunk_is_str(pc, ":", 1))
{
set_chunk_type(pc, CT_CLASS_COLON);
flags |= PCF_IN_CLASS_BASE;
LOG_FMT(LFTOR, "%s: class colon on line %d\n",
__func__, pc->orig_line);
}
if (chunk_is_semicolon(pc))
{
LOG_FMT(LFTOR, "%s: bailed on semicolon on line %d\n",
__func__, pc->orig_line);
return;
}
pc->flags |= flags;
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
}
if (pc == NULL)
{
LOG_FMT(LFTOR, "%s: bailed on NULL\n", __func__);
return;
}
set_paren_parent(pc, start->type);
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
while (pc != NULL)
{
pc->flags |= PCF_IN_CLASS;
if ((pc->brace_level > level) || ((pc->flags & PCF_IN_PREPROC) != 0))
{
pc = chunk_get_next_ncnl(pc);
continue;
}
if ((pc->type == CT_BRACE_CLOSE) && (pc->brace_level < level))
{
LOG_FMT(LFTOR, "%s: %d] Hit brace close\n", __func__, pc->orig_line);
pc = chunk_get_next_ncnl(pc, CNAV_PREPROC);
if (pc && (pc->type == CT_SEMICOLON))
{
set_chunk_parent(pc, start->type);
}
return;
}
next = chunk_get_next_ncnl(pc, CNAV_PREPROC);
if (chunkstack_match(cs, pc))
{
if ((next != NULL) && (next->len() == 1) && (next->str[0] == '('))
{
set_chunk_type(pc, CT_FUNC_CLASS_DEF);
LOG_FMT(LFTOR, "%d] Marked CTor/DTor %s\n", pc->orig_line, pc->str.c_str());
mark_cpp_constructor(pc);
}
else
{
make_type(pc);
}
}
pc = next;
}
}
/**
* We're on a 'namespace' skip the word and then set the parent of the braces.
*/
static void mark_namespace(chunk_t *pns)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *br_close;
bool is_using = false;
pc = chunk_get_prev_ncnl(pns);
if (chunk_is_token(pc, CT_USING))
{
is_using = true;
set_chunk_parent(pns, CT_USING);
}
pc = chunk_get_next_ncnl(pns);
while (pc != NULL)
{
set_chunk_parent(pc, CT_NAMESPACE);
if (pc->type != CT_BRACE_OPEN)
{
if (pc->type == CT_SEMICOLON)
{
if (is_using)
{
set_chunk_parent(pc, CT_USING);
}
return;
}
pc = chunk_get_next_ncnl(pc);
continue;
}
if ((cpd.settings[UO_indent_namespace_limit].n > 0) &&
((br_close = chunk_skip_to_match(pc)) != NULL))
{
int diff = br_close->orig_line - pc->orig_line;
if (diff > cpd.settings[UO_indent_namespace_limit].n)
{
pc->flags |= PCF_LONG_BLOCK;
br_close->flags |= PCF_LONG_BLOCK;
}
}
flag_parens(pc, PCF_IN_NAMESPACE, CT_NONE, CT_NAMESPACE, false);
return;
}
}
/**
* Skips the D 'align()' statement and the colon, if present.
* align(2) int foo; -- returns 'int'
* align(4): -- returns 'int'
* int bar;
*/
static chunk_t *skip_align(chunk_t *start)
{
chunk_t *pc = start;
if (pc->type == CT_ALIGN)
{
pc = chunk_get_next_ncnl(pc);
if (pc->type == CT_PAREN_OPEN)
{
pc = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level);
pc = chunk_get_next_ncnl(pc);
if (pc->type == CT_COLON)
{
pc = chunk_get_next_ncnl(pc);
}
}
}
return(pc);
}
/**
* Examines the stuff between braces { }.
* There should only be variable definitions and methods.
* Skip the methods, as they will get handled elsewhere.
*/
static void mark_struct_union_body(chunk_t *start)
{
LOG_FUNC_ENTRY();
chunk_t *pc = start;
while ((pc != NULL) &&
(pc->level >= start->level) &&
!((pc->level == start->level) && (pc->type == CT_BRACE_CLOSE)))
{
// LOG_FMT(LSYS, "%s: %d:%d %s:%s\n", __func__, pc->orig_line, pc->orig_col,
// pc->str.c_str(), get_token_name(pc->parent_type));
if ((pc->type == CT_BRACE_OPEN) ||
(pc->type == CT_BRACE_CLOSE) ||
(pc->type == CT_SEMICOLON))
{
pc = chunk_get_next_ncnl(pc);
}
if (pc->type == CT_ALIGN)
{
pc = skip_align(pc); // "align(x)" or "align(x):"
}
else
{
pc = fix_var_def(pc);
}
}
}
/**
* Sets the parent for comments.
*/
void mark_comments(void)
{
LOG_FUNC_ENTRY();
chunk_t *cur;
chunk_t *next;
bool prev_nl = true;
bool next_nl;
cur = chunk_get_head();
while (cur != NULL)
{
next = chunk_get_next_nvb(cur);
next_nl = (next == NULL) || chunk_is_newline(next);
if (chunk_is_comment(cur))
{
if (next_nl && prev_nl)
{
set_chunk_parent(cur, CT_COMMENT_WHOLE);
}
else if (next_nl)
{
set_chunk_parent(cur, CT_COMMENT_END);
}
else if (prev_nl)
{
set_chunk_parent(cur, CT_COMMENT_START);
}
else
{
set_chunk_parent(cur, CT_COMMENT_EMBED);
}
}
prev_nl = chunk_is_newline(cur);
cur = next;
}
}
/**
* Marks statement starts in a macro body.
* REVISIT: this may already be done
*/
static void mark_define_expressions(void)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *prev;
bool in_define = false;
bool first = true;
pc = chunk_get_head();
prev = pc;
while (pc != NULL)
{
if (!in_define)
{
if ((pc->type == CT_PP_DEFINE) ||
(pc->type == CT_PP_IF) ||
(pc->type == CT_PP_ELSE))
{
in_define = true;
first = true;
}
}
else
{
if (((pc->flags & PCF_IN_PREPROC) == 0) || (pc->type == CT_PREPROC))
{
in_define = false;
}
else
{
if ((pc->type != CT_MACRO) &&
(first ||
(prev->type == CT_PAREN_OPEN) ||
(prev->type == CT_ARITH) ||
(prev->type == CT_CARET) ||
(prev->type == CT_ASSIGN) ||
(prev->type == CT_COMPARE) ||
(prev->type == CT_RETURN) ||
(prev->type == CT_GOTO) ||
(prev->type == CT_CONTINUE) ||
(prev->type == CT_PAREN_OPEN) ||
(prev->type == CT_FPAREN_OPEN) ||
(prev->type == CT_SPAREN_OPEN) ||
(prev->type == CT_BRACE_OPEN) ||
chunk_is_semicolon(prev) ||
(prev->type == CT_COMMA) ||
(prev->type == CT_COLON) ||
(prev->type == CT_QUESTION)))
{
pc->flags |= PCF_EXPR_START;
first = false;
}
}
}
prev = pc;
pc = chunk_get_next(pc);
}
}
/**
* We are on the C++ 'template' keyword.
* What follows should be the following:
*
* template <class identifier> function_declaration;
* template <typename identifier> function_declaration;
* template <class identifier> class class_declaration;
* template <typename identifier> class class_declaration;
*
* Change the 'class' inside the <> to CT_TYPE.
* Set the parent to the class after the <> to CT_TEMPLATE.
* Set the parent of the semicolon to CT_TEMPLATE.
*/
static void handle_cpp_template(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
int level;
tmp = chunk_get_next_ncnl(pc);
if (tmp->type != CT_ANGLE_OPEN)
{
return;
}
set_chunk_parent(tmp, CT_TEMPLATE);
level = tmp->level;
while ((tmp = chunk_get_next(tmp)) != NULL)
{
if ((tmp->type == CT_CLASS) ||
(tmp->type == CT_STRUCT))
{
set_chunk_type(tmp, CT_TYPE);
}
else if ((tmp->type == CT_ANGLE_CLOSE) && (tmp->level == level))
{
set_chunk_parent(tmp, CT_TEMPLATE);
break;
}
}
if (tmp != NULL)
{
tmp = chunk_get_next_ncnl(tmp);
if ((tmp != NULL) &&
((tmp->type == CT_CLASS) || (tmp->type == CT_STRUCT)))
{
set_chunk_parent(tmp, CT_TEMPLATE);
/* REVISTI: This may be a bit risky - might need to track the { }; */
tmp = chunk_get_next_type(tmp, CT_SEMICOLON, tmp->level);
if (tmp != NULL)
{
set_chunk_parent(tmp, CT_TEMPLATE);
}
}
}
}
/**
* Verify and then mark C++ lambda expressions.
* The expected format is '[...](...){...}' or '[...](...) -> type {...}'
* sq_o is '[' CT_SQUARE_OPEN or '[]' CT_TSQUARE
* Split the '[]' so we can control the space
*/
static void handle_cpp_lambda(chunk_t *sq_o)
{
LOG_FUNC_ENTRY();
chunk_t *sq_c;
chunk_t *pa_o;
chunk_t *pa_c;
chunk_t *br_o;
chunk_t *br_c;
chunk_t *ret = NULL;
sq_c = sq_o; /* assuming '[]' */
if (sq_o->type == CT_SQUARE_OPEN)
{
/* make sure there is a ']' */
sq_c = chunk_skip_to_match(sq_o);
if (!sq_c)
{
return;
}
}
/* Make sure a '(' is next */
pa_o = chunk_get_next_ncnl(sq_c);
if (!pa_o || (pa_o->type != CT_PAREN_OPEN))
{
return;
}
/* and now find the ')' */
pa_c = chunk_skip_to_match(pa_o);
if (!pa_c)
{
return;
}
/* Check if keyword 'mutable' is before '->' */
br_o = chunk_get_next_ncnl(pa_c);
if (chunk_is_str(br_o, "mutable", 7))
{
br_o = chunk_get_next_ncnl(br_o);
}
/* Make sure a '{' or '->' is next */
if (chunk_is_str(br_o, "->", 2))
{
ret = br_o;
/* REVISIT: really should check the stuff we are skipping */
br_o = chunk_get_next_type(br_o, CT_BRACE_OPEN, br_o->level);
}
if (!br_o || (br_o->type != CT_BRACE_OPEN))
{
return;
}
/* and now find the '}' */
br_c = chunk_skip_to_match(br_o);
if (!br_c)
{
return;
}
/* This looks like a lambda expression */
if (sq_o->type == CT_TSQUARE)
{
/* split into two chunks */
chunk_t nc;
nc = *sq_o;
set_chunk_type(sq_o, CT_SQUARE_OPEN);
sq_o->str.resize(1);
sq_o->orig_col_end = sq_o->orig_col + 1;
nc.type = CT_SQUARE_CLOSE;
nc.str.pop_front();
nc.orig_col++;
nc.column++;
sq_c = chunk_add_after(&nc, sq_o);
}
set_chunk_parent(sq_o, CT_CPP_LAMBDA);
set_chunk_parent(sq_c, CT_CPP_LAMBDA);
set_chunk_type(pa_o, CT_FPAREN_OPEN);
set_chunk_parent(pa_o, CT_CPP_LAMBDA);
set_chunk_type(pa_c, CT_FPAREN_CLOSE);
set_chunk_parent(pa_c, CT_CPP_LAMBDA);
set_chunk_parent(br_o, CT_CPP_LAMBDA);
set_chunk_parent(br_c, CT_CPP_LAMBDA);
if (ret)
{
set_chunk_type(ret, CT_CPP_LAMBDA_RET);
ret = chunk_get_next_ncnl(ret);
while (ret != br_o)
{
make_type(ret);
ret = chunk_get_next_ncnl(ret);
}
}
fix_fcn_def_params(pa_o);
}
/**
* Parse off the types in the D template args, adds to cs
* returns the close_paren
*/
static chunk_t *get_d_template_types(ChunkStack& cs, chunk_t *open_paren)
{
LOG_FUNC_ENTRY();
chunk_t *tmp = open_paren;
bool maybe_type = true;
while (((tmp = chunk_get_next_ncnl(tmp)) != NULL) &&
(tmp->level > open_paren->level))
{
if ((tmp->type == CT_TYPE) || (tmp->type == CT_WORD))
{
if (maybe_type)
{
make_type(tmp);
cs.Push_Back(tmp);
}
maybe_type = false;
}
else if (tmp->type == CT_COMMA)
{
maybe_type = true;
}
}
return tmp;
}
static bool chunkstack_match(ChunkStack& cs, chunk_t *pc)
{
chunk_t *tmp;
int idx;
for (idx = 0; idx < cs.Len(); idx++)
{
tmp = cs.GetChunk(idx);
if (pc->str.equals(tmp->str))
{
return true;
}
}
return false;
}
/**
* We are on the D 'template' keyword.
* What follows should be the following:
*
* template NAME ( TYPELIST ) { BODY }
*
* Set the parent of NAME to template, change NAME to CT_TYPE.
* Set the parent of the parens and braces to CT_TEMPLATE.
* Scan the body for each type in TYPELIST and change the type to CT_TYPE.
*/
static void handle_d_template(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *name;
chunk_t *po;
chunk_t *tmp;
name = chunk_get_next_ncnl(pc);
po = chunk_get_next_ncnl(name);
if (!name || ((name->type != CT_WORD) && (name->type != CT_WORD)))
{
/* TODO: log an error, expected NAME */
return;
}
if (!po || (po->type != CT_PAREN_OPEN))
{
/* TODO: log an error, expected '(' */
return;
}
set_chunk_type(name, CT_TYPE);
set_chunk_parent(name, CT_TEMPLATE);
set_chunk_parent(po, CT_TEMPLATE);
ChunkStack cs;
tmp = get_d_template_types(cs, po);
if (!tmp || (tmp->type != CT_PAREN_CLOSE))
{
/* TODO: log an error, expected ')' */
return;
}
set_chunk_parent(tmp, CT_TEMPLATE);
tmp = chunk_get_next_ncnl(tmp);
if (tmp->type != CT_BRACE_OPEN)
{
/* TODO: log an error, expected '{' */
return;
}
set_chunk_parent(tmp, CT_TEMPLATE);
po = tmp;
tmp = po;
while (((tmp = chunk_get_next_ncnl(tmp)) != NULL) &&
(tmp->level > po->level))
{
if ((tmp->type == CT_WORD) && chunkstack_match(cs, tmp))
{
set_chunk_type(tmp, CT_TYPE);
}
}
if (tmp->type != CT_BRACE_CLOSE)
{
/* TODO: log an error, expected '}' */
}
set_chunk_parent(tmp, CT_TEMPLATE);
}
/**
* We are on a word followed by a angle open which is part of a template.
* If the angle close is followed by a open paren, then we are on a template
* function def or a template function call:
* Vector2<float>(...) [: ...[, ...]] { ... }
* Or we could be on a variable def if it's followed by a word:
* Renderer<rgb32> rend;
*/
static void mark_template_func(chunk_t *pc, chunk_t *pc_next)
{
LOG_FUNC_ENTRY();
chunk_t *angle_close;
chunk_t *after;
/* We know angle_close must be there... */
angle_close = chunk_get_next_type(pc_next, CT_ANGLE_CLOSE, pc->level);
after = chunk_get_next_ncnl(angle_close);
if (after != NULL)
{
if (chunk_is_str(after, "(", 1))
{
if (angle_close->flags & PCF_IN_FCN_CALL)
{
LOG_FMT(LTEMPFUNC, "%s: marking '%s' in line %d as a FUNC_CALL\n",
__func__, pc->str.c_str(), pc->orig_line);
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: marking '%s' in line %d as a FUNC_CALL 2\n",
__func__, pc->str.c_str(), pc->orig_line);
// its a function!!!
set_chunk_type(pc, CT_FUNC_CALL);
mark_function(pc);
}
}
else if (after->type == CT_WORD)
{
// its a type!
set_chunk_type(pc, CT_TYPE);
pc->flags |= PCF_VAR_TYPE;
after->flags |= PCF_VAR_DEF;
}
}
}
/**
* Just mark every CT_WORD until a semicolon as CT_SQL_WORD.
* Adjust the levels if pc is CT_SQL_BEGIN
*/
static void mark_exec_sql(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
/* Change CT_WORD to CT_SQL_WORD */
for (tmp = chunk_get_next(pc); tmp != NULL; tmp = chunk_get_next(tmp))
{
set_chunk_parent(tmp, pc->type);
if (tmp->type == CT_WORD)
{
set_chunk_type(tmp, CT_SQL_WORD);
}
if (tmp->type == CT_SEMICOLON)
{
break;
}
}
if ((pc->type != CT_SQL_BEGIN) ||
(tmp == NULL) || (tmp->type != CT_SEMICOLON))
{
return;
}
for (tmp = chunk_get_next(tmp);
(tmp != NULL) && (tmp->type != CT_SQL_END);
tmp = chunk_get_next(tmp))
{
tmp->level++;
}
}
/**
* Skips over the rest of the template if ang_open is indeed a CT_ANGLE_OPEN.
* Points to the chunk after the CT_ANGLE_CLOSE.
* If the chunk isn't an CT_ANGLE_OPEN, then it is returned.
*/
chunk_t *skip_template_next(chunk_t *ang_open)
{
if ((ang_open != NULL) && (ang_open->type == CT_ANGLE_OPEN))
{
chunk_t *pc;
pc = chunk_get_next_type(ang_open, CT_ANGLE_CLOSE, ang_open->level);
return(chunk_get_next_ncnl(pc));
}
return(ang_open);
}
/**
* Skips over the rest of the template if ang_close is indeed a CT_ANGLE_CLOSE.
* Points to the chunk before the CT_ANGLE_OPEN
* If the chunk isn't an CT_ANGLE_CLOSE, then it is returned.
*/
chunk_t *skip_template_prev(chunk_t *ang_close)
{
if ((ang_close != NULL) && (ang_close->type == CT_ANGLE_CLOSE))
{
chunk_t *pc;
pc = chunk_get_prev_type(ang_close, CT_ANGLE_OPEN, ang_close->level);
return(chunk_get_prev_ncnl(pc));
}
return(ang_close);
}
/**
* Skips the rest of the array definitions if ary_def is indeed a
* CT_TSQUARE or CT_SQUARE_OPEN
*/
chunk_t *skip_tsquare_next(chunk_t *ary_def)
{
if (ary_def && ((ary_def->type == CT_SQUARE_OPEN) ||
(ary_def->type == CT_TSQUARE)))
{
return chunk_get_next_nisq(ary_def);
}
return(ary_def);
}
/**
* If attr is CT_ATTRIBUTE, then skip it and the parens and return the chunk
* after the CT_FPAREN_CLOSE.
* If the chunk isn't an CT_ATTRIBUTE, then it is returned.
*/
chunk_t *skip_attribute_next(chunk_t *attr)
{
if ((attr != NULL) && (attr->type == CT_ATTRIBUTE))
{
chunk_t *pc = chunk_get_next(attr);
if ((pc != NULL) && (pc->type == CT_FPAREN_OPEN))
{
pc = chunk_get_next_type(attr, CT_FPAREN_CLOSE, attr->level);
return(chunk_get_next_ncnl(pc));
}
return(pc);
}
return(attr);
}
/**
* If fp_close is a CT_FPAREN_CLOSE with a parent of CT_ATTRIBUTE, then skip it
* and the '__attribute__' thingy and return the chunk before CT_ATTRIBUTE.
* Otherwise return fp_close.
*/
chunk_t *skip_attribute_prev(chunk_t *fp_close)
{
if ((fp_close != NULL) &&
(fp_close->type == CT_FPAREN_CLOSE) &&
(fp_close->parent_type == CT_ATTRIBUTE))
{
chunk_t *pc;
pc = chunk_get_prev_type(fp_close, CT_ATTRIBUTE, fp_close->level);
return(chunk_get_prev_ncnl(pc));
}
return(fp_close);
}
/**
* Process an ObjC 'class'
* pc is the chunk after '@implementation' or '@interface' or '@protocol'.
* Change colons, etc. Processes stuff until '@end'.
* Skips anything in braces.
*/
static void handle_oc_class(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
bool hit_scope = false;
int do_pl = 1;
LOG_FMT(LOCCLASS, "%s: start [%s] [%s] line %d\n", __func__,
pc->str.c_str(), get_token_name(pc->parent_type), pc->orig_line);
if (pc->parent_type == CT_OC_PROTOCOL)
{
tmp = chunk_get_next_ncnl(pc);
if (chunk_is_semicolon(tmp))
{
set_chunk_parent(tmp, pc->parent_type);
LOG_FMT(LOCCLASS, "%s: bail on semicolon\n", __func__);
return;
}
}
tmp = pc;
while ((tmp = chunk_get_next_nnl(tmp)) != NULL)
{
LOG_FMT(LOCCLASS, "%s: %d [%s]\n", __func__,
tmp->orig_line, tmp->str.c_str());
if (tmp->type == CT_OC_END)
{
break;
}
if ((do_pl == 1) && chunk_is_str(tmp, "<", 1))
{
set_chunk_type(tmp, CT_ANGLE_OPEN);
set_chunk_parent(tmp, CT_OC_PROTO_LIST);
do_pl = 2;
}
if ((do_pl == 2) && chunk_is_str(tmp, ">", 1))
{
set_chunk_type(tmp, CT_ANGLE_CLOSE);
set_chunk_parent(tmp, CT_OC_PROTO_LIST);
do_pl = 0;
}
if (tmp->type == CT_BRACE_OPEN)
{
do_pl = 0;
set_chunk_parent(tmp, CT_OC_CLASS);
tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);
if (tmp != NULL)
{
set_chunk_parent(tmp, CT_OC_CLASS);
}
}
else if (tmp->type == CT_COLON)
{
set_chunk_type(tmp, hit_scope ? CT_OC_COLON : CT_CLASS_COLON);
if (tmp->type == CT_CLASS_COLON)
{
set_chunk_parent(tmp, CT_OC_CLASS);
}
}
else if (chunk_is_str(tmp, "-", 1) || chunk_is_str(tmp, "+", 1))
{
do_pl = 0;
if (chunk_is_newline(chunk_get_prev(tmp)))
{
set_chunk_type(tmp, CT_OC_SCOPE);
tmp->flags |= PCF_STMT_START;
hit_scope = true;
}
}
if (do_pl == 2)
{
set_chunk_parent(tmp, CT_OC_PROTO_LIST);
}
}
if ((tmp != NULL) && (tmp->type == CT_BRACE_OPEN))
{
tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);
if (tmp != NULL)
{
set_chunk_parent(tmp, CT_OC_CLASS);
}
}
}
/* Mark Objective-C blocks (aka lambdas or closures)
* The syntax and usage is exactly like C function pointers
* but instead of an asterisk they have a caret as pointer symbol.
* Although it may look expensive this functions is only triggered
* on appearance of an OC_BLOCK_CARET for LANG_OC.
* repeat(10, ^{ putc('0'+d); });
* typedef void (^workBlk_t)(void);
*
* @param pc points to the '^'
*/
static void handle_oc_block_literal(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *tmp = pc;
chunk_t *prev = chunk_get_prev_ncnl(pc);
chunk_t *next = chunk_get_next_ncnl(pc);
if (!pc || !prev || !next)
{
return; /* let's be paranoid */
}
chunk_t *apo; /* arg paren open */
chunk_t *apc; /* arg paren close */
chunk_t *bbo; /* block brace open */
chunk_t *bbc; /* block brace close */
/* block literal: '^ RTYPE ( ARGS ) { }'
* RTYPE and ARGS are optional
*/
LOG_FMT(LOCBLK, "%s: block literal @ %d:%d\n", __func__, pc->orig_line, pc->orig_col);
apo = NULL;
bbo = NULL;
LOG_FMT(LOCBLK, "%s: + scan", __func__);
for (tmp = next; tmp; tmp = chunk_get_next_ncnl(tmp))
{
LOG_FMT(LOCBLK, " %s", tmp->text());
if ((tmp->level < pc->level) || (tmp->type == CT_SEMICOLON))
{
LOG_FMT(LOCBLK, "[DONE]");
break;
}
if (tmp->level == pc->level)
{
if (chunk_is_paren_open(tmp))
{
apo = tmp;
LOG_FMT(LOCBLK, "[PAREN]");
}
if (tmp->type == CT_BRACE_OPEN)
{
LOG_FMT(LOCBLK, "[BRACE]");
bbo = tmp;
break;
}
}
}
/* make sure we have braces */
bbc = chunk_skip_to_match(bbo);
if (!bbo || !bbc)
{
LOG_FMT(LOCBLK, " -- no braces found\n");
return;
}
LOG_FMT(LOCBLK, "\n");
/* we are on a block literal for sure */
set_chunk_type(pc, CT_OC_BLOCK_CARET);
set_chunk_parent(pc, CT_OC_BLOCK_EXPR);
/* handle the optional args */
chunk_t *lbp; /* last before paren - end of return type, if any */
if (apo)
{
apc = chunk_skip_to_match(apo);
if (chunk_is_paren_close(apc))
{
LOG_FMT(LOCBLK, " -- marking parens @ %d:%d and %d:%d\n",
apo->orig_line, apo->orig_col, apc->orig_line, apc->orig_col);
flag_parens(apo, PCF_OC_ATYPE, CT_FPAREN_OPEN, CT_OC_BLOCK_EXPR, true);
fix_fcn_def_params(apo);
}
lbp = chunk_get_prev_ncnl(apo);
}
else
{
lbp = chunk_get_prev_ncnl(bbo);
}
/* mark the return type, if any */
while (lbp != pc)
{
LOG_FMT(LOCBLK, " -- lbp %s[%s]\n", lbp->text(), get_token_name(lbp->type));
make_type(lbp);
lbp->flags |= PCF_OC_RTYPE;
set_chunk_parent(lbp, CT_OC_BLOCK_EXPR);
lbp = chunk_get_prev_ncnl(lbp);
}
/* mark the braces */
set_chunk_parent(bbo, CT_OC_BLOCK_EXPR);
set_chunk_parent(bbc, CT_OC_BLOCK_EXPR);
}
/**
* Mark Objective-C block types.
* The syntax and usage is exactly like C function pointers
* but instead of an asterisk they have a caret as pointer symbol.
* typedef void (^workBlk_t)(void);
* const char * (^workVar)(void);
* -(void)Foo:(void(^)())blk { }
*
* This is triggered when the sequence '(' '^' is found.
*
* @param pc points to the '^'
*/
static void handle_oc_block_type(chunk_t *pc)
{
LOG_FUNC_ENTRY();
if (!pc)
{
return;
}
if (pc->flags & PCF_IN_TYPEDEF)
{
LOG_FMT(LOCBLK, "%s: skip block type @ %d:%d -- in typedef\n",
__func__, pc->orig_line, pc->orig_col);
return;
}
chunk_t *tpo; /* type paren open */
chunk_t *tpc; /* type paren close */
chunk_t *nam; /* name (if any) of '^' */
chunk_t *apo; /* arg paren open */
chunk_t *apc; /* arg paren close */
/* make sure we have '( ^' */
tpo = chunk_get_prev_ncnl(pc);
if (chunk_is_paren_open(tpo))
{
/* block type: 'RTYPE (^LABEL)(ARGS)'
* LABEL is optional.
*/
tpc = chunk_skip_to_match(tpo); /* type close paren (after '^') */
nam = chunk_get_prev_ncnl(tpc); /* name (if any) or '^' */
apo = chunk_get_next_ncnl(tpc); /* arg open paren */
apc = chunk_skip_to_match(apo); /* arg close paren */
// If this is a block literal instead of a block type, 'nam' will actually
// be the closing bracket of the block.
// We run into this situation if a block literal is enclosed in parentheses.
if (chunk_is_closing_brace(nam))
{
return handle_oc_block_literal(pc);
}
if (chunk_is_paren_close(apc))
{
chunk_t *aft = chunk_get_next_ncnl(apc);
c_token_t pt;
if (chunk_is_str(nam, "^", 1))
{
set_chunk_type(nam, CT_PTR_TYPE);
pt = CT_FUNC_TYPE;
}
else if (chunk_is_token(aft, CT_ASSIGN) || chunk_is_token(aft, CT_SEMICOLON))
{
set_chunk_type(nam, CT_FUNC_VAR);
pt = CT_FUNC_VAR;
}
else
{
set_chunk_type(nam, CT_FUNC_TYPE);
pt = CT_FUNC_TYPE;
}
LOG_FMT(LOCBLK, "%s: block type @ %d:%d (%s)[%s]\n", __func__,
pc->orig_line, pc->orig_col, nam->text(), get_token_name(nam->type));
set_chunk_type(pc, CT_PTR_TYPE);
set_chunk_parent(pc, pt); //CT_OC_BLOCK_TYPE;
set_chunk_type(tpo, CT_TPAREN_OPEN);
set_chunk_parent(tpo, pt); //CT_OC_BLOCK_TYPE;
set_chunk_type(tpc, CT_TPAREN_CLOSE);
set_chunk_parent(tpc, pt); //CT_OC_BLOCK_TYPE;
set_chunk_type(apo, CT_FPAREN_OPEN);
set_chunk_parent(apo, CT_FUNC_PROTO);
set_chunk_type(apc, CT_FPAREN_CLOSE);
set_chunk_parent(apc, CT_FUNC_PROTO);
fix_fcn_def_params(apo);
mark_function_return_type(nam, chunk_get_prev_ncnl(tpo), pt);
}
}
}
/**
* Process a type that is enclosed in parens in message decls.
* TODO: handle block types, which get special formatting
*
* @param pc points to the open paren
* @return the chunk after the type
*/
static chunk_t *handle_oc_md_type(chunk_t *paren_open, c_token_t ptype, UINT64 flags, bool& did_it)
{
chunk_t *paren_close;
if (!chunk_is_paren_open(paren_open) ||
((paren_close = chunk_skip_to_match(paren_open)) == NULL))
{
did_it = false;
return paren_open;
}
did_it = true;
set_chunk_parent(paren_open, ptype);
paren_open->flags |= flags;
set_chunk_parent(paren_close, ptype);
paren_close->flags |= flags;
for (chunk_t *cur = chunk_get_next_ncnl(paren_open);
cur != paren_close;
cur = chunk_get_next_ncnl(cur))
{
LOG_FMT(LOCMSGD, " <%s|%s>", cur->text(), get_token_name(cur->type));
cur->flags |= flags;
make_type(cur);
}
/* returning the chunk after the paren close */
return chunk_get_next_ncnl(paren_close);
}
/**
* Process an ObjC message spec/dec
*
* Specs:
* -(void) foo ARGS;
*
* Decl:
* -(void) foo ARGS { }
*
* LABEL : (ARGTYPE) ARGNAME
*
* ARGS is ': (ARGTYPE) ARGNAME [MOREARGS...]'
* MOREARGS is ' [ LABEL] : (ARGTYPE) ARGNAME '
* -(void) foo: (int) arg: { }
* -(void) foo: (int) arg: { }
* -(void) insertObject:(id)anObject atIndex:(int)index
*/
static void handle_oc_message_decl(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
bool in_paren = false;
int paren_cnt = 0;
int arg_cnt = 0;
c_token_t pt;
bool did_it;
/* Figure out if this is a spec or decl */
tmp = pc;
while ((tmp = chunk_get_next(tmp)) != NULL)
{
if (tmp->level < pc->level)
{
/* should not happen */
return;
}
if ((tmp->type == CT_SEMICOLON) ||
(tmp->type == CT_BRACE_OPEN))
{
break;
}
}
if (tmp == NULL)
{
return;
}
pt = (tmp->type == CT_SEMICOLON) ? CT_OC_MSG_SPEC : CT_OC_MSG_DECL;
set_chunk_type(pc, CT_OC_SCOPE);
set_chunk_parent(pc, pt);
LOG_FMT(LOCMSGD, "%s: %s @ %d:%d -", __func__, get_token_name(pt), pc->orig_line, pc->orig_col);
/* format: -(TYPE) NAME [: (TYPE)NAME */
/* handle the return type */
tmp = handle_oc_md_type(chunk_get_next_ncnl(pc), pt, PCF_OC_RTYPE, did_it);
if (!did_it)
{
LOG_FMT(LOCMSGD, " -- missing type parens\n");
return;
}
/* expect the method name/label */
if (!chunk_is_token(tmp, CT_WORD))
{
LOG_FMT(LOCMSGD, " -- missing method name\n");
return;
}
chunk_t *label = tmp;
set_chunk_type(tmp, pt);
set_chunk_parent(tmp, pt);
pc = chunk_get_next_ncnl(tmp);
LOG_FMT(LOCMSGD, " [%s]%s", pc->text(), get_token_name(pc->type));
/* if we have a colon next, we have args */
if ((pc->type == CT_COLON) || (pc->type == CT_OC_COLON))
{
pc = label;
while (true)
{
/* skip optional label */
if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, pt))
{
set_chunk_parent(pc, pt);
pc = chunk_get_next_ncnl(pc);
}
/* a colon must be next */
if (!chunk_is_str(pc, ":", 1))
{
break;
}
set_chunk_type(pc, CT_OC_COLON);
set_chunk_parent(pc, pt);
pc = chunk_get_next_ncnl(pc);
/* next is the type in parens */
LOG_FMT(LOCMSGD, " (%s)", pc->text());
tmp = handle_oc_md_type(pc, pt, PCF_OC_ATYPE, did_it);
if (!did_it)
{
LOG_FMT(LWARN, "%s: %d:%d expected type\n", __func__, pc->orig_line, pc->orig_col);
break;
}
pc = tmp;
/* we should now be on the arg name */
pc->flags |= PCF_VAR_DEF;
LOG_FMT(LOCMSGD, " arg[%s]", pc->text());
pc = chunk_get_next_ncnl(pc);
}
}
LOG_FMT(LOCMSGD, " end[%s]", pc->text());
if (chunk_is_token(pc, CT_BRACE_OPEN))
{
set_chunk_parent(pc, pt);
pc = chunk_skip_to_match(pc);
if (pc)
{
set_chunk_parent(pc, pt);
}
}
else if (chunk_is_token(pc, CT_SEMICOLON))
{
set_chunk_parent(pc, pt);
}
LOG_FMT(LOCMSGD, "\n");
return;
/* Mark everything */
tmp = pc;
while ((tmp = chunk_get_next(tmp)) != NULL)
{
LOG_FMT(LOCMSGD, " [%s]", tmp->text());
if ((tmp->type == CT_SEMICOLON) ||
(tmp->type == CT_BRACE_OPEN))
{
set_chunk_parent(tmp, pt);
break;
}
/* Mark first parens as return type */
if ((arg_cnt == 0) &&
((tmp->type == CT_PAREN_OPEN) ||
(tmp->type == CT_PAREN_CLOSE)))
{
set_chunk_parent(tmp, CT_OC_RTYPE);
in_paren = (tmp->type == CT_PAREN_OPEN);
if (!in_paren)
{
paren_cnt++;
arg_cnt++;
}
}
else if ((tmp->type == CT_PAREN_OPEN) ||
(tmp->type == CT_PAREN_CLOSE))
{
set_chunk_parent(tmp, pt);
in_paren = (tmp->type == CT_PAREN_OPEN);
if (!in_paren)
{
paren_cnt++;
}
}
else if (tmp->type == CT_WORD)
{
if (in_paren)
{
set_chunk_type(tmp, CT_TYPE);
set_chunk_parent(tmp, pt);
}
else if (paren_cnt == 1)
{
set_chunk_type(tmp, pt);
}
else
{
tmp->flags |= PCF_VAR_DEF;
}
}
else if (tmp->type == CT_COLON)
{
set_chunk_type(tmp, CT_OC_COLON);
set_chunk_parent(tmp, pt);
}
}
if ((tmp != NULL) && (tmp->type == CT_BRACE_OPEN))
{
tmp = chunk_skip_to_match(tmp);
if (tmp)
{
set_chunk_parent(tmp, pt);
}
}
LOG_FMT(LOCMSGD, "\n");
}
/**
* Process an ObjC message send statement:
* [ class func: val1 name2: val2 name3: val3] ; // named params
* [ class func: val1 : val2 : val3] ; // unnamed params
* [ class <proto> self method ] ; // with protocol
* [[NSMutableString alloc] initWithString: @"" ] // class from msg
* [func(a,b,c) lastObject ] // class from func
*
* Mainly find the matching ']' and ';' and mark the colons.
*
* @param os points to the open square '['
*/
static void handle_oc_message_send(chunk_t *os)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
chunk_t *cs = chunk_get_next(os);
while ((cs != NULL) && (cs->level > os->level))
{
cs = chunk_get_next(cs);
}
if ((cs == NULL) || (cs->type != CT_SQUARE_CLOSE))
{
return;
}
LOG_FMT(LOCMSG, "%s: line %d, col %d\n", __func__, os->orig_line, os->orig_col);
tmp = chunk_get_next_ncnl(cs);
if (chunk_is_semicolon(tmp))
{
set_chunk_parent(tmp, CT_OC_MSG);
}
set_chunk_parent(os, CT_OC_MSG);
os->flags |= PCF_IN_OC_MSG;
set_chunk_parent(cs, CT_OC_MSG);
cs->flags |= PCF_IN_OC_MSG;
/* expect a word first thing or [...] */
tmp = chunk_get_next_ncnl(os);
if (tmp->type == CT_SQUARE_OPEN || tmp->type == CT_PAREN_OPEN)
{
tmp = chunk_skip_to_match(tmp);
}
else if ((tmp->type != CT_WORD) && (tmp->type != CT_TYPE) && (tmp->type != CT_STRING))
{
LOG_FMT(LOCMSG, "%s: %d:%d expected identifier, not '%s' [%s]\n", __func__,
tmp->orig_line, tmp->orig_col,
tmp->text(), get_token_name(tmp->type));
return;
}
else
{
chunk_t *tt = chunk_get_next_ncnl(tmp);
if (chunk_is_paren_open(tt))
{
set_chunk_type(tmp, CT_FUNC_CALL);
tmp = chunk_get_prev_ncnl(set_paren_parent(tt, CT_FUNC_CALL));
}
else
{
set_chunk_type(tmp, CT_OC_MSG_CLASS);
}
}
/* handle '< protocol >' */
tmp = chunk_get_next_ncnl(tmp);
if (chunk_is_str(tmp, "<", 1))
{
chunk_t *ao = tmp;
chunk_t *ac = chunk_get_next_str(ao, ">", 1, ao->level);
if (ac)
{
set_chunk_type(ao, CT_ANGLE_OPEN);
set_chunk_parent(ao, CT_OC_PROTO_LIST);
set_chunk_type(ac, CT_ANGLE_CLOSE);
set_chunk_parent(ac, CT_OC_PROTO_LIST);
for (tmp = chunk_get_next(ao); tmp != ac; tmp = chunk_get_next(tmp))
{
tmp->level += 1;
set_chunk_parent(tmp, CT_OC_PROTO_LIST);
}
}
tmp = chunk_get_next_ncnl(ac);
}
if (tmp && ((tmp->type == CT_WORD) || (tmp->type == CT_TYPE)))
{
set_chunk_type(tmp, CT_OC_MSG_FUNC);
}
chunk_t *prev = NULL;
for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp))
{
tmp->flags |= PCF_IN_OC_MSG;
if (tmp->level == cs->level + 1)
{
if (tmp->type == CT_COLON)
{
set_chunk_type(tmp, CT_OC_COLON);
if ((prev != NULL) && ((prev->type == CT_WORD) || (prev->type == CT_TYPE)))
{
/* Might be a named param, check previous block */
chunk_t *pp = chunk_get_prev(prev);
if ((pp != NULL) &&
(pp->type != CT_OC_COLON) &&
(pp->type != CT_ARITH) &&
(pp->type != CT_CARET))
{
set_chunk_type(prev, CT_OC_MSG_NAME);
set_chunk_parent(tmp, CT_OC_MSG_NAME);
}
}
}
}
prev = tmp;
}
}
/**
* Process an C# [] thingy:
* [assembly: xxx]
* [AttributeUsage()]
* [@X]
*
* Set the next chunk to a statement start after the close ']'
*
* @param os points to the open square '['
*/
static void handle_cs_square_stmt(chunk_t *os)
{
LOG_FUNC_ENTRY();
chunk_t *tmp;
chunk_t *cs = chunk_get_next(os);
while ((cs != NULL) && (cs->level > os->level))
{
cs = chunk_get_next(cs);
}
if ((cs == NULL) || (cs->type != CT_SQUARE_CLOSE))
{
return;
}
set_chunk_parent(os, CT_CS_SQ_STMT);
set_chunk_parent(cs, CT_CS_SQ_STMT);
for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp))
{
set_chunk_parent(tmp, CT_CS_SQ_STMT);
if (tmp->type == CT_COLON)
{
set_chunk_type(tmp, CT_CS_SQ_COLON);
}
}
tmp = chunk_get_next_ncnl(cs);
if (tmp != NULL)
{
tmp->flags |= PCF_STMT_START | PCF_EXPR_START;
}
}
/**
* We are on a brace open that is preceded by a word or square close.
* Set the brace parent to CT_CS_PROPERTY and find the first item in the
* property and set its parent, too.
*/
static void handle_cs_property(chunk_t *bro)
{
LOG_FUNC_ENTRY();
chunk_t *pc;
bool did_prop = false;
set_paren_parent(bro, CT_CS_PROPERTY);
pc = bro;
while ((pc = chunk_get_prev_ncnl(pc)) != NULL)
{
if (pc->level == bro->level)
{
if (!did_prop && ((pc->type == CT_WORD) || (pc->type == CT_THIS)))
{
set_chunk_type(pc, CT_CS_PROPERTY);
did_prop = true;
}
else
{
set_chunk_parent(pc, CT_CS_PROPERTY);
make_type(pc);
}
if (pc->flags & PCF_STMT_START)
{
break;
}
}
}
}
/**
* We hit a ']' followed by a WORD. This may be a multidimensional array type.
* Example: int[,,] x;
* If there is nothing but commas between the open and close, then mark it.
*/
static void handle_cs_array_type(chunk_t *pc)
{
chunk_t *prev = chunk_get_prev(pc);
for (prev = chunk_get_prev(pc);
prev && (prev->type == CT_COMMA);
prev = chunk_get_prev(prev))
{
/* empty */
}
if (prev && (prev->type == CT_SQUARE_OPEN))
{
while (pc != prev)
{
pc->parent_type = CT_TYPE;
pc = chunk_get_prev(pc);
}
prev->parent_type = CT_TYPE;
}
}
/**
* Remove 'return;' that appears as the last statement in a function
*/
void remove_extra_returns()
{
LOG_FUNC_ENTRY();
chunk_t *pc;
chunk_t *semi;
chunk_t *cl_br;
pc = chunk_get_head();
while (pc != NULL)
{
if ((pc->type == CT_RETURN) && ((pc->flags & PCF_IN_PREPROC) == 0))
{
semi = chunk_get_next_ncnl(pc);
cl_br = chunk_get_next_ncnl(semi);
if ((semi != NULL) && (semi->type == CT_SEMICOLON) &&
(cl_br != NULL) && (cl_br->type == CT_BRACE_CLOSE) &&
((cl_br->parent_type == CT_FUNC_DEF) ||
(cl_br->parent_type == CT_FUNC_CLASS_DEF)))
{
LOG_FMT(LRMRETURN, "Removed 'return;' on line %d\n", pc->orig_line);
chunk_del(pc);
chunk_del(semi);
pc = cl_br;
}
}
pc = chunk_get_next(pc);
}
}
/**
* A func wrap chunk and what follows should be treated as a function name.
* Create new text for the chunk and call it a CT_FUNCTION.
*
* A type wrap chunk and what follows should be treated as a simple type.
* Create new text for the chunk and call it a CT_TYPE.
*/
static void handle_wrap(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *opp = chunk_get_next(pc);
chunk_t *name = chunk_get_next(opp);
chunk_t *clp = chunk_get_next(name);
argval_t pav = (pc->type == CT_FUNC_WRAP) ?
cpd.settings[UO_sp_func_call_paren].a :
cpd.settings[UO_sp_cpp_cast_paren].a;
argval_t av = (pc->type == CT_FUNC_WRAP) ?
cpd.settings[UO_sp_inside_fparen].a :
cpd.settings[UO_sp_inside_paren_cast].a;
if ((clp != NULL) &&
(opp->type == CT_PAREN_OPEN) &&
((name->type == CT_WORD) || (name->type == CT_TYPE)) &&
(clp->type == CT_PAREN_CLOSE))
{
const char *psp = (pav & AV_ADD) ? " " : "";
const char *fsp = (av & AV_ADD) ? " " : "";
pc->str.append(psp);
pc->str.append("(");
pc->str.append(fsp);
pc->str.append(name->str);
pc->str.append(fsp);
pc->str.append(")");
set_chunk_type(pc, (pc->type == CT_FUNC_WRAP) ? CT_FUNCTION : CT_TYPE);
pc->orig_col_end = pc->orig_col + pc->len();
chunk_del(opp);
chunk_del(name);
chunk_del(clp);
}
}
/**
* A proto wrap chunk and what follows should be treated as a function proto.
*
* RETTYPE PROTO_WRAP( NAME, PARAMS );
* RETTYPE gets changed with make_type().
* PROTO_WRAP is marked as CT_FUNC_PROTO or CT_FUNC_DEF.
* NAME is marked as CT_WORD.
* PARAMS is all marked as prototype parameters.
*/
static void handle_proto_wrap(chunk_t *pc)
{
LOG_FUNC_ENTRY();
chunk_t *opp = chunk_get_next_ncnl(pc);
chunk_t *name = chunk_get_next_ncnl(opp);
chunk_t *tmp = chunk_get_next_ncnl(chunk_get_next_ncnl(name));
chunk_t *clp = chunk_skip_to_match(opp);
chunk_t *cma = chunk_get_next_ncnl(clp);
if (!opp || !name || !clp || !cma || !tmp ||
((name->type != CT_WORD) && (name->type != CT_TYPE)) ||
(tmp->type != CT_PAREN_OPEN) ||
(opp->type != CT_PAREN_OPEN))
{
return;
}
if (cma->type == CT_SEMICOLON)
{
set_chunk_type(pc, CT_FUNC_PROTO);
}
else if (cma->type == CT_BRACE_OPEN)
{
set_chunk_type(pc, CT_FUNC_DEF);
}
else
{
return;
}
set_chunk_parent(opp, pc->type);
set_chunk_parent(clp, pc->type);
set_chunk_parent(tmp, CT_PROTO_WRAP);
fix_fcn_def_params(tmp);
tmp = chunk_skip_to_match(tmp);
if (tmp)
{
set_chunk_parent(tmp, CT_PROTO_WRAP);
}
/* Mark return type (TODO: move to own function) */
tmp = pc;
while ((tmp = chunk_get_prev_ncnl(tmp)) != NULL)
{
if (!chunk_is_type(tmp) &&
(tmp->type != CT_OPERATOR) &&
(tmp->type != CT_WORD) &&
(tmp->type != CT_ADDR))
{
break;
}
set_chunk_parent(tmp, pc->type);
make_type(tmp);
}
}
/**
* Java assert statments are: "assert EXP1 [: EXP2] ;"
* Mark the parent of the colon and semicolon
*/
static void handle_java_assert(chunk_t *pc)
{
LOG_FUNC_ENTRY();
bool did_colon = false;
chunk_t *tmp = pc;
while ((tmp = chunk_get_next(tmp)) != NULL)
{
if (tmp->level == pc->level)
{
if (!did_colon && (tmp->type == CT_COLON))
{
did_colon = true;
set_chunk_parent(tmp, pc->type);
}
if (tmp->type == CT_SEMICOLON)
{
set_chunk_parent(tmp, pc->type);
break;
}
}
}
}