git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* a problem with git diff
@ 2020-04-16 16:43 Guy Maurel
  2020-04-16 22:55 ` brian m. carlson
  0 siblings, 1 reply; 5+ messages in thread
From: Guy Maurel @ 2020-04-16 16:43 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 432 bytes --]

Hello!

Using the appended two files:
  git diff combine.cpp-2020-04-16-A combine.cpp-2020-04-16-R > git-diff.diff-Y

doesn't show the same differences as with:
  meld combine.cpp-2020-04-16-R combine.cpp-2020-04-16-A

Have a look at git-diff.diff-Y at line 210:
-static void process_returns(void)
+static void mark_lvalue(chunk_t *pc)

which is NOT correct.

What is to do?
Thanks
-- 
Guy Maurel
Sebastian-Fischer-Weg 13
89077 Ulm

[-- Attachment #2: combine.cpp-2020-04-16-A --]
[-- Type: text/plain, Size: 135489 bytes --]

/**
 * @file combine.cpp
 * Labels the chunks as needed.
 *
 * @author  Ben Gardner
 * @author  Guy Maurel since version 0.62 for uncrustify4Qt
 *          October 2015, 2016
 * @license GPL v2+
 */

#include "combine.h"

#include "chunk_list.h"
#include "combine_labels.h"
#include "ChunkStack.h"
#include "error_types.h"
#include "flag_parens.h"
#include "lang_pawn.h"
#include "language_tools.h"
#include "log_rules.h"
#include "newlines.h"
#include "prototypes.h"
#include "tokenize_cleanup.h"
#include "unc_ctype.h"
#include "uncrustify.h"
#include "uncrustify_types.h"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <map>

using namespace std;
using namespace uncrustify;


/**
 * Mark the parens and colons in:
 *   asm volatile ( "xx" : "xx" (l), "yy"(h) : ...  );
 *
 * @param pc  the CT_ASM item
 */
static void flag_asm(chunk_t *pc);


/**
 * Combines two tokens into {{ and }} if inside parens and nothing is between
 * either pair.
 */
static void check_double_brace_init(chunk_t *bo1);


/**
 * 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)();
 * typedef <return type>([*]func)(params);
 * typedef <return type>(__stdcall *func)(); Bug # 633    MS-specific extension
 *                                           include the config-file "test/config/MS-calling_conventions.cfg"
 * 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 *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);


/**
 * 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 *pc);


/**
 * 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 *pc);


static void process_returns(void);


/**
 * 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);


/**
 * 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);


/**
 *  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);


/**
 * 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);


/**
 * Process an ObjC message spec/dec
 *
 * Specs:
 * -(void) foo ARGS;
 *
 * Declaration:
 * -(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);


/**
 * 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 pc  points to the open square '['
 */
static void handle_oc_message_send(chunk_t *pc);


//! Process @Property values and re-arrange them if necessary
static void handle_oc_property_decl(chunk_t *pc);

//! Process @available annotation
static void handle_oc_available(chunk_t *pc);

/**
 * Process a type that is enclosed in parens in message declarations.
 * 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, pcf_flags_t flags, bool &did_it);

/**
 * Process an C# [] thingy:
 *    [assembly: xxx]
 *    [AttributeUsage()]
 *    [@X]
 *
 * Set the next chunk to a statement start after the close ']'
 *
 * @param pc  points to the open square '['
 */
static void handle_cs_square_stmt(chunk_t *pc);


/**
 * 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 *pc);


/**
 * 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);


/**
 * 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);


/**
 * 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 *pc);


/**
 * 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);


/**
 * 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);


/**
 * A proto wrap chunk and what follows should be treated as a function proto.
 *
 * RETTYPE PROTO_WRAP( NAME, PARAMS ); or 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);


static bool is_oc_block(chunk_t *pc);


/**
 * Java assert statements are: "assert EXP1 [: EXP2] ;"
 * Mark the parent of the colon and semicolon
 */
static void handle_java_assert(chunk_t *pc);


void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_t clr_flags, scope_e nav)
{
   LOG_FUNC_ENTRY();

   while (start != nullptr && start != end)
   {
      chunk_flags_upd(start, clr_flags, set_flags);

      start = chunk_get_next(start, nav);

      if (start == nullptr)
      {
         return;
      }
   }

   if (end != nullptr)
   {
      chunk_flags_upd(end, clr_flags, set_flags);
   }
}


static void flag_asm(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   chunk_t *tmp = chunk_get_next_ncnl(pc, scope_e::PREPROC);

   if (!chunk_is_token(tmp, CT_QUALIFIER))
   {
      return;
   }
   chunk_t *po = chunk_get_next_ncnl(tmp, scope_e::PREPROC);

   if (!chunk_is_paren_open(po))
   {
      return;
   }
   chunk_t *end = chunk_skip_to_match(po, scope_e::PREPROC);

   if (end == nullptr)
   {
      return;
   }
   set_chunk_parent(po, CT_ASM);
   set_chunk_parent(end, CT_ASM);

   for (  tmp = chunk_get_next_ncnl(po, scope_e::PREPROC);
          tmp != nullptr
       && tmp != end;
          tmp = chunk_get_next_ncnl(tmp, scope_e::PREPROC))
   {
      if (chunk_is_token(tmp, CT_COLON))
      {
         set_chunk_type(tmp, CT_ASM_COLON);
      }
      else if (chunk_is_token(tmp, CT_DC_MEMBER))
      {
         // if there is a string on both sides, then this is two ASM_COLONs
         if (  chunk_is_token(chunk_get_next_ncnl(tmp, scope_e::PREPROC), CT_STRING)
            && chunk_is_token(chunk_get_prev_ncnlni(tmp, scope_e::PREPROC), CT_STRING)) // Issue #2279
         {
            chunk_t nc;

            nc = *tmp;

            tmp->str.resize(1);
            tmp->orig_col_end = tmp->orig_col + 1;
            set_chunk_type(tmp, CT_ASM_COLON);

            set_chunk_type(&nc, tmp->type);
            nc.str.pop_front();
            nc.orig_col++;
            nc.column++;
            chunk_add_after(&nc, tmp);
         }
      }
   }

   tmp = chunk_get_next_ncnl(end, scope_e::PREPROC);

   if (tmp == nullptr)
   {
      return;
   }

   if (chunk_is_token(tmp, CT_SEMICOLON))
   {
      set_chunk_parent(tmp, CT_ASM);
   }
} // flag_asm


void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next)
{
   LOG_FUNC_ENTRY();
   chunk_t *tmp;

   // separate the uses of CT_ASSIGN sign '='
   // into CT_ASSIGN_DEFAULT_ARG, CT_ASSIGN_FUNC_PROTO
   if (  chunk_is_token(pc, CT_ASSIGN)
      && get_chunk_parent_type(pc) == CT_FUNC_PROTO
      && (  pc->flags.test(PCF_IN_FCN_DEF)                            // Issue #2236
         || pc->flags.test(PCF_IN_CONST_ARGS)))
   {
      LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      log_pcf_flags(LFCNR, pc->flags);
      set_chunk_type(pc, CT_ASSIGN_DEFAULT_ARG);
   }

   if (  (  chunk_is_token(prev, CT_FPAREN_CLOSE)
         || (  (  chunk_is_str(prev, "const", 5)
               || chunk_is_str(prev, "override", 8))
            && chunk_is_token(prev->prev, CT_FPAREN_CLOSE)))
      && chunk_is_token(pc, CT_ASSIGN)
      && (  chunk_is_token(next, CT_DEFAULT)
         || chunk_is_token(next, CT_DELETE)
         || chunk_is_str(next, "0", 1)))
   {
      set_chunk_type(pc, CT_ASSIGN_FUNC_PROTO);
   }

   if (chunk_is_token(pc, CT_OC_AT))
   {
      if (  chunk_is_token(next, CT_PAREN_OPEN)
         || chunk_is_token(next, CT_BRACE_OPEN)
         || chunk_is_token(next, 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 (  language_is_set(LANG_D)
      && chunk_is_token(pc, CT_QUALIFIER)
      && chunk_is_str(pc, "const", 5)
      && chunk_is_token(next, CT_PAREN_OPEN))
   {
      set_chunk_type(pc, CT_D_CAST);
      set_paren_parent(next, pc->type);
   }

   if (  chunk_is_token(next, CT_PAREN_OPEN)
      && (  chunk_is_token(pc, CT_D_CAST)
         || chunk_is_token(pc, CT_DELEGATE)
         || chunk_is_token(pc, CT_ALIGN)))
   {
      // mark the parenthesis parent
      tmp = set_paren_parent(next, pc->type);

      // For a D cast - convert the next item
      if (  chunk_is_token(pc, CT_D_CAST)
         && tmp != nullptr)
      {
         if (chunk_is_token(tmp, CT_STAR))
         {
            set_chunk_type(tmp, CT_DEREF);
         }
         else if (chunk_is_token(tmp, CT_AMP))
         {
            set_chunk_type(tmp, CT_ADDR);
         }
         else if (chunk_is_token(tmp, CT_MINUS))
         {
            set_chunk_type(tmp, CT_NEG);
         }
         else if (chunk_is_token(tmp, 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 (chunk_is_token(pc, CT_DELEGATE))
      {
         if (tmp != nullptr)
         {
            set_chunk_parent(tmp, CT_DELEGATE);

            if (tmp->level == tmp->brace_level)
            {
               chunk_flags_set(tmp, PCF_VAR_1ST_DEF);
            }
         }

         for (tmp = chunk_get_prev_ncnlni(pc); tmp != nullptr; tmp = chunk_get_prev_ncnlni(tmp)) // Issue #2279
         {
            if (  chunk_is_semicolon(tmp)
               || chunk_is_token(tmp, CT_BRACE_OPEN)
               || chunk_is_token(tmp, CT_VBRACE_OPEN))
            {
               break;
            }
            make_type(tmp);
         }
      }

      if (  chunk_is_token(pc, CT_ALIGN)
         && tmp != nullptr)
      {
         if (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            set_paren_parent(tmp, pc->type);
         }
         else if (chunk_is_token(tmp, CT_COLON))
         {
            set_chunk_parent(tmp, pc->type);
         }
      }
   } // paren open + cast/align/delegate

   if (chunk_is_token(pc, CT_INVARIANT))
   {
      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         set_chunk_parent(next, pc->type);
         tmp = chunk_get_next(next);

         while (tmp != nullptr)
         {
            if (chunk_is_token(tmp, 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 (  chunk_is_token(prev, CT_BRACE_OPEN)
      && get_chunk_parent_type(prev) != CT_CS_PROPERTY
      && (  chunk_is_token(pc, CT_GETSET)
         || chunk_is_token(pc, CT_GETSET_EMPTY)))
   {
      flag_parens(prev, PCF_NONE, CT_NONE, CT_GETSET, false);
   }

   if (chunk_is_token(pc, CT_ASM))
   {
      flag_asm(pc);
   }

   // clang stuff - A new derived type is introduced to C and, by extension, Objective-C, C++, and Objective-C++
   if (language_is_set(LANG_C | LANG_CPP | LANG_OC))
   {
      if (chunk_is_token(pc, CT_CARET))
      {
         if (  pc->flags.test(PCF_EXPR_START)
            || pc->flags.test(PCF_IN_PREPROC))
         {
            handle_oc_block_literal(pc);
         }
      }
   }

   // Objective C stuff
   if (language_is_set(LANG_OC))
   {
      // Check for message declarations
      if (pc->flags.test(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.test(PCF_EXPR_START)
         || pc->flags.test(PCF_IN_PREPROC))
      {
         if (chunk_is_token(pc, CT_SQUARE_OPEN))
         {
            handle_oc_message_send(pc);
         }
      }

      if (chunk_is_token(pc, CT_OC_PROPERTY))
      {
         handle_oc_property_decl(pc);
      }

      if (chunk_is_token(pc, CT_OC_AVAILABLE))
      {
         handle_oc_available(pc);
      }
   }

   // C# stuff
   if (language_is_set(LANG_CS))
   {
      // '[assembly: xxx]' stuff
      if (  pc->flags.test(PCF_EXPR_START)
         && chunk_is_token(pc, CT_SQUARE_OPEN))
      {
         handle_cs_square_stmt(pc);
      }

      if (  chunk_is_token(next, CT_BRACE_OPEN)
         && get_chunk_parent_type(next) == CT_NONE
         && (  chunk_is_token(pc, CT_SQUARE_CLOSE)
            || chunk_is_token(pc, CT_ANGLE_CLOSE)
            || chunk_is_token(pc, CT_WORD)))
      {
         handle_cs_property(next);
      }

      if (  chunk_is_token(pc, CT_SQUARE_CLOSE)
         && chunk_is_token(next, CT_WORD))
      {
         handle_cs_array_type(pc);
      }

      if (  (  chunk_is_token(pc, CT_LAMBDA)
            || chunk_is_token(pc, CT_DELEGATE))
         && chunk_is_token(next, CT_BRACE_OPEN))
      {
         set_paren_parent(next, pc->type);
      }

      if (chunk_is_token(pc, CT_WHEN) && pc->next->type != CT_SPAREN_OPEN)
      {
         set_chunk_type(pc, CT_WORD);
      }
   }

   if (  language_is_set(LANG_JAVA)
      && chunk_is_token(pc, CT_LAMBDA)
      && chunk_is_token(next, CT_BRACE_OPEN))
   {
      set_paren_parent(next, pc->type);
   }

   if (chunk_is_token(pc, CT_NEW))
   {
      chunk_t *ts = nullptr;
      tmp = next;

      if (chunk_is_token(tmp, CT_TSQUARE))
      {
         ts  = tmp;
         tmp = chunk_get_next_ncnl(tmp);
      }

      if (  chunk_is_token(tmp, CT_BRACE_OPEN)
         || chunk_is_token(tmp, CT_PAREN_OPEN))
      {
         set_paren_parent(tmp, pc->type);

         if (ts != nullptr)
         {
            set_chunk_parent(ts, pc->type);
         }
      }
   }

   // C++11 Lambda stuff
   if (  language_is_set(LANG_CPP)
      && (  chunk_is_token(pc, CT_SQUARE_OPEN)
         || chunk_is_token(pc, CT_TSQUARE)))
   {
      handle_cpp_lambda(pc);
   }

   // FIXME: which language does this apply to?
   // Issue #2432
   if (!language_is_set(LANG_OC))
   {
      if (  chunk_is_token(pc, CT_ASSIGN)
         && chunk_is_token(next, CT_SQUARE_OPEN))
      {
         set_paren_parent(next, CT_ASSIGN);

         // Mark one-liner assignment
         tmp = next;

         while ((tmp = chunk_get_next_nc(tmp)) != nullptr)
         {
            if (chunk_is_newline(tmp))
            {
               break;
            }

            if (chunk_is_token(tmp, CT_SQUARE_CLOSE) && next->level == tmp->level)
            {
               chunk_flags_set(tmp, PCF_ONE_LINER);
               chunk_flags_set(next, PCF_ONE_LINER);
               break;
            }
         }
      }
   }

   if (chunk_is_token(pc, CT_ASSERT))
   {
      handle_java_assert(pc);
   }

   if (chunk_is_token(pc, CT_ANNOTATION))
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_paren_open(tmp))
      {
         set_paren_parent(tmp, CT_ANNOTATION);
      }
   }

   if (chunk_is_token(pc, CT_SIZEOF) && language_is_set(LANG_ALLC))
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_token(tmp, CT_ELLIPSIS))
      {
         set_chunk_parent(tmp, CT_SIZEOF);
      }
   }

   if (chunk_is_token(pc, CT_DECLTYPE))
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_paren_open(tmp))
      {
         // decltype may be followed by a braced-init-list
         tmp = set_paren_parent(tmp, CT_DECLTYPE);

         if (chunk_is_opening_brace(tmp))
         {
            tmp = set_paren_parent(tmp, CT_BRACED_INIT_LIST);

            if (tmp)
            {
               chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START);
            }
         }
         else
         {
            if (chunk_is_token(tmp, CT_WORD))
            {
               chunk_flags_set(tmp, PCF_VAR_1ST_DEF);
            }
         }
      }
   }

   // A [] in C# and D only follows a type
   if (  chunk_is_token(pc, CT_TSQUARE)
      && language_is_set(LANG_D | LANG_CS | LANG_VALA))
   {
      if (chunk_is_token(prev, CT_WORD))
      {
         set_chunk_type(prev, CT_TYPE);
      }

      if (chunk_is_token(next, CT_WORD))
      {
         chunk_flags_set(next, PCF_VAR_1ST_DEF);
      }
   }

   if (  chunk_is_token(pc, CT_SQL_EXEC)
      || chunk_is_token(pc, CT_SQL_BEGIN)
      || chunk_is_token(pc, CT_SQL_END))
   {
      mark_exec_sql(pc);
   }

   if (chunk_is_token(pc, CT_PROTO_WRAP))
   {
      handle_proto_wrap(pc);
   }

   // Handle the typedef
   if (chunk_is_token(pc, CT_TYPEDEF))
   {
      fix_typedef(pc);
   }

   if (  chunk_is_token(pc, CT_ENUM)
      || chunk_is_token(pc, CT_STRUCT)
      || chunk_is_token(pc, CT_UNION)
      || (  chunk_is_token(pc, CT_CLASS)
         && !language_is_set(LANG_D)))
   {
      if (prev->type != CT_TYPEDEF)
      {
         fix_enum_struct_union(pc);
      }
   }

   if (chunk_is_token(pc, CT_EXTERN))
   {
      if (chunk_is_paren_open(next))
      {
         tmp = flag_parens(next, PCF_NONE, CT_NONE, CT_EXTERN, true);

         if (chunk_is_token(tmp, 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 (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            set_paren_parent(tmp, CT_EXTERN);
         }
      }
   }

   if (chunk_is_token(pc, CT_TEMPLATE))
   {
      if (language_is_set(LANG_D))
      {
         handle_d_template(pc);
      }
      else
      {
         handle_cpp_template(pc);
      }
   }

   if (  chunk_is_token(pc, CT_WORD)
      && chunk_is_token(next, CT_ANGLE_OPEN)
      && get_chunk_parent_type(next) == CT_TEMPLATE)
   {
      mark_template_func(pc, next);
   }

   if (  chunk_is_token(pc, CT_SQUARE_CLOSE)
      && chunk_is_token(next, CT_PAREN_OPEN))
   {
      flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_NONE, false);
   }

   if (chunk_is_token(pc, CT_TYPE_CAST))
   {
      fix_type_cast(pc);
   }

   if (  get_chunk_parent_type(pc) == CT_ASSIGN
      && (chunk_is_token(pc, CT_BRACE_OPEN) || chunk_is_token(pc, CT_SQUARE_OPEN)))
   {
      // Mark everything in here as in assign
      flag_parens(pc, PCF_IN_ARRAY_ASSIGN, pc->type, CT_NONE, false);
   }

   if (chunk_is_token(pc, 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 (chunk_is_token(next, CT_PAREN_OPEN))
   {
      tmp = chunk_get_next_ncnl(next);

      if (  language_is_set(LANG_C | LANG_CPP | 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 (  (  chunk_is_token(tmp, CT_OC_BLOCK_CARET)
               || chunk_is_token(tmp, CT_CARET))
            && chunk_is_token(pc, CT_WORD))
         {
            LOG_FMT(LFCN, "%s(%d): (1) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
            set_chunk_type(pc, CT_FUNC_CALL);
         }
      }
      else if (  chunk_is_token(pc, CT_WORD)
              || chunk_is_token(pc, CT_OPERATOR_VAL))
      {
         set_chunk_type(pc, CT_FUNCTION);
      }
      else if (chunk_is_token(pc, CT_FIXED))
      {
         set_chunk_type(pc, CT_FUNCTION);
         set_chunk_parent(pc, CT_FIXED);
      }
      else if (chunk_is_token(pc, CT_TYPE))
      {
         /*
          * If we are on a type, then we are either on a C++ style cast, an
          * array reference, 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...
          */
         LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());

         bool is_byref_array = false;

         if (language_is_set(LANG_CPP))
         {
            // If the open paren is followed by an ampersand, an optional word,
            // a close parenthesis, and an open square bracket, then it is an
            // array being passed by reference, not a cast
            tmp = chunk_get_next_ncnl(next);

            if (chunk_is_token(tmp, CT_AMP))
            {
               auto tmp2 = chunk_get_next_ncnl(tmp);

               if (chunk_is_token(tmp2, CT_WORD))
               {
                  tmp2 = chunk_get_next_ncnl(tmp2);
               }

               if (chunk_is_token(tmp2, CT_PAREN_CLOSE))
               {
                  tmp2 = chunk_get_next_ncnl(tmp2);

                  if (chunk_is_token(tmp2, CT_SQUARE_OPEN))
                  {
                     is_byref_array = true;
                     set_chunk_type(tmp, CT_BYREF);
                  }
               }
            }
         }

         if (!is_byref_array)
         {
            tmp = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);

            if (tmp != nullptr)
            {
               tmp = chunk_get_next(tmp);

               if (chunk_is_token(tmp, CT_PAREN_OPEN))
               {
                  set_chunk_type(pc, CT_FUNCTION);
               }
               else
               {
                  if (  get_chunk_parent_type(pc) == CT_NONE
                     && !pc->flags.test(PCF_IN_TYPEDEF))
                  {
                     tmp = chunk_get_next_ncnl(next);

                     if (chunk_is_token(tmp, 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);
                     }
                  }
               }
            }
         }
      }
   }

   if (language_is_set(LANG_PAWN))
   {
      if (  chunk_is_token(pc, CT_FUNCTION)
         && pc->brace_level > 0)
      {
         LOG_FMT(LFCN, "%s(%d): (2) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         set_chunk_type(pc, CT_FUNC_CALL);
      }

      if (  chunk_is_token(pc, CT_STATE)
         && chunk_is_token(next, CT_PAREN_OPEN))
      {
         set_paren_parent(next, pc->type);
      }
   }
   else
   {
      if (  (  chunk_is_token(pc, CT_FUNCTION)
            || chunk_is_token(pc, CT_FUNC_DEF))
         && (  (get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR)
            || !is_oc_block(pc)))
      {
         mark_function(pc);
      }
   }

   // Detect C99 member stuff
   if (  chunk_is_token(pc, CT_MEMBER)
      && (  chunk_is_token(prev, CT_COMMA)
         || chunk_is_token(prev, CT_BRACE_OPEN)))
   {
      set_chunk_type(pc, CT_C99_MEMBER);
      set_chunk_parent(next, CT_C99_MEMBER);
   }

   // Mark function parens and braces
   if (  chunk_is_token(pc, CT_FUNC_DEF)
      || chunk_is_token(pc, CT_FUNC_CALL)
      || chunk_is_token(pc, CT_FUNC_CALL_USER)
      || chunk_is_token(pc, CT_FUNC_PROTO))
   {
      tmp = next;

      if (chunk_is_token(tmp, CT_SQUARE_OPEN))
      {
         tmp = set_paren_parent(tmp, pc->type);
      }
      else if (chunk_is_token(tmp, CT_TSQUARE) || get_chunk_parent_type(tmp) == CT_OPERATOR)
      {
         tmp = chunk_get_next_ncnl(tmp);
      }

      if (tmp != nullptr)
      {
         if (chunk_is_paren_open(tmp))
         {
            tmp = flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, pc->type, false);

            if (tmp != nullptr)
            {
               if (chunk_is_token(tmp, CT_BRACE_OPEN))
               {
                  if (  get_chunk_parent_type(tmp) != CT_DOUBLE_BRACE
                     && !pc->flags.test(PCF_IN_CONST_ARGS))
                  {
                     set_paren_parent(tmp, pc->type);
                  }
               }
               else if (  chunk_is_semicolon(tmp)
                       && chunk_is_token(pc, CT_FUNC_PROTO))
               {
                  set_chunk_parent(tmp, pc->type);
               }
            }
         }
      }
   }

   // Mark the parameters in catch()
   if (chunk_is_token(pc, CT_CATCH) && chunk_is_token(next, CT_SPAREN_OPEN))
   {
      fix_fcn_def_params(next);
   }

   if (chunk_is_token(pc, CT_THROW) && chunk_is_token(prev, CT_FPAREN_CLOSE))
   {
      set_chunk_parent(pc, get_chunk_parent_type(prev));

      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         set_paren_parent(next, CT_THROW);
      }
   }

   // Mark the braces in: "for_each_entry(xxx) { }"
   if (  chunk_is_token(pc, CT_BRACE_OPEN)
      && get_chunk_parent_type(pc) != CT_DOUBLE_BRACE
      && chunk_is_token(prev, CT_FPAREN_CLOSE)
      && (  get_chunk_parent_type(prev) == CT_FUNC_CALL
         || get_chunk_parent_type(prev) == CT_FUNC_CALL_USER)
      && !pc->flags.test(PCF_IN_CONST_ARGS))
   {
      LOG_FMT(LFCN, "%s(%d): (3) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      set_paren_parent(pc, CT_FUNC_CALL);
   }

   /*
    * Check for a close parenthesis followed by an open parenthesis,
    * which means that we are on a function type declaration (C/C++ only?).
    * Note that typedefs are already taken care of.
    */
   if (  !pc->flags.test_any(PCF_IN_TYPEDEF | PCF_IN_TEMPLATE)
      && get_chunk_parent_type(pc) != CT_CPP_CAST
      && get_chunk_parent_type(pc) != CT_C_CAST
      && !pc->flags.test(PCF_IN_PREPROC)
      && !is_oc_block(pc)
      && get_chunk_parent_type(pc) != CT_OC_MSG_DECL
      && get_chunk_parent_type(pc) != CT_OC_MSG_SPEC
      && chunk_is_str(pc, ")", 1)
      && chunk_is_str(next, "(", 1))
   {
      if (language_is_set(LANG_D))
      {
         flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
      }
      else
      {
         mark_function_type(pc);
      }
   }

   if (  (chunk_is_token(pc, CT_CLASS) || chunk_is_token(pc, CT_STRUCT))
      && pc->level == pc->brace_level)
   {
      if (pc->type != CT_STRUCT || !language_is_set(LANG_C))
      {
         mark_class_ctor(pc);
      }
   }

   if (chunk_is_token(pc, CT_OC_CLASS))
   {
      handle_oc_class(pc);
   }
   // TODO: Check for stuff that can only occur at the start of an statement

   if (!language_is_set(LANG_D))
   {
      /*
       * Check a parenthesis pair to see if it is a cast.
       * Note that SPAREN and FPAREN have already been marked.
       */
      if (  chunk_is_token(pc, CT_PAREN_OPEN)
         && (  get_chunk_parent_type(pc) == CT_NONE
            || get_chunk_parent_type(pc) == CT_OC_MSG
            || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR
            || get_chunk_parent_type(pc) == CT_CS_SQ_STMT)           // Issue # 1256
         && (  chunk_is_token(next, CT_WORD)
            || chunk_is_token(next, CT_TYPE)
            || chunk_is_token(next, CT_STRUCT)
            || chunk_is_token(next, CT_QUALIFIER)
            || chunk_is_token(next, CT_MEMBER)
            || chunk_is_token(next, CT_DC_MEMBER)
            || chunk_is_token(next, CT_ENUM)
            || chunk_is_token(next, CT_UNION))
         && prev->type != CT_DECLTYPE
         && prev->type != CT_SIZEOF
         && get_chunk_parent_type(prev) != CT_SIZEOF
         && get_chunk_parent_type(prev) != CT_OPERATOR
         && !pc->flags.test(PCF_IN_TYPEDEF))
      {
         fix_casts(pc);
      }
   }

   if (language_is_set(LANG_CPP))
   {
      chunk_t *nnext = chunk_get_next_ncnl(next);

      // handle parent_type of assigns in special functions (ro5 + pure virtual)
      if (  pc->flags.test_any(PCF_IN_STRUCT | PCF_IN_CLASS)
         && chunk_is_token(pc, CT_ASSIGN)
         && chunk_is_token(nnext, CT_SEMICOLON)
         && (  chunk_is_token(next, CT_DEFAULT)
            || chunk_is_token(next, CT_DELETE)
            || (chunk_is_token(next, CT_NUMBER) && chunk_is_str(next, "0", 1))))
      {
         const size_t level        = pc->level;
         bool         found_status = false;
         chunk_t      *pprev       = chunk_get_prev(pc);

         for ( ; (  pprev != nullptr
                 && pprev->level >= level
                 && pprev->type != CT_SEMICOLON
                 && pprev->type != CT_ACCESS_COLON)
               ; pprev = chunk_get_prev(pprev))
         {
            if (pprev->level != level)
            {
               continue;
            }

            if (chunk_is_token(next, CT_NUMBER))
            {
               if (  pprev->type == CT_QUALIFIER
                  && chunk_is_str(pprev, "virtual", 7))
               {
                  found_status = true;
                  break;
               }
            }
            else
            {
               if (  pprev->type == CT_FUNC_CLASS_PROTO  // ctor/dtor
                  || pprev->type == CT_FUNC_PROTO)       // normal function
               {
                  found_status = true;
                  break;
               }
            }
         }

         if (found_status)
         {
            set_chunk_parent(pc, pprev->type);
         }
      }
      // Issue #2332
      bool we_have_a_case_before = false;

      if (chunk_is_token(pc, CT_COLON))
      {
         // check if we have a case before
         chunk_t *switch_before = chunk_get_prev_type(pc, CT_CASE, pc->level);

         if (switch_before != nullptr)
         {
            LOG_FMT(LFCNR, "%s(%d): switch_before->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n",
                    __func__, __LINE__, switch_before->orig_line, switch_before->orig_col,
                    switch_before->text(), get_token_name(switch_before->type));
            we_have_a_case_before = true;
         }
      }

      // Detect a braced-init-list
      if (  chunk_is_token(pc, CT_WORD)
         || chunk_is_token(pc, CT_TYPE)
         || chunk_is_token(pc, CT_ASSIGN)
         || chunk_is_token(pc, CT_RETURN)
         || chunk_is_token(pc, CT_COMMA)
         || chunk_is_token(pc, CT_ANGLE_CLOSE)
         || chunk_is_token(pc, CT_SQUARE_CLOSE)
         || chunk_is_token(pc, CT_TSQUARE)
         || chunk_is_token(pc, CT_FPAREN_OPEN)
         || chunk_is_token(pc, CT_QUESTION)
         || (  chunk_is_token(pc, CT_COLON)
            && !we_have_a_case_before)
         || (  chunk_is_token(pc, CT_BRACE_OPEN)
            && (  get_chunk_parent_type(pc) == CT_NONE
               || get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST)))
      {
         log_pcf_flags(LFCNR, pc->flags);
         auto brace_open = chunk_get_next_ncnl(pc);

         if (  chunk_is_token(brace_open, CT_BRACE_OPEN)
            && (  get_chunk_parent_type(brace_open) == CT_NONE
               || get_chunk_parent_type(brace_open) == CT_ASSIGN
               || get_chunk_parent_type(brace_open) == CT_RETURN
               || get_chunk_parent_type(brace_open) == CT_BRACED_INIT_LIST))
         {
            log_pcf_flags(LFCNR, brace_open->flags);
            auto brace_close = chunk_skip_to_match(next);

            if (chunk_is_token(brace_close, CT_BRACE_CLOSE))
            {
               set_chunk_parent(brace_open, CT_BRACED_INIT_LIST);
               set_chunk_parent(brace_close, CT_BRACED_INIT_LIST);

               tmp = chunk_get_next_ncnl(brace_close);

               if (tmp)
               {
                  chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START);
               }
               // TODO: Change pc->type CT_WORD -> CT_TYPE
               // for the case CT_ASSIGN (and others).

               // TODO: Move this block to the fix_fcn_call_args function.
               if (chunk_is_token(pc, CT_WORD) && pc->flags.test(PCF_IN_FCN_CALL))
               {
                  set_chunk_type(pc, CT_TYPE);
               }
            }
         }
      }
   }

   // Check for stuff that can only occur at the start of an expression
   if (  pc->flags.test(PCF_EXPR_START)
      || (prev->flags.test(PCF_EXPR_START) && get_chunk_parent_type(pc) == CT_OC_AT))
   {
      // Change STAR, MINUS, and PLUS in the easy cases
      if (chunk_is_token(pc, CT_STAR))
      {
         // issue #596
         // [0x100062020:IN_SPAREN,IN_FOR,STMT_START,EXPR_START,PUNCTUATOR]
         // prev->type is CT_COLON ==> CT_DEREF
         if (chunk_is_token(prev, CT_ANGLE_CLOSE))
         {
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (chunk_is_token(prev, CT_COLON))
         {
            set_chunk_type(pc, CT_DEREF);
         }
         else
         {
            set_chunk_type(pc, CT_DEREF);
         }
      }

      if (  language_is_set(LANG_CPP)
         && chunk_is_token(pc, CT_CARET)
         && chunk_is_token(prev, CT_ANGLE_CLOSE))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }

      if (  language_is_set(LANG_CS)
         && (chunk_is_token(pc, CT_QUESTION))
         && (chunk_is_token(prev, CT_ANGLE_CLOSE)))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }

      if (chunk_is_token(pc, CT_MINUS))
      {
         set_chunk_type(pc, CT_NEG);
      }

      if (chunk_is_token(pc, CT_PLUS))
      {
         set_chunk_type(pc, CT_POS);
      }

      if (chunk_is_token(pc, CT_INCDEC_AFTER))
      {
         set_chunk_type(pc, CT_INCDEC_BEFORE);
      }

      if (chunk_is_token(pc, CT_AMP))
      {
         if (chunk_is_token(prev, CT_ANGLE_CLOSE))             // Issue #2324
         {
            set_chunk_type(pc, CT_BYREF);
         }
         else
         {
            set_chunk_type(pc, CT_ADDR);
         }
      }

      if (chunk_is_token(pc, CT_CARET))
      {
         if (language_is_set(LANG_C | LANG_CPP | 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.test(PCF_IN_TYPEDEF)
      && get_chunk_parent_type(prev) != CT_CPP_CAST
      && !prev->flags.test(PCF_IN_FCN_DEF)
      && (  chunk_is_token(pc, CT_STRUCT)
         || chunk_is_token(pc, CT_UNION)
         || chunk_is_token(pc, CT_CLASS)
         || chunk_is_token(pc, CT_ENUM)))
   {
      tmp = chunk_skip_dc_member(next);

      if ((chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD)))
      {
         set_chunk_parent(tmp, pc->type);
         set_chunk_type(tmp, CT_TYPE);

         tmp = chunk_get_next_ncnl(tmp);
      }

      if (chunk_is_token(tmp, CT_BRACE_OPEN))
      {
         tmp = chunk_skip_to_match(tmp);

         if (tmp != nullptr)
         {
            tmp = chunk_get_next_ncnl(tmp);
         }
      }

      if (  tmp != nullptr
         && (chunk_is_ptr_operator(tmp) || chunk_is_token(tmp, CT_WORD)))
      {
         mark_variable_definition(tmp);
      }
   }

   /*
    * Change the parenthesis pair after a function/macro-function
    * CT_PAREN_OPEN => CT_FPAREN_OPEN
    */
   if (chunk_is_token(pc, CT_MACRO_FUNC))
   {
      flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_MACRO_FUNC, false);
   }

   if (  chunk_is_token(pc, CT_MACRO_OPEN)
      || chunk_is_token(pc, CT_MACRO_ELSE)
      || chunk_is_token(pc, CT_MACRO_CLOSE))
   {
      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, false);
      }
   }

   if (chunk_is_token(pc, CT_DELETE) && chunk_is_token(next, CT_TSQUARE))
   {
      set_chunk_parent(next, CT_DELETE);
   }

   // Change CT_STAR to CT_PTR_TYPE or CT_ARITH or CT_DEREF
   if (  chunk_is_token(pc, CT_STAR)
      || (language_is_set(LANG_CPP) && chunk_is_token(pc, CT_CARET)))
   {
      if (chunk_is_paren_close(next) || chunk_is_token(next, CT_COMMA))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (language_is_set(LANG_OC) && chunk_is_token(next, 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, get_chunk_parent_type(prev));

         set_chunk_type(next, CT_PTR_TYPE);
         set_chunk_parent(next, get_chunk_parent_type(pc));
      }
      else if (  chunk_is_token(pc, CT_STAR)
              && (  chunk_is_token(prev, CT_DECLTYPE)
                 || chunk_is_token(prev, CT_SIZEOF)
                 || chunk_is_token(prev, CT_DELETE)
                 || (pc && get_chunk_parent_type(pc) == CT_SIZEOF)))
      {
         set_chunk_type(pc, CT_DEREF);
      }
      else if (  (  chunk_is_token(prev, CT_WORD)
                 && chunk_ends_type(prev)
                 && !prev->flags.test(PCF_IN_FCN_CTOR))
              || chunk_is_token(prev, CT_DC_MEMBER)
              || chunk_is_token(prev, CT_PTR_TYPE))
      {
         LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n   ",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type));
         log_pcf_flags(LFCNR, pc->flags);
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (chunk_is_token(next, CT_SQUARE_OPEN) && !language_is_set(LANG_OC))  // issue # 408
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (chunk_is_token(pc, CT_STAR))
      {
         // Add check for CT_DC_MEMBER CT_WORD CT_STAR sequence
         // to convert CT_WORD into CT_TYPE
         // and CT_STAR into CT_PTR_TYPE
         // look for an assign backward to distinguish between
         //    double result = Constants::PI * factor;
         // and
         //    ::some::name * foo;
         if (  chunk_is_token(prev, CT_WORD)
            && chunk_is_token(prev->prev, CT_DC_MEMBER)
            && language_is_set(LANG_CPP))
         {
            // Issue 1402
            bool assign_found = false;
            tmp = pc;

            while (tmp != nullptr)
            {
               if (chunk_is_token(tmp, CT_SEMICOLON))
               {
                  break;
               }
               else if (chunk_is_token(tmp, CT_ASSIGN))
               {
                  assign_found = true;
                  break;
               }
               tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279
            }

            if (assign_found)
            {
               // double result = Constants::PI * factor;
               set_chunk_type(pc, CT_ARITH);
            }
            else
            {
               //    ::some::name * foo;
               set_chunk_type(prev, CT_TYPE);
               set_chunk_type(pc, CT_PTR_TYPE);
            }
         }

         /*
          * A star can have three meanings
          * 1. CT_DEREF    = pointer dereferencing
          * 2. CT_PTR_TYPE = pointer definition
          * 3. CT_ARITH    = arithmetic multiplication
          *
          * 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.
          */
         if (chunk_is_token(prev, CT_TYPE))
         {
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (  chunk_is_token(pc->next, CT_SEMICOLON)      // Issue #2319
                 || (  chunk_is_token(pc->next, CT_STAR)
                    && chunk_is_token(pc->next->next, CT_SEMICOLON)))
         {
            // example:
            //    using AbstractLinkPtr = AbstractLink*;
            //    using AbstractLinkPtrPtr = AbstractLink**;
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (  (  get_chunk_parent_type(pc) == CT_FUNC_DEF
                    && (chunk_is_opening_brace(next) || chunk_is_star(pc->next)))
                 || (next->type == CT_QUALIFIER))               // Issue #2648
         {
            // example:
            // auto getComponent(Color *color) -> Component * {
            // auto getComponent(Color *color) -> Component ** {
            // auto getComponent(Color *color) -> Component * _Nonnull
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (  chunk_is_token(pc->next, CT_SEMICOLON)      // Issue #2319
                 || (  chunk_is_token(pc->next, CT_STAR)
                    && chunk_is_token(pc->next->next, CT_STAR)))
         {
            // more pointers are NOT yet possible
            fprintf(stderr, "Too many pointers\n");
            fprintf(stderr, "at line %zu, column %zu.\n", pc->orig_line, pc->orig_col);
            fprintf(stderr, "Please make a report.\n");
            log_flush(true);
            exit(EX_SOFTWARE);
         }
         else
         {
            // Issue 1402
            set_chunk_type(pc,
                           (  prev->flags.test(PCF_PUNCTUATOR)
                           && (  !chunk_is_paren_close(prev)
                              || chunk_is_token(prev, CT_SPAREN_CLOSE)
                              || get_chunk_parent_type(prev) == CT_MACRO_FUNC)
                           && prev->type != CT_SQUARE_CLOSE
                           && prev->type != CT_DC_MEMBER) ? CT_DEREF : CT_ARITH);
         }

         if (pc->flags.test(PCF_IN_TYPEDEF))  // Issue #1255/#633
         {
            tmp = pc;

            while (tmp != nullptr)
            {
               if (  chunk_is_token(tmp, CT_SEMICOLON)
                  || chunk_is_token(tmp, CT_BRACE_OPEN))
               {
                  break;
               }
               else if (chunk_is_token(tmp, CT_TYPEDEF))
               {
                  set_chunk_type(pc, CT_PTR_TYPE);
               }
               tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279
            }
         }
      }
   }

   if (chunk_is_token(pc, CT_AMP))
   {
      if (chunk_is_token(prev, CT_DELETE))
      {
         set_chunk_type(pc, CT_ADDR);
      }
      else if (chunk_is_token(prev, CT_TYPE))
      {
         set_chunk_type(pc, CT_BYREF);
      }
      else if (chunk_is_token(next, CT_FPAREN_CLOSE) || chunk_is_token(next, CT_COMMA))
      {
         // fix the bug #654
         // connect(&mapper, SIGNAL(mapped(QString &)), this, SLOT(onSomeEvent(QString &)));
         set_chunk_type(pc, CT_BYREF);
      }
      else if (get_chunk_parent_type(pc) == CT_USING_ALIAS)
      {
         // fix the Issue # 1689
         // using reference = value_type &;
         set_chunk_type(pc->prev, CT_TYPE);
         set_chunk_type(pc, CT_BYREF);
      }
      else
      {
         // Issue # 1398
         if (  pc->flags.test(PCF_IN_FCN_DEF)
            && chunk_is_token(prev, CT_WORD)
            && chunk_is_token(pc, CT_AMP)
            && chunk_is_token(next, CT_WORD))
         {
            /*
             * Change CT_WORD before CT_AMP before CT_WORD to CT_TYPE
             */
            set_chunk_type(prev, CT_TYPE);
         }
         else
         {
            set_chunk_type(pc, CT_ARITH);

            if (chunk_is_token(prev, CT_WORD))
            {
               tmp = chunk_get_prev_ncnlni(prev); // Issue #2279

               if (tmp != nullptr)
               {
                  if (  chunk_is_semicolon(tmp)
                     || chunk_is_token(tmp, CT_BRACE_OPEN)
                     || chunk_is_token(tmp, CT_QUALIFIER))
                  {
                     set_chunk_type(pc, CT_BYREF);
                     set_chunk_type(prev, CT_TYPE);

                     if (!(  chunk_is_token(next, CT_OPERATOR)
                          || chunk_is_token(next, CT_TYPE)
                          || chunk_is_token(next, CT_DC_MEMBER)))
                     {
                        LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
                                __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
                        chunk_flags_set(next, PCF_VAR_1ST);
                     }
                  }
                  else if (chunk_is_token(tmp, CT_DC_MEMBER))
                  {
                     set_chunk_type(prev, CT_TYPE);

                     if (!chunk_is_token(next, CT_TYPE))            // Issue #2103
                     {
                        set_chunk_type(pc, CT_BYREF);
                     }
                  }
               }
            }
         }
      }
   }

   if (chunk_is_token(pc, CT_MINUS) || chunk_is_token(pc, CT_PLUS))
   {
      if (  chunk_is_token(prev, CT_POS)
         || chunk_is_token(prev, CT_NEG)
         || chunk_is_token(prev, CT_ARITH))
      {
         set_chunk_type(pc, (pc->type == CT_MINUS) ? CT_NEG : CT_POS);
      }
      else if (chunk_is_token(prev, CT_OC_CLASS))
      {
         set_chunk_type(pc, (chunk_is_token(pc, CT_MINUS)) ? CT_NEG : CT_POS);
      }
      else
      {
         set_chunk_type(pc, CT_ARITH);
      }
   }

   /*
    * Bug # 634
    * Check for extern "C" NSString* i;
    * NSString is a type
    * change CT_WORD => CT_TYPE     for pc
    * change CT_STAR => CT_PTR_TYPE for pc-next
    */
   if (chunk_is_token(pc, CT_WORD))     // here NSString
   {
      if (pc->next != nullptr)          // here *
      {
         if (pc->next->type == CT_STAR) // here *
         {
            // compare text with "C" to find extern "C" instructions
            if (pc->prev != nullptr)
            {
               if (pc->prev->type == CT_STRING)
               {
                  if (unc_text::compare(pc->prev->text(), "\"C\"") == 0)
                  {
                     if (pc->prev->prev->type == CT_EXTERN)
                     {
                        set_chunk_type(pc, CT_TYPE);            // change CT_WORD => CT_TYPE
                        set_chunk_type(pc->next, CT_PTR_TYPE);  // change CT_STAR => CT_PTR_TYPE
                     }
                  }
               }
            }

            // Issue #322 STDMETHOD(GetValues)(BSTR bsName, REFDATA** pData);
            if (  (pc->next->next != nullptr)
               && pc->next->next->type == CT_STAR
               && pc->flags.test(PCF_IN_CONST_ARGS))
            {
               // change CT_STAR => CT_PTR_TYPE
               set_chunk_type(pc->next, CT_PTR_TYPE);
               set_chunk_type(pc->next->next, CT_PTR_TYPE);
            }

            // Issue #222 whatever3 *(func_ptr)( whatever4 *foo2, ...
            if (  (pc->next->next != nullptr)
               && pc->next->next->type == CT_WORD
               && pc->flags.test(PCF_IN_FCN_DEF))
            {
               // look for the opening parenthesis
               // Issue 1403
               tmp = chunk_get_prev_type(pc, CT_FPAREN_OPEN, pc->level - 1);

               if (  tmp != nullptr
                  && get_chunk_parent_type(tmp) != CT_FUNC_CTOR_VAR)
               {
                  set_chunk_type(pc->next, CT_PTR_TYPE);
               }
            }
         }
      }
   }

   /*
    * Bug # 634
    * Check for __attribute__((visibility ("default"))) NSString* i;
    * NSString is a type
    * change CT_WORD => CT_TYPE     for pc
    * change CT_STAR => CT_PTR_TYPE for pc-next
    */
   if (chunk_is_token(pc, CT_WORD))     // here NSString
   {
      if (pc->next != nullptr)          // here *
      {
         if (pc->next->type == CT_STAR) // here *
         {
            tmp = pc;

            while ((tmp != nullptr))
            {
               if (chunk_is_token(tmp, CT_ATTRIBUTE))
               {
                  LOG_FMT(LFCNR, "%s(%d): ATTRIBUTE found, type is %s, text() '%s'\n",
                          __func__, __LINE__, get_token_name(tmp->type), tmp->text());
                  LOG_FMT(LFCNR, "for token, type is %s, text() '%s'\n", get_token_name(pc->type), pc->text());
                  // change CT_WORD => CT_TYPE
                  set_chunk_type(pc, CT_TYPE);
                  // change CT_STAR => CT_PTR_TYPE
                  set_chunk_type(pc->next, CT_PTR_TYPE);
               }

               if (tmp->flags.test(PCF_STMT_START))
               {
                  // we are at beginning of the line
                  break;
               }
               tmp = chunk_get_prev(tmp);
            }
         }
      }
   }

   /*
    * Issue # 1689
    * Check for using reference = value_type&;
    * is it a Type alias, alias template?
    */
   if (chunk_is_token(pc, CT_USING))
   {
      // look for CT_ASSIGN before CT_SEMICOLON at the end of the statement
      bool    assign_found = false;
      bool    is_preproc   = pc->flags.test(PCF_IN_PREPROC);
      chunk_t *temp;

      for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp))
      {
         LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', type is %s\n",
                 __func__, __LINE__, temp->orig_line, temp->orig_col, temp->text(), get_token_name(temp->type));

         if (chunk_is_token(temp, CT_ASSIGN))
         {
            assign_found = true;
            break;
         }

         if (  chunk_is_token(temp, CT_SEMICOLON)
            || (  is_preproc
               && (  !temp->flags.test(PCF_IN_PREPROC)
                  || chunk_is_token(temp, CT_PREPROC))))
         {
            break;
         }
      }

      if (assign_found)
      {
         // it is a Type alias, alias template
         for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp))
         {
            if (get_chunk_parent_type(temp) == CT_NONE)
            {
               set_chunk_parent(temp, CT_USING_ALIAS);
            }

            if (  chunk_is_token(temp, CT_SEMICOLON)
               || (  is_preproc
                  && (  !temp->flags.test(PCF_IN_PREPROC)
                     || chunk_is_token(temp, CT_PREPROC))))
            {
               break;
            }
         }
      }
   }

   // Issue #548: inline T && someFunc(foo * *p, bar && q) { }
   if (  pc->type == CT_BOOL
      && !pc->flags.test(PCF_IN_PREPROC)
      && chunk_is_str(pc, "&&", 2)
      && chunk_ends_type(pc->prev))
   {
      set_chunk_type(pc, CT_BYREF);
   }

   // Issue #1704
   if (  chunk_is_token(pc, CT_INCDEC_AFTER)
      && pc->flags.test(PCF_IN_PREPROC))
   {
      chunk_t *tmp_2 = chunk_get_next(pc);
      log_pcf_flags(LFTYPE, pc->flags);

      if (chunk_is_token(tmp_2, CT_WORD))
      {
         set_chunk_type(pc, CT_INCDEC_BEFORE);
      }
   }
} // do_symbol_check


static void check_double_brace_init(chunk_t *bo1)
{
   LOG_FUNC_ENTRY();
   LOG_FMT(LJDBI, "%s(%d): orig_line is %zu, orig_col is %zu", __func__, __LINE__, bo1->orig_line, bo1->orig_col);
   chunk_t *pc = chunk_get_prev_ncnlni(bo1);   // Issue #2279

   if (pc == nullptr)
   {
      return;
   }

   if (chunk_is_paren_close(pc))
   {
      chunk_t *bo2 = chunk_get_next(bo1);

      if (bo2 == nullptr)
      {
         return;
      }

      if (chunk_is_token(bo2, CT_BRACE_OPEN))
      {
         // found a potential double brace
         chunk_t *bc2 = chunk_skip_to_match(bo2);

         if (bc2 == nullptr)
         {
            return;
         }
         chunk_t *bc1 = chunk_get_next(bc2);

         if (bc1 == nullptr)
         {
            return;
         }

         if (chunk_is_token(bc1, CT_BRACE_CLOSE))
         {
            LOG_FMT(LJDBI, " - end, orig_line is %zu, orig_col is %zu\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");
} // check_double_brace_init


void fix_symbols(void)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc;
   chunk_t dummy;

   cpd.unc_stage = unc_stage_e::FIX_SYMBOLS;

   mark_define_expressions();

   bool is_cpp  = language_is_set(LANG_CPP);
   bool is_java = language_is_set(LANG_JAVA);

   for (pc = chunk_get_head(); pc != nullptr; pc = chunk_get_next_ncnl(pc))
   {
      if (  chunk_is_token(pc, CT_FUNC_WRAP)
         || chunk_is_token(pc, CT_TYPE_WRAP))
      {
         handle_wrap(pc);
      }

      if (chunk_is_token(pc, CT_ASSIGN))
      {
         mark_lvalue(pc);
      }
      // a brace immediately preceeded by word in C++11 is an initializer list though it may also
      // by a type casting initializer list if the word is really a type; sadly unucustify knows
      // only builtin types and knows nothing of user-defined types
      chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279

      if (  is_cpp
         && chunk_is_token(pc, CT_BRACE_OPEN)
         && (  chunk_is_token(prev, CT_WORD)
            || chunk_is_token(prev, CT_TYPE)))
      {
         mark_lvalue(pc);
      }

      if (  is_java
         && chunk_is_token(pc, CT_BRACE_OPEN))
      {
         check_double_brace_init(pc);
      }

      if (chunk_is_token(pc, CT_ATTRIBUTE))
      {
         chunk_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC);

         if (  next != nullptr
            && chunk_is_token(next, CT_PAREN_OPEN))
         {
            flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_ATTRIBUTE, false);
         }
      }
   }

   pc = chunk_get_head();

   if (pc == nullptr)
   {
      return;
   }

   if (  chunk_is_newline(pc)
      || chunk_is_comment(pc))
   {
      pc = chunk_get_next_ncnl(pc);
   }

   while (pc != nullptr)
   {
      if (chunk_is_token(pc, CT_IGNORED))
      {
         pc = chunk_get_next_ncnl(pc);
         continue;
      }
      LOG_FMT(LFCNR, "%s(%d): pc->orig_line       is %zu, orig_col is %zu, text() is '%s', type is %s\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type));
      chunk_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC);   // Issue #2279

      if (prev == nullptr)
      {
         prev = &dummy;
      }
      else
      {
         // Issue #2279
         LOG_FMT(LFCNR, "%s(%d): prev(ni)->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n",
                 __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text(), get_token_name(prev->type));
      }
      chunk_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC);

      if (next == nullptr)
      {
         next = &dummy;
      }
      else
      {
         // Issue #2279
         LOG_FMT(LFCNR, "%s(%d): next->orig_line     is %zu, orig_col is %zu, text() is '%s', type is %s\n",
                 __func__, __LINE__, next->orig_line, next->orig_col, next->text(), get_token_name(next->type));
      }
      LOG_FMT(LFCNR, "%s(%d): do_symbol_check(%s, %s, %s)\n",
              __func__, __LINE__, prev->text(), pc->text(), next->text());
      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 != nullptr)
   {
      LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s, parent_type is %s\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type), get_token_name(pc->parent_type));

      // Can't have a variable definition inside [ ]
      if (square_level < 0)
      {
         if (chunk_is_token(pc, CT_SQUARE_OPEN))
         {
            square_level = pc->level;
         }
      }
      else
      {
         if (pc->level <= static_cast<size_t>(square_level))
         {
            square_level = -1;
         }
      }

      if (  chunk_is_token(pc, CT_EXTERN)
         && language_is_set(LANG_ALLC))
      {
         chunk_t *next = chunk_get_next_ncnl(pc);

         if (chunk_is_token(next, CT_STRING))
         {
            chunk_t *tmp = chunk_get_next_ncnl(next);

            while (tmp != nullptr)
            {
               if (  (chunk_is_token(tmp, CT_TYPE))
                  || (chunk_is_token(tmp, CT_BRACE_OPEN))
                  || (chunk_is_token(tmp, CT_ATTRIBUTE)))
               {
                  break;
               }

               if (chunk_is_token(tmp, CT_WORD))
               {
                  chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
                  break;
               }
               tmp = chunk_get_next_ncnl(tmp);
            }
         }
      }

      if (  chunk_is_token(pc, CT_ATTRIBUTE)
         && language_is_set(LANG_ALLC))
      {
         chunk_t *tmp = skip_attribute_next(pc);

         if (chunk_is_token(tmp, CT_WORD))
         {
            chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
         }
      }

      if (  chunk_is_token(pc, CT_BRACE_OPEN)                       // Issue #2332
         && get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST)
      {
         LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', look for CT_BRACE_OPEN\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         pc = chunk_get_next_type(pc, CT_BRACE_CLOSE, pc->level);
      }
      /*
       * A variable definition is possible after at the start of a statement
       * that starts with: DC_MEMBER, QUALIFIER, TYPE, or WORD
       */
      // Issue #2279
      // Issue #2478
      LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s, parent_type is %s\n   ",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type), get_token_name(pc->parent_type));
      log_pcf_flags(LFCNR, pc->flags);

      if (  (square_level < 0)
         && pc->flags.test(PCF_STMT_START)
         && (  chunk_is_token(pc, CT_QUALIFIER)
            || chunk_is_token(pc, CT_TYPE)
            || chunk_is_token(pc, CT_TYPENAME)
            || chunk_is_token(pc, CT_DC_MEMBER)                         // Issue #2478
            || chunk_is_token(pc, CT_WORD))
         && get_chunk_parent_type(pc) != CT_ENUM
         && !pc->flags.test(PCF_IN_ENUM))
      {
         pc = fix_variable_definition(pc);
      }
      else
      {
         pc = chunk_get_next_ncnl(pc);
      }
   }
} // fix_symbols


static void process_returns(void)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc;

   pc = chunk_get_head();

   while (pc != nullptr)
   {
      if (pc->type != CT_RETURN)
      {
         pc = chunk_get_next_type(pc, CT_RETURN, -1);
         continue;
      }
      pc = process_return(pc);
   }
}


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_ppa_get_next_ncnl(pc);

   if (  next == nullptr || chunk_is_semicolon(next)
      || chunk_is_token(next, CT_NEWLINE))
   {
      return(next);
   }
   log_rule_B("nl_return_expr");

   if (  options::nl_return_expr() != IARF_IGNORE
      && !pc->flags.test(PCF_IN_PREPROC))
   {
      newline_iarf(pc, options::nl_return_expr());
   }

   if (chunk_is_token(next, CT_PAREN_OPEN))
   {
      // See if the return is fully paren'd
      cpar = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);

      if (cpar == nullptr)
      {
         return(nullptr);
      }
      semi = chunk_ppa_get_next_ncnl(cpar);

      if (semi == nullptr)
      {
         return(nullptr);
      }

      if (chunk_is_token(semi, CT_NEWLINE) || chunk_is_semicolon(semi))
      {
         log_rule_B("mod_paren_on_return");

         if (options::mod_paren_on_return() == IARF_REMOVE)
         {
            LOG_FMT(LRETURN, "%s(%d): removing parens on orig_line %zu\n",
                    __func__, __LINE__, pc->orig_line);

            // lower the level of everything
            for (temp = next; temp != cpar; temp = chunk_get_next(temp))
            {
               if (temp->level == 0)
               {
                  fprintf(stderr, "%s(%d): temp->level is ZERO, cannot be decremented, at line %zu, column %zu\n",
                          __func__, __LINE__, temp->orig_line, temp->orig_col);
                  log_flush(true);
                  exit(EX_SOFTWARE);
               }
               temp->level--;
            }

            // delete the parenthesis
            chunk_del(next);
            chunk_del(cpar);

            // back up following chunks
            temp = semi;

            while (temp != nullptr && temp->type != CT_NEWLINE)
            {
               temp->column       = temp->column - 2;
               temp->orig_col     = temp->orig_col - 2;
               temp->orig_col_end = temp->orig_col_end - 2;
               temp               = chunk_get_next(temp);
            }
         }
         else
         {
            LOG_FMT(LRETURN, "%s(%d): keeping parens on orig_line %zu\n",
                    __func__, __LINE__, 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?
   log_rule_B("mod_paren_on_return");

   if (!(options::mod_paren_on_return() & IARF_ADD))
   {
      return(next);
   }

   // Issue #1917
   // Never add parens to a braced init list; that breaks the code
   //   return {args...};    // C++11 type elision; okay
   //   return ({args...});  // ill-formed
   if (  language_is_set(LANG_CPP) && chunk_is_token(next, CT_BRACE_OPEN)
      && get_chunk_parent_type(next) == CT_BRACED_INIT_LIST)
   {
      LOG_FMT(LRETURN, "%s(%d): not adding parens around braced initializer"
              " on orig_line %zd\n",
              __func__, __LINE__, pc->orig_line);
      return(next);
   }
   // find the next semicolon on the same level
   semi = next;

   if (pc->flags.test(PCF_IN_PREPROC))
   {
      while ((semi = semi->next) != nullptr)
      {
         if (!semi->flags.test(PCF_IN_PREPROC))
         {
            break;
         }

         if (semi->level < pc->level)
         {
            return(semi);
         }

         if (chunk_is_semicolon(semi) && pc->level == semi->level)
         {
            break;
         }
      }
   }
   else
   {
      while ((semi = chunk_get_next(semi)) != nullptr)
      {
         if (semi->level < pc->level)
         {
            return(semi);
         }

         if (chunk_is_semicolon(semi) && pc->level == semi->level)
         {
            break;
         }
      }
   }

   if (semi)
   {
      // add the parenthesis
      set_chunk_type(&chunk, CT_PAREN_OPEN);
      set_chunk_parent(&chunk, CT_RETURN);
      chunk.str         = "(";
      chunk.level       = pc->level;
      chunk.brace_level = pc->brace_level;
      chunk.orig_line   = pc->orig_line;
      chunk.orig_col    = next->orig_col - 1;
      chunk.flags       = pc->flags & PCF_COPY_FLAGS;
      chunk_add_before(&chunk, next);

      set_chunk_type(&chunk, CT_PAREN_CLOSE);
      chunk.str       = ")";
      chunk.orig_line = semi->orig_line;
      chunk.orig_col  = semi->orig_col - 1;
      cpar            = chunk_add_before(&chunk, semi);

      LOG_FMT(LRETURN, "%s(%d): added parens on orig_line %zu\n",
              __func__, __LINE__, pc->orig_line);

      for (temp = next; temp != cpar; temp = chunk_get_next(temp))
      {
         temp->level++;
      }
   }
   return(semi);
} // process_return


static bool is_ucase_str(const char *str, size_t len)
{
   while (len-- > 0)
   {
      if (unc_toupper(*str) != *str)
      {
         return(false);
      }
      str++;
   }
   return(true);
}


static bool is_oc_block(chunk_t *pc)
{
   return(  pc != nullptr
         && (  get_chunk_parent_type(pc) == CT_OC_BLOCK_TYPE
            || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR
            || get_chunk_parent_type(pc) == CT_OC_BLOCK_ARG
            || get_chunk_parent_type(pc) == CT_OC_BLOCK
            || chunk_is_token(pc, CT_OC_BLOCK_CARET)
            || (pc->next != nullptr && pc->next->type == CT_OC_BLOCK_CARET)
            || (pc->prev != nullptr && pc->prev->type == CT_OC_BLOCK_CARET)));
}


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 = nullptr;
   chunk_t    *paren_close;
   const char *verb      = "likely";
   const char *detail    = "";
   size_t     count      = 0;
   int        word_count = 0;
   bool       nope;
   bool       doubtful_cast = false;


   LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, start->text(), start->orig_line, start->orig_col);

   prev = chunk_get_prev_ncnlni(start);   // Issue #2279

   if (prev == nullptr)
   {
      return;
   }

   if (chunk_is_token(prev, CT_PP_DEFINED))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after defined\n",
              __func__, __LINE__);
      return;
   }

   if (chunk_is_token(prev, CT_ANGLE_CLOSE))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after > (template)\n",
              __func__, __LINE__);
      return;
   }
   // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren
   pc    = chunk_get_next_ncnl(start);
   first = pc;

   while (  pc != nullptr
         && (  chunk_is_type(pc)
            || chunk_is_token(pc, CT_WORD)
            || chunk_is_token(pc, CT_QUALIFIER)
            || chunk_is_token(pc, CT_DC_MEMBER)
            || chunk_is_token(pc, CT_PP)
            || chunk_is_token(pc, CT_STAR)
            || chunk_is_token(pc, CT_QUESTION)
            || chunk_is_token(pc, CT_CARET)
            || chunk_is_token(pc, CT_TSQUARE)
            || (  (  chunk_is_token(pc, CT_ANGLE_OPEN)
                  || chunk_is_token(pc, CT_ANGLE_CLOSE))
               && language_is_set(LANG_OC | LANG_JAVA))
            || (  (  chunk_is_token(pc, CT_QUESTION)
                  || chunk_is_token(pc, CT_COMMA)
                  || chunk_is_token(pc, CT_MEMBER))
               && language_is_set(LANG_JAVA))
            || chunk_is_token(pc, CT_AMP)))
   {
      LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));

      if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER)))
      {
         word_count++;
      }
      else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP))
      {
         // might be negativ, such as with:
         // a = val + (CFoo::bar_t)7;
         word_count--;
      }
      last = pc;
      pc   = chunk_get_next_ncnl(pc);
      count++;
   }

   if (  pc == nullptr
      || pc->type != CT_PAREN_CLOSE
      || chunk_is_token(prev, CT_OC_CLASS))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast, hit type is %s\n",
              __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type));
      return;
   }

   if (word_count > 1)
   {
      LOG_FMT(LCASTS, "%s(%d):  -- too many words: %d\n",
              __func__, __LINE__, word_count);
      return;
   }
   paren_close = pc;

   // If last is a type or star/caret, we have a cast for sure
   if (  chunk_is_token(last, CT_STAR)
      || chunk_is_token(last, CT_CARET)
      || chunk_is_token(last, CT_PTR_TYPE)
      || chunk_is_token(last, CT_TYPE)
      || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA)))
   {
      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 (language_is_set(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 (chunk_is_token(after, CT_PAREN_OPEN));

      if (after == nullptr)
      {
         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - hit NULL\n",
                 __func__, __LINE__);
         return;
      }
      nope = false;

      if (chunk_is_ptr_operator(pc))
      {
         // star (*) and address (&) are ambiguous
         if (  chunk_is_token(after, CT_NUMBER_FP)
            || chunk_is_token(after, CT_NUMBER)
            || chunk_is_token(after, CT_STRING)
            || doubtful_cast)
         {
            nope = true;
         }
      }
      else if (chunk_is_token(pc, CT_MINUS))
      {
         // (UINT8)-1 or (foo)-1 or (FOO)-'a'
         if (chunk_is_token(after, CT_STRING) || doubtful_cast)
         {
            nope = true;
         }
      }
      else if (chunk_is_token(pc, 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_THIS
              && pc->type != CT_TYPE
              && pc->type != CT_PAREN_OPEN
              && pc->type != CT_STRING
              && pc->type != CT_DECLTYPE
              && pc->type != CT_SIZEOF
              && get_chunk_parent_type(pc) != CT_SIZEOF
              && pc->type != CT_FUNC_CALL
              && pc->type != CT_FUNC_CALL_USER
              && pc->type != CT_FUNCTION
              && pc->type != CT_BRACE_OPEN
              && (!(  chunk_is_token(pc, CT_SQUARE_OPEN)
                   && language_is_set(LANG_OC))))
      {
         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by text() '%s', type is %s\n",
                 __func__, __LINE__, pc->text(), get_token_name(pc->type));
         return;
      }

      if (nope)
      {
         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - text() '%s' followed by type %s\n",
                 __func__, __LINE__, pc->text(), get_token_name(after->type));
         return;
      }
   }
   // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't
   pc = chunk_get_next_ncnl(paren_close);

   if (pc == nullptr)
   {
      return;
   }

   if (  chunk_is_semicolon(pc)
      || chunk_is_token(pc, CT_COMMA)
      || chunk_is_token(pc, CT_BOOL)               // Issue #2151
      || chunk_is_paren_close(pc))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by type %s\n",
              __func__, __LINE__, 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(%d):  -- %s c-cast: (",
           __func__, __LINE__, verb);

   for (pc = first;
        pc != nullptr && pc != paren_close;
        pc = chunk_get_next_ncnl(pc))
   {
      set_chunk_parent(pc, CT_C_CAST);
      make_type(pc);
      LOG_FMT(LCASTS, " %s", pc->text());
   }

   LOG_FMT(LCASTS, " )%s\n", detail);

   // Mark the next item as an expression start
   pc = chunk_get_next_ncnl(paren_close);

   if (pc != nullptr)
   {
      chunk_flags_set(pc, PCF_EXPR_START);

      if (chunk_is_opening_brace(pc))
      {
         set_paren_parent(pc, get_chunk_parent_type(start));
      }
   }
} // fix_casts


static void fix_type_cast(chunk_t *start)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc;

   pc = chunk_get_next_ncnl(start);

   if (pc == nullptr || pc->type != CT_ANGLE_OPEN)
   {
      return;
   }

   while (  ((pc = chunk_get_next_ncnl(pc)) != nullptr)
         && pc->level >= start->level)
   {
      if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE))
      {
         pc = chunk_get_next_ncnl(pc);

         if (pc == nullptr)
         {
            return;
         }

         if (chunk_is_str(pc, "(", 1))
         {
            set_paren_parent(pc, CT_TYPE_CAST);
         }
         return;
      }
      make_type(pc);
   }
}


static void fix_enum_struct_union(chunk_t *pc)
{
   LOG_FUNC_ENTRY();
   chunk_t     *next;
   chunk_t     *prev        = nullptr;
   pcf_flags_t flags        = PCF_VAR_1ST_DEF;
   auto const  in_fcn_paren = pc->flags & PCF_IN_FCN_DEF;

   // Make sure this wasn't a cast
   if (get_chunk_parent_type(pc) == CT_C_CAST)
   {
      return;
   }
   // the next item is either a type or open brace
   next = chunk_get_next_ncnl(pc);

   // the enum-key might be enum, enum class or enum struct (TODO)
   if (chunk_is_token(next, CT_ENUM_CLASS))
   {
      next = chunk_get_next_ncnl(next); // get the next one
   }

   if (language_is_set(LANG_CPP))
   {
      next = skip_attribute_next(next); // get the next one
   }

   // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace
   if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON))
   {
      // i.e. "enum xyz : unsigned int { ... };"
      // i.e. "enum class xyz : unsigned int { ... };"
      // i.e. "enum : unsigned int { ... };"
      // xyz is a type

      // save the type if it exists
      if (!chunk_is_token(next, CT_COLON))
      {
         set_chunk_parent(next, pc->type);
         prev = next;
         next = chunk_get_next_ncnl(next);
      }

      if (next == nullptr)
      {
         return;
      }
      set_chunk_parent(next, pc->type);
      auto const is_struct_or_class =
         (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS));

      // next up is either a colon, open brace, or open parenthesis (pawn)
      if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN))
      {
         next = set_paren_parent(next, CT_ENUM);
      }
      else if (chunk_is_token(next, CT_COLON))
      {
         if (chunk_is_token(pc, CT_ENUM))
         {
            // enum TYPE : INT_TYPE { ... };
            next = chunk_get_next_ncnl(next);

            if (next != nullptr)
            {
               make_type(next);
               next = chunk_get_next_ncnl(next);

               // enum TYPE : unsigned int { ... };
               if (chunk_is_token(next, CT_TYPE))
               {
                  // get the next part of the type
                  next = chunk_get_next_ncnl(next);
               }
            }
         }
         else if (is_struct_or_class)
         {
            next = skip_parent_types(next);
         }
      }
      else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN))
      {
         // Fix #1267 structure attributes
         // struct __attribute__(align(x)) struct_name;
         // skip to matching parenclose and make next token as type.
         next = chunk_skip_to_match(next);
         next = chunk_get_next_ncnl(next);
         set_chunk_type(next, CT_TYPE);
         set_chunk_parent(next, pc->type);
      }

      if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration
      {
         set_chunk_parent(next, pc->type);
         flag_series(pc, prev, PCF_INCOMPLETE);
         return;
      }
   }

   if (chunk_is_token(next, CT_BRACE_OPEN))
   {
      auto const flag = [pc] {
         switch (pc->type)
         {
         case CT_ENUM:
            return(PCF_IN_ENUM);

         case CT_STRUCT:
            return(PCF_IN_STRUCT);

         case CT_CLASS:
            return(PCF_IN_CLASS);

         default:
            return(PCF_NONE);
         }
      }();

      flag_parens(next, flag, CT_NONE, CT_NONE, false);

      if (  chunk_is_token(pc, CT_UNION)
         || chunk_is_token(pc, CT_STRUCT)
         || chunk_is_token(pc, CT_CLASS))
      {
         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 != nullptr)
      {
         set_chunk_parent(next, pc->type);
         next = chunk_get_next_ncnl(next);
      }
      prev = nullptr;
   }
   // reset var name parent type
   else if (next && prev)
   {
      set_chunk_parent(prev, CT_NONE);
   }

   if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE))
   {
      return;
   }

   if (!chunk_is_semicolon(next))
   {
      // Pawn does not require a semicolon after an enum
      if (language_is_set(LANG_PAWN))
      {
         return;
      }

      /*
       * D does not require a semicolon after an enum, but we add one to make
       * other code happy.
       */
      if (language_is_set(LANG_D))
      {
         next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next));   // Issue #2279
      }
   }

   // We are either pointing to a ';' or a variable
   while (  next != nullptr
         && !chunk_is_semicolon(next)
         && next->type != CT_ASSIGN
         && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any())
   {
      if (next->level == pc->level)
      {
         if (chunk_is_token(next, CT_WORD))
         {
            chunk_flags_set(next, flags);
            flags &= ~PCF_VAR_1ST;   // clear the first flag for the next items
            LOG_FMT(LCASTS, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         }

         if (  chunk_is_token(next, CT_STAR)
            || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET)))
         {
            set_chunk_type(next, CT_PTR_TYPE);
         }

         // If we hit a comma in a function param, we are done
         if (  (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE))
            && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL)))
         {
            return;
         }
      }
      next = chunk_get_next_ncnl(next);
   }

   if (  next != nullptr
      && chunk_is_token(next, CT_SEMICOLON))
   {
      set_chunk_parent(next, pc->type);
   }
} // fix_enum_struct_union


static void fix_typedef(chunk_t *start)
{
   LOG_FUNC_ENTRY();

   if (start == nullptr)
   {
      return;
   }
   LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n",
           __func__, __LINE__, start->orig_line, start->orig_col);

   chunk_t *the_type = nullptr;
   chunk_t *last_op  = nullptr;

   /*
    * Mark everything in the typedef and scan for ")(", which makes it a
    * function type
    */
   for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC)
        ; next != nullptr && next->level >= start->level
        ; next = chunk_get_next_ncnl(next, scope_e::PREPROC))
   {
      chunk_flags_set(next, PCF_IN_TYPEDEF);

      if (start->level == next->level)
      {
         if (chunk_is_semicolon(next))
         {
            set_chunk_parent(next, CT_TYPEDEF);
            break;
         }

         if (chunk_is_token(next, CT_ATTRIBUTE))
         {
            break;
         }

         if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN))
         {
            set_chunk_parent(next, CT_TYPEDEF);
            break;
         }
         make_type(next);

         if (chunk_is_token(next, CT_TYPE))
         {
            the_type = next;
         }
         chunk_flags_clr(next, PCF_VAR_1ST_DEF);

         if (*next->str.c_str() == '(')
         {
            last_op = next;
         }
      }
   }

   // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def
   if (  last_op != nullptr
      && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM))
   {
      flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false);
      fix_fcn_def_params(last_op);

      the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC);   // Issue #2279

      if (the_type == nullptr)
      {
         return;
      }
      chunk_t *open_paren = nullptr;

      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_ncnlni(the_type, scope_e::PREPROC);   // Issue #2279

         if (the_type == nullptr)
         {
            return;
         }
      }
      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(%d): fcn typedef text() '%s', on orig_line %zu\n",
              __func__, __LINE__, the_type->text(), the_type->orig_line);

      // If we are aligning on the open parenthesis, grab that instead
      log_rule_B("align_typedef_func");

      if (open_paren != nullptr && options::align_typedef_func() == 1)
      {
         the_type = open_paren;
      }
      log_rule_B("align_typedef_func");

      if (options::align_typedef_func() != 0)
      {
         LOG_FMT(LTYPEDEF, "%s(%d):  -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n",
                 __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col);
         chunk_flags_set(the_type, 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
    */
   chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC);

   if (after == nullptr)
   {
      return;
   }

   if (  after->type != CT_ENUM
      && after->type != CT_STRUCT
      && after->type != CT_UNION)
   {
      if (the_type != nullptr)
      {
         // We have just a regular typedef
         LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n",
                 __func__, __LINE__, the_type->text(), the_type->orig_line);
         chunk_flags_set(the_type, PCF_ANCHOR);
      }
      return;
   }
   // We have a struct/union/enum, next should be either a type or {
   chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC);

   if (next == nullptr)
   {
      return;
   }

   if (chunk_is_token(next, CT_TYPE))
   {
      next = chunk_get_next_ncnl(next, scope_e::PREPROC);

      if (next == nullptr)
      {
         return;
      }
   }

   if (chunk_is_token(next, CT_BRACE_OPEN))
   {
      // Skip to the closing brace
      chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC);

      if (br_c != nullptr)
      {
         const c_token_t tag = after->type;
         set_chunk_parent(next, tag);
         set_chunk_parent(br_c, tag);

         if (tag == CT_ENUM)
         {
            flag_series(after, br_c, PCF_IN_ENUM);
         }
         else if (tag == CT_STRUCT)
         {
            flag_series(after, br_c, PCF_IN_STRUCT);
         }
      }
   }

   if (the_type != nullptr)
   {
      LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n",
              __func__, __LINE__, get_token_name(after->type), the_type->text(),
              the_type->orig_line);
      chunk_flags_set(the_type, PCF_ANCHOR);
   }
} // fix_typedef


//static void mark_variable_stack(ChunkStack &cs, log_sev_t sev)
//{
//   UNUSED(sev);
//   LOG_FUNC_ENTRY();
//
//   // throw out the last word and mark the rest
//   chunk_t *var_name = cs.Pop_Back();
//
//   if (var_name && var_name->prev->type == CT_DC_MEMBER)
//   {
//      cs.Push_Back(var_name);
//   }
//
//   if (var_name != nullptr)
//   {
//      LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n",
//              __func__, __LINE__, var_name->orig_line, var_name->orig_col);
//
//      size_t  word_cnt = 0;
//      chunk_t *word_type;
//
//      while ((word_type = cs.Pop_Back()) != nullptr)
//      {
//         if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE))
//         {
//            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
//                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text());
//            set_chunk_type(word_type, CT_TYPE);
//            chunk_flags_set(word_type, PCF_VAR_TYPE);
//         }
//         word_cnt++;
//      }
//
//      if (chunk_is_token(var_name, CT_WORD))
//      {
//         if (word_cnt > 0)
//         {
//            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n",
//                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
//            chunk_flags_set(var_name, PCF_VAR_DEF);
//         }
//         else
//         {
//            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
//                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
//            set_chunk_type(var_name, CT_TYPE);
//            chunk_flags_set(var_name, PCF_VAR_TYPE);
//         }
//      }
//   }
//} // mark_variable_stack


//static void fix_fcn_def_params(chunk_t *start)
//{
//   LOG_FUNC_ENTRY();
//
//   if (start == nullptr)
//   {
//      return;
//   }
//   LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n",
//           __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level);
//
//   while (start != nullptr && !chunk_is_paren_open(start))
//   {
//      start = chunk_get_next_ncnl(start);
//   }
//
//   if (start == nullptr)// Coverity CID 76003, 1100782
//   {
//      return;
//   }
//   // ensure start chunk holds a single '(' character
//   assert((start->len() == 1) && (start->str[0] == '('));
//
//   ChunkStack cs;
//   size_t     level = start->level + 1;
//   chunk_t    *pc   = start;
//
//   while ((pc = chunk_get_next_ncnl(pc)) != nullptr)
//   {
//      if (  ((start->len() == 1) && (start->str[0] == ')'))
//         || pc->level < level)
//      {
//         LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n",
//                 __func__, __LINE__, pc->text(), pc->orig_line);
//         break;
//      }
//      LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n",
//              __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at",
//              pc->text(), pc->orig_line, pc->level);
//
//      if (pc->level > level)
//      {
//         continue;
//      }
//
//      if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc))
//      {
//         set_chunk_type(pc, CT_PTR_TYPE);
//         cs.Push_Back(pc);
//      }
//      else if (  chunk_is_token(pc, CT_AMP)
//              || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2)))
//      {
//         set_chunk_type(pc, CT_BYREF);
//         cs.Push_Back(pc);
//      }
//      else if (chunk_is_token(pc, CT_TYPE_WRAP))
//      {
//         cs.Push_Back(pc);
//      }
//      else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))
//      {
//         cs.Push_Back(pc);
//      }
//      else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN))
//      {
//         mark_variable_stack(cs, LFCNP);
//
//         if (chunk_is_token(pc, CT_ASSIGN))
//         {
//            // Mark assignment for default param spacing
//            set_chunk_parent(pc, CT_FUNC_PROTO);
//         }
//      }
//   }
//   mark_variable_stack(cs, LFCNP);
//} // fix_fcn_def_params


static void handle_cpp_template(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   chunk_t *tmp = chunk_get_next_ncnl(pc);

   if (tmp->type != CT_ANGLE_OPEN)
   {
      return;
   }
   set_chunk_parent(tmp, CT_TEMPLATE);

   size_t level = tmp->level;

   while ((tmp = chunk_get_next(tmp)) != nullptr)
   {
      if (chunk_is_token(tmp, CT_CLASS) || chunk_is_token(tmp, CT_STRUCT))
      {
         set_chunk_type(tmp, CT_TYPE);
      }
      else if (chunk_is_token(tmp, CT_ANGLE_CLOSE) && tmp->level == level)
      {
         set_chunk_parent(tmp, CT_TEMPLATE);
         break;
      }
   }

   if (tmp != nullptr)
   {
      tmp = chunk_get_next_ncnl(tmp);

      if (chunk_is_token(tmp, CT_CLASS) || chunk_is_token(tmp, CT_STRUCT))
      {
         set_chunk_parent(tmp, CT_TEMPLATE);

         // REVISIT: This may be a bit risky - might need to track the { };
         tmp = chunk_get_next_type(tmp, CT_SEMICOLON, tmp->level);

         if (tmp != nullptr)
         {
            set_chunk_parent(tmp, CT_TEMPLATE);
         }
      }
   }
} // handle_cpp_template


static void handle_cpp_lambda(chunk_t *sq_o)
{
   LOG_FUNC_ENTRY();

   chunk_t *ret = nullptr;

   // abort if type of the previous token is not contained in this whitelist
   chunk_t *prev = chunk_get_prev_ncnlni(sq_o);   // Issue #2279

   if (  prev == nullptr
      || (  prev->type != CT_ASSIGN
         && prev->type != CT_COMMA
         && prev->type != CT_PAREN_OPEN   // allow Js like self invoking lambda syntax: ([](){})();
         && prev->type != CT_FPAREN_OPEN
         && prev->type != CT_SQUARE_OPEN
         && prev->type != CT_BRACE_OPEN
         && prev->type != CT_SEMICOLON
         && prev->type != CT_RETURN))
   {
      return;
   }
   chunk_t *sq_c = sq_o; // assuming '[]'

   if (chunk_is_token(sq_o, CT_SQUARE_OPEN))
   {
      // make sure there is a ']'
      sq_c = chunk_skip_to_match(sq_o);

      if (!sq_c)
      {
         return;
      }
   }
   chunk_t *pa_o = chunk_get_next_ncnl(sq_c);

   // check to see if there is a lambda-specifier in the pa_o chunk;
   // assuming chunk is CT_EXECUTION_CONTEXT, ignore lambda-specifier
   while (pa_o->type == CT_EXECUTION_CONTEXT)
   {
      // set pa_o to next chunk after this specifier
      pa_o = chunk_get_next_ncnl(pa_o);
   }

   if (pa_o == nullptr)
   {
      return;
   }
   chunk_t *pa_c = nullptr;

   // lambda-declarator '( params )' is optional
   if (chunk_is_token(pa_o, CT_PAREN_OPEN))
   {
      // and now find the ')'
      pa_c = chunk_skip_to_match(pa_o);

      if (pa_c == nullptr)
      {
         return;
      }
   }
   // Check for 'mutable' keyword: '[]() mutable {}' or []() mutable -> ret {}
   chunk_t *br_o = pa_c ? chunk_get_next_ncnl(pa_c) : pa_o;

   if (chunk_is_str(br_o, "mutable", 7))
   {
      br_o = chunk_get_next_ncnl(br_o);
   }
   //TODO: also check for exception and attribute between [] ... {}

   // skip possible arrow syntax: '-> ret'
   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 == nullptr
      || br_o->type != CT_BRACE_OPEN)
   {
      return;
   }
   // and now find the '}'
   chunk_t *br_c = chunk_skip_to_match(br_o);

   if (br_c == nullptr)
   {
      return;
   }

   // This looks like a lambda expression
   if (chunk_is_token(sq_o, 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);
      /*
       * bug # 664
       *
       * The original orig_col of CT_SQUARE_CLOSE is stored at orig_col_end
       * of CT_TSQUARE. CT_SQUARE_CLOSE orig_col and orig_col_end values
       * are calculate from orig_col_end of CT_TSQUARE.
       */
      nc.orig_col        = sq_o->orig_col_end - 1;
      nc.column          = static_cast<int>(nc.orig_col);
      nc.orig_col_end    = sq_o->orig_col_end;
      sq_o->orig_col_end = sq_o->orig_col + 1;

      set_chunk_type(&nc, CT_SQUARE_CLOSE);
      nc.str.pop_front();
      sq_c = chunk_add_after(&nc, sq_o);
   }
   set_chunk_parent(sq_o, CT_CPP_LAMBDA);
   set_chunk_parent(sq_c, CT_CPP_LAMBDA);

   if (pa_c != nullptr)
   {
      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 != nullptr)
   {
      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);
      }
   }

   if (pa_c != nullptr)
   {
      fix_fcn_def_params(pa_o);
   }
   //handle self calling lambda paren
   chunk_t *call_pa_o = chunk_get_next_ncnl(br_c);

   if (chunk_is_token(call_pa_o, CT_PAREN_OPEN))
   {
      chunk_t *call_pa_c = chunk_skip_to_match(call_pa_o);

      if (call_pa_c != nullptr)
      {
         set_chunk_type(call_pa_o, CT_FPAREN_OPEN);
         set_chunk_parent(call_pa_o, CT_FUNC_CALL);
         set_chunk_type(call_pa_c, CT_FPAREN_CLOSE);
         set_chunk_parent(call_pa_c, CT_FUNC_CALL);
      }
   }
} // handle_cpp_lambda


static void handle_d_template(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   chunk_t *name = chunk_get_next_ncnl(pc);
   chunk_t *po   = chunk_get_next_ncnl(name);

   //if (!name || (name->type != CT_WORD && name->type != CT_WORD))  Coverity CID 76000 Same on both sides, 2016-03-16
   if (!name || name->type != CT_WORD)
   {
      // TODO: log an error, expected NAME
      return;
   }

   if (  po == nullptr
      || 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;
   chunk_t    *tmp = get_d_template_types(cs, po);

   if (  tmp == nullptr
      || 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)) != nullptr)
         && tmp->level > po->level)
   {
      if (chunk_is_token(tmp, CT_WORD) && chunkstack_match(cs, tmp))
      {
         set_chunk_type(tmp, CT_TYPE);
      }
   }
//   if (!chunk_is_token(tmp, CT_BRACE_CLOSE))
//   {
//      // TODO: log an error, expected '}'
//   }
   set_chunk_parent(tmp, CT_TEMPLATE);
} // handle_d_template


chunk_t *skip_template_next(chunk_t *ang_open)
{
   if (chunk_is_token(ang_open, CT_ANGLE_OPEN))
   {
      chunk_t *pc = chunk_get_next_type(ang_open, CT_ANGLE_CLOSE, ang_open->level);
      return(chunk_get_next_ncnl(pc));
   }
   return(ang_open);
}


static void handle_oc_class(chunk_t *pc)
{
   enum class angle_state_e : unsigned int
   {
      NONE  = 0,
      OPEN  = 1, // '<' found
      CLOSE = 2, // '>' found
   };

   LOG_FUNC_ENTRY();
   chunk_t       *tmp;
   bool          hit_scope     = false;
   bool          passed_name   = false; // Did we pass the name of the class and now there can be only protocols, not generics
   int           generic_level = 0;     // level of depth of generic
   angle_state_e as            = angle_state_e::NONE;

   LOG_FMT(LOCCLASS, "%s(%d): start [%s] [%s] line %zu\n",
           __func__, __LINE__, pc->text(), get_token_name(get_chunk_parent_type(pc)), pc->orig_line);

   if (get_chunk_parent_type(pc) == CT_OC_PROTOCOL)
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_semicolon(tmp))
      {
         set_chunk_parent(tmp, get_chunk_parent_type(pc));
         LOG_FMT(LOCCLASS, "%s(%d):   bail on semicolon\n", __func__, __LINE__);
         return;
      }
   }
   tmp = pc;

   while ((tmp = chunk_get_next_nnl(tmp)) != nullptr)
   {
      LOG_FMT(LOCCLASS, "%s(%d):       orig_line is %zu, [%s]\n",
              __func__, __LINE__, tmp->orig_line, tmp->text());

      if (chunk_is_token(tmp, CT_OC_END))
      {
         break;
      }

      if (chunk_is_token(tmp, CT_PAREN_OPEN))
      {
         passed_name = true;
      }

      if (chunk_is_str(tmp, "<", 1))
      {
         set_chunk_type(tmp, CT_ANGLE_OPEN);

         if (passed_name)
         {
            set_chunk_parent(tmp, CT_OC_PROTO_LIST);
         }
         else
         {
            set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);
            generic_level++;
         }
         as = angle_state_e::OPEN;
      }

      if (chunk_is_str(tmp, ">", 1))
      {
         set_chunk_type(tmp, CT_ANGLE_CLOSE);

         if (passed_name)
         {
            set_chunk_parent(tmp, CT_OC_PROTO_LIST);
            as = angle_state_e::CLOSE;
         }
         else
         {
            set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);

            if (generic_level == 0)
            {
               fprintf(stderr, "%s(%d): generic_level is ZERO, cannot be decremented, at line %zu, column %zu\n",
                       __func__, __LINE__, tmp->orig_line, tmp->orig_col);
               log_flush(true);
               exit(EX_SOFTWARE);
            }
            generic_level--;

            if (generic_level == 0)
            {
               as = angle_state_e::CLOSE;
            }
         }
      }

      if (chunk_is_str(tmp, ">>", 2))
      {
         set_chunk_type(tmp, CT_ANGLE_CLOSE);
         set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);
         split_off_angle_close(tmp);
         generic_level -= 1;

         if (generic_level == 0)
         {
            as = angle_state_e::CLOSE;
         }
      }

      if (  chunk_is_token(tmp, CT_BRACE_OPEN)
         && get_chunk_parent_type(tmp) != CT_ASSIGN)
      {
         as = angle_state_e::CLOSE;
         set_chunk_parent(tmp, CT_OC_CLASS);
         tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);

         if (  tmp != nullptr
            && get_chunk_parent_type(tmp) != CT_ASSIGN)
         {
            set_chunk_parent(tmp, CT_OC_CLASS);
         }
      }
      else if (chunk_is_token(tmp, CT_COLON))
      {
         if (as != angle_state_e::OPEN)
         {
            passed_name = true;
         }
         set_chunk_type(tmp, hit_scope ? CT_OC_COLON : CT_CLASS_COLON);

         if (chunk_is_token(tmp, CT_CLASS_COLON))
         {
            set_chunk_parent(tmp, CT_OC_CLASS);
         }
      }
      else if (chunk_is_str(tmp, "-", 1) || chunk_is_str(tmp, "+", 1))
      {
         as = angle_state_e::CLOSE;

         if (chunk_is_newline(chunk_get_prev(tmp)))
         {
            set_chunk_type(tmp, CT_OC_SCOPE);
            chunk_flags_set(tmp, PCF_STMT_START);
            hit_scope = true;
         }
      }

      if (as == angle_state_e::OPEN)
      {
         if (passed_name)
         {
            set_chunk_parent(tmp, CT_OC_PROTO_LIST);
         }
         else
         {
            set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);
         }
      }
   }

   if (chunk_is_token(tmp, CT_BRACE_OPEN))
   {
      tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);

      if (tmp != nullptr)
      {
         set_chunk_parent(tmp, CT_OC_CLASS);
      }
   }
} // handle_oc_class


static void handle_oc_block_literal(chunk_t *pc)
{
   LOG_FUNC_ENTRY();
   chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279
   chunk_t *next = chunk_get_next_ncnl(pc);

   if (  pc == nullptr
      || prev == nullptr
      || next == nullptr)
   {
      return; // let's be paranoid
   }
   /*
    * block literal: '^ RTYPE ( ARGS ) { }'
    * RTYPE and ARGS are optional
    */
   LOG_FMT(LOCBLK, "%s(%d): block literal @ orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col);

   chunk_t *apo = nullptr; // arg paren open
   chunk_t *bbo = nullptr; // block brace open
   chunk_t *bbc;           // block brace close

   LOG_FMT(LOCBLK, "%s(%d):  + scan", __func__, __LINE__);
   chunk_t *tmp;

   for (tmp = next; tmp; tmp = chunk_get_next_ncnl(tmp))
   {
      /* handle '< protocol >' */
      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);
      }
      LOG_FMT(LOCBLK, " '%s'", tmp->text());

      if (tmp->level < pc->level || chunk_is_token(tmp, 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 (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            LOG_FMT(LOCBLK, "[BRACE]");
            bbo = tmp;
            break;
         }
      }
   }

   // make sure we have braces
   bbc = chunk_skip_to_match(bbo);

   if (  bbo == nullptr
      || bbc == nullptr)
   {
      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)
   {
      chunk_t *apc = chunk_skip_to_match(apo);  // arg parenthesis close

      if (chunk_is_paren_close(apc))
      {
         LOG_FMT(LOCBLK, " -- marking parens @ apo->orig_line is %zu, apo->orig_col is %zu and apc->orig_line is %zu, apc->orig_col is %zu\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_ncnlni(apo);   // Issue #2279
   }
   else
   {
      lbp = chunk_get_prev_ncnlni(bbo);   // Issue #2279
   }

   // 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);
      chunk_flags_set(lbp, PCF_OC_RTYPE);
      set_chunk_parent(lbp, CT_OC_BLOCK_EXPR);
      lbp = chunk_get_prev_ncnlni(lbp);   // Issue #2279
   }
   // mark the braces
   set_chunk_parent(bbo, CT_OC_BLOCK_EXPR);
   set_chunk_parent(bbc, CT_OC_BLOCK_EXPR);
} // handle_oc_block_literal


static void handle_oc_block_type(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   if (pc == nullptr)
   {
      return;
   }

   if (pc->flags.test(PCF_IN_TYPEDEF))
   {
      LOG_FMT(LOCBLK, "%s(%d): skip block type @ orig_line is %zu, orig_col is %zu, -- in typedef\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col);
      return;
   }
   // make sure we have '( ^'
   chunk_t *tpo = chunk_get_prev_ncnlni(pc); // type paren open   Issue #2279

   if (chunk_is_paren_open(tpo))
   {
      /*
       * block type: 'RTYPE (^LABEL)(ARGS)'
       * LABEL is optional.
       */
      chunk_t *tpc = chunk_skip_to_match(tpo);   // type close paren (after '^')
      chunk_t *nam = chunk_get_prev_ncnlni(tpc); // name (if any) or '^'   Issue #2279
      chunk_t *apo = chunk_get_next_ncnl(tpc);   // arg open paren
      chunk_t *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));
      }

      // Check apo is '(' or else this might be a block literal. Issue 2643.
      if (!chunk_is_paren_open(apo))
      {
         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(%d): block type @ orig_line is %zu, orig_col is %zu, text() '%s'[%s]\n",
                 __func__, __LINE__, 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_ncnlni(tpo), pt);   // Issue #2279
      }
   }
} // handle_oc_block_type


static chunk_t *handle_oc_md_type(chunk_t *paren_open, c_token_t ptype, pcf_flags_t flags, bool &did_it)
{
   chunk_t *paren_close;

   if (  !chunk_is_paren_open(paren_open)
      || ((paren_close = chunk_skip_to_match(paren_open)) == nullptr))
   {
      did_it = false;
      return(paren_open);
   }
   did_it = true;

   set_chunk_parent(paren_open, ptype);
   chunk_flags_set(paren_open, flags);
   set_chunk_parent(paren_close, ptype);
   chunk_flags_set(paren_close, 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));
      chunk_flags_set(cur, flags);
      make_type(cur);
   }

   // returning the chunk after the paren close
   return(chunk_get_next_ncnl(paren_close));
}


static void handle_oc_message_decl(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   bool did_it;
   //bool      in_paren  = false;
   //int       paren_cnt = 0;
   //int       arg_cnt   = 0;

   // Figure out if this is a spec or decl
   chunk_t *tmp = pc;

   while ((tmp = chunk_get_next(tmp)) != nullptr)
   {
      if (tmp->level < pc->level)
      {
         // should not happen
         return;
      }

      if (chunk_is_token(tmp, CT_SEMICOLON) || chunk_is_token(tmp, CT_BRACE_OPEN))
      {
         break;
      }
   }

   if (tmp == nullptr)
   {
      return;
   }
   c_token_t 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(%d): %s @ orig_line is %zu, orig_col is %zu -",
           __func__, __LINE__, 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;
   }  // expect the method name/label

   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 (chunk_is_token(pc, CT_COLON) || chunk_is_token(pc, 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): orig_line is %zu, orig_col is %zu expected type\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col);
            break;
         }
         // attributes for a method parameter sit between the parameter type and the parameter name
         pc = skip_attribute_next(tmp);
         // we should now be on the arg name
         chunk_flags_set(pc, 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 != nullptr)
      {
         set_chunk_parent(pc, pt);
      }
   }
   else if (chunk_is_token(pc, CT_SEMICOLON))
   {
      set_chunk_parent(pc, pt);
   }
   LOG_FMT(LOCMSGD, "\n");
} // handle_oc_message_decl


static void handle_oc_message_send(chunk_t *os)
{
   LOG_FUNC_ENTRY();

   chunk_t *cs = chunk_get_next(os);

   while (cs != nullptr && cs->level > os->level)
   {
      cs = chunk_get_next(cs);
   }

   if (cs == nullptr || cs->type != CT_SQUARE_CLOSE)
   {
      return;
   }
   LOG_FMT(LOCMSG, "%s(%d): orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, os->orig_line, os->orig_col);

   chunk_t *tmp = chunk_get_next_ncnl(cs);

   if (chunk_is_semicolon(tmp))
   {
      set_chunk_parent(tmp, CT_OC_MSG);
   }
   // expect a word first thing or [...]
   tmp = chunk_get_next_ncnl(os);

   if (  chunk_is_token(tmp, CT_SQUARE_OPEN) || chunk_is_token(tmp, CT_PAREN_OPEN)
      || (chunk_is_token(tmp, CT_OC_AT)))
   {
      chunk_t *tt = chunk_get_next_ncnl(tmp);

      if ((chunk_is_token(tmp, CT_OC_AT)) && tt)
      {
         if (  (chunk_is_token(tt, CT_PAREN_OPEN))
            || (chunk_is_token(tt, CT_BRACE_OPEN))
            || (chunk_is_token(tt, CT_SQUARE_OPEN)))
         {
            tmp = tt;
         }
         else
         {
            LOG_FMT(LOCMSG, "%s(%d): tmp->orig_line is %zu, tmp->orig_col is %zu, expected identifier, not '%s' [%s]\n",
                    __func__, __LINE__, tmp->orig_line, tmp->orig_col,
                    tmp->text(), get_token_name(tmp->type));
            return;
         }
      }
      tmp = chunk_skip_to_match(tmp);
   }
   else if (  tmp->type != CT_WORD
           && tmp->type != CT_TYPE
           && tmp->type != CT_THIS
           && tmp->type != CT_STAR
           && tmp->type != CT_STRING)
   {
      LOG_FMT(LOCMSG, "%s(%d): orig_line is %zu, orig_col is %zu, expected identifier, not '%s' [%s]\n",
              __func__, __LINE__, tmp->orig_line, tmp->orig_col,
              tmp->text(), get_token_name(tmp->type));
      return;
   }
   else
   {
      if (chunk_is_star(tmp)) // Issue #2722
      {
         set_chunk_type(tmp, CT_PTR_TYPE);
         tmp = chunk_get_next_ncnl(tmp);
      }
      chunk_t *tt = chunk_get_next_ncnl(tmp);

      if (chunk_is_paren_open(tt))
      {
         LOG_FMT(LFCN, "%s(%d): (18) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
         set_chunk_type(tmp, CT_FUNC_CALL);
         tmp = chunk_get_prev_ncnlni(set_paren_parent(tt, CT_FUNC_CALL));   // Issue #2279
      }
      else
      {
         set_chunk_type(tmp, CT_OC_MSG_CLASS);
      }
   }
   set_chunk_parent(os, CT_OC_MSG);
   chunk_flags_set(os, PCF_IN_OC_MSG);
   set_chunk_parent(cs, CT_OC_MSG);
   chunk_flags_set(cs, PCF_IN_OC_MSG);

   // 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);
   }
   // handle 'object.property' and 'collection[index]'
   else
   {
      while (tmp)
      {
         if (chunk_is_token(tmp, CT_MEMBER))  // move past [object.prop1.prop2
         {
            chunk_t *typ = chunk_get_next_ncnl(tmp);

            if (chunk_is_token(typ, CT_WORD) || chunk_is_token(typ, CT_TYPE))
            {
               tmp = chunk_get_next_ncnl(typ);
            }
            else
            {
               break;
            }
         }
         else if (chunk_is_token(tmp, CT_SQUARE_OPEN))  // move past [collection[index]
         {
            chunk_t *tcs = chunk_get_next_ncnl(tmp);

            while (tcs != nullptr && tcs->level > tmp->level)
            {
               tcs = chunk_get_next_ncnl(tcs);
            }

            if (chunk_is_token(tcs, CT_SQUARE_CLOSE))
            {
               tmp = chunk_get_next_ncnl(tcs);
            }
            else
            {
               break;
            }
         }
         else
         {
            break;
         }
      }
   }

   // [(self.foo.bar) method]
   if (chunk_is_paren_open(tmp))
   {
      tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp));
   }

   if (chunk_is_token(tmp, CT_WORD) || chunk_is_token(tmp, CT_TYPE))
   {
      set_chunk_type(tmp, CT_OC_MSG_FUNC);
   }
   chunk_t *prev = nullptr;

   for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp))
   {
      chunk_flags_set(tmp, PCF_IN_OC_MSG);

      if (tmp->level == cs->level + 1)
      {
         if (chunk_is_token(tmp, CT_COLON))
         {
            set_chunk_type(tmp, CT_OC_COLON);

            if (chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE))
            {
               // Might be a named param, check previous block
               chunk_t *pp = chunk_get_prev(prev);

               if (  pp != nullptr
                  && 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;
   }
} // handle_oc_message_send


static void handle_oc_available(chunk_t *os)
{
   os = chunk_get_next(os);

   while (os != nullptr)
   {
      c_token_t origType = os->type;
      set_chunk_type(os, CT_OC_AVAILABLE_VALUE);

      if (origType == CT_PAREN_CLOSE)
      {
         break;
      }
      os = chunk_get_next(os);
   }
}


static void handle_oc_property_decl(chunk_t *os)
{
   log_rule_B("mod_sort_oc_properties");

   if (options::mod_sort_oc_properties())
   {
      typedef std::vector<chunk_t *> ChunkGroup;

      chunk_t                 *next       = chunk_get_next(os);
      chunk_t                 *open_paren = nullptr;

      std::vector<ChunkGroup> class_chunks;       // class
      std::vector<ChunkGroup> thread_chunks;      // atomic, nonatomic
      std::vector<ChunkGroup> readwrite_chunks;   // readwrite, readonly
      std::vector<ChunkGroup> ref_chunks;         // retain, copy, assign, weak, strong, unsafe_unretained
      std::vector<ChunkGroup> getter_chunks;      // getter
      std::vector<ChunkGroup> setter_chunks;      // setter
      std::vector<ChunkGroup> nullability_chunks; // nonnull, nullable, null_unspecified, null_resettable
      std::vector<ChunkGroup> other_chunks;       // any words other than above

      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         open_paren = next;
         next       = chunk_get_next(next);

         /*
          * Determine location of the property attributes
          * NOTE: Did not do this in the combine.cpp do_symbol_check as
          * I was not sure what the ramifications of adding a new type
          * for each of the below types would be. It did break some items
          * when I attempted to add them so this is my hack for now.
          */
         while (next != nullptr && next->type != CT_PAREN_CLOSE)
         {
            if (chunk_is_token(next, CT_OC_PROPERTY_ATTR))
            {
               if (  chunk_is_str(next, "atomic", 6)
                  || chunk_is_str(next, "nonatomic", 9))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  thread_chunks.push_back(chunkGroup);
               }
               else if (  chunk_is_str(next, "readonly", 8)
                       || chunk_is_str(next, "readwrite", 9))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  readwrite_chunks.push_back(chunkGroup);
               }
               else if (  chunk_is_str(next, "assign", 6)
                       || chunk_is_str(next, "retain", 6)
                       || chunk_is_str(next, "copy", 4)
                       || chunk_is_str(next, "strong", 6)
                       || chunk_is_str(next, "weak", 4)
                       || chunk_is_str(next, "unsafe_unretained", 17))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  ref_chunks.push_back(chunkGroup);
               }
               else if (chunk_is_str(next, "getter", 6))
               {
                  ChunkGroup chunkGroup;

                  do
                  {
                     chunkGroup.push_back(next);
                     next = chunk_get_next(next);
                  } while (  next
                          && next->type != CT_COMMA
                          && next->type != CT_PAREN_CLOSE);

                  next = next->prev;

                  // coverity CID 160946
                  if (next == nullptr)
                  {
                     break;
                  }
                  getter_chunks.push_back(chunkGroup);
               }
               else if (chunk_is_str(next, "setter", 6))
               {
                  ChunkGroup chunkGroup;

                  do
                  {
                     chunkGroup.push_back(next);
                     next = chunk_get_next(next);
                  } while (  next
                          && next->type != CT_COMMA
                          && next->type != CT_PAREN_CLOSE);

                  next = chunk_get_prev(next);

                  if (next == nullptr)
                  {
                     break;
                  }
                  setter_chunks.push_back(chunkGroup);
               }
               else if (  chunk_is_str(next, "nullable", 8)
                       || chunk_is_str(next, "nonnull", 7)
                       || chunk_is_str(next, "null_resettable", 15)
                       || chunk_is_str(next, "null_unspecified", 16))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  nullability_chunks.push_back(chunkGroup);
               }
               else if (chunk_is_str(next, "class", 5))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  class_chunks.push_back(chunkGroup);
               }
               else
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  other_chunks.push_back(chunkGroup);
               }
            }
            else if (chunk_is_word(next))
            {
               if (chunk_is_str(next, "class", 5))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  class_chunks.push_back(chunkGroup);
               }
               else
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  other_chunks.push_back(chunkGroup);
               }
            }
            next = chunk_get_next(next);
         }
         log_rule_B("mod_sort_oc_property_class_weight");
         int class_w = options::mod_sort_oc_property_class_weight();
         log_rule_B("mod_sort_oc_property_thread_safe_weight");
         int thread_w = options::mod_sort_oc_property_thread_safe_weight();
         log_rule_B("mod_sort_oc_property_readwrite_weight");
         int readwrite_w = options::mod_sort_oc_property_readwrite_weight();
         log_rule_B("mod_sort_oc_property_reference_weight");
         int ref_w = options::mod_sort_oc_property_reference_weight();
         log_rule_B("mod_sort_oc_property_getter_weight");
         int getter_w = options::mod_sort_oc_property_getter_weight();
         log_rule_B("mod_sort_oc_property_setter_weight");
         int setter_w = options::mod_sort_oc_property_setter_weight();
         log_rule_B("mod_sort_oc_property_nullability_weight");
         int nullability_w = options::mod_sort_oc_property_nullability_weight();

         //
         std::multimap<int, std::vector<ChunkGroup> > sorted_chunk_map;
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(class_w, class_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(thread_w, thread_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(readwrite_w, readwrite_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(ref_w, ref_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(getter_w, getter_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(setter_w, setter_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(nullability_w, nullability_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(std::numeric_limits<int>::min(), other_chunks));

         chunk_t *curr_chunk = open_paren;

         for (multimap<int, std::vector<ChunkGroup> >::reverse_iterator it = sorted_chunk_map.rbegin(); it != sorted_chunk_map.rend(); ++it)
         {
            std::vector<ChunkGroup> chunk_groups = (*it).second;

            for (auto chunk_group : chunk_groups)
            {
               for (auto chunk : chunk_group)
               {
                  chunk->orig_prev_sp = 0;

                  if (chunk != curr_chunk)
                  {
                     chunk_move_after(chunk, curr_chunk);
                     curr_chunk = chunk;
                  }
                  else
                  {
                     curr_chunk = chunk_get_next(curr_chunk);
                  }
               }

               // add the parenthesis
               chunk_t endchunk;
               set_chunk_type(&endchunk, CT_COMMA);
               set_chunk_parent(&endchunk, get_chunk_parent_type(curr_chunk));
               endchunk.str         = ",";
               endchunk.level       = curr_chunk->level;
               endchunk.brace_level = curr_chunk->brace_level;
               endchunk.orig_line   = curr_chunk->orig_line;
               endchunk.orig_col    = curr_chunk->orig_col;
               endchunk.column      = curr_chunk->orig_col_end + 1;
               endchunk.flags       = curr_chunk->flags & PCF_COPY_FLAGS;
               chunk_add_after(&endchunk, curr_chunk);
               curr_chunk = curr_chunk->next;
            }
         }

         // Remove the extra comma's that we did not move
         while (curr_chunk && curr_chunk->type != CT_PAREN_CLOSE)
         {
            chunk_t *rm_chunk = curr_chunk;
            curr_chunk = chunk_get_next(curr_chunk);
            chunk_del(rm_chunk);
         }
      }
   }
   chunk_t *tmp = chunk_get_next_ncnl(os);

   if (chunk_is_paren_open(tmp))
   {
      tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp));
   }
   fix_variable_definition(tmp);
} // handle_oc_property_decl


static void handle_cs_square_stmt(chunk_t *os)
{
   LOG_FUNC_ENTRY();

   chunk_t *cs = chunk_get_next(os);

   while (cs != nullptr && cs->level > os->level)
   {
      cs = chunk_get_next(cs);
   }

   if (cs == nullptr || cs->type != CT_SQUARE_CLOSE)
   {
      return;
   }
   set_chunk_parent(os, CT_CS_SQ_STMT);
   set_chunk_parent(cs, CT_CS_SQ_STMT);

   chunk_t *tmp;

   for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp))
   {
      set_chunk_parent(tmp, CT_CS_SQ_STMT);

      if (chunk_is_token(tmp, CT_COLON))
      {
         set_chunk_type(tmp, CT_CS_SQ_COLON);
      }
   }

   tmp = chunk_get_next_ncnl(cs);

   if (tmp != nullptr)
   {
      chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
   }
}


static void handle_cs_property(chunk_t *bro)
{
   LOG_FUNC_ENTRY();

   set_paren_parent(bro, CT_CS_PROPERTY);

   bool    did_prop = false;
   chunk_t *pc      = bro;

   while ((pc = chunk_get_prev_ncnlni(pc)) != nullptr)   // Issue #2279
   {
      if (pc->level == bro->level)
      {
         //prevent scanning back past 'new' in expressions like new List<int> {1,2,3}
         // Issue # 1620, UNI-24090.cs
         if (chunk_is_token(pc, CT_NEW))
         {
            break;
         }

         if (  !did_prop
            && (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, 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.test(PCF_STMT_START))
         {
            break;
         }
      }
   }
}


static void handle_cs_array_type(chunk_t *pc)
{
   chunk_t *prev;

   for (prev = chunk_get_prev(pc);
        chunk_is_token(prev, CT_COMMA);
        prev = chunk_get_prev(prev))
   {
      // empty
   }

   if (chunk_is_token(prev, CT_SQUARE_OPEN))
   {
      while (pc != prev)
      {
         set_chunk_parent(pc, CT_TYPE);
         pc = chunk_get_prev(pc);
      }
      set_chunk_parent(prev, 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);

   log_rule_B("sp_func_call_paren");
   log_rule_B("sp_cpp_cast_paren");
   iarf_e pav = (pc->type == CT_FUNC_WRAP) ?
                options::sp_func_call_paren() :
                options::sp_cpp_cast_paren();

   log_rule_B("sp_inside_fparen");
   log_rule_B("sp_inside_paren_cast");
   iarf_e av = (pc->type == CT_FUNC_WRAP) ?
               options::sp_inside_fparen() :
               options::sp_inside_paren_cast();

   if (  chunk_is_token(clp, CT_PAREN_CLOSE)
      && chunk_is_token(opp, CT_PAREN_OPEN)
      && (chunk_is_token(name, CT_WORD) || chunk_is_token(name, CT_TYPE)))
   {
      const char *psp = (pav & IARF_ADD) ? " " : "";
      const char *fsp = (av & IARF_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);
   }
} // handle_wrap


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)
      || opp->type != CT_PAREN_OPEN)
   {
      return;
   }

   if (chunk_is_token(cma, CT_SEMICOLON))
   {
      set_chunk_type(pc, CT_FUNC_PROTO);
   }
   else if (chunk_is_token(cma, CT_BRACE_OPEN))
   {
      LOG_FMT(LFCN, "%s(%d): (19) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      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);

   if (chunk_is_token(tmp, CT_PAREN_OPEN))
   {
      fix_fcn_def_params(tmp);
   }
   else
   {
      fix_fcn_def_params(opp);
      set_chunk_type(name, CT_WORD);
   }
   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_ncnlni(tmp)) != nullptr)   // Issue #2279
   {
      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);
   }
} // handle_proto_wrap


/**
 * Java assert statements 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)) != nullptr)
   {
      if (tmp->level == pc->level)
      {
         if (!did_colon && chunk_is_token(tmp, CT_COLON))
         {
            did_colon = true;
            set_chunk_parent(tmp, pc->type);
         }

         if (chunk_is_token(tmp, CT_SEMICOLON))
         {
            set_chunk_parent(tmp, pc->type);
            break;
         }
      }
   }
}

[-- Attachment #3: combine.cpp-2020-04-16-R --]
[-- Type: text/plain, Size: 221541 bytes --]

/**
 * @file combine.cpp
 * Labels the chunks as needed.
 *
 * @author  Ben Gardner
 * @author  Guy Maurel since version 0.62 for uncrustify4Qt
 *          October 2015, 2016
 * @license GPL v2+
 */

#include "combine.h"

#include "chunk_list.h"
#include "combine_labels.h"
#include "ChunkStack.h"
#include "error_types.h"
#include "flag_parens.h"
#include "lang_pawn.h"
#include "language_tools.h"
#include "log_rules.h"
#include "newlines.h"
#include "prototypes.h"
#include "tokenize_cleanup.h"
#include "unc_ctype.h"
#include "uncrustify.h"
#include "uncrustify_types.h"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <map>

using namespace std;
using namespace uncrustify;


/**
 * Mark the parens and colons in:
 *   asm volatile ( "xx" : "xx" (l), "yy"(h) : ...  );
 *
 * @param pc  the CT_ASM item
 */
static void flag_asm(chunk_t *pc);


/**
 * Combines two tokens into {{ and }} if inside parens and nothing is between
 * either pair.
 */
static void check_double_brace_init(chunk_t *bo1);


/**
 * 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)();
 * typedef <return type>([*]func)(params);
 * typedef <return type>(__stdcall *func)(); Bug # 633    MS-specific extension
 *                                           include the config-file "test/config/MS-calling_conventions.cfg"
 * 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 *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);


/**
 * 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 *pc);


/**
 * 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 *pc);


static void process_returns(void);


/**
 * 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);


/**
 * TODO: add doc cmt
 *
 */
static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags);


/**
 * 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 *pclass);


static void mark_cpp_constructor(chunk_t *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);


/**
 * 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);


/**
 * 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);


/**
 * 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);


/**
 *  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);


/**
 * 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);


/**
 * Process an ObjC message spec/dec
 *
 * Specs:
 * -(void) foo ARGS;
 *
 * Declaration:
 * -(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);


/**
 * 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 pc  points to the open square '['
 */
static void handle_oc_message_send(chunk_t *pc);


//! Process @Property values and re-arrange them if necessary
static void handle_oc_property_decl(chunk_t *pc);

//! Process @available annotation
static void handle_oc_available(chunk_t *pc);

/**
 * Process a type that is enclosed in parens in message declarations.
 * 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, pcf_flags_t flags, bool &did_it);

/**
 * Process an C# [] thingy:
 *    [assembly: xxx]
 *    [AttributeUsage()]
 *    [@X]
 *
 * Set the next chunk to a statement start after the close ']'
 *
 * @param pc  points to the open square '['
 */
static void handle_cs_square_stmt(chunk_t *pc);


/**
 * 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 *pc);


/**
 * 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);


/**
 * 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);


/**
 * 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 *pc);


/**
 * 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);


/**
 * 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);


/**
 * A proto wrap chunk and what follows should be treated as a function proto.
 *
 * RETTYPE PROTO_WRAP( NAME, PARAMS ); or 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);


static bool is_oc_block(chunk_t *pc);


/**
 * Java assert statements are: "assert EXP1 [: EXP2] ;"
 * Mark the parent of the colon and semicolon
 */
static void handle_java_assert(chunk_t *pc);


/**
 * 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);


static bool chunkstack_match(ChunkStack &cs, chunk_t *pc);


void make_type(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   if (pc != nullptr)
   {
      if (chunk_is_token(pc, CT_WORD))
      {
         set_chunk_type(pc, CT_TYPE);
      }
      else if (  (  chunk_is_star(pc)
                 || chunk_is_msref(pc)
                 || chunk_is_nullable(pc))
              && chunk_is_type(pc->prev))                              // Issue # 2640
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (  chunk_is_addr(pc)
              && !chunk_is_token(pc->prev, CT_SQUARE_OPEN))            // Issue # 2166
      {
         set_chunk_type(pc, CT_BYREF);
      }
   }
}


void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_t clr_flags, scope_e nav)
{
   LOG_FUNC_ENTRY();

   while (start != nullptr && start != end)
   {
      chunk_flags_upd(start, clr_flags, set_flags);

      start = chunk_get_next(start, nav);

      if (start == nullptr)
      {
         return;
      }
   }

   if (end != nullptr)
   {
      chunk_flags_upd(end, clr_flags, set_flags);
   }
}


chunk_t *set_paren_parent(chunk_t *start, c_token_t parent)
{
   LOG_FUNC_ENTRY();
   chunk_t *end;

   end = chunk_skip_to_match(start, scope_e::PREPROC);

   if (end != nullptr)
   {
      LOG_FMT(LFLPAREN, "%s(%d): %zu:%zu '%s' and %zu:%zu '%s' type is %s, parent_type is %s",
              __func__, __LINE__, 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);
   }
   LOG_FMT(LFLPAREN, "%s(%d):\n", __func__, __LINE__);
   return(chunk_get_next_ncnl(end, scope_e::PREPROC));
}


static void flag_asm(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   chunk_t *tmp = chunk_get_next_ncnl(pc, scope_e::PREPROC);

   if (!chunk_is_token(tmp, CT_QUALIFIER))
   {
      return;
   }
   chunk_t *po = chunk_get_next_ncnl(tmp, scope_e::PREPROC);

   if (!chunk_is_paren_open(po))
   {
      return;
   }
   chunk_t *end = chunk_skip_to_match(po, scope_e::PREPROC);

   if (end == nullptr)
   {
      return;
   }
   set_chunk_parent(po, CT_ASM);
   set_chunk_parent(end, CT_ASM);

   for (  tmp = chunk_get_next_ncnl(po, scope_e::PREPROC);
          tmp != nullptr
       && tmp != end;
          tmp = chunk_get_next_ncnl(tmp, scope_e::PREPROC))
   {
      if (chunk_is_token(tmp, CT_COLON))
      {
         set_chunk_type(tmp, CT_ASM_COLON);
      }
      else if (chunk_is_token(tmp, CT_DC_MEMBER))
      {
         // if there is a string on both sides, then this is two ASM_COLONs
         if (  chunk_is_token(chunk_get_next_ncnl(tmp, scope_e::PREPROC), CT_STRING)
            && chunk_is_token(chunk_get_prev_ncnlni(tmp, scope_e::PREPROC), CT_STRING)) // Issue #2279
         {
            chunk_t nc;

            nc = *tmp;

            tmp->str.resize(1);
            tmp->orig_col_end = tmp->orig_col + 1;
            set_chunk_type(tmp, CT_ASM_COLON);

            set_chunk_type(&nc, tmp->type);
            nc.str.pop_front();
            nc.orig_col++;
            nc.column++;
            chunk_add_after(&nc, tmp);
         }
      }
   }

   tmp = chunk_get_next_ncnl(end, scope_e::PREPROC);

   if (tmp == nullptr)
   {
      return;
   }

   if (chunk_is_token(tmp, CT_SEMICOLON))
   {
      set_chunk_parent(tmp, CT_ASM);
   }
} // flag_asm


static bool chunk_ends_type(chunk_t *start)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc       = start;
   bool    ret       = false;
   size_t  cnt       = 0;
   bool    last_expr = false;
   bool    last_lval = false;

   for ( ; pc != nullptr; pc = chunk_get_prev_ncnlni(pc)) // Issue #2279
   {
      LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s', orig_line %zu, orig_col %zu\n   ",
              __func__, __LINE__, get_token_name(pc->type), pc->text(),
              pc->orig_line, pc->orig_col);
      log_pcf_flags(LFTYPE, pc->flags);

      if (  chunk_is_token(pc, CT_WORD)
         || chunk_is_token(pc, CT_TYPE)
         || chunk_is_token(pc, CT_PTR_TYPE)
         || chunk_is_token(pc, CT_STAR)
         || chunk_is_token(pc, CT_STRUCT)
         || chunk_is_token(pc, CT_DC_MEMBER)
         || chunk_is_token(pc, CT_PP)
         || chunk_is_token(pc, CT_QUALIFIER)
         || (  language_is_set(LANG_CPP | LANG_OC)                       // Issue #2727
            && get_chunk_parent_type(pc) == CT_TEMPLATE
            && (  chunk_is_token(pc, CT_ANGLE_OPEN)
               || chunk_is_token(pc, CT_ANGLE_CLOSE)))
         || (  language_is_set(LANG_CS)
            && (chunk_is_token(pc, CT_MEMBER))))
      {
         cnt++;
         last_expr = pc->flags.test(PCF_EXPR_START)
                     && !pc->flags.test(PCF_IN_FCN_CALL);
         last_lval = pc->flags.test(PCF_LVALUE);
         continue;
      }

      if (  (  chunk_is_semicolon(pc)
            && !pc->flags.test(PCF_IN_FOR))
         || chunk_is_token(pc, CT_TYPEDEF)
         || chunk_is_token(pc, CT_BRACE_OPEN)
         || chunk_is_token(pc, CT_BRACE_CLOSE)
         || chunk_is_token(pc, CT_VBRACE_CLOSE)
         || chunk_is_token(pc, CT_FPAREN_CLOSE)
         || chunk_is_forin(pc)
         || chunk_is_token(pc, CT_MACRO)
         || chunk_is_token(pc, CT_PP_IF)
         || chunk_is_token(pc, CT_PP_ELSE)
         || chunk_is_token(pc, CT_PP_ENDIF)
         || (  (  chunk_is_token(pc, CT_COMMA)
               && !pc->flags.test(PCF_IN_FCN_CALL))
            && last_expr)
         || (  chunk_is_token(pc, CT_SPAREN_OPEN)
            && last_lval))
      {
         ret = cnt > 0;
      }
      break;
   }

   if (pc == nullptr)
   {
      // first token
      ret = true;
   }
   LOG_FMT(LFTYPE, "%s(%d): first token verdict: %s\n",
           __func__, __LINE__, ret ? "yes" : "no");

   return(ret);
} // chunk_ends_type


void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next)
{
   LOG_FUNC_ENTRY();
   chunk_t *tmp;

   // separate the uses of CT_ASSIGN sign '='
   // into CT_ASSIGN_DEFAULT_ARG, CT_ASSIGN_FUNC_PROTO
   if (  chunk_is_token(pc, CT_ASSIGN)
      && get_chunk_parent_type(pc) == CT_FUNC_PROTO
      && (  pc->flags.test(PCF_IN_FCN_DEF)                            // Issue #2236
         || pc->flags.test(PCF_IN_CONST_ARGS)))
   {
      LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      log_pcf_flags(LFCNR, pc->flags);
      set_chunk_type(pc, CT_ASSIGN_DEFAULT_ARG);
   }

   if (  (  chunk_is_token(prev, CT_FPAREN_CLOSE)
         || (  (  chunk_is_str(prev, "const", 5)
               || chunk_is_str(prev, "override", 8))
            && chunk_is_token(prev->prev, CT_FPAREN_CLOSE)))
      && chunk_is_token(pc, CT_ASSIGN)
      && (  chunk_is_token(next, CT_DEFAULT)
         || chunk_is_token(next, CT_DELETE)
         || chunk_is_str(next, "0", 1)))
   {
      set_chunk_type(pc, CT_ASSIGN_FUNC_PROTO);
   }

   if (chunk_is_token(pc, CT_OC_AT))
   {
      if (  chunk_is_token(next, CT_PAREN_OPEN)
         || chunk_is_token(next, CT_BRACE_OPEN)
         || chunk_is_token(next, 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 (  language_is_set(LANG_D)
      && chunk_is_token(pc, CT_QUALIFIER)
      && chunk_is_str(pc, "const", 5)
      && chunk_is_token(next, CT_PAREN_OPEN))
   {
      set_chunk_type(pc, CT_D_CAST);
      set_paren_parent(next, pc->type);
   }

   if (  chunk_is_token(next, CT_PAREN_OPEN)
      && (  chunk_is_token(pc, CT_D_CAST)
         || chunk_is_token(pc, CT_DELEGATE)
         || chunk_is_token(pc, CT_ALIGN)))
   {
      // mark the parenthesis parent
      tmp = set_paren_parent(next, pc->type);

      // For a D cast - convert the next item
      if (  chunk_is_token(pc, CT_D_CAST)
         && tmp != nullptr)
      {
         if (chunk_is_token(tmp, CT_STAR))
         {
            set_chunk_type(tmp, CT_DEREF);
         }
         else if (chunk_is_token(tmp, CT_AMP))
         {
            set_chunk_type(tmp, CT_ADDR);
         }
         else if (chunk_is_token(tmp, CT_MINUS))
         {
            set_chunk_type(tmp, CT_NEG);
         }
         else if (chunk_is_token(tmp, 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 (chunk_is_token(pc, CT_DELEGATE))
      {
         if (tmp != nullptr)
         {
            set_chunk_parent(tmp, CT_DELEGATE);

            if (tmp->level == tmp->brace_level)
            {
               chunk_flags_set(tmp, PCF_VAR_1ST_DEF);
            }
         }

         for (tmp = chunk_get_prev_ncnlni(pc); tmp != nullptr; tmp = chunk_get_prev_ncnlni(tmp)) // Issue #2279
         {
            if (  chunk_is_semicolon(tmp)
               || chunk_is_token(tmp, CT_BRACE_OPEN)
               || chunk_is_token(tmp, CT_VBRACE_OPEN))
            {
               break;
            }
            make_type(tmp);
         }
      }

      if (  chunk_is_token(pc, CT_ALIGN)
         && tmp != nullptr)
      {
         if (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            set_paren_parent(tmp, pc->type);
         }
         else if (chunk_is_token(tmp, CT_COLON))
         {
            set_chunk_parent(tmp, pc->type);
         }
      }
   } // paren open + cast/align/delegate

   if (chunk_is_token(pc, CT_INVARIANT))
   {
      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         set_chunk_parent(next, pc->type);
         tmp = chunk_get_next(next);

         while (tmp != nullptr)
         {
            if (chunk_is_token(tmp, 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 (  chunk_is_token(prev, CT_BRACE_OPEN)
      && get_chunk_parent_type(prev) != CT_CS_PROPERTY
      && (  chunk_is_token(pc, CT_GETSET)
         || chunk_is_token(pc, CT_GETSET_EMPTY)))
   {
      flag_parens(prev, PCF_NONE, CT_NONE, CT_GETSET, false);
   }

   if (chunk_is_token(pc, CT_ASM))
   {
      flag_asm(pc);
   }

   // clang stuff - A new derived type is introduced to C and, by extension, Objective-C, C++, and Objective-C++
   if (language_is_set(LANG_C | LANG_CPP | LANG_OC))
   {
      if (chunk_is_token(pc, CT_CARET))
      {
         if (  pc->flags.test(PCF_EXPR_START)
            || pc->flags.test(PCF_IN_PREPROC))
         {
            handle_oc_block_literal(pc);
         }
      }
   }

   // Objective C stuff
   if (language_is_set(LANG_OC))
   {
      // Check for message declarations
      if (pc->flags.test(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.test(PCF_EXPR_START)
         || pc->flags.test(PCF_IN_PREPROC))
      {
         if (chunk_is_token(pc, CT_SQUARE_OPEN))
         {
            handle_oc_message_send(pc);
         }
      }

      if (chunk_is_token(pc, CT_OC_PROPERTY))
      {
         handle_oc_property_decl(pc);
      }

      if (chunk_is_token(pc, CT_OC_AVAILABLE))
      {
         handle_oc_available(pc);
      }
   }

   // C# stuff
   if (language_is_set(LANG_CS))
   {
      // '[assembly: xxx]' stuff
      if (  pc->flags.test(PCF_EXPR_START)
         && chunk_is_token(pc, CT_SQUARE_OPEN))
      {
         handle_cs_square_stmt(pc);
      }

      if (  chunk_is_token(next, CT_BRACE_OPEN)
         && get_chunk_parent_type(next) == CT_NONE
         && (  chunk_is_token(pc, CT_SQUARE_CLOSE)
            || chunk_is_token(pc, CT_ANGLE_CLOSE)
            || chunk_is_token(pc, CT_WORD)))
      {
         handle_cs_property(next);
      }

      if (  chunk_is_token(pc, CT_SQUARE_CLOSE)
         && chunk_is_token(next, CT_WORD))
      {
         handle_cs_array_type(pc);
      }

      if (  (  chunk_is_token(pc, CT_LAMBDA)
            || chunk_is_token(pc, CT_DELEGATE))
         && chunk_is_token(next, CT_BRACE_OPEN))
      {
         set_paren_parent(next, pc->type);
      }

      if (chunk_is_token(pc, CT_WHEN) && pc->next->type != CT_SPAREN_OPEN)
      {
         set_chunk_type(pc, CT_WORD);
      }
   }

   if (  language_is_set(LANG_JAVA)
      && chunk_is_token(pc, CT_LAMBDA)
      && chunk_is_token(next, CT_BRACE_OPEN))
   {
      set_paren_parent(next, pc->type);
   }

   if (chunk_is_token(pc, CT_NEW))
   {
      chunk_t *ts = nullptr;
      tmp = next;

      if (chunk_is_token(tmp, CT_TSQUARE))
      {
         ts  = tmp;
         tmp = chunk_get_next_ncnl(tmp);
      }

      if (  chunk_is_token(tmp, CT_BRACE_OPEN)
         || chunk_is_token(tmp, CT_PAREN_OPEN))
      {
         set_paren_parent(tmp, pc->type);

         if (ts != nullptr)
         {
            set_chunk_parent(ts, pc->type);
         }
      }
   }

   // C++11 Lambda stuff
   if (  language_is_set(LANG_CPP)
      && (  chunk_is_token(pc, CT_SQUARE_OPEN)
         || chunk_is_token(pc, CT_TSQUARE)))
   {
      handle_cpp_lambda(pc);
   }

   // FIXME: which language does this apply to?
   // Issue #2432
   if (!language_is_set(LANG_OC))
   {
      if (  chunk_is_token(pc, CT_ASSIGN)
         && chunk_is_token(next, CT_SQUARE_OPEN))
      {
         set_paren_parent(next, CT_ASSIGN);

         // Mark one-liner assignment
         tmp = next;

         while ((tmp = chunk_get_next_nc(tmp)) != nullptr)
         {
            if (chunk_is_newline(tmp))
            {
               break;
            }

            if (chunk_is_token(tmp, CT_SQUARE_CLOSE) && next->level == tmp->level)
            {
               chunk_flags_set(tmp, PCF_ONE_LINER);
               chunk_flags_set(next, PCF_ONE_LINER);
               break;
            }
         }
      }
   }

   if (chunk_is_token(pc, CT_ASSERT))
   {
      handle_java_assert(pc);
   }

   if (chunk_is_token(pc, CT_ANNOTATION))
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_paren_open(tmp))
      {
         set_paren_parent(tmp, CT_ANNOTATION);
      }
   }

   if (chunk_is_token(pc, CT_SIZEOF) && language_is_set(LANG_ALLC))
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_token(tmp, CT_ELLIPSIS))
      {
         set_chunk_parent(tmp, CT_SIZEOF);
      }
   }

   if (chunk_is_token(pc, CT_DECLTYPE))
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_paren_open(tmp))
      {
         // decltype may be followed by a braced-init-list
         tmp = set_paren_parent(tmp, CT_DECLTYPE);

         if (chunk_is_opening_brace(tmp))
         {
            tmp = set_paren_parent(tmp, CT_BRACED_INIT_LIST);

            if (tmp)
            {
               chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START);
            }
         }
         else
         {
            if (chunk_is_token(tmp, CT_WORD))
            {
               chunk_flags_set(tmp, PCF_VAR_1ST_DEF);
            }
         }
      }
   }

   // A [] in C# and D only follows a type
   if (  chunk_is_token(pc, CT_TSQUARE)
      && language_is_set(LANG_D | LANG_CS | LANG_VALA))
   {
      if (chunk_is_token(prev, CT_WORD))
      {
         set_chunk_type(prev, CT_TYPE);
      }

      if (chunk_is_token(next, CT_WORD))
      {
         chunk_flags_set(next, PCF_VAR_1ST_DEF);
      }
   }

   if (  chunk_is_token(pc, CT_SQL_EXEC)
      || chunk_is_token(pc, CT_SQL_BEGIN)
      || chunk_is_token(pc, CT_SQL_END))
   {
      mark_exec_sql(pc);
   }

   if (chunk_is_token(pc, CT_PROTO_WRAP))
   {
      handle_proto_wrap(pc);
   }

   // Handle the typedef
   if (chunk_is_token(pc, CT_TYPEDEF))
   {
      fix_typedef(pc);
   }

   if (  chunk_is_token(pc, CT_ENUM)
      || chunk_is_token(pc, CT_STRUCT)
      || chunk_is_token(pc, CT_UNION)
      || (  chunk_is_token(pc, CT_CLASS)
         && !language_is_set(LANG_D)))
   {
      if (prev->type != CT_TYPEDEF)
      {
         fix_enum_struct_union(pc);
      }
   }

   if (chunk_is_token(pc, CT_EXTERN))
   {
      if (chunk_is_paren_open(next))
      {
         tmp = flag_parens(next, PCF_NONE, CT_NONE, CT_EXTERN, true);

         if (chunk_is_token(tmp, 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 (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            set_paren_parent(tmp, CT_EXTERN);
         }
      }
   }

   if (chunk_is_token(pc, CT_TEMPLATE))
   {
      if (language_is_set(LANG_D))
      {
         handle_d_template(pc);
      }
      else
      {
         handle_cpp_template(pc);
      }
   }

   if (  chunk_is_token(pc, CT_WORD)
      && chunk_is_token(next, CT_ANGLE_OPEN)
      && get_chunk_parent_type(next) == CT_TEMPLATE)
   {
      mark_template_func(pc, next);
   }

   if (  chunk_is_token(pc, CT_SQUARE_CLOSE)
      && chunk_is_token(next, CT_PAREN_OPEN))
   {
      flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_NONE, false);
   }

   if (chunk_is_token(pc, CT_TYPE_CAST))
   {
      fix_type_cast(pc);
   }

   if (  get_chunk_parent_type(pc) == CT_ASSIGN
      && (chunk_is_token(pc, CT_BRACE_OPEN) || chunk_is_token(pc, CT_SQUARE_OPEN)))
   {
      // Mark everything in here as in assign
      flag_parens(pc, PCF_IN_ARRAY_ASSIGN, pc->type, CT_NONE, false);
   }

   if (chunk_is_token(pc, 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 (chunk_is_token(next, CT_PAREN_OPEN))
   {
      tmp = chunk_get_next_ncnl(next);

      if (  language_is_set(LANG_C | LANG_CPP | 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 (  (  chunk_is_token(tmp, CT_OC_BLOCK_CARET)
               || chunk_is_token(tmp, CT_CARET))
            && chunk_is_token(pc, CT_WORD))
         {
            LOG_FMT(LFCN, "%s(%d): (1) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
            set_chunk_type(pc, CT_FUNC_CALL);
         }
      }
      else if (  chunk_is_token(pc, CT_WORD)
              || chunk_is_token(pc, CT_OPERATOR_VAL))
      {
         set_chunk_type(pc, CT_FUNCTION);
      }
      else if (chunk_is_token(pc, CT_FIXED))
      {
         set_chunk_type(pc, CT_FUNCTION);
         set_chunk_parent(pc, CT_FIXED);
      }
      else if (chunk_is_token(pc, CT_TYPE))
      {
         /*
          * If we are on a type, then we are either on a C++ style cast, an
          * array reference, 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...
          */
         LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());

         bool is_byref_array = false;

         if (language_is_set(LANG_CPP))
         {
            // If the open paren is followed by an ampersand, an optional word,
            // a close parenthesis, and an open square bracket, then it is an
            // array being passed by reference, not a cast
            tmp = chunk_get_next_ncnl(next);

            if (chunk_is_token(tmp, CT_AMP))
            {
               auto tmp2 = chunk_get_next_ncnl(tmp);

               if (chunk_is_token(tmp2, CT_WORD))
               {
                  tmp2 = chunk_get_next_ncnl(tmp2);
               }

               if (chunk_is_token(tmp2, CT_PAREN_CLOSE))
               {
                  tmp2 = chunk_get_next_ncnl(tmp2);

                  if (chunk_is_token(tmp2, CT_SQUARE_OPEN))
                  {
                     is_byref_array = true;
                     set_chunk_type(tmp, CT_BYREF);
                  }
               }
            }
         }

         if (!is_byref_array)
         {
            tmp = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);

            if (tmp != nullptr)
            {
               tmp = chunk_get_next(tmp);

               if (chunk_is_token(tmp, CT_PAREN_OPEN))
               {
                  set_chunk_type(pc, CT_FUNCTION);
               }
               else
               {
                  if (  get_chunk_parent_type(pc) == CT_NONE
                     && !pc->flags.test(PCF_IN_TYPEDEF))
                  {
                     tmp = chunk_get_next_ncnl(next);

                     if (chunk_is_token(tmp, 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);
                     }
                  }
               }
            }
         }
      }
   }

   if (language_is_set(LANG_PAWN))
   {
      if (  chunk_is_token(pc, CT_FUNCTION)
         && pc->brace_level > 0)
      {
         LOG_FMT(LFCN, "%s(%d): (2) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         set_chunk_type(pc, CT_FUNC_CALL);
      }

      if (  chunk_is_token(pc, CT_STATE)
         && chunk_is_token(next, CT_PAREN_OPEN))
      {
         set_paren_parent(next, pc->type);
      }
   }
   else
   {
      if (  (  chunk_is_token(pc, CT_FUNCTION)
            || chunk_is_token(pc, CT_FUNC_DEF))
         && (  (get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR)
            || !is_oc_block(pc)))
      {
         mark_function(pc);
      }
   }

   // Detect C99 member stuff
   if (  chunk_is_token(pc, CT_MEMBER)
      && (  chunk_is_token(prev, CT_COMMA)
         || chunk_is_token(prev, CT_BRACE_OPEN)))
   {
      set_chunk_type(pc, CT_C99_MEMBER);
      set_chunk_parent(next, CT_C99_MEMBER);
   }

   // Mark function parens and braces
   if (  chunk_is_token(pc, CT_FUNC_DEF)
      || chunk_is_token(pc, CT_FUNC_CALL)
      || chunk_is_token(pc, CT_FUNC_CALL_USER)
      || chunk_is_token(pc, CT_FUNC_PROTO))
   {
      tmp = next;

      if (chunk_is_token(tmp, CT_SQUARE_OPEN))
      {
         tmp = set_paren_parent(tmp, pc->type);
      }
      else if (chunk_is_token(tmp, CT_TSQUARE) || get_chunk_parent_type(tmp) == CT_OPERATOR)
      {
         tmp = chunk_get_next_ncnl(tmp);
      }

      if (tmp != nullptr)
      {
         if (chunk_is_paren_open(tmp))
         {
            tmp = flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, pc->type, false);

            if (tmp != nullptr)
            {
               if (chunk_is_token(tmp, CT_BRACE_OPEN))
               {
                  if (  get_chunk_parent_type(tmp) != CT_DOUBLE_BRACE
                     && !pc->flags.test(PCF_IN_CONST_ARGS))
                  {
                     set_paren_parent(tmp, pc->type);
                  }
               }
               else if (  chunk_is_semicolon(tmp)
                       && chunk_is_token(pc, CT_FUNC_PROTO))
               {
                  set_chunk_parent(tmp, pc->type);
               }
            }
         }
      }
   }

   // Mark the parameters in catch()
   if (chunk_is_token(pc, CT_CATCH) && chunk_is_token(next, CT_SPAREN_OPEN))
   {
      fix_fcn_def_params(next);
   }

   if (chunk_is_token(pc, CT_THROW) && chunk_is_token(prev, CT_FPAREN_CLOSE))
   {
      set_chunk_parent(pc, get_chunk_parent_type(prev));

      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         set_paren_parent(next, CT_THROW);
      }
   }

   // Mark the braces in: "for_each_entry(xxx) { }"
   if (  chunk_is_token(pc, CT_BRACE_OPEN)
      && get_chunk_parent_type(pc) != CT_DOUBLE_BRACE
      && chunk_is_token(prev, CT_FPAREN_CLOSE)
      && (  get_chunk_parent_type(prev) == CT_FUNC_CALL
         || get_chunk_parent_type(prev) == CT_FUNC_CALL_USER)
      && !pc->flags.test(PCF_IN_CONST_ARGS))
   {
      LOG_FMT(LFCN, "%s(%d): (3) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      set_paren_parent(pc, CT_FUNC_CALL);
   }

   /*
    * Check for a close parenthesis followed by an open parenthesis,
    * which means that we are on a function type declaration (C/C++ only?).
    * Note that typedefs are already taken care of.
    */
   if (  !pc->flags.test_any(PCF_IN_TYPEDEF | PCF_IN_TEMPLATE)
      && get_chunk_parent_type(pc) != CT_CPP_CAST
      && get_chunk_parent_type(pc) != CT_C_CAST
      && !pc->flags.test(PCF_IN_PREPROC)
      && !is_oc_block(pc)
      && get_chunk_parent_type(pc) != CT_OC_MSG_DECL
      && get_chunk_parent_type(pc) != CT_OC_MSG_SPEC
      && chunk_is_str(pc, ")", 1)
      && chunk_is_str(next, "(", 1))
   {
      if (language_is_set(LANG_D))
      {
         flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
      }
      else
      {
         mark_function_type(pc);
      }
   }

   if (  (chunk_is_token(pc, CT_CLASS) || chunk_is_token(pc, CT_STRUCT))
      && pc->level == pc->brace_level)
   {
      if (pc->type != CT_STRUCT || !language_is_set(LANG_C))
      {
         mark_class_ctor(pc);
      }
   }

   if (chunk_is_token(pc, CT_OC_CLASS))
   {
      handle_oc_class(pc);
   }
   // TODO: Check for stuff that can only occur at the start of an statement

   if (!language_is_set(LANG_D))
   {
      /*
       * Check a parenthesis pair to see if it is a cast.
       * Note that SPAREN and FPAREN have already been marked.
       */
      if (  chunk_is_token(pc, CT_PAREN_OPEN)
         && (  get_chunk_parent_type(pc) == CT_NONE
            || get_chunk_parent_type(pc) == CT_OC_MSG
            || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR
            || get_chunk_parent_type(pc) == CT_CS_SQ_STMT)           // Issue # 1256
         && (  chunk_is_token(next, CT_WORD)
            || chunk_is_token(next, CT_TYPE)
            || chunk_is_token(next, CT_STRUCT)
            || chunk_is_token(next, CT_QUALIFIER)
            || chunk_is_token(next, CT_MEMBER)
            || chunk_is_token(next, CT_DC_MEMBER)
            || chunk_is_token(next, CT_ENUM)
            || chunk_is_token(next, CT_UNION))
         && prev->type != CT_DECLTYPE
         && prev->type != CT_SIZEOF
         && get_chunk_parent_type(prev) != CT_SIZEOF
         && get_chunk_parent_type(prev) != CT_OPERATOR
         && !pc->flags.test(PCF_IN_TYPEDEF))
      {
         fix_casts(pc);
      }
   }

   if (language_is_set(LANG_CPP))
   {
      chunk_t *nnext = chunk_get_next_ncnl(next);

      // handle parent_type of assigns in special functions (ro5 + pure virtual)
      if (  pc->flags.test_any(PCF_IN_STRUCT | PCF_IN_CLASS)
         && chunk_is_token(pc, CT_ASSIGN)
         && chunk_is_token(nnext, CT_SEMICOLON)
         && (  chunk_is_token(next, CT_DEFAULT)
            || chunk_is_token(next, CT_DELETE)
            || (chunk_is_token(next, CT_NUMBER) && chunk_is_str(next, "0", 1))))
      {
         const size_t level        = pc->level;
         bool         found_status = false;
         chunk_t      *pprev       = chunk_get_prev(pc);

         for ( ; (  pprev != nullptr
                 && pprev->level >= level
                 && pprev->type != CT_SEMICOLON
                 && pprev->type != CT_ACCESS_COLON)
               ; pprev = chunk_get_prev(pprev))
         {
            if (pprev->level != level)
            {
               continue;
            }

            if (chunk_is_token(next, CT_NUMBER))
            {
               if (  pprev->type == CT_QUALIFIER
                  && chunk_is_str(pprev, "virtual", 7))
               {
                  found_status = true;
                  break;
               }
            }
            else
            {
               if (  pprev->type == CT_FUNC_CLASS_PROTO  // ctor/dtor
                  || pprev->type == CT_FUNC_PROTO)       // normal function
               {
                  found_status = true;
                  break;
               }
            }
         }

         if (found_status)
         {
            set_chunk_parent(pc, pprev->type);
         }
      }
      // Issue #2332
      bool we_have_a_case_before = false;

      if (chunk_is_token(pc, CT_COLON))
      {
         // check if we have a case before
         chunk_t *switch_before = chunk_get_prev_type(pc, CT_CASE, pc->level);

         if (switch_before != nullptr)
         {
            LOG_FMT(LFCNR, "%s(%d): switch_before->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n",
                    __func__, __LINE__, switch_before->orig_line, switch_before->orig_col,
                    switch_before->text(), get_token_name(switch_before->type));
            we_have_a_case_before = true;
         }
      }

      // Detect a braced-init-list
      if (  chunk_is_token(pc, CT_WORD)
         || chunk_is_token(pc, CT_TYPE)
         || chunk_is_token(pc, CT_ASSIGN)
         || chunk_is_token(pc, CT_RETURN)
         || chunk_is_token(pc, CT_COMMA)
         || chunk_is_token(pc, CT_ANGLE_CLOSE)
         || chunk_is_token(pc, CT_SQUARE_CLOSE)
         || chunk_is_token(pc, CT_TSQUARE)
         || chunk_is_token(pc, CT_FPAREN_OPEN)
         || chunk_is_token(pc, CT_QUESTION)
         || (  chunk_is_token(pc, CT_COLON)
            && !we_have_a_case_before)
         || (  chunk_is_token(pc, CT_BRACE_OPEN)
            && (  get_chunk_parent_type(pc) == CT_NONE
               || get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST)))
      {
         log_pcf_flags(LFCNR, pc->flags);
         auto brace_open = chunk_get_next_ncnl(pc);

         if (  chunk_is_token(brace_open, CT_BRACE_OPEN)
            && (  get_chunk_parent_type(brace_open) == CT_NONE
               || get_chunk_parent_type(brace_open) == CT_ASSIGN
               || get_chunk_parent_type(brace_open) == CT_RETURN
               || get_chunk_parent_type(brace_open) == CT_BRACED_INIT_LIST))
         {
            log_pcf_flags(LFCNR, brace_open->flags);
            auto brace_close = chunk_skip_to_match(next);

            if (chunk_is_token(brace_close, CT_BRACE_CLOSE))
            {
               set_chunk_parent(brace_open, CT_BRACED_INIT_LIST);
               set_chunk_parent(brace_close, CT_BRACED_INIT_LIST);

               tmp = chunk_get_next_ncnl(brace_close);

               if (tmp)
               {
                  chunk_flags_clr(tmp, PCF_EXPR_START | PCF_STMT_START);
               }
               // TODO: Change pc->type CT_WORD -> CT_TYPE
               // for the case CT_ASSIGN (and others).

               // TODO: Move this block to the fix_fcn_call_args function.
               if (chunk_is_token(pc, CT_WORD) && pc->flags.test(PCF_IN_FCN_CALL))
               {
                  set_chunk_type(pc, CT_TYPE);
               }
            }
         }
      }
   }

   // Check for stuff that can only occur at the start of an expression
   if (  pc->flags.test(PCF_EXPR_START)
      || (prev->flags.test(PCF_EXPR_START) && get_chunk_parent_type(pc) == CT_OC_AT))
   {
      // Change STAR, MINUS, and PLUS in the easy cases
      if (chunk_is_token(pc, CT_STAR))
      {
         // issue #596
         // [0x100062020:IN_SPAREN,IN_FOR,STMT_START,EXPR_START,PUNCTUATOR]
         // prev->type is CT_COLON ==> CT_DEREF
         if (chunk_is_token(prev, CT_ANGLE_CLOSE))
         {
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (chunk_is_token(prev, CT_COLON))
         {
            set_chunk_type(pc, CT_DEREF);
         }
         else
         {
            set_chunk_type(pc, CT_DEREF);
         }
      }

      if (  language_is_set(LANG_CPP)
         && chunk_is_token(pc, CT_CARET)
         && chunk_is_token(prev, CT_ANGLE_CLOSE))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }

      if (  language_is_set(LANG_CS)
         && (chunk_is_token(pc, CT_QUESTION))
         && (chunk_is_token(prev, CT_ANGLE_CLOSE)))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }

      if (chunk_is_token(pc, CT_MINUS))
      {
         set_chunk_type(pc, CT_NEG);
      }

      if (chunk_is_token(pc, CT_PLUS))
      {
         set_chunk_type(pc, CT_POS);
      }

      if (chunk_is_token(pc, CT_INCDEC_AFTER))
      {
         set_chunk_type(pc, CT_INCDEC_BEFORE);
      }

      if (chunk_is_token(pc, CT_AMP))
      {
         if (chunk_is_token(prev, CT_ANGLE_CLOSE))             // Issue #2324
         {
            set_chunk_type(pc, CT_BYREF);
         }
         else
         {
            set_chunk_type(pc, CT_ADDR);
         }
      }

      if (chunk_is_token(pc, CT_CARET))
      {
         if (language_is_set(LANG_C | LANG_CPP | 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.test(PCF_IN_TYPEDEF)
      && get_chunk_parent_type(prev) != CT_CPP_CAST
      && !prev->flags.test(PCF_IN_FCN_DEF)
      && (  chunk_is_token(pc, CT_STRUCT)
         || chunk_is_token(pc, CT_UNION)
         || chunk_is_token(pc, CT_CLASS)
         || chunk_is_token(pc, CT_ENUM)))
   {
      tmp = chunk_skip_dc_member(next);

      if ((chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD)))
      {
         set_chunk_parent(tmp, pc->type);
         set_chunk_type(tmp, CT_TYPE);

         tmp = chunk_get_next_ncnl(tmp);
      }

      if (chunk_is_token(tmp, CT_BRACE_OPEN))
      {
         tmp = chunk_skip_to_match(tmp);

         if (tmp != nullptr)
         {
            tmp = chunk_get_next_ncnl(tmp);
         }
      }

      if (  tmp != nullptr
         && (chunk_is_ptr_operator(tmp) || chunk_is_token(tmp, CT_WORD)))
      {
         mark_variable_definition(tmp);
      }
   }

   /*
    * Change the parenthesis pair after a function/macro-function
    * CT_PAREN_OPEN => CT_FPAREN_OPEN
    */
   if (chunk_is_token(pc, CT_MACRO_FUNC))
   {
      flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_MACRO_FUNC, false);
   }

   if (  chunk_is_token(pc, CT_MACRO_OPEN)
      || chunk_is_token(pc, CT_MACRO_ELSE)
      || chunk_is_token(pc, CT_MACRO_CLOSE))
   {
      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, false);
      }
   }

   if (chunk_is_token(pc, CT_DELETE) && chunk_is_token(next, CT_TSQUARE))
   {
      set_chunk_parent(next, CT_DELETE);
   }

   // Change CT_STAR to CT_PTR_TYPE or CT_ARITH or CT_DEREF
   if (  chunk_is_token(pc, CT_STAR)
      || (language_is_set(LANG_CPP) && chunk_is_token(pc, CT_CARET)))
   {
      if (chunk_is_paren_close(next) || chunk_is_token(next, CT_COMMA))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (language_is_set(LANG_OC) && chunk_is_token(next, 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, get_chunk_parent_type(prev));

         set_chunk_type(next, CT_PTR_TYPE);
         set_chunk_parent(next, get_chunk_parent_type(pc));
      }
      else if (  chunk_is_token(pc, CT_STAR)
              && (  chunk_is_token(prev, CT_DECLTYPE)
                 || chunk_is_token(prev, CT_SIZEOF)
                 || chunk_is_token(prev, CT_DELETE)
                 || (pc && get_chunk_parent_type(pc) == CT_SIZEOF)))
      {
         set_chunk_type(pc, CT_DEREF);
      }
      else if (  (  chunk_is_token(prev, CT_WORD)
                 && chunk_ends_type(prev)
                 && !prev->flags.test(PCF_IN_FCN_CTOR))
              || chunk_is_token(prev, CT_DC_MEMBER)
              || chunk_is_token(prev, CT_PTR_TYPE))
      {
         LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n   ",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type));
         log_pcf_flags(LFCNR, pc->flags);
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (chunk_is_token(next, CT_SQUARE_OPEN) && !language_is_set(LANG_OC))  // issue # 408
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (chunk_is_token(pc, CT_STAR))
      {
         // Add check for CT_DC_MEMBER CT_WORD CT_STAR sequence
         // to convert CT_WORD into CT_TYPE
         // and CT_STAR into CT_PTR_TYPE
         // look for an assign backward to distinguish between
         //    double result = Constants::PI * factor;
         // and
         //    ::some::name * foo;
         if (  chunk_is_token(prev, CT_WORD)
            && chunk_is_token(prev->prev, CT_DC_MEMBER)
            && language_is_set(LANG_CPP))
         {
            // Issue 1402
            bool assign_found = false;
            tmp = pc;

            while (tmp != nullptr)
            {
               if (chunk_is_token(tmp, CT_SEMICOLON))
               {
                  break;
               }
               else if (chunk_is_token(tmp, CT_ASSIGN))
               {
                  assign_found = true;
                  break;
               }
               tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279
            }

            if (assign_found)
            {
               // double result = Constants::PI * factor;
               set_chunk_type(pc, CT_ARITH);
            }
            else
            {
               //    ::some::name * foo;
               set_chunk_type(prev, CT_TYPE);
               set_chunk_type(pc, CT_PTR_TYPE);
            }
         }

         /*
          * A star can have three meanings
          * 1. CT_DEREF    = pointer dereferencing
          * 2. CT_PTR_TYPE = pointer definition
          * 3. CT_ARITH    = arithmetic multiplication
          *
          * 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.
          */
         if (chunk_is_token(prev, CT_TYPE))
         {
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (  chunk_is_token(pc->next, CT_SEMICOLON)      // Issue #2319
                 || (  chunk_is_token(pc->next, CT_STAR)
                    && chunk_is_token(pc->next->next, CT_SEMICOLON)))
         {
            // example:
            //    using AbstractLinkPtr = AbstractLink*;
            //    using AbstractLinkPtrPtr = AbstractLink**;
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (  (  get_chunk_parent_type(pc) == CT_FUNC_DEF
                    && (chunk_is_opening_brace(next) || chunk_is_star(pc->next)))
                 || (next->type == CT_QUALIFIER))               // Issue #2648
         {
            // example:
            // auto getComponent(Color *color) -> Component * {
            // auto getComponent(Color *color) -> Component ** {
            // auto getComponent(Color *color) -> Component * _Nonnull
            set_chunk_type(pc, CT_PTR_TYPE);
         }
         else if (  chunk_is_token(pc->next, CT_SEMICOLON)      // Issue #2319
                 || (  chunk_is_token(pc->next, CT_STAR)
                    && chunk_is_token(pc->next->next, CT_STAR)))
         {
            // more pointers are NOT yet possible
            fprintf(stderr, "Too many pointers\n");
            fprintf(stderr, "at line %zu, column %zu.\n", pc->orig_line, pc->orig_col);
            fprintf(stderr, "Please make a report.\n");
            log_flush(true);
            exit(EX_SOFTWARE);
         }
         else
         {
            // Issue 1402
            set_chunk_type(pc,
                           (  prev->flags.test(PCF_PUNCTUATOR)
                           && (  !chunk_is_paren_close(prev)
                              || chunk_is_token(prev, CT_SPAREN_CLOSE)
                              || get_chunk_parent_type(prev) == CT_MACRO_FUNC)
                           && prev->type != CT_SQUARE_CLOSE
                           && prev->type != CT_DC_MEMBER) ? CT_DEREF : CT_ARITH);
         }

         if (pc->flags.test(PCF_IN_TYPEDEF))  // Issue #1255/#633
         {
            tmp = pc;

            while (tmp != nullptr)
            {
               if (  chunk_is_token(tmp, CT_SEMICOLON)
                  || chunk_is_token(tmp, CT_BRACE_OPEN))
               {
                  break;
               }
               else if (chunk_is_token(tmp, CT_TYPEDEF))
               {
                  set_chunk_type(pc, CT_PTR_TYPE);
               }
               tmp = chunk_get_prev_ncnlni(tmp); // Issue #2279
            }
         }
      }
   }

   if (chunk_is_token(pc, CT_AMP))
   {
      if (chunk_is_token(prev, CT_DELETE))
      {
         set_chunk_type(pc, CT_ADDR);
      }
      else if (chunk_is_token(prev, CT_TYPE))
      {
         set_chunk_type(pc, CT_BYREF);
      }
      else if (chunk_is_token(next, CT_FPAREN_CLOSE) || chunk_is_token(next, CT_COMMA))
      {
         // fix the bug #654
         // connect(&mapper, SIGNAL(mapped(QString &)), this, SLOT(onSomeEvent(QString &)));
         set_chunk_type(pc, CT_BYREF);
      }
      else if (get_chunk_parent_type(pc) == CT_USING_ALIAS)
      {
         // fix the Issue # 1689
         // using reference = value_type &;
         set_chunk_type(pc->prev, CT_TYPE);
         set_chunk_type(pc, CT_BYREF);
      }
      else
      {
         // Issue # 1398
         if (  pc->flags.test(PCF_IN_FCN_DEF)
            && chunk_is_token(prev, CT_WORD)
            && chunk_is_token(pc, CT_AMP)
            && chunk_is_token(next, CT_WORD))
         {
            /*
             * Change CT_WORD before CT_AMP before CT_WORD to CT_TYPE
             */
            set_chunk_type(prev, CT_TYPE);
         }
         else
         {
            set_chunk_type(pc, CT_ARITH);

            if (chunk_is_token(prev, CT_WORD))
            {
               tmp = chunk_get_prev_ncnlni(prev); // Issue #2279

               if (tmp != nullptr)
               {
                  if (  chunk_is_semicolon(tmp)
                     || chunk_is_token(tmp, CT_BRACE_OPEN)
                     || chunk_is_token(tmp, CT_QUALIFIER))
                  {
                     set_chunk_type(pc, CT_BYREF);
                     set_chunk_type(prev, CT_TYPE);

                     if (!(  chunk_is_token(next, CT_OPERATOR)
                          || chunk_is_token(next, CT_TYPE)
                          || chunk_is_token(next, CT_DC_MEMBER)))
                     {
                        LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
                                __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
                        chunk_flags_set(next, PCF_VAR_1ST);
                     }
                  }
                  else if (chunk_is_token(tmp, CT_DC_MEMBER))
                  {
                     set_chunk_type(prev, CT_TYPE);

                     if (!chunk_is_token(next, CT_TYPE))            // Issue #2103
                     {
                        set_chunk_type(pc, CT_BYREF);
                     }
                  }
               }
            }
         }
      }
   }

   if (chunk_is_token(pc, CT_MINUS) || chunk_is_token(pc, CT_PLUS))
   {
      if (  chunk_is_token(prev, CT_POS)
         || chunk_is_token(prev, CT_NEG)
         || chunk_is_token(prev, CT_ARITH))
      {
         set_chunk_type(pc, (pc->type == CT_MINUS) ? CT_NEG : CT_POS);
      }
      else if (chunk_is_token(prev, CT_OC_CLASS))
      {
         set_chunk_type(pc, (chunk_is_token(pc, CT_MINUS)) ? CT_NEG : CT_POS);
      }
      else
      {
         set_chunk_type(pc, CT_ARITH);
      }
   }

   /*
    * Bug # 634
    * Check for extern "C" NSString* i;
    * NSString is a type
    * change CT_WORD => CT_TYPE     for pc
    * change CT_STAR => CT_PTR_TYPE for pc-next
    */
   if (chunk_is_token(pc, CT_WORD))     // here NSString
   {
      if (pc->next != nullptr)          // here *
      {
         if (pc->next->type == CT_STAR) // here *
         {
            // compare text with "C" to find extern "C" instructions
            if (pc->prev != nullptr)
            {
               if (pc->prev->type == CT_STRING)
               {
                  if (unc_text::compare(pc->prev->text(), "\"C\"") == 0)
                  {
                     if (pc->prev->prev->type == CT_EXTERN)
                     {
                        set_chunk_type(pc, CT_TYPE);            // change CT_WORD => CT_TYPE
                        set_chunk_type(pc->next, CT_PTR_TYPE);  // change CT_STAR => CT_PTR_TYPE
                     }
                  }
               }
            }

            // Issue #322 STDMETHOD(GetValues)(BSTR bsName, REFDATA** pData);
            if (  (pc->next->next != nullptr)
               && pc->next->next->type == CT_STAR
               && pc->flags.test(PCF_IN_CONST_ARGS))
            {
               // change CT_STAR => CT_PTR_TYPE
               set_chunk_type(pc->next, CT_PTR_TYPE);
               set_chunk_type(pc->next->next, CT_PTR_TYPE);
            }

            // Issue #222 whatever3 *(func_ptr)( whatever4 *foo2, ...
            if (  (pc->next->next != nullptr)
               && pc->next->next->type == CT_WORD
               && pc->flags.test(PCF_IN_FCN_DEF))
            {
               // look for the opening parenthesis
               // Issue 1403
               tmp = chunk_get_prev_type(pc, CT_FPAREN_OPEN, pc->level - 1);

               if (  tmp != nullptr
                  && get_chunk_parent_type(tmp) != CT_FUNC_CTOR_VAR)
               {
                  set_chunk_type(pc->next, CT_PTR_TYPE);
               }
            }
         }
      }
   }

   /*
    * Bug # 634
    * Check for __attribute__((visibility ("default"))) NSString* i;
    * NSString is a type
    * change CT_WORD => CT_TYPE     for pc
    * change CT_STAR => CT_PTR_TYPE for pc-next
    */
   if (chunk_is_token(pc, CT_WORD))     // here NSString
   {
      if (pc->next != nullptr)          // here *
      {
         if (pc->next->type == CT_STAR) // here *
         {
            tmp = pc;

            while ((tmp != nullptr))
            {
               if (chunk_is_token(tmp, CT_ATTRIBUTE))
               {
                  LOG_FMT(LFCNR, "%s(%d): ATTRIBUTE found, type is %s, text() '%s'\n",
                          __func__, __LINE__, get_token_name(tmp->type), tmp->text());
                  LOG_FMT(LFCNR, "for token, type is %s, text() '%s'\n", get_token_name(pc->type), pc->text());
                  // change CT_WORD => CT_TYPE
                  set_chunk_type(pc, CT_TYPE);
                  // change CT_STAR => CT_PTR_TYPE
                  set_chunk_type(pc->next, CT_PTR_TYPE);
               }

               if (tmp->flags.test(PCF_STMT_START))
               {
                  // we are at beginning of the line
                  break;
               }
               tmp = chunk_get_prev(tmp);
            }
         }
      }
   }

   /*
    * Issue # 1689
    * Check for using reference = value_type&;
    * is it a Type alias, alias template?
    */
   if (chunk_is_token(pc, CT_USING))
   {
      // look for CT_ASSIGN before CT_SEMICOLON at the end of the statement
      bool    assign_found = false;
      bool    is_preproc   = pc->flags.test(PCF_IN_PREPROC);
      chunk_t *temp;

      for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp))
      {
         LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', type is %s\n",
                 __func__, __LINE__, temp->orig_line, temp->orig_col, temp->text(), get_token_name(temp->type));

         if (chunk_is_token(temp, CT_ASSIGN))
         {
            assign_found = true;
            break;
         }

         if (  chunk_is_token(temp, CT_SEMICOLON)
            || (  is_preproc
               && (  !temp->flags.test(PCF_IN_PREPROC)
                  || chunk_is_token(temp, CT_PREPROC))))
         {
            break;
         }
      }

      if (assign_found)
      {
         // it is a Type alias, alias template
         for (temp = pc; temp != nullptr; temp = chunk_get_next_ncnl(temp))
         {
            if (get_chunk_parent_type(temp) == CT_NONE)
            {
               set_chunk_parent(temp, CT_USING_ALIAS);
            }

            if (  chunk_is_token(temp, CT_SEMICOLON)
               || (  is_preproc
                  && (  !temp->flags.test(PCF_IN_PREPROC)
                     || chunk_is_token(temp, CT_PREPROC))))
            {
               break;
            }
         }
      }
   }

   // Issue #548: inline T && someFunc(foo * *p, bar && q) { }
   if (  pc->type == CT_BOOL
      && !pc->flags.test(PCF_IN_PREPROC)
      && chunk_is_str(pc, "&&", 2)
      && chunk_ends_type(pc->prev))
   {
      set_chunk_type(pc, CT_BYREF);
   }

   // Issue #1704
   if (  chunk_is_token(pc, CT_INCDEC_AFTER)
      && pc->flags.test(PCF_IN_PREPROC))
   {
      chunk_t *tmp_2 = chunk_get_next(pc);
      log_pcf_flags(LFTYPE, pc->flags);

      if (chunk_is_token(tmp_2, CT_WORD))
      {
         set_chunk_type(pc, CT_INCDEC_BEFORE);
      }
   }
} // do_symbol_check


static void check_double_brace_init(chunk_t *bo1)
{
   LOG_FUNC_ENTRY();
   LOG_FMT(LJDBI, "%s(%d): orig_line is %zu, orig_col is %zu", __func__, __LINE__, bo1->orig_line, bo1->orig_col);
   chunk_t *pc = chunk_get_prev_ncnlni(bo1);   // Issue #2279

   if (pc == nullptr)
   {
      return;
   }

   if (chunk_is_paren_close(pc))
   {
      chunk_t *bo2 = chunk_get_next(bo1);

      if (bo2 == nullptr)
      {
         return;
      }

      if (chunk_is_token(bo2, CT_BRACE_OPEN))
      {
         // found a potential double brace
         chunk_t *bc2 = chunk_skip_to_match(bo2);

         if (bc2 == nullptr)
         {
            return;
         }
         chunk_t *bc1 = chunk_get_next(bc2);

         if (bc1 == nullptr)
         {
            return;
         }

         if (chunk_is_token(bc1, CT_BRACE_CLOSE))
         {
            LOG_FMT(LJDBI, " - end, orig_line is %zu, orig_col is %zu\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");
} // check_double_brace_init


void fix_symbols(void)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc;
   chunk_t dummy;

   cpd.unc_stage = unc_stage_e::FIX_SYMBOLS;

   mark_define_expressions();

   bool is_cpp  = language_is_set(LANG_CPP);
   bool is_java = language_is_set(LANG_JAVA);

   for (pc = chunk_get_head(); pc != nullptr; pc = chunk_get_next_ncnl(pc))
   {
      if (  chunk_is_token(pc, CT_FUNC_WRAP)
         || chunk_is_token(pc, CT_TYPE_WRAP))
      {
         handle_wrap(pc);
      }

      if (chunk_is_token(pc, CT_ASSIGN))
      {
         mark_lvalue(pc);
      }
      // a brace immediately preceeded by word in C++11 is an initializer list though it may also
      // by a type casting initializer list if the word is really a type; sadly unucustify knows
      // only builtin types and knows nothing of user-defined types
      chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279

      if (  is_cpp
         && chunk_is_token(pc, CT_BRACE_OPEN)
         && (  chunk_is_token(prev, CT_WORD)
            || chunk_is_token(prev, CT_TYPE)))
      {
         mark_lvalue(pc);
      }

      if (  is_java
         && chunk_is_token(pc, CT_BRACE_OPEN))
      {
         check_double_brace_init(pc);
      }

      if (chunk_is_token(pc, CT_ATTRIBUTE))
      {
         chunk_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC);

         if (  next != nullptr
            && chunk_is_token(next, CT_PAREN_OPEN))
         {
            flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, CT_ATTRIBUTE, false);
         }
      }
   }

   pc = chunk_get_head();

   if (pc == nullptr)
   {
      return;
   }

   if (  chunk_is_newline(pc)
      || chunk_is_comment(pc))
   {
      pc = chunk_get_next_ncnl(pc);
   }

   while (pc != nullptr)
   {
      if (chunk_is_token(pc, CT_IGNORED))
      {
         pc = chunk_get_next_ncnl(pc);
         continue;
      }
      LOG_FMT(LFCNR, "%s(%d): pc->orig_line       is %zu, orig_col is %zu, text() is '%s', type is %s\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type));
      chunk_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC);   // Issue #2279

      if (prev == nullptr)
      {
         prev = &dummy;
      }
      else
      {
         // Issue #2279
         LOG_FMT(LFCNR, "%s(%d): prev(ni)->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s\n",
                 __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text(), get_token_name(prev->type));
      }
      chunk_t *next = chunk_get_next_ncnl(pc, scope_e::PREPROC);

      if (next == nullptr)
      {
         next = &dummy;
      }
      else
      {
         // Issue #2279
         LOG_FMT(LFCNR, "%s(%d): next->orig_line     is %zu, orig_col is %zu, text() is '%s', type is %s\n",
                 __func__, __LINE__, next->orig_line, next->orig_col, next->text(), get_token_name(next->type));
      }
      LOG_FMT(LFCNR, "%s(%d): do_symbol_check(%s, %s, %s)\n",
              __func__, __LINE__, prev->text(), pc->text(), next->text());
      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 != nullptr)
   {
      LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s, parent_type is %s\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type), get_token_name(pc->parent_type));

      // Can't have a variable definition inside [ ]
      if (square_level < 0)
      {
         if (chunk_is_token(pc, CT_SQUARE_OPEN))
         {
            square_level = pc->level;
         }
      }
      else
      {
         if (pc->level <= static_cast<size_t>(square_level))
         {
            square_level = -1;
         }
      }

      if (  chunk_is_token(pc, CT_EXTERN)
         && language_is_set(LANG_ALLC))
      {
         chunk_t *next = chunk_get_next_ncnl(pc);

         if (chunk_is_token(next, CT_STRING))
         {
            chunk_t *tmp = chunk_get_next_ncnl(next);

            while (tmp != nullptr)
            {
               if (  (chunk_is_token(tmp, CT_TYPE))
                  || (chunk_is_token(tmp, CT_BRACE_OPEN))
                  || (chunk_is_token(tmp, CT_ATTRIBUTE)))
               {
                  break;
               }

               if (chunk_is_token(tmp, CT_WORD))
               {
                  chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
                  break;
               }
               tmp = chunk_get_next_ncnl(tmp);
            }
         }
      }

      if (  chunk_is_token(pc, CT_ATTRIBUTE)
         && language_is_set(LANG_ALLC))
      {
         chunk_t *tmp = skip_attribute_next(pc);

         if (chunk_is_token(tmp, CT_WORD))
         {
            chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
         }
      }

      if (  chunk_is_token(pc, CT_BRACE_OPEN)                       // Issue #2332
         && get_chunk_parent_type(pc) == CT_BRACED_INIT_LIST)
      {
         LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', look for CT_BRACE_OPEN\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         pc = chunk_get_next_type(pc, CT_BRACE_CLOSE, pc->level);
      }
      /*
       * A variable definition is possible after at the start of a statement
       * that starts with: DC_MEMBER, QUALIFIER, TYPE, or WORD
       */
      // Issue #2279
      // Issue #2478
      LOG_FMT(LFCNR, "%s(%d): pc->orig_line is %zu, orig_col is %zu, text() is '%s', type is %s, parent_type is %s\n   ",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type), get_token_name(pc->parent_type));
      log_pcf_flags(LFCNR, pc->flags);

      if (  (square_level < 0)
         && pc->flags.test(PCF_STMT_START)
         && (  chunk_is_token(pc, CT_QUALIFIER)
            || chunk_is_token(pc, CT_TYPE)
            || chunk_is_token(pc, CT_TYPENAME)
            || chunk_is_token(pc, CT_DC_MEMBER)                         // Issue #2478
            || chunk_is_token(pc, CT_WORD))
         && get_chunk_parent_type(pc) != CT_ENUM
         && !pc->flags.test(PCF_IN_ENUM))
      {
         pc = fix_variable_definition(pc);
      }
      else
      {
         pc = chunk_get_next_ncnl(pc);
      }
   }
} // fix_symbols


static void mark_lvalue(chunk_t *pc)
{
   LOG_FUNC_ENTRY();
   chunk_t *prev;

   if (pc->flags.test(PCF_IN_PREPROC))
   {
      return;
   }

   for (prev = chunk_get_prev_ncnlni(pc);     // Issue #2279
        prev != nullptr;
        prev = chunk_get_prev_ncnlni(prev))   // Issue #2279
   {
      if (  prev->level < pc->level
         || chunk_is_token(prev, CT_ASSIGN)
         || chunk_is_token(prev, CT_COMMA)
         || chunk_is_token(prev, CT_BOOL)
         || chunk_is_semicolon(prev)
         || chunk_is_str(prev, "(", 1)
         || chunk_is_str(prev, "{", 1)
         || chunk_is_str(prev, "[", 1)
         || prev->flags.test(PCF_IN_PREPROC))
      {
         break;
      }
      chunk_flags_set(prev, PCF_LVALUE);

      if (prev->level == pc->level && chunk_is_str(prev, "&", 1))
      {
         make_type(prev);
      }
   }
}


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 != nullptr)
   {
      // Step backwards from pc and mark the parent of the return type
      LOG_FMT(LFCNR, "%s(%d): (backwards) return type for '%s' @ orig_line is %zu, orig_col is %zu\n",
              __func__, __LINE__, fname->text(), fname->orig_line, fname->orig_col);

      chunk_t *first = pc;

      while (pc != nullptr)
      {
         LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', type is %s, ",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type));
         log_pcf_flags(LFCNR, pc->flags);

         if (chunk_is_token(pc, CT_ANGLE_CLOSE))
         {
            pc = skip_template_prev(pc);

            if (pc == nullptr || chunk_is_token(pc, CT_TEMPLATE))
            {
               //either expression is not complete or this is smth like 'template<T> void func()'
               //  - we are not interested in 'template<T>' part
               break;
            }
            else
            {
               //this is smth like 'vector<int> func()' and 'pc' is currently on 'vector' - just proceed
            }
         }

         if (  (  !chunk_is_type(pc)
               && pc->type != CT_OPERATOR
               && pc->type != CT_WORD
               && pc->type != CT_ADDR)
            || pc->flags.test(PCF_IN_PREPROC))
         {
            break;
         }

         if (!chunk_is_ptr_operator(pc))
         {
            first = pc;
         }
         pc = chunk_get_prev_ncnlni(pc);   // Issue #2279
      }
      LOG_FMT(LFCNR, "%s(%d): marking returns...", __func__, __LINE__);

      // Changing words to types into tuple return types in CS.
      bool is_return_tuple = false;

      if (chunk_is_token(pc, CT_PAREN_CLOSE) && !pc->flags.test(PCF_IN_PREPROC))
      {
         first           = chunk_skip_to_match_rev(pc);
         is_return_tuple = true;
      }
      pc = first;

      while (pc != nullptr)
      {
         LOG_FMT(LFCNR, " text() '%s', type is %s", pc->text(), get_token_name(pc->type));

         if (parent_type != CT_NONE)
         {
            set_chunk_parent(pc, parent_type);
         }
         chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279

         if (  !is_return_tuple
            || pc->type != CT_WORD
            || (prev != nullptr && prev->type != CT_TYPE))
         {
            make_type(pc);
         }

         if (pc == start)
         {
            break;
         }
         pc = chunk_get_next_ncnl(pc);

         //template angles should keep parent type CT_TEMPLATE
         if (chunk_is_token(pc, CT_ANGLE_OPEN))
         {
            pc = chunk_get_next_type(pc, CT_ANGLE_CLOSE, pc->level);

            if (pc == start)
            {
               break;
            }
            pc = chunk_get_next_ncnl(pc);
         }
      }
      LOG_FMT(LFCNR, "\n");

      // Back up and mark parent type on friend declarations
      if (parent_type != CT_NONE && first && first->flags.test(PCF_IN_CLASS))
      {
         pc = chunk_get_prev_ncnlni(first);   // Issue #2279

         if (chunk_is_token(pc, CT_FRIEND))
         {
            LOG_FMT(LFCNR, "%s(%d): marking friend\n", __func__, __LINE__);
            set_chunk_parent(pc, parent_type);
            // A friend might be preceded by a template specification, as in:
            //   template <...> friend type func(...);
            // If so, we need to mark that also
            pc = chunk_get_prev_ncnlni(pc);   // Issue #2279

            if (chunk_is_token(pc, CT_ANGLE_CLOSE))
            {
               pc = skip_template_prev(pc);

               if (chunk_is_token(pc, CT_TEMPLATE))
               {
                  LOG_FMT(LFCNR, "%s(%d): marking friend template\n",
                          __func__, __LINE__);
                  set_chunk_parent(pc, parent_type);
               }
            }
         }
      }
   }
} // mark_function_return_type


static bool mark_function_type(chunk_t *pc)
{
   LOG_FUNC_ENTRY();
   LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s' @ orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, get_token_name(pc->type), pc->text(),
           pc->orig_line, pc->orig_col);

   size_t    star_count = 0;
   size_t    word_count = 0;
   chunk_t   *ptrcnk    = nullptr;
   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
   chunk_t *varcnk = chunk_get_prev_ncnlni(pc);   // Issue #2279

   varcnk = chunk_get_prev_ssq(varcnk);

   if (varcnk != nullptr && !chunk_is_word(varcnk))
   {
      if (  language_is_set(LANG_OC)
         && chunk_is_str(varcnk, "^", 1)
         && chunk_is_paren_open(chunk_get_prev_ncnlni(varcnk)))   // Issue #2279
      {
         // anonymous ObjC block type -- RTYPE (^)(ARGS)
         anon = true;
      }
      else
      {
         LOG_FMT(LFTYPE, "%s(%d): not a word: text() '%s', type is %s, @ orig_line is %zu:, orig_col is %zu\n",
                 __func__, __LINE__, varcnk->text(), get_token_name(varcnk->type),
                 varcnk->orig_line, varcnk->orig_col);
         goto nogo_exit;
      }
   }
   apo = chunk_get_next_ncnl(pc);

   if (apo == nullptr)
   {
      return(false);
   }
   apc = chunk_skip_to_match(apo);

   if (  apc != nullptr
      && (  !chunk_is_paren_open(apo)
         || ((apc = chunk_skip_to_match(apo)) == nullptr)))
   {
      LOG_FMT(LFTYPE, "%s(%d): not followed by parens\n", __func__, __LINE__);
      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(%d): not followed by '{' or ';'\n", __func__, __LINE__);
      goto nogo_exit;
   }
   ptp = pc->flags.test(PCF_IN_TYPEDEF) ? CT_FUNC_TYPE : CT_FUNC_VAR;

   tmp = pc;

   while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr)   // Issue #2279
   {
      tmp = chunk_get_prev_ssq(tmp);

      LOG_FMT(LFTYPE, " -- type is %s, %s on orig_line %zu, orig_col is %zu",
              get_token_name(tmp->type), tmp->text(),
              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)
              || chunk_is_token(tmp, CT_WORD)
              || chunk_is_token(tmp, CT_TYPE))
      {
         word_count++;
         LOG_FMT(LFTYPE, " -- TYPE(%s)\n", tmp->text());
      }
      else if (chunk_is_token(tmp, 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: type is %s, text() '%s', on orig_line %zu, orig_col %zu\n",
                 get_token_name(tmp->type), tmp->text(),
                 tmp->orig_line, tmp->orig_col);
         goto nogo_exit;
      }
   }

   // Fixes #issue 1577
   // Allow word count 2 incase of function pointer declaration.
   // Ex: bool (__stdcall* funcptr)(int, int);
   if (  star_count > 1
      || (word_count > 1 && !(word_count == 2 && ptp == CT_FUNC_VAR))
      || ((star_count + word_count) == 0))
   {
      LOG_FMT(LFTYPE, "%s(%d): bad counts word: %zu, star: %zu\n",
              __func__, __LINE__, 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_ncnlni(tmp)))   // Issue #2279
   {
      goto nogo_exit;
   }

   if (ptrcnk)
   {
      set_chunk_type(ptrcnk, CT_PTR_TYPE);
   }

   if (!anon)
   {
      if (pc->flags.test(PCF_IN_TYPEDEF))
      {
         set_chunk_type(varcnk, CT_TYPE);
      }
      else
      {
         set_chunk_type(varcnk, CT_FUNC_VAR);
         chunk_flags_set(varcnk, 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.test(PCF_IN_TYPEDEF) ? CT_TYPEDEF : CT_FUNC_VAR);
   }
   else if (chunk_is_token(aft, CT_BRACE_OPEN))
   {
      flag_parens(aft, PCF_NONE, CT_NONE, pt, false);
   }
   // Step backwards to the previous open paren and mark everything a
   tmp = pc;

   while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr)   // Issue #2279
   {
      LOG_FMT(LFTYPE, " ++ type is %s, text() '%s', on orig_line %zu, orig_col %zu\n",
              get_token_name(tmp->type), tmp->text(),
              tmp->orig_line, tmp->orig_col);

      if (*tmp->str.c_str() == '(')
      {
         if (!pc->flags.test(PCF_IN_TYPEDEF))
         {
            chunk_flags_set(tmp, PCF_VAR_1ST_DEF);
         }
         set_chunk_type(tmp, CT_TPAREN_OPEN);
         set_chunk_parent(tmp, ptp);

         tmp = chunk_get_prev_ncnlni(tmp);   // Issue #2279

         if (  chunk_is_token(tmp, CT_FUNCTION)
            || chunk_is_token(tmp, CT_FUNC_CALL)
            || chunk_is_token(tmp, CT_FUNC_CALL_USER)
            || chunk_is_token(tmp, CT_FUNC_DEF)
            || chunk_is_token(tmp, CT_FUNC_PROTO))
         {
            set_chunk_type(tmp, CT_TYPE);
            chunk_flags_clr(tmp, 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 orig_line is %zu, orig_col is %zu\n",
              __func__, __LINE__, tmp->orig_line, tmp->orig_col);
      flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
   }
   return(false);
} // mark_function_type


static void process_returns(void)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc;

   pc = chunk_get_head();

   while (pc != nullptr)
   {
      if (pc->type != CT_RETURN)
      {
         pc = chunk_get_next_type(pc, CT_RETURN, -1);
         continue;
      }
      pc = process_return(pc);
   }
}


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_ppa_get_next_ncnl(pc);

   if (  next == nullptr || chunk_is_semicolon(next)
      || chunk_is_token(next, CT_NEWLINE))
   {
      return(next);
   }
   log_rule_B("nl_return_expr");

   if (  options::nl_return_expr() != IARF_IGNORE
      && !pc->flags.test(PCF_IN_PREPROC))
   {
      newline_iarf(pc, options::nl_return_expr());
   }

   if (chunk_is_token(next, CT_PAREN_OPEN))
   {
      // See if the return is fully paren'd
      cpar = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);

      if (cpar == nullptr)
      {
         return(nullptr);
      }
      semi = chunk_ppa_get_next_ncnl(cpar);

      if (semi == nullptr)
      {
         return(nullptr);
      }

      if (chunk_is_token(semi, CT_NEWLINE) || chunk_is_semicolon(semi))
      {
         log_rule_B("mod_paren_on_return");

         if (options::mod_paren_on_return() == IARF_REMOVE)
         {
            LOG_FMT(LRETURN, "%s(%d): removing parens on orig_line %zu\n",
                    __func__, __LINE__, pc->orig_line);

            // lower the level of everything
            for (temp = next; temp != cpar; temp = chunk_get_next(temp))
            {
               if (temp->level == 0)
               {
                  fprintf(stderr, "%s(%d): temp->level is ZERO, cannot be decremented, at line %zu, column %zu\n",
                          __func__, __LINE__, temp->orig_line, temp->orig_col);
                  log_flush(true);
                  exit(EX_SOFTWARE);
               }
               temp->level--;
            }

            // delete the parenthesis
            chunk_del(next);
            chunk_del(cpar);

            // back up following chunks
            temp = semi;

            while (temp != nullptr && temp->type != CT_NEWLINE)
            {
               temp->column       = temp->column - 2;
               temp->orig_col     = temp->orig_col - 2;
               temp->orig_col_end = temp->orig_col_end - 2;
               temp               = chunk_get_next(temp);
            }
         }
         else
         {
            LOG_FMT(LRETURN, "%s(%d): keeping parens on orig_line %zu\n",
                    __func__, __LINE__, 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?
   log_rule_B("mod_paren_on_return");

   if (!(options::mod_paren_on_return() & IARF_ADD))
   {
      return(next);
   }

   // Issue #1917
   // Never add parens to a braced init list; that breaks the code
   //   return {args...};    // C++11 type elision; okay
   //   return ({args...});  // ill-formed
   if (  language_is_set(LANG_CPP) && chunk_is_token(next, CT_BRACE_OPEN)
      && get_chunk_parent_type(next) == CT_BRACED_INIT_LIST)
   {
      LOG_FMT(LRETURN, "%s(%d): not adding parens around braced initializer"
              " on orig_line %zd\n",
              __func__, __LINE__, pc->orig_line);
      return(next);
   }
   // find the next semicolon on the same level
   semi = next;

   if (pc->flags.test(PCF_IN_PREPROC))
   {
      while ((semi = semi->next) != nullptr)
      {
         if (!semi->flags.test(PCF_IN_PREPROC))
         {
            break;
         }

         if (semi->level < pc->level)
         {
            return(semi);
         }

         if (chunk_is_semicolon(semi) && pc->level == semi->level)
         {
            break;
         }
      }
   }
   else
   {
      while ((semi = chunk_get_next(semi)) != nullptr)
      {
         if (semi->level < pc->level)
         {
            return(semi);
         }

         if (chunk_is_semicolon(semi) && pc->level == semi->level)
         {
            break;
         }
      }
   }

   if (semi)
   {
      // add the parenthesis
      set_chunk_type(&chunk, CT_PAREN_OPEN);
      set_chunk_parent(&chunk, CT_RETURN);
      chunk.str         = "(";
      chunk.level       = pc->level;
      chunk.brace_level = pc->brace_level;
      chunk.orig_line   = pc->orig_line;
      chunk.orig_col    = next->orig_col - 1;
      chunk.flags       = pc->flags & PCF_COPY_FLAGS;
      chunk_add_before(&chunk, next);

      set_chunk_type(&chunk, CT_PAREN_CLOSE);
      chunk.str       = ")";
      chunk.orig_line = semi->orig_line;
      chunk.orig_col  = semi->orig_col - 1;
      cpar            = chunk_add_before(&chunk, semi);

      LOG_FMT(LRETURN, "%s(%d): added parens on orig_line %zu\n",
              __func__, __LINE__, pc->orig_line);

      for (temp = next; temp != cpar; temp = chunk_get_next(temp))
      {
         temp->level++;
      }
   }
   return(semi);
} // process_return


static bool is_ucase_str(const char *str, size_t len)
{
   while (len-- > 0)
   {
      if (unc_toupper(*str) != *str)
      {
         return(false);
      }
      str++;
   }
   return(true);
}


static bool is_oc_block(chunk_t *pc)
{
   return(  pc != nullptr
         && (  get_chunk_parent_type(pc) == CT_OC_BLOCK_TYPE
            || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR
            || get_chunk_parent_type(pc) == CT_OC_BLOCK_ARG
            || get_chunk_parent_type(pc) == CT_OC_BLOCK
            || chunk_is_token(pc, CT_OC_BLOCK_CARET)
            || (pc->next != nullptr && pc->next->type == CT_OC_BLOCK_CARET)
            || (pc->prev != nullptr && pc->prev->type == CT_OC_BLOCK_CARET)));
}


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 = nullptr;
   chunk_t    *paren_close;
   const char *verb      = "likely";
   const char *detail    = "";
   size_t     count      = 0;
   int        word_count = 0;
   bool       nope;
   bool       doubtful_cast = false;


   LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, start->text(), start->orig_line, start->orig_col);

   prev = chunk_get_prev_ncnlni(start);   // Issue #2279

   if (prev == nullptr)
   {
      return;
   }

   if (chunk_is_token(prev, CT_PP_DEFINED))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after defined\n",
              __func__, __LINE__);
      return;
   }

   if (chunk_is_token(prev, CT_ANGLE_CLOSE))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after > (template)\n",
              __func__, __LINE__);
      return;
   }
   // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren
   pc    = chunk_get_next_ncnl(start);
   first = pc;

   while (  pc != nullptr
         && (  chunk_is_type(pc)
            || chunk_is_token(pc, CT_WORD)
            || chunk_is_token(pc, CT_QUALIFIER)
            || chunk_is_token(pc, CT_DC_MEMBER)
            || chunk_is_token(pc, CT_PP)
            || chunk_is_token(pc, CT_STAR)
            || chunk_is_token(pc, CT_QUESTION)
            || chunk_is_token(pc, CT_CARET)
            || chunk_is_token(pc, CT_TSQUARE)
            || (  (  chunk_is_token(pc, CT_ANGLE_OPEN)
                  || chunk_is_token(pc, CT_ANGLE_CLOSE))
               && language_is_set(LANG_OC | LANG_JAVA))
            || (  (  chunk_is_token(pc, CT_QUESTION)
                  || chunk_is_token(pc, CT_COMMA)
                  || chunk_is_token(pc, CT_MEMBER))
               && language_is_set(LANG_JAVA))
            || chunk_is_token(pc, CT_AMP)))
   {
      LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));

      if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER)))
      {
         word_count++;
      }
      else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP))
      {
         // might be negativ, such as with:
         // a = val + (CFoo::bar_t)7;
         word_count--;
      }
      last = pc;
      pc   = chunk_get_next_ncnl(pc);
      count++;
   }

   if (  pc == nullptr
      || pc->type != CT_PAREN_CLOSE
      || chunk_is_token(prev, CT_OC_CLASS))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast, hit type is %s\n",
              __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type));
      return;
   }

   if (word_count > 1)
   {
      LOG_FMT(LCASTS, "%s(%d):  -- too many words: %d\n",
              __func__, __LINE__, word_count);
      return;
   }
   paren_close = pc;

   // If last is a type or star/caret, we have a cast for sure
   if (  chunk_is_token(last, CT_STAR)
      || chunk_is_token(last, CT_CARET)
      || chunk_is_token(last, CT_PTR_TYPE)
      || chunk_is_token(last, CT_TYPE)
      || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA)))
   {
      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 (language_is_set(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 (chunk_is_token(after, CT_PAREN_OPEN));

      if (after == nullptr)
      {
         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - hit NULL\n",
                 __func__, __LINE__);
         return;
      }
      nope = false;

      if (chunk_is_ptr_operator(pc))
      {
         // star (*) and address (&) are ambiguous
         if (  chunk_is_token(after, CT_NUMBER_FP)
            || chunk_is_token(after, CT_NUMBER)
            || chunk_is_token(after, CT_STRING)
            || doubtful_cast)
         {
            nope = true;
         }
      }
      else if (chunk_is_token(pc, CT_MINUS))
      {
         // (UINT8)-1 or (foo)-1 or (FOO)-'a'
         if (chunk_is_token(after, CT_STRING) || doubtful_cast)
         {
            nope = true;
         }
      }
      else if (chunk_is_token(pc, 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_THIS
              && pc->type != CT_TYPE
              && pc->type != CT_PAREN_OPEN
              && pc->type != CT_STRING
              && pc->type != CT_DECLTYPE
              && pc->type != CT_SIZEOF
              && get_chunk_parent_type(pc) != CT_SIZEOF
              && pc->type != CT_FUNC_CALL
              && pc->type != CT_FUNC_CALL_USER
              && pc->type != CT_FUNCTION
              && pc->type != CT_BRACE_OPEN
              && (!(  chunk_is_token(pc, CT_SQUARE_OPEN)
                   && language_is_set(LANG_OC))))
      {
         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by text() '%s', type is %s\n",
                 __func__, __LINE__, pc->text(), get_token_name(pc->type));
         return;
      }

      if (nope)
      {
         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - text() '%s' followed by type %s\n",
                 __func__, __LINE__, pc->text(), get_token_name(after->type));
         return;
      }
   }
   // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't
   pc = chunk_get_next_ncnl(paren_close);

   if (pc == nullptr)
   {
      return;
   }

   if (  chunk_is_semicolon(pc)
      || chunk_is_token(pc, CT_COMMA)
      || chunk_is_token(pc, CT_BOOL)               // Issue #2151
      || chunk_is_paren_close(pc))
   {
      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by type %s\n",
              __func__, __LINE__, 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(%d):  -- %s c-cast: (",
           __func__, __LINE__, verb);

   for (pc = first;
        pc != nullptr && pc != paren_close;
        pc = chunk_get_next_ncnl(pc))
   {
      set_chunk_parent(pc, CT_C_CAST);
      make_type(pc);
      LOG_FMT(LCASTS, " %s", pc->text());
   }

   LOG_FMT(LCASTS, " )%s\n", detail);

   // Mark the next item as an expression start
   pc = chunk_get_next_ncnl(paren_close);

   if (pc != nullptr)
   {
      chunk_flags_set(pc, PCF_EXPR_START);

      if (chunk_is_opening_brace(pc))
      {
         set_paren_parent(pc, get_chunk_parent_type(start));
      }
   }
} // fix_casts


static void fix_type_cast(chunk_t *start)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc;

   pc = chunk_get_next_ncnl(start);

   if (pc == nullptr || pc->type != CT_ANGLE_OPEN)
   {
      return;
   }

   while (  ((pc = chunk_get_next_ncnl(pc)) != nullptr)
         && pc->level >= start->level)
   {
      if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE))
      {
         pc = chunk_get_next_ncnl(pc);

         if (pc == nullptr)
         {
            return;
         }

         if (chunk_is_str(pc, "(", 1))
         {
            set_paren_parent(pc, CT_TYPE_CAST);
         }
         return;
      }
      make_type(pc);
   }
}


static void fix_enum_struct_union(chunk_t *pc)
{
   LOG_FUNC_ENTRY();
   chunk_t     *next;
   chunk_t     *prev        = nullptr;
   pcf_flags_t flags        = PCF_VAR_1ST_DEF;
   auto const  in_fcn_paren = pc->flags & PCF_IN_FCN_DEF;

   // Make sure this wasn't a cast
   if (get_chunk_parent_type(pc) == CT_C_CAST)
   {
      return;
   }
   // the next item is either a type or open brace
   next = chunk_get_next_ncnl(pc);

   // the enum-key might be enum, enum class or enum struct (TODO)
   if (chunk_is_token(next, CT_ENUM_CLASS))
   {
      next = chunk_get_next_ncnl(next); // get the next one
   }

   if (language_is_set(LANG_CPP))
   {
      next = skip_attribute_next(next); // get the next one
   }

   // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace
   if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON))
   {
      // i.e. "enum xyz : unsigned int { ... };"
      // i.e. "enum class xyz : unsigned int { ... };"
      // i.e. "enum : unsigned int { ... };"
      // xyz is a type

      // save the type if it exists
      if (!chunk_is_token(next, CT_COLON))
      {
         set_chunk_parent(next, pc->type);
         prev = next;
         next = chunk_get_next_ncnl(next);
      }

      if (next == nullptr)
      {
         return;
      }
      set_chunk_parent(next, pc->type);
      auto const is_struct_or_class =
         (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS));

      // next up is either a colon, open brace, or open parenthesis (pawn)
      if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN))
      {
         next = set_paren_parent(next, CT_ENUM);
      }
      else if (chunk_is_token(next, CT_COLON))
      {
         if (chunk_is_token(pc, CT_ENUM))
         {
            // enum TYPE : INT_TYPE { ... };
            next = chunk_get_next_ncnl(next);

            if (next != nullptr)
            {
               make_type(next);
               next = chunk_get_next_ncnl(next);

               // enum TYPE : unsigned int { ... };
               if (chunk_is_token(next, CT_TYPE))
               {
                  // get the next part of the type
                  next = chunk_get_next_ncnl(next);
               }
            }
         }
         else if (is_struct_or_class)
         {
            next = skip_parent_types(next);
         }
      }
      else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN))
      {
         // Fix #1267 structure attributes
         // struct __attribute__(align(x)) struct_name;
         // skip to matching parenclose and make next token as type.
         next = chunk_skip_to_match(next);
         next = chunk_get_next_ncnl(next);
         set_chunk_type(next, CT_TYPE);
         set_chunk_parent(next, pc->type);
      }

      if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration
      {
         set_chunk_parent(next, pc->type);
         flag_series(pc, prev, PCF_INCOMPLETE);
         return;
      }
   }

   if (chunk_is_token(next, CT_BRACE_OPEN))
   {
      auto const flag = [pc] {
         switch (pc->type)
         {
         case CT_ENUM:
            return(PCF_IN_ENUM);

         case CT_STRUCT:
            return(PCF_IN_STRUCT);

         case CT_CLASS:
            return(PCF_IN_CLASS);

         default:
            return(PCF_NONE);
         }
      }();

      flag_parens(next, flag, CT_NONE, CT_NONE, false);

      if (  chunk_is_token(pc, CT_UNION)
         || chunk_is_token(pc, CT_STRUCT)
         || chunk_is_token(pc, CT_CLASS))
      {
         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 != nullptr)
      {
         set_chunk_parent(next, pc->type);
         next = chunk_get_next_ncnl(next);
      }
      prev = nullptr;
   }
   // reset var name parent type
   else if (next && prev)
   {
      set_chunk_parent(prev, CT_NONE);
   }

   if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE))
   {
      return;
   }

   if (!chunk_is_semicolon(next))
   {
      // Pawn does not require a semicolon after an enum
      if (language_is_set(LANG_PAWN))
      {
         return;
      }

      /*
       * D does not require a semicolon after an enum, but we add one to make
       * other code happy.
       */
      if (language_is_set(LANG_D))
      {
         next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next));   // Issue #2279
      }
   }

   // We are either pointing to a ';' or a variable
   while (  next != nullptr
         && !chunk_is_semicolon(next)
         && next->type != CT_ASSIGN
         && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any())
   {
      if (next->level == pc->level)
      {
         if (chunk_is_token(next, CT_WORD))
         {
            chunk_flags_set(next, flags);
            flags &= ~PCF_VAR_1ST;   // clear the first flag for the next items
            LOG_FMT(LCASTS, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         }

         if (  chunk_is_token(next, CT_STAR)
            || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET)))
         {
            set_chunk_type(next, CT_PTR_TYPE);
         }

         // If we hit a comma in a function param, we are done
         if (  (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE))
            && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL)))
         {
            return;
         }
      }
      next = chunk_get_next_ncnl(next);
   }

   if (  next != nullptr
      && chunk_is_token(next, CT_SEMICOLON))
   {
      set_chunk_parent(next, pc->type);
   }
} // fix_enum_struct_union


static void fix_typedef(chunk_t *start)
{
   LOG_FUNC_ENTRY();

   if (start == nullptr)
   {
      return;
   }
   LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n",
           __func__, __LINE__, start->orig_line, start->orig_col);

   chunk_t *the_type = nullptr;
   chunk_t *last_op  = nullptr;

   /*
    * Mark everything in the typedef and scan for ")(", which makes it a
    * function type
    */
   for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC)
        ; next != nullptr && next->level >= start->level
        ; next = chunk_get_next_ncnl(next, scope_e::PREPROC))
   {
      chunk_flags_set(next, PCF_IN_TYPEDEF);

      if (start->level == next->level)
      {
         if (chunk_is_semicolon(next))
         {
            set_chunk_parent(next, CT_TYPEDEF);
            break;
         }

         if (chunk_is_token(next, CT_ATTRIBUTE))
         {
            break;
         }

         if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN))
         {
            set_chunk_parent(next, CT_TYPEDEF);
            break;
         }
         make_type(next);

         if (chunk_is_token(next, CT_TYPE))
         {
            the_type = next;
         }
         chunk_flags_clr(next, PCF_VAR_1ST_DEF);

         if (*next->str.c_str() == '(')
         {
            last_op = next;
         }
      }
   }

   // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def
   if (  last_op != nullptr
      && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM))
   {
      flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false);
      fix_fcn_def_params(last_op);

      the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC);   // Issue #2279

      if (the_type == nullptr)
      {
         return;
      }
      chunk_t *open_paren = nullptr;

      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_ncnlni(the_type, scope_e::PREPROC);   // Issue #2279

         if (the_type == nullptr)
         {
            return;
         }
      }
      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(%d): fcn typedef text() '%s', on orig_line %zu\n",
              __func__, __LINE__, the_type->text(), the_type->orig_line);

      // If we are aligning on the open parenthesis, grab that instead
      log_rule_B("align_typedef_func");

      if (open_paren != nullptr && options::align_typedef_func() == 1)
      {
         the_type = open_paren;
      }
      log_rule_B("align_typedef_func");

      if (options::align_typedef_func() != 0)
      {
         LOG_FMT(LTYPEDEF, "%s(%d):  -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n",
                 __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col);
         chunk_flags_set(the_type, 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
    */
   chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC);

   if (after == nullptr)
   {
      return;
   }

   if (  after->type != CT_ENUM
      && after->type != CT_STRUCT
      && after->type != CT_UNION)
   {
      if (the_type != nullptr)
      {
         // We have just a regular typedef
         LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n",
                 __func__, __LINE__, the_type->text(), the_type->orig_line);
         chunk_flags_set(the_type, PCF_ANCHOR);
      }
      return;
   }
   // We have a struct/union/enum, next should be either a type or {
   chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC);

   if (next == nullptr)
   {
      return;
   }

   if (chunk_is_token(next, CT_TYPE))
   {
      next = chunk_get_next_ncnl(next, scope_e::PREPROC);

      if (next == nullptr)
      {
         return;
      }
   }

   if (chunk_is_token(next, CT_BRACE_OPEN))
   {
      // Skip to the closing brace
      chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC);

      if (br_c != nullptr)
      {
         const c_token_t tag = after->type;
         set_chunk_parent(next, tag);
         set_chunk_parent(br_c, tag);

         if (tag == CT_ENUM)
         {
            flag_series(after, br_c, PCF_IN_ENUM);
         }
         else if (tag == CT_STRUCT)
         {
            flag_series(after, br_c, PCF_IN_STRUCT);
         }
      }
   }

   if (the_type != nullptr)
   {
      LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n",
              __func__, __LINE__, get_token_name(after->type), the_type->text(),
              the_type->orig_line);
      chunk_flags_set(the_type, PCF_ANCHOR);
   }
} // fix_typedef


static void mark_variable_stack(ChunkStack &cs, log_sev_t sev)
{
   UNUSED(sev);
   LOG_FUNC_ENTRY();

   // throw out the last word and mark the rest
   chunk_t *var_name = cs.Pop_Back();

   if (var_name && var_name->prev->type == CT_DC_MEMBER)
   {
      cs.Push_Back(var_name);
   }

   if (var_name != nullptr)
   {
      LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n",
              __func__, __LINE__, var_name->orig_line, var_name->orig_col);

      size_t  word_cnt = 0;
      chunk_t *word_type;

      while ((word_type = cs.Pop_Back()) != nullptr)
      {
         if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE))
         {
            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text());
            set_chunk_type(word_type, CT_TYPE);
            chunk_flags_set(word_type, PCF_VAR_TYPE);
         }
         word_cnt++;
      }

      if (chunk_is_token(var_name, CT_WORD))
      {
         if (word_cnt > 0)
         {
            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n",
                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
            chunk_flags_set(var_name, PCF_VAR_DEF);
         }
         else
         {
            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
            set_chunk_type(var_name, CT_TYPE);
            chunk_flags_set(var_name, PCF_VAR_TYPE);
         }
      }
   }
} // mark_variable_stack


static void fix_fcn_def_params(chunk_t *start)
{
   LOG_FUNC_ENTRY();

   if (start == nullptr)
   {
      return;
   }
   LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n",
           __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level);

   while (start != nullptr && !chunk_is_paren_open(start))
   {
      start = chunk_get_next_ncnl(start);
   }

   if (start == nullptr)// Coverity CID 76003, 1100782
   {
      return;
   }
   // ensure start chunk holds a single '(' character
   assert((start->len() == 1) && (start->str[0] == '('));

   ChunkStack cs;
   size_t     level = start->level + 1;
   chunk_t    *pc   = start;

   while ((pc = chunk_get_next_ncnl(pc)) != nullptr)
   {
      if (  ((start->len() == 1) && (start->str[0] == ')'))
         || pc->level < level)
      {
         LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n",
                 __func__, __LINE__, pc->text(), pc->orig_line);
         break;
      }
      LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n",
              __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at",
              pc->text(), pc->orig_line, pc->level);

      if (pc->level > level)
      {
         continue;
      }

      if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
         cs.Push_Back(pc);
      }
      else if (  chunk_is_token(pc, CT_AMP)
              || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2)))
      {
         set_chunk_type(pc, CT_BYREF);
         cs.Push_Back(pc);
      }
      else if (chunk_is_token(pc, CT_TYPE_WRAP))
      {
         cs.Push_Back(pc);
      }
      else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))
      {
         cs.Push_Back(pc);
      }
      else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN))
      {
         mark_variable_stack(cs, LFCNP);

         if (chunk_is_token(pc, CT_ASSIGN))
         {
            // Mark assignment for default param spacing
            set_chunk_parent(pc, CT_FUNC_PROTO);
         }
      }
   }
   mark_variable_stack(cs, LFCNP);
} // fix_fcn_def_params


static chunk_t *skip_to_next_statement(chunk_t *pc)
{
   while (  pc != nullptr
         && !chunk_is_semicolon(pc)
         && pc->type != CT_BRACE_OPEN
         && pc->type != CT_BRACE_CLOSE)
   {
      pc = chunk_get_next_ncnl(pc);
   }
   return(pc);
}


static chunk_t *fix_variable_definition(chunk_t *start)
{
   LOG_FUNC_ENTRY();
   chunk_t    *pc = start;
   chunk_t    *end;
   chunk_t    *tmp_pc;
   ChunkStack cs;
   int        idx;
   int        ref_idx;

   LOG_FMT(LFVD, "%s(%d): start at pc->orig_line is %zu, pc->orig_col is %zu\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col);

   // Scan for words and types and stars oh my!
   while (  chunk_is_token(pc, CT_TYPE)
         || chunk_is_token(pc, CT_WORD)
         || chunk_is_token(pc, CT_QUALIFIER)
         || chunk_is_token(pc, CT_TYPENAME)
         || chunk_is_token(pc, CT_DC_MEMBER)
         || chunk_is_token(pc, CT_MEMBER)
         || chunk_is_ptr_operator(pc))
   {
      LOG_FMT(LFVD, "%s(%d):   1:pc->text() '%s', type is %s\n",
              __func__, __LINE__, pc->text(), get_token_name(pc->type));
      cs.Push_Back(pc);
      pc = chunk_get_next_ncnl(pc);

      if (pc == nullptr)
      {
         LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__);
         return(nullptr);
      }
      LOG_FMT(LFVD, "%s(%d):   2:pc->text() '%s', type is %s\n",
              __func__, __LINE__, pc->text(), get_token_name(pc->type));

      // Skip templates and attributes
      pc = skip_template_next(pc);

      if (pc == nullptr)
      {
         LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__);
         return(nullptr);
      }
      LOG_FMT(LFVD, "%s(%d):   3:pc->text() '%s', type is %s\n",
              __func__, __LINE__, pc->text(), get_token_name(pc->type));

      pc = skip_attribute_next(pc);

      if (pc == nullptr)
      {
         LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__);
         return(nullptr);
      }
      LOG_FMT(LFVD, "%s(%d):   4:pc->text() '%s', type is %s\n",
              __func__, __LINE__, pc->text(), get_token_name(pc->type));

      if (language_is_set(LANG_JAVA))
      {
         pc = skip_tsquare_next(pc);
         LOG_FMT(LFVD, "%s(%d):   5:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type));
      }
   }
   end = pc;

   if (end == nullptr)
   {
      LOG_FMT(LFVD, "%s(%d): end is nullptr\n", __func__, __LINE__);
      return(nullptr);
   }
   LOG_FMT(LFVD, "%s(%d): end->type is %s\n", __func__, __LINE__, get_token_name(end->type));

   if (  cs.Len() == 1
      && chunk_is_token(end, CT_BRACE_OPEN)
      && get_chunk_parent_type(end) == CT_BRACED_INIT_LIST)
   {
      set_chunk_type(cs.Get(0)->m_pc, CT_TYPE);
   }

   // Function defs are handled elsewhere
   if (  (cs.Len() <= 1)
      || chunk_is_token(end, CT_FUNC_DEF)
      || chunk_is_token(end, CT_FUNC_PROTO)
      || chunk_is_token(end, CT_FUNC_CLASS_DEF)
      || chunk_is_token(end, CT_FUNC_CLASS_PROTO)
      || chunk_is_token(end, CT_OPERATOR))
   {
      return(skip_to_next_statement(end));
   }
   // ref_idx points to the alignable part of the variable definition
   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;
         }

         if (idx == 0)
         {
            fprintf(stderr, "%s(%d): idx is ZERO, cannot be decremented, at line %zu, column %zu\n",
                    __func__, __LINE__, tmp_pc->orig_line, tmp_pc->orig_col);
            log_flush(true);
            exit(EX_SOFTWARE);
         }
         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, "%s(%d): ref_idx(%d) is '%s'\n", __func__, __LINE__, ref_idx, tmp_pc->text());

   // No type part found!
   if (ref_idx <= 0)
   {
      return(skip_to_next_statement(end));
   }
   LOG_FMT(LFVD2, "%s(%d): orig_line is %zu, TYPE : ", __func__, __LINE__, start->orig_line);

   for (size_t idxForCs = 0; idxForCs < cs.Len() - 1; idxForCs++)
   {
      tmp_pc = cs.Get(idxForCs)->m_pc;
      make_type(tmp_pc);
      chunk_flags_set(tmp_pc, PCF_VAR_TYPE);
      LOG_FMT(LFVD2, " text() is '%s', type is %s", tmp_pc->text(), get_token_name(tmp_pc->type));
   }

   LOG_FMT(LFVD2, "\n");

   // OK we have two or more items, mark types up to the end.
   LOG_FMT(LFVD, "%s(%d): pc->orig_line is %zu, pc->orig_col is %zu\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col);
   mark_variable_definition(cs.Get(cs.Len() - 1)->m_pc);

   if (chunk_is_token(end, CT_COMMA))
   {
      return(chunk_get_next_ncnl(end));
   }
   return(skip_to_next_statement(end));
} // fix_variable_definition


static chunk_t *skip_expression(chunk_t *start)
{
   chunk_t *pc = start;

   while (pc != nullptr && pc->level >= start->level)
   {
      if (  pc->level == start->level
         && (chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA)))
      {
         return(pc);
      }
      pc = chunk_get_next_ncnl(pc);
   }
   return(pc);
}


bool go_on(chunk_t *pc, chunk_t *start)
{
   if (pc == nullptr || pc->level != start->level)
   {
      return(false);
   }

   if (pc->flags.test(PCF_IN_FOR))
   {
      return((!chunk_is_semicolon(pc)) && (!(chunk_is_token(pc, CT_COLON))));
   }
   return(!chunk_is_semicolon(pc));
} // go_on


static chunk_t *mark_variable_definition(chunk_t *start)
{
   LOG_FUNC_ENTRY();

   if (start == nullptr)
   {
      return(nullptr);
   }
   chunk_t     *pc   = start;
   pcf_flags_t flags = PCF_VAR_1ST_DEF;

   LOG_FMT(LVARDEF, "%s(%d): orig_line %zu, orig_col %zu, text() '%s', type is %s\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(),
           get_token_name(pc->type));

   // Issue #596
   bool bit_field_colon_is_present = false;

   while (go_on(pc, start))
   {
      if (  chunk_is_token(pc, CT_WORD)
         || chunk_is_token(pc, CT_FUNC_CTOR_VAR))
      {
         auto const orig_flags = pc->flags;

         if (!pc->flags.test(PCF_IN_ENUM))
         {
            chunk_flags_set(pc, flags);
         }
         flags &= ~PCF_VAR_1ST;
         LOG_FMT(LVARDEF, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());

         LOG_FMT(LVARDEF,
                 "%s(%d): orig_line is %zu, marked text() '%s'[%s] "
                 "in orig_col %zu, flags: %s -> %s\n",
                 __func__, __LINE__, pc->orig_line, pc->text(),
                 get_token_name(pc->type), pc->orig_col,
                 pcf_flags_str(orig_flags).c_str(),
                 pcf_flags_str(pc->flags).c_str());
      }
      else if (  !bit_field_colon_is_present                      // Issue #2689
              && (  chunk_is_star(pc)
                 || chunk_is_msref(pc)))
      {
         set_chunk_type(pc, CT_PTR_TYPE);
      }
      else if (chunk_is_addr(pc))
      {
         set_chunk_type(pc, CT_BYREF);
      }
      else if (  chunk_is_token(pc, CT_SQUARE_OPEN)
              || chunk_is_token(pc, CT_ASSIGN))
      {
         pc = skip_expression(pc);
         continue;
      }
      else if (chunk_is_token(pc, CT_COLON))
      {
         bit_field_colon_is_present = true;                    // Issue #2689
      }
      pc = chunk_get_next_ncnl(pc);
   }
   return(pc);
} // mark_variable_definition


static bool can_be_full_param(chunk_t *start, chunk_t *end)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LFPARAM, "%s:", __func__);

   int     word_count = 0;
   int     type_count = 0;
   chunk_t *pc;

   for (pc = start;
        pc != nullptr && pc != end;
        pc = chunk_get_next_ncnl(pc, scope_e::PREPROC))
   {
      LOG_FMT(LFPARAM, " [%s]", pc->text());

      if (  chunk_is_token(pc, CT_QUALIFIER)
         || chunk_is_token(pc, CT_STRUCT)
         || chunk_is_token(pc, CT_ENUM)
         || chunk_is_token(pc, CT_UNION)
         || chunk_is_token(pc, CT_TYPENAME))
      {
         LOG_FMT(LFPARAM, " <== %s! (yes)\n", get_token_name(pc->type));
         return(true);
      }

      if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))
      {
         ++word_count;

         if (chunk_is_token(pc, CT_TYPE))
         {
            ++type_count;
         }
      }
      else if (chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_DC_MEMBER))
      {
         if (word_count > 0)
         {
            --word_count;
         }
      }
      else if (pc != start && chunk_is_ptr_operator(pc))
      {
         // chunk is OK
      }
      else if (chunk_is_token(pc, CT_ASSIGN))
      {
         // chunk is OK (default values)
         break;
      }
      else if (chunk_is_token(pc, CT_ANGLE_OPEN))
      {
         LOG_FMT(LFPARAM, " <== template\n");
         return(true);
      }
      else if (chunk_is_token(pc, CT_ELLIPSIS))
      {
         LOG_FMT(LFPARAM, " <== elipses\n");
         return(true);
      }
      else if (word_count == 0 && chunk_is_token(pc, CT_PAREN_OPEN))
      {
         // Check for old-school func proto param '(type)'
         chunk_t *tmp1 = chunk_skip_to_match(pc, scope_e::PREPROC);

         if (tmp1 == nullptr)
         {
            return(false);
         }
         chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC);

         if (tmp2 == nullptr)
         {
            return(false);
         }

         if (chunk_is_token(tmp2, CT_COMMA) || chunk_is_paren_close(tmp2))
         {
            do
            {
               pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);

               if (pc == nullptr)
               {
                  return(false);
               }
               LOG_FMT(LFPARAM, " [%s]", pc->text());
            } while (pc != tmp1);

            // reset some vars to allow [] after parens
            word_count = 1;
            type_count = 1;
         }
         else
         {
            LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type));
            return(false);
         }
      }
      else if (  (word_count == 1 || (word_count == type_count))
              && chunk_is_token(pc, CT_PAREN_OPEN))
      {
         // Check for func proto param 'void (*name)' or 'void (*name)(params)' or 'void (^name)(params)'
         // <name> can be optional
         chunk_t *tmp1 = chunk_get_next_ncnl(pc, scope_e::PREPROC);

         if (tmp1 == nullptr)
         {
            return(false);
         }
         chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC);

         if (tmp2 == nullptr)
         {
            return(false);
         }
         chunk_t *tmp3 = (chunk_is_str(tmp2, ")", 1)) ? tmp2 : chunk_get_next_ncnl(tmp2, scope_e::PREPROC);

         if (tmp3 == nullptr)
         {
            return(false);
         }

         if (  !chunk_is_str(tmp3, ")", 1)
            || !(chunk_is_str(tmp1, "*", 1) || chunk_is_str(tmp1, "^", 1)) // Issue #2656
            || !(tmp2->type == CT_WORD || chunk_is_str(tmp2, ")", 1)))
         {
            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, scope_e::PREPROC);

         if (tmp1 == nullptr)
         {
            return(false);
         }

         if (chunk_is_str(tmp1, "(", 1))
         {
            tmp3 = chunk_skip_to_match(tmp1, scope_e::PREPROC);
         }
         pc = tmp3;

         // reset some vars to allow [] after parens
         word_count = 1;
         type_count = 1;
      }
      else if (chunk_is_token(pc, CT_TSQUARE))
      {
         // ignore it
      }
      else if (word_count == 1 && chunk_is_token(pc, CT_SQUARE_OPEN))
      {
         // skip over any array stuff
         pc = chunk_skip_to_match(pc, scope_e::PREPROC);
      }
      else if (word_count == 2 && chunk_is_token(pc, CT_SQUARE_OPEN))
      {
         // Bug #671: is it such as: bool foo[FOO_MAX]
         pc = chunk_skip_to_match(pc, scope_e::PREPROC);
      }
      else if (  word_count == 1
              && language_is_set(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_count);
         return(false);
      }
   }

   chunk_t *last = chunk_get_prev_ncnlni(pc);   // Issue #2279

   if (chunk_is_ptr_operator(last))
   {
      LOG_FMT(LFPARAM, " <== [%s] sure!\n", get_token_name(pc->type));
      return(true);
   }

   if (word_count < 2 && type_count < 1 && start->brace_level > 0)
   {
      LOG_FMT(LFPARAM, " !MVP!");
      // Oh, joy, we are in Most Vexing Parse territory
      auto const brace =
         chunk_get_prev_type(start, CT_BRACE_OPEN, start->brace_level - 1);

      if (brace)
      {
         LOG_FMT(LFPARAM, " (matching %s brace at %zu:%zu)",
                 get_token_name(get_chunk_parent_type(brace)),
                 brace->orig_line, brace->orig_col);
      }

      if (  brace
         && (  get_chunk_parent_type(brace) == CT_CLASS
            || get_chunk_parent_type(brace) == CT_STRUCT))
      {
         // A Most Vexing Parse variable declaration cannot occur in the body
         // of a struct/class, so we probably have a function prototype
         LOG_FMT(LFPARAM, " <== [%s] Likely!\n",
                 (pc == nullptr ? "nullptr" : get_token_name(pc->type)));
         return(true);
      }
   }
   bool ret = (  word_count >= 2
              || (word_count == 1 && type_count == 1));

   LOG_FMT(LFPARAM, " <== [%s] %s!\n",
           (pc == nullptr ? "nullptr" : get_token_name(pc->type)),
           ret ? "Yup" : "Unlikely");
   return(ret);
} // can_be_full_param


static void mark_function(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   if (pc == nullptr)
   {
      return;
   }
   LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
   chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279
   chunk_t *next = chunk_get_next_ncnlnp(pc);

   if (next == nullptr)
   {
      return;
   }
   chunk_t *tmp;
   chunk_t *semi = nullptr;
   chunk_t *paren_open;
   chunk_t *paren_close;

   // Find out what is before the operator
   if (get_chunk_parent_type(pc) == CT_OPERATOR)
   {
      LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      log_pcf_flags(LGUY, pc->flags);
      chunk_t *pc_op = chunk_get_prev_type(pc, CT_OPERATOR, pc->level);

      if (  pc_op != nullptr
         && pc_op->flags.test(PCF_EXPR_START))
      {
         LOG_FMT(LFCN, "%s(%d): (4) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         set_chunk_type(pc, CT_FUNC_CALL);
      }

      if (language_is_set(LANG_CPP))
      {
         tmp = pc;

         while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr)   // Issue #2279
         {
            if (  chunk_is_token(tmp, CT_BRACE_CLOSE)
               || chunk_is_token(tmp, CT_BRACE_OPEN)             // Issue 575
               || chunk_is_token(tmp, CT_SEMICOLON))
            {
               break;
            }

            if (  chunk_is_paren_open(tmp)
               && !pc->flags.test(PCF_IN_PREPROC))               // Issue #2703
            {
               LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
                       __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
               LOG_FMT(LFCN, "%s(%d): (5) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                       __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
               set_chunk_type(pc, CT_FUNC_CALL);
               break;
            }

            if (chunk_is_token(tmp, CT_ASSIGN))
            {
               LOG_FMT(LFCN, "%s(%d): (6) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                       __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
               set_chunk_type(pc, CT_FUNC_CALL);
               break;
            }

            if (chunk_is_token(tmp, CT_TEMPLATE))
            {
               LOG_FMT(LFCN, "%s(%d): (7) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                       __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
               set_chunk_type(pc, CT_FUNC_DEF);
               break;
            }

            if (chunk_is_token(tmp, CT_BRACE_OPEN))
            {
               if (get_chunk_parent_type(tmp) == CT_FUNC_DEF)
               {
                  LOG_FMT(LFCN, "%s(%d): (8) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                          __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
                  set_chunk_type(pc, CT_FUNC_CALL);
               }

               if (  get_chunk_parent_type(tmp) == CT_CLASS
                  || get_chunk_parent_type(tmp) == CT_STRUCT)
               {
                  LOG_FMT(LFCN, "%s(%d): (9) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                          __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
                  set_chunk_type(pc, CT_FUNC_DEF);
               }
               break;
            }
         }

         if (  tmp != nullptr
            && pc->type != CT_FUNC_CALL)
         {
            // Mark the return type
            while (  (tmp = chunk_get_next_ncnl(tmp)) != pc
                  && tmp != nullptr)
            {
               make_type(tmp); // Mark the return type
            }
         }
      }
   }

   if (chunk_is_ptr_operator(next))
   {
      next = chunk_get_next_ncnlnp(next);

      if (next == nullptr)
      {
         return;
      }
   }
   LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s, type is %s, parent_type is %s\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(),
           get_token_name(pc->type), get_token_name(get_chunk_parent_type(pc)));
   LOG_FMT(LFCN, "   level is %zu, brace_level is %zu, next->text() '%s', next->type is %s, next->level is %zu\n",
           pc->level, pc->brace_level,
           next->text(), get_token_name(next->type), next->level);

   if (pc->flags.test(PCF_IN_CONST_ARGS))
   {
      set_chunk_type(pc, CT_FUNC_CTOR_VAR);
      LOG_FMT(LFCN, "%s(%d):   1) Marked [%s] as FUNC_CTOR_VAR on line %zu col %zu\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
      next = skip_template_next(next);

      if (next == nullptr)
      {
         return;
      }
      flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, true);
      return;
   }
   // Skip over any template and attribute madness
   next = skip_template_next(next);

   if (next == nullptr)
   {
      return;
   }
   next = skip_attribute_next(next);

   if (next == nullptr)
   {
      return;
   }
   // Find the open and close parenthesis
   paren_open  = chunk_get_next_str(pc, "(", 1, pc->level);
   paren_close = chunk_get_next_str(paren_open, ")", 1, pc->level);

   if (  paren_open == nullptr
      || paren_close == nullptr)
   {
      LOG_FMT(LFCN, "%s(%d): No parens found for [%s] on orig_line %zu, orig_col %zu\n",
              __func__, __LINE__, pc->text(), 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 (  tmp != nullptr
      && chunk_is_str(tmp, "(", 1))
   {
      chunk_t *tmp1;
      chunk_t *tmp2;
      chunk_t *tmp3;

      // skip over any leading class/namespace in: "T(F::*A)();"
      tmp1 = chunk_get_next_ncnl(next);

      while (tmp1 != nullptr)
      {
         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 = nullptr;
      }
      else
      {
         tmp3 = chunk_get_next_ncnl(tmp2);
      }
      tmp3 = chunk_get_next_ssq(tmp3);

      if (  chunk_is_str(tmp3, ")", 1)
         && (  chunk_is_star(tmp1)
            || chunk_is_msref(tmp1)
            || (language_is_set(LANG_OC) && chunk_is_token(tmp1, CT_CARET)))
         && (tmp2 == nullptr || chunk_is_token(tmp2, CT_WORD)))
      {
         if (tmp2)
         {
            LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function variable '%s', changing '%s' into a type\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, tmp2->text(), pc->text());
            set_chunk_type(tmp2, CT_FUNC_VAR);
            flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_VAR, false);

            LOG_FMT(LFCN, "%s(%d): paren open @ orig_line %zu, orig_col %zu\n",
                    __func__, __LINE__, paren_open->orig_line, paren_open->orig_col);
         }
         else
         {
            LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function type, changing '%s' into a type\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());

            if (tmp2)
            {
               set_chunk_type(tmp2, CT_FUNC_TYPE);
            }
            flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_TYPE, false);
         }
         set_chunk_type(pc, CT_TYPE);
         set_chunk_type(tmp1, CT_PTR_TYPE);
         chunk_flags_clr(pc, PCF_VAR_1ST_DEF);

         if (tmp2 != nullptr)
         {
            chunk_flags_set(tmp2, PCF_VAR_1ST_DEF);
         }
         flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_PROTO, false);
         fix_fcn_def_params(tmp);
         return;
      }
      LOG_FMT(LFCN, "%s(%d): chained function calls? text() is '%s', orig_line is %zu, orig_col is %zu\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
   }

   // Assume it is a function call if not already labeled
   if (chunk_is_token(pc, CT_FUNCTION))
   {
      LOG_FMT(LFCN, "%s(%d): examine: text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
      // look for an assigment. Issue #575
      chunk_t *temp = chunk_get_next_type(pc, CT_ASSIGN, pc->level);

      if (temp != nullptr)
      {
         LOG_FMT(LFCN, "%s(%d): assigment found, orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, temp->orig_line, temp->orig_col, temp->text());
         LOG_FMT(LFCN, "%s(%d): (10) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         set_chunk_type(pc, CT_FUNC_CALL);
      }
      else
      {
         LOG_FMT(LFCN, "%s(%d): (11) SET TO %s: orig_line is %zu, orig_col is %zu, text() '%s'",
                 __func__, __LINE__, (get_chunk_parent_type(pc) == CT_OPERATOR) ? "CT_FUNC_DEF" : "CT_FUNC_CALL",
                 pc->orig_line, pc->orig_col, pc->text());
         set_chunk_type(pc, (get_chunk_parent_type(pc) == CT_OPERATOR) ? CT_FUNC_DEF : CT_FUNC_CALL);
      }
   }
   LOG_FMT(LFCN, "%s(%d): Check for C++ function def, text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
           __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));

   if (prev != nullptr)
   {
      LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
              __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type));
   }

   // Check for C++ function def
   if (  chunk_is_token(pc, CT_FUNC_CLASS_DEF)
      || (  prev != nullptr
         && (  chunk_is_token(prev, CT_INV)
            || chunk_is_token(prev, CT_DC_MEMBER))))
   {
      chunk_t *destr = nullptr;

      if (chunk_is_token(prev, 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;
         // Point to the item previous to the class name
         prev = chunk_get_prev_ncnlnp(prev);
      }

      if (chunk_is_token(prev, CT_DC_MEMBER))
      {
         prev = chunk_get_prev_ncnlnp(prev);
         LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
                 get_token_name(prev->type));
         prev = skip_template_prev(prev);
         LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
                 get_token_name(prev->type));
         prev = skip_attribute_prev(prev);
         LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
                 get_token_name(prev->type));

         if (chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE))
         {
            if (pc->str.equals(prev->str))
            {
               LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                       __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col,
                       get_token_name(prev->type));
               set_chunk_type(pc, CT_FUNC_CLASS_DEF);
               LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu - FOUND %sSTRUCTOR for '%s', type is %s\n",
                       __func__, __LINE__,
                       prev->orig_line, prev->orig_col,
                       (destr != nullptr) ? "DE" : "CON",
                       prev->text(), get_token_name(prev->type));

               mark_cpp_constructor(pc);
               return;
            }
            // 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 (  chunk_is_token(pc, CT_FUNC_CALL)
      && (  pc->level == pc->brace_level
         || pc->level == 1)
      && !pc->flags.test(PCF_IN_ARRAY_ASSIGN))
   {
      bool isa_def  = false;
      bool hit_star = false;
      LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col,
              get_token_name(pc->type));

      if (prev == nullptr)
      {
         LOG_FMT(LFCN, "%s(%d): Checking func call: prev is NULL\n",
                 __func__, __LINE__);
      }
      else
      {
         LOG_FMT(LFCN, "%s(%d): Checking func call: prev->text() '%s', prev->type is %s\n",
                 __func__, __LINE__, prev->text(), 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 stuff 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 != nullptr)
      {
         LOG_FMT(LFCN, "%s(%d): next step with: prev->orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text());

         if (get_chunk_parent_type(pc) == CT_FIXED)
         {
            isa_def = true;
         }

         if (prev->flags.test(PCF_IN_PREPROC))
         {
            prev = chunk_get_prev_ncnlnp(prev);
            continue;
         }

         // Some code slips an attribute between the type and function
         if (  chunk_is_token(prev, CT_FPAREN_CLOSE)
            && get_chunk_parent_type(prev) == CT_ATTRIBUTE)
         {
            prev = skip_attribute_prev(prev);
            continue;
         }

         // skip const(TYPE)
         if (  chunk_is_token(prev, CT_PAREN_CLOSE)
            && get_chunk_parent_type(prev) == CT_D_CAST)
         {
            LOG_FMT(LFCN, "%s(%d): --> For sure a prototype or definition\n",
                    __func__, __LINE__);
            isa_def = true;
            break;
         }

         if (get_chunk_parent_type(prev) == CT_DECLSPEC)  // Issue 1289
         {
            prev = chunk_skip_to_match_rev(prev);
            prev = chunk_get_prev(prev);

            if (chunk_is_token(prev, CT_DECLSPEC))
            {
               prev = chunk_get_prev(prev);
            }
         }

         // if it was determined that this could be a function definition
         // but one of the preceding tokens is a CT_MEMBER than this is not a
         // fcn def, issue #1466
         if (  isa_def
            && chunk_is_token(prev, CT_MEMBER))
         {
            isa_def = false;
         }

         // get first chunk before: A::B::pc | this.B.pc | this->B->pc
         if (  chunk_is_token(prev, CT_DC_MEMBER)
            || chunk_is_token(prev, CT_MEMBER))
         {
            while (  chunk_is_token(prev, CT_DC_MEMBER)
                  || chunk_is_token(prev, CT_MEMBER))
            {
               prev = chunk_get_prev_ncnlnp(prev);

               if (  prev == nullptr
                  || (  prev->type != CT_WORD
                     && prev->type != CT_TYPE
                     && prev->type != CT_THIS))
               {
                  LOG_FMT(LFCN, "%s(%d): --? skipped MEMBER and landed on %s\n",
                          __func__, __LINE__, (prev == nullptr) ? "<null>" : get_token_name(prev->type));
                  break;
               }
               LOG_FMT(LFCN, "%s(%d): <skip> '%s'\n",
                       __func__, __LINE__, prev->text());

               // Issue #1112
               // clarification: this will skip the CT_WORD, CT_TYPE or CT_THIS landing on either
               // another CT_DC_MEMBER or CT_MEMBER or a token that indicates the context of the
               // token in question; therefore, exit loop when not a CT_DC_MEMBER or CT_MEMBER
               prev = chunk_get_prev_ncnlnp(prev);

               if (prev == nullptr)
               {
                  LOG_FMT(LFCN, "%s(%d): prev is nullptr\n",
                          __func__, __LINE__);
               }
               else
               {
                  LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
                          __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text());
               }
            }

            if (prev == nullptr)
            {
               break;
            }
         }

         // If we are on a TYPE or WORD, then this could be a proto or def
         if (  chunk_is_token(prev, CT_TYPE)
            || chunk_is_token(prev, CT_WORD))
         {
            if (!hit_star)
            {
               LOG_FMT(LFCN, "%s(%d):   --> For sure a prototype or definition\n",
                       __func__, __LINE__);
               isa_def = true;
               break;
            }
            chunk_t *prev_prev = chunk_get_prev_ncnlnp(prev);

            if (!chunk_is_token(prev_prev, CT_QUESTION))               // Issue #1753
            {
               LOG_FMT(LFCN, "%s(%d):   --> maybe a proto/def\n",
                       __func__, __LINE__);

               LOG_FMT(LFCN, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s, parent_type is %s\n",
                       __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
                       get_token_name(prev->type), get_token_name(get_chunk_parent_type(prev)));
               log_pcf_flags(LFCN, pc->flags);
               isa_def = true;
            }
         }

         if (chunk_is_ptr_operator(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_ptr_operator(prev))
         {
            LOG_FMT(LFCN, "%s(%d):  --> Stopping on prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                    __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type));

            // certain tokens are unlikely to precede a prototype or definition
            if (  chunk_is_token(prev, CT_ARITH)
               || chunk_is_token(prev, CT_ASSIGN)
               || chunk_is_token(prev, CT_COMMA)
               || (chunk_is_token(prev, CT_STRING) && get_chunk_parent_type(prev) != CT_EXTERN)  // fixes issue 1259
               || chunk_is_token(prev, CT_STRING_MULTI)
               || chunk_is_token(prev, CT_NUMBER)
               || chunk_is_token(prev, CT_NUMBER_FP)
               || chunk_is_token(prev, CT_FPAREN_OPEN)) // issue #1464
            {
               isa_def = false;
            }
            break;
         }

         // Skip over template and attribute stuff
         if (chunk_is_token(prev, CT_ANGLE_CLOSE))
         {
            prev = skip_template_prev(prev);
         }
         else
         {
            prev = chunk_get_prev_ncnlnp(prev);
         }
      }
      //LOG_FMT(LFCN, " -- stopped on %s [%s]\n",
      //        prev->text(), get_token_name(prev->type));

      // Fixes issue #1634
      if (chunk_is_paren_close(prev))
      {
         chunk_t *preproc = chunk_get_next_ncnl(prev);

         if (chunk_is_token(preproc, CT_PREPROC))
         {
            size_t pp_level = preproc->pp_level;

            if (chunk_is_token(chunk_get_next_ncnl(preproc), CT_PP_ELSE))
            {
               do
               {
                  preproc = chunk_get_prev_ncnlni(preproc);      // Issue #2279

                  if (chunk_is_token(preproc, CT_PP_IF))
                  {
                     preproc = chunk_get_prev_ncnlni(preproc);   // Issue #2279

                     if (preproc->pp_level == pp_level)
                     {
                        prev = chunk_get_prev_ncnlnp(preproc);
                        break;
                     }
                  }
               } while (preproc != nullptr);
            }
         }
      }

      if (  isa_def
         && prev != nullptr
         && (  (  chunk_is_paren_close(prev)
               && get_chunk_parent_type(prev) != CT_D_CAST
               && get_chunk_parent_type(prev) != CT_MACRO_OPEN  // Issue #2726
               && get_chunk_parent_type(prev) != CT_MACRO_CLOSE)
            || prev->type == CT_ASSIGN
            || prev->type == CT_RETURN))
      {
         LOG_FMT(LFCN, "%s(%d): -- overriding DEF due to prev is '%s', type is %s\n",
                 __func__, __LINE__, prev->text(), get_token_name(prev->type));
         isa_def = false;
      }

      // Fixes issue #1266, identification of a tuple return type in CS.
      if (  !isa_def
         && chunk_is_token(prev, CT_PAREN_CLOSE)
         && chunk_get_next_ncnl(prev) == pc)
      {
         tmp = chunk_skip_to_match_rev(prev);

         while (  tmp != nullptr                     // Issue #2315
               && tmp != prev)
         {
            if (chunk_is_token(tmp, CT_COMMA) && tmp->level == prev->level + 1)
            {
               LOG_FMT(LFCN, "%s(%d): -- overriding call due to tuple return type -- prev is '%s', type is %s\n",
                       __func__, __LINE__, prev->text(), get_token_name(prev->type));
               isa_def = true;
               break;
            }
            tmp = chunk_get_next_ncnl(tmp);
         }
      }

      if (isa_def)
      {
         LOG_FMT(LFCN, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                 __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
         LOG_FMT(LFCN, "%s(%d): (12) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
         set_chunk_type(pc, CT_FUNC_DEF);

         if (prev == nullptr)
         {
            prev = chunk_get_head();
         }

         for (  tmp = prev; (tmp != nullptr)
             && tmp != pc; tmp = chunk_get_next_ncnlnp(tmp))
         {
            LOG_FMT(LFCN, "%s(%d): text() is '%s', type is %s\n",
                    __func__, __LINE__, tmp->text(), get_token_name(tmp->type));
            make_type(tmp);
         }
      }
   }

   if (pc->type != CT_FUNC_DEF)
   {
      LOG_FMT(LFCN, "%s(%d):  Detected type %s, text() is '%s', on orig_line %zu, orig_col %zu\n",
              __func__, __LINE__, get_token_name(pc->type),
              pc->text(), pc->orig_line, pc->orig_col);

      tmp = flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false);

      if (  chunk_is_token(tmp, CT_BRACE_OPEN)
         && get_chunk_parent_type(tmp) != 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 parenthesis 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)) != nullptr)
   {
      // Only care about brace or semicolon on the same level
      if (tmp->level < pc->level)
      {
         // No semicolon - guess that it is a prototype
         chunk_flags_clr(pc, PCF_VAR_1ST_DEF);
         set_chunk_type(pc, CT_FUNC_PROTO);
         break;
      }
      else if (tmp->level == pc->level)
      {
         if (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            // its a function def for sure
            break;
         }
         else if (chunk_is_semicolon(tmp))
         {
            // Set the parent for the semicolon for later
            semi = tmp;
            chunk_flags_clr(pc, PCF_VAR_1ST_DEF);
            set_chunk_type(pc, CT_FUNC_PROTO);
            LOG_FMT(LFCN, "%s(%d):   2) Marked text() is '%s', as FUNC_PROTO on orig_line %zu, orig_col %zu\n",
                    __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
            break;
         }
         else if (chunk_is_token(pc, CT_COMMA))
         {
            set_chunk_type(pc, CT_FUNC_CTOR_VAR);
            LOG_FMT(LFCN, "%s(%d):   2) Marked text() is '%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n",
                    __func__, __LINE__, pc->text(), 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, without being able to accurately determine if an
    * identifier is a type (which would require us to more or less be a full
    * compiler), 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 (  language_is_set(LANG_CPP)
      && chunk_is_token(pc, CT_FUNC_PROTO)
      && get_chunk_parent_type(pc) != CT_OPERATOR)
   {
      LOG_FMT(LFPARAM, "%s(%d):", __func__, __LINE__);
      LOG_FMT(LFPARAM, "  checking '%s' for constructor variable %s %s\n",
              pc->text(),
              get_token_name(paren_open->type),
              get_token_name(paren_close->type));

      /*
       * Check the token at the start of the statement. If it's 'extern', we
       * definitely have a function prototype.
       */
      tmp = pc;

      while (  tmp != nullptr
            && !tmp->flags.test(PCF_STMT_START))
      {
         tmp = chunk_get_prev_ncnlni(tmp);   // Issue #2279
      }
      const bool is_extern = (tmp && tmp->str.equals("extern"));

      /*
       * 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 (  chunk_is_token(tmp, 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_extern
         && is_param && ref != tmp)
      {
         if (!can_be_full_param(ref, tmp))
         {
            is_param = false;
         }
      }

      if (  !is_extern
         && !is_param)
      {
         set_chunk_type(pc, CT_FUNC_CTOR_VAR);
         LOG_FMT(LFCN, "%s(%d):   3) Marked text() '%s' as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n",
                 __func__, __LINE__, pc->text(), 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 != nullptr
            && get_chunk_parent_type(br_open) != CT_EXTERN
            && get_chunk_parent_type(br_open) != CT_NAMESPACE)
         {
            // Do a check to see if the level is right
            prev = chunk_get_prev_ncnlni(pc);   // Issue #2279

            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 != nullptr
                  && get_chunk_parent_type(p_op) != CT_CLASS
                  && get_chunk_parent_type(p_op) != CT_STRUCT
                  && get_chunk_parent_type(p_op) != CT_NAMESPACE)
               {
                  set_chunk_type(pc, CT_FUNC_CTOR_VAR);
                  LOG_FMT(LFCN, "%s(%d):   4) Marked text() is'%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n",
                          __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
               }
            }
         }
      }
   }

   if (semi != nullptr)
   {
      set_chunk_parent(semi, pc->type);
   }

   // Issue # 1403, 2152
   if (chunk_is_token(paren_open->prev, CT_FUNC_CTOR_VAR))
   {
      flag_parens(paren_open, PCF_IN_FCN_CTOR, CT_FPAREN_OPEN, pc->type, false);
   }
   else
   {
      flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, false);
   }
   //flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, true);

   if (chunk_is_token(pc, CT_FUNC_CTOR_VAR))
   {
      chunk_flags_set(pc, PCF_VAR_1ST_DEF);
      return;
   }

   if (chunk_is_token(next, CT_TSQUARE))
   {
      next = chunk_get_next_ncnl(next);

      if (next == nullptr)
      {
         return;
      }
   }
   // Mark parameters and return type
   fix_fcn_def_params(next);
   mark_function_return_type(pc, chunk_get_prev_ncnlni(pc), pc->type);   // Issue #2279

   /* mark C# where chunk */
   if (  language_is_set(LANG_CS)
      && (  (chunk_is_token(pc, CT_FUNC_DEF))
         || (chunk_is_token(pc, CT_FUNC_PROTO))))
   {
      tmp = chunk_get_next_ncnl(paren_close);
      pcf_flags_t in_where_spec_flags = PCF_NONE;

      while (  (tmp != nullptr)
            && (tmp->type != CT_BRACE_OPEN)
            && (tmp->type != CT_SEMICOLON))
      {
         mark_where_chunk(tmp, pc->type, tmp->flags | in_where_spec_flags);
         in_where_spec_flags = tmp->flags & PCF_IN_WHERE_SPEC;

         tmp = chunk_get_next_ncnl(tmp);
      }
   }

   // Find the brace pair and set the parent
   if (chunk_is_token(pc, CT_FUNC_DEF))
   {
      tmp = chunk_get_next_ncnl(paren_close);

      while (  tmp != nullptr
            && tmp->type != CT_BRACE_OPEN)
      {
         LOG_FMT(LFCN, "%s(%d): (13) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
         set_chunk_parent(tmp, CT_FUNC_DEF);

         if (!chunk_is_semicolon(tmp))
         {
            chunk_flags_set(tmp, PCF_OLD_FCN_PARAMS);
         }
         tmp = chunk_get_next_ncnl(tmp);
      }

      if (chunk_is_token(tmp, CT_BRACE_OPEN))
      {
         LOG_FMT(LFCN, "%s(%d): (14) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
         set_chunk_parent(tmp, CT_FUNC_DEF);
         tmp = chunk_skip_to_match(tmp);

         if (tmp != nullptr)
         {
            LOG_FMT(LFCN, "%s(%d): (15) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                    __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
            set_chunk_parent(tmp, CT_FUNC_DEF);
         }
      }
   }
} // mark_function


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_ncnlni(pc);   // Issue #2279

   if (chunk_is_token(tmp, CT_INV) || chunk_is_token(tmp, CT_DESTRUCTOR))
   {
      set_chunk_type(tmp, CT_DESTRUCTOR);
      set_chunk_parent(pc, CT_DESTRUCTOR);
      is_destr = true;
   }
   LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, FOUND %sSTRUCTOR for '%s'[%s] prev '%s'[%s]\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col,
           is_destr ? "DE" : "CON",
           pc->text(), get_token_name(pc->type),
           tmp->text(), 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:%zu Expected '(', got: [%s]\n",
              cpd.filename.c_str(), paren_open->orig_line,
              paren_open->text());
      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(%d): text() '%s'\n", __func__, __LINE__, after->text());

   // Scan until the brace open, mark everything
   tmp = paren_open;
   bool hit_colon = false;

   while (  tmp != nullptr
         && (tmp->type != CT_BRACE_OPEN || tmp->level != paren_open->level)
         && !chunk_is_semicolon(tmp))
   {
      LOG_FMT(LFTOR, "%s(%d): tmp is '%s', orig_line is %zu, orig_col is %zu\n",
              __func__, __LINE__, tmp->text(), tmp->orig_line, tmp->orig_col);
      chunk_flags_set(tmp, 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_ncnlni(tmp));   // Issue #2279

         if (chunk_is_token(var, CT_TYPE) || chunk_is_token(var, 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 (chunk_is_token(tmp, CT_BRACE_OPEN))
   {
      set_paren_parent(paren_open, CT_FUNC_CLASS_DEF);
      set_paren_parent(tmp, CT_FUNC_CLASS_DEF);
      LOG_FMT(LFCN, "%s(%d):  Marked '%s' as FUNC_CLASS_DEF on orig_line %zu, orig_col %zu\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
   }
   else
   {
      set_chunk_parent(tmp, CT_FUNC_CLASS_PROTO);
      set_chunk_type(pc, CT_FUNC_CLASS_PROTO);
      LOG_FMT(LFCN, "%s(%d):  Marked '%s' as FUNC_CLASS_PROTO on orig_line %zu, orig_col %zu\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
   }
} // mark_cpp_constructor


static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags)
{
   /* TODO: should have options to control spacing around the ':' as well as newline ability for the
    * constraint clauses (should it break up a 'where A : B where C : D' on the same line? wrap? etc.) */

   if (chunk_is_token(pc, CT_WHERE))
   {
      set_chunk_type(pc, CT_WHERE_SPEC);
      set_chunk_parent(pc, parent_type);
      flags |= PCF_IN_WHERE_SPEC;
      LOG_FMT(LFTOR, "%s: where-spec on line %zu\n",
              __func__, pc->orig_line);
   }
   else if (flags.test(PCF_IN_WHERE_SPEC))
   {
      if (chunk_is_str(pc, ":", 1))
      {
         set_chunk_type(pc, CT_WHERE_COLON);
         LOG_FMT(LFTOR, "%s: where-spec colon on line %zu\n",
                 __func__, pc->orig_line);
      }
      else if ((chunk_is_token(pc, CT_STRUCT)) || (chunk_is_token(pc, CT_CLASS)))
      {
         /* class/struct inside of a where-clause confuses parser for indentation; set it as a word so it looks like the rest */
         set_chunk_type(pc, CT_WORD);
      }
   }

   if (flags.test(PCF_IN_WHERE_SPEC))
   {
      chunk_flags_set(pc, PCF_IN_WHERE_SPEC);
   }
   return(flags);
}


static void mark_class_ctor(chunk_t *start)
{
   LOG_FUNC_ENTRY();

   LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, start is '%s', parent_type is %s\n",
           __func__, __LINE__, start->orig_line, start->orig_col, start->text(),
           get_token_name(get_chunk_parent_type(start)));
   log_pcf_flags(LFTOR, start->flags);

   chunk_t *pclass = chunk_get_next_ncnl(start, scope_e::PREPROC);

   LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
           __func__, __LINE__, pclass->text());
   log_pcf_flags(LFTOR, pclass->flags);

   if (language_is_set(LANG_CPP))
   {
      pclass = skip_attribute_next(pclass);
      LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
              __func__, __LINE__, pclass->text());
   }

   if (get_chunk_parent_type(start) == CT_TEMPLATE)
   {
      // look after the class name
      chunk_t *openingTemplate = chunk_get_next_ncnl(pclass);
      LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, openingTemplate is '%s', type is %s\n",
              __func__, __LINE__, openingTemplate->orig_line, openingTemplate->orig_col,
              openingTemplate->text(), get_token_name(openingTemplate->type));

      if (chunk_is_token(openingTemplate, CT_ANGLE_OPEN))
      {
         chunk_t *closingTemplate = chunk_skip_to_match(openingTemplate);
         LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, closingTemplate is '%s', type is %s\n",
                 __func__, __LINE__, closingTemplate->orig_line, closingTemplate->orig_col,
                 closingTemplate->text(), get_token_name(closingTemplate->type));
         chunk_t *thirdToken = chunk_get_next_ncnl(closingTemplate);
         LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, thirdToken is '%s', type is %s\n",
                 __func__, __LINE__, thirdToken->orig_line, thirdToken->orig_col,
                 thirdToken->text(), get_token_name(thirdToken->type));

         if (chunk_is_token(thirdToken, CT_DC_MEMBER))
         {
            pclass = chunk_get_next_ncnl(thirdToken);
            LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, pclass is '%s', type is %s\n",
                    __func__, __LINE__, pclass->orig_line, pclass->orig_col,
                    pclass->text(), get_token_name(pclass->type));
         }
      }
   }
   pclass = skip_attribute_next(pclass);
   LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
           __func__, __LINE__, pclass->text());

   if (chunk_is_token(pclass, CT_DECLSPEC))  // Issue 1289
   {
      pclass = chunk_get_next_ncnl(pclass);
      LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
              __func__, __LINE__, pclass->text());

      if (chunk_is_token(pclass, CT_PAREN_OPEN))
      {
         pclass = chunk_get_next_ncnl(chunk_skip_to_match(pclass));
         LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
                 __func__, __LINE__, pclass->text());
      }
   }

   if (  pclass == nullptr
      || (pclass->type != CT_TYPE && pclass->type != CT_WORD))
   {
      return;
   }
   chunk_t *next = chunk_get_next_ncnl(pclass, scope_e::PREPROC);

   while (  chunk_is_token(next, CT_TYPE)
         || chunk_is_token(next, CT_WORD)
         || chunk_is_token(next, CT_DC_MEMBER))
   {
      pclass = next;
      LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
              __func__, __LINE__, pclass->text());
      next = chunk_get_next_ncnl(next, scope_e::PREPROC);
   }
   chunk_t *pc   = chunk_get_next_ncnl(pclass, scope_e::PREPROC);
   size_t  level = pclass->brace_level + 1;

   if (pc == nullptr)
   {
      LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu. Bailed on NULL\n",
              __func__, __LINE__, pclass->text(), pclass->orig_line);
      return;
   }
   // Add the class name
   ChunkStack cs;

   cs.Push_Back(pclass);

   LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu (next is '%s')\n",
           __func__, __LINE__, pclass->text(), pclass->orig_line, pc->text());

   // detect D template class: "class foo(x) { ... }"
   if (language_is_set(LANG_D) && chunk_is_token(next, CT_PAREN_OPEN))              // Coverity CID 76004
   {
      set_chunk_parent(next, CT_TEMPLATE);

      next = get_d_template_types(cs, next);

      if (chunk_is_token(next, CT_PAREN_CLOSE))
      {
         set_chunk_parent(next, CT_TEMPLATE);
      }
   }
   // Find the open brace, abort on semicolon
   pcf_flags_t flags = PCF_NONE;

   while (pc != nullptr && pc->type != CT_BRACE_OPEN)
   {
      LOG_FMT(LFTOR, " [%s]", pc->text());

      flags = mark_where_chunk(pc, start->type, flags);

      if (!flags.test(PCF_IN_WHERE_SPEC) && chunk_is_str(pc, ":", 1))
      {
         set_chunk_type(pc, CT_CLASS_COLON);
         flags |= PCF_IN_CLASS_BASE;
         LOG_FMT(LFTOR, "%s(%d): class colon on line %zu\n",
                 __func__, __LINE__, pc->orig_line);
      }

      if (chunk_is_semicolon(pc))
      {
         LOG_FMT(LFTOR, "%s(%d): bailed on semicolon on line %zu\n",
                 __func__, __LINE__, pc->orig_line);
         return;
      }
      chunk_flags_set(pc, flags);
      pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);
   }

   if (pc == nullptr)
   {
      LOG_FMT(LFTOR, "%s(%d): bailed on NULL\n", __func__, __LINE__);
      return;
   }
   set_paren_parent(pc, start->type);
   chunk_flags_set(pc, PCF_IN_CLASS);

   pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);
   LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
           __func__, __LINE__, pclass->text());

   while (pc != nullptr)
   {
      LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n",
              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
      chunk_flags_set(pc, PCF_IN_CLASS);

      if (  pc->brace_level > level
         || pc->level > pc->brace_level
         || pc->flags.test(PCF_IN_PREPROC))
      {
         pc = chunk_get_next_ncnl(pc);
         continue;
      }

      if (chunk_is_token(pc, CT_BRACE_CLOSE) && pc->brace_level < level)
      {
         LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, Hit brace close\n",
                 __func__, __LINE__, pc->orig_line);
         pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);

         if (chunk_is_token(pc, CT_SEMICOLON))
         {
            set_chunk_parent(pc, start->type);
         }
         return;
      }
      next = chunk_get_next_ncnl(pc, scope_e::PREPROC);

      if (chunkstack_match(cs, pc))
      {
         LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n",
                 __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
         // Issue #1333 Formatter removes semicolon after variable initializer at class level(C#)
         // if previous chunk is 'new' operator it is variable initializer not a CLASS_FUNC_DEF.
         chunk_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC);   // Issue #2279
         LOG_FMT(LFTOR, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type));

         // Issue #1003, next->type should not be CT_FPAREN_OPEN
         if (  prev != nullptr
            && (prev->type != CT_NEW))
         {
            bool is_func_class_def = false;

            if (chunk_is_token(next, CT_PAREN_OPEN))
            {
               is_func_class_def = true;
            }
            else if (chunk_is_token(next, CT_ANGLE_OPEN))          // Issue # 1737
            {
               chunk_t *closeAngle    = chunk_skip_to_match(next);
               chunk_t *afterTemplate = chunk_get_next(closeAngle);

               if (chunk_is_token(afterTemplate, CT_PAREN_OPEN))
               {
                  is_func_class_def = true;
               }
            }
            else
            {
               LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
                       __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
               make_type(pc);
            }

            if (is_func_class_def)
            {
               set_chunk_type(pc, CT_FUNC_CLASS_DEF);
               LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s, Marked CTor/DTor\n",
                       __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
               mark_cpp_constructor(pc);
            }
         }
      }
      pc = next;
   }
} // mark_class_ctor


static chunk_t *skip_align(chunk_t *start)
{
   chunk_t *pc = start;

   if (chunk_is_token(pc, CT_ALIGN))
   {
      pc = chunk_get_next_ncnl(pc);

      if (chunk_is_token(pc, CT_PAREN_OPEN))
      {
         pc = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level);
         pc = chunk_get_next_ncnl(pc);

         if (chunk_is_token(pc, CT_COLON))
         {
            pc = chunk_get_next_ncnl(pc);
         }
      }
   }
   return(pc);
}


chunk_t *skip_parent_types(chunk_t *colon)
{
   auto pc = chunk_get_next_ncnlnp(colon);

   while (pc)
   {
      // Skip access specifier
      if (chunk_is_token(pc, CT_ACCESS))
      {
         pc = chunk_get_next_ncnlnp(pc);
         continue;
      }

      // Check for a type name
      if (!(chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)))
      {
         LOG_FMT(LPCU,
                 "%s is confused; expected a word at %zu:%zu "
                 "following type list at %zu:%zu\n", __func__,
                 colon->orig_line, colon->orig_col,
                 pc->orig_line, pc->orig_col);
         return(colon);
      }
      // Get next token
      auto next = skip_template_next(chunk_get_next_ncnlnp(pc));

      if (chunk_is_token(next, CT_DC_MEMBER) || chunk_is_token(next, CT_COMMA))
      {
         pc = chunk_get_next_ncnlnp(next);
      }
      else if (next)
      {
         LOG_FMT(LPCU, "%s -> %zu:%zu ('%s')\n", __func__,
                 next->orig_line, next->orig_col, next->text());
         return(next);
      }
      else
      {
         break;
      }
   }
   LOG_FMT(LPCU, "%s: did not find end of type list (start was %zu:%zu)\n",
           __func__, colon->orig_line, colon->orig_col);
   return(colon);
} // skip_parent_types


static void mark_struct_union_body(chunk_t *start)
{
   LOG_FUNC_ENTRY();
   chunk_t *pc = start;

   while (  pc != nullptr
         && pc->level >= start->level
         && !(pc->level == start->level && chunk_is_token(pc, CT_BRACE_CLOSE)))
   {
      if (  chunk_is_token(pc, CT_BRACE_OPEN)
         || chunk_is_token(pc, CT_BRACE_CLOSE)
         || chunk_is_token(pc, CT_SEMICOLON))
      {
         pc = chunk_get_next_ncnl(pc);

         if (pc == nullptr)
         {
            break;
         }
      }

      if (chunk_is_token(pc, CT_ALIGN))
      {
         pc = skip_align(pc); // "align(x)" or "align(x):"

         if (pc == nullptr)
         {
            break;
         }
      }
      else
      {
         pc = fix_variable_definition(pc);

         if (pc == nullptr)
         {
            break;
         }
      }
   }
} // mark_struct_union_body


void mark_comments(void)
{
   LOG_FUNC_ENTRY();

   cpd.unc_stage = unc_stage_e::MARK_COMMENTS;

   bool    prev_nl = true;
   chunk_t *cur    = chunk_get_head();

   while (cur != nullptr)
   {
      chunk_t *next   = chunk_get_next_nvb(cur);
      bool    next_nl = (next == nullptr) || 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;
   }
}


static void mark_define_expressions(void)
{
   LOG_FUNC_ENTRY();

   bool    in_define = false;
   bool    first     = true;
   chunk_t *pc       = chunk_get_head();
   chunk_t *prev     = pc;

   while (pc != nullptr)
   {
      if (!in_define)
      {
         if (  chunk_is_token(pc, CT_PP_DEFINE)
            || chunk_is_token(pc, CT_PP_IF)
            || chunk_is_token(pc, CT_PP_ELSE))
         {
            in_define = true;
            first     = true;
         }
      }
      else
      {
         if (!pc->flags.test(PCF_IN_PREPROC) || chunk_is_token(pc, CT_PREPROC))
         {
            in_define = false;
         }
         else
         {
            if (  pc->type != CT_MACRO
               && (  first
                  || chunk_is_token(prev, CT_PAREN_OPEN)
                  || chunk_is_token(prev, CT_ARITH)
                  || chunk_is_token(prev, CT_CARET)
                  || chunk_is_token(prev, CT_ASSIGN)
                  || chunk_is_token(prev, CT_COMPARE)
                  || chunk_is_token(prev, CT_RETURN)
                  || chunk_is_token(prev, CT_GOTO)
                  || chunk_is_token(prev, CT_CONTINUE)
                  || chunk_is_token(prev, CT_FPAREN_OPEN)
                  || chunk_is_token(prev, CT_SPAREN_OPEN)
                  || chunk_is_token(prev, CT_BRACE_OPEN)
                  || chunk_is_semicolon(prev)
                  || chunk_is_token(prev, CT_COMMA)
                  || chunk_is_token(prev, CT_COLON)
                  || chunk_is_token(prev, CT_QUESTION)))
            {
               chunk_flags_set(pc, PCF_EXPR_START);
               first = false;
            }
         }
      }
      prev = pc;
      pc   = chunk_get_next(pc);
   }
} // mark_define_expressions


static void handle_cpp_template(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   chunk_t *tmp = chunk_get_next_ncnl(pc);

   if (tmp->type != CT_ANGLE_OPEN)
   {
      return;
   }
   set_chunk_parent(tmp, CT_TEMPLATE);

   size_t level = tmp->level;

   while ((tmp = chunk_get_next(tmp)) != nullptr)
   {
      if (chunk_is_token(tmp, CT_CLASS) || chunk_is_token(tmp, CT_STRUCT))
      {
         set_chunk_type(tmp, CT_TYPE);
      }
      else if (chunk_is_token(tmp, CT_ANGLE_CLOSE) && tmp->level == level)
      {
         set_chunk_parent(tmp, CT_TEMPLATE);
         break;
      }
   }

   if (tmp != nullptr)
   {
      tmp = chunk_get_next_ncnl(tmp);

      if (chunk_is_token(tmp, CT_CLASS) || chunk_is_token(tmp, CT_STRUCT))
      {
         set_chunk_parent(tmp, CT_TEMPLATE);

         // REVISIT: This may be a bit risky - might need to track the { };
         tmp = chunk_get_next_type(tmp, CT_SEMICOLON, tmp->level);

         if (tmp != nullptr)
         {
            set_chunk_parent(tmp, CT_TEMPLATE);
         }
      }
   }
} // handle_cpp_template


static void handle_cpp_lambda(chunk_t *sq_o)
{
   LOG_FUNC_ENTRY();

   chunk_t *ret = nullptr;

   // abort if type of the previous token is not contained in this whitelist
   chunk_t *prev = chunk_get_prev_ncnlni(sq_o);   // Issue #2279

   if (  prev == nullptr
      || (  prev->type != CT_ASSIGN
         && prev->type != CT_COMMA
         && prev->type != CT_PAREN_OPEN   // allow Js like self invoking lambda syntax: ([](){})();
         && prev->type != CT_FPAREN_OPEN
         && prev->type != CT_SQUARE_OPEN
         && prev->type != CT_BRACE_OPEN
         && prev->type != CT_SEMICOLON
         && prev->type != CT_RETURN))
   {
      return;
   }
   chunk_t *sq_c = sq_o; // assuming '[]'

   if (chunk_is_token(sq_o, CT_SQUARE_OPEN))
   {
      // make sure there is a ']'
      sq_c = chunk_skip_to_match(sq_o);

      if (!sq_c)
      {
         return;
      }
   }
   chunk_t *pa_o = chunk_get_next_ncnl(sq_c);

   // check to see if there is a lambda-specifier in the pa_o chunk;
   // assuming chunk is CT_EXECUTION_CONTEXT, ignore lambda-specifier
   while (pa_o->type == CT_EXECUTION_CONTEXT)
   {
      // set pa_o to next chunk after this specifier
      pa_o = chunk_get_next_ncnl(pa_o);
   }

   if (pa_o == nullptr)
   {
      return;
   }
   chunk_t *pa_c = nullptr;

   // lambda-declarator '( params )' is optional
   if (chunk_is_token(pa_o, CT_PAREN_OPEN))
   {
      // and now find the ')'
      pa_c = chunk_skip_to_match(pa_o);

      if (pa_c == nullptr)
      {
         return;
      }
   }
   // Check for 'mutable' keyword: '[]() mutable {}' or []() mutable -> ret {}
   chunk_t *br_o = pa_c ? chunk_get_next_ncnl(pa_c) : pa_o;

   if (chunk_is_str(br_o, "mutable", 7))
   {
      br_o = chunk_get_next_ncnl(br_o);
   }
   //TODO: also check for exception and attribute between [] ... {}

   // skip possible arrow syntax: '-> ret'
   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 == nullptr
      || br_o->type != CT_BRACE_OPEN)
   {
      return;
   }
   // and now find the '}'
   chunk_t *br_c = chunk_skip_to_match(br_o);

   if (br_c == nullptr)
   {
      return;
   }

   // This looks like a lambda expression
   if (chunk_is_token(sq_o, 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);
      /*
       * bug # 664
       *
       * The original orig_col of CT_SQUARE_CLOSE is stored at orig_col_end
       * of CT_TSQUARE. CT_SQUARE_CLOSE orig_col and orig_col_end values
       * are calculate from orig_col_end of CT_TSQUARE.
       */
      nc.orig_col        = sq_o->orig_col_end - 1;
      nc.column          = static_cast<int>(nc.orig_col);
      nc.orig_col_end    = sq_o->orig_col_end;
      sq_o->orig_col_end = sq_o->orig_col + 1;

      set_chunk_type(&nc, CT_SQUARE_CLOSE);
      nc.str.pop_front();
      sq_c = chunk_add_after(&nc, sq_o);
   }
   set_chunk_parent(sq_o, CT_CPP_LAMBDA);
   set_chunk_parent(sq_c, CT_CPP_LAMBDA);

   if (pa_c != nullptr)
   {
      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 != nullptr)
   {
      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);
      }
   }

   if (pa_c != nullptr)
   {
      fix_fcn_def_params(pa_o);
   }
   //handle self calling lambda paren
   chunk_t *call_pa_o = chunk_get_next_ncnl(br_c);

   if (chunk_is_token(call_pa_o, CT_PAREN_OPEN))
   {
      chunk_t *call_pa_c = chunk_skip_to_match(call_pa_o);

      if (call_pa_c != nullptr)
      {
         set_chunk_type(call_pa_o, CT_FPAREN_OPEN);
         set_chunk_parent(call_pa_o, CT_FUNC_CALL);
         set_chunk_type(call_pa_c, CT_FPAREN_CLOSE);
         set_chunk_parent(call_pa_c, CT_FUNC_CALL);
      }
   }
} // handle_cpp_lambda


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)) != nullptr)
         && tmp->level > open_paren->level)
   {
      if (chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD))
      {
         if (maybe_type)
         {
            make_type(tmp);
            cs.Push_Back(tmp);
         }
         maybe_type = false;
      }
      else if (chunk_is_token(tmp, CT_COMMA))
      {
         maybe_type = true;
      }
   }
   return(tmp);
}


static bool chunkstack_match(ChunkStack &cs, chunk_t *pc)
{
   for (size_t idx = 0; idx < cs.Len(); idx++)
   {
      chunk_t *tmp = cs.GetChunk(idx);

      if (pc->str.equals(tmp->str))
      {
         return(true);
      }
   }

   return(false);
}


static void handle_d_template(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   chunk_t *name = chunk_get_next_ncnl(pc);
   chunk_t *po   = chunk_get_next_ncnl(name);

   //if (!name || (name->type != CT_WORD && name->type != CT_WORD))  Coverity CID 76000 Same on both sides, 2016-03-16
   if (!name || name->type != CT_WORD)
   {
      // TODO: log an error, expected NAME
      return;
   }

   if (  po == nullptr
      || 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;
   chunk_t    *tmp = get_d_template_types(cs, po);

   if (  tmp == nullptr
      || 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)) != nullptr)
         && tmp->level > po->level)
   {
      if (chunk_is_token(tmp, CT_WORD) && chunkstack_match(cs, tmp))
      {
         set_chunk_type(tmp, CT_TYPE);
      }
   }
//   if (!chunk_is_token(tmp, CT_BRACE_CLOSE))
//   {
//      // TODO: log an error, expected '}'
//   }
   set_chunk_parent(tmp, CT_TEMPLATE);
} // handle_d_template


static void mark_template_func(chunk_t *pc, chunk_t *pc_next)
{
   LOG_FUNC_ENTRY();

   // We know angle_close must be there...
   chunk_t *angle_close = chunk_get_next_type(pc_next, CT_ANGLE_CLOSE, pc->level);
   chunk_t *after       = chunk_get_next_ncnl(angle_close);

   if (after != nullptr)
   {
      if (chunk_is_str(after, "(", 1))
      {
         if (angle_close->flags.test(PCF_IN_FCN_CALL))
         {
            LOG_FMT(LTEMPFUNC, "%s(%d): marking '%s' in line %zu as a FUNC_CALL\n",
                    __func__, __LINE__, pc->text(), pc->orig_line);
            LOG_FMT(LFCN, "%s(%d): (16) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
            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(%d): marking '%s' in line %zu as a FUNC_CALL 2\n",
                    __func__, __LINE__, pc->text(), pc->orig_line);
            // its a function!!!
            LOG_FMT(LFCN, "%s(%d): (17) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
            set_chunk_type(pc, CT_FUNC_CALL);
            mark_function(pc);
         }
      }
      else if (chunk_is_token(after, CT_WORD))
      {
         // its a type!
         set_chunk_type(pc, CT_TYPE);
         chunk_flags_set(pc, PCF_VAR_TYPE);
         chunk_flags_set(after, PCF_VAR_DEF);
      }
   }
} // mark_template_func


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 != nullptr; tmp = chunk_get_next(tmp))
   {
      set_chunk_parent(tmp, pc->type);

      if (chunk_is_token(tmp, CT_WORD))
      {
         set_chunk_type(tmp, CT_SQL_WORD);
      }

      if (chunk_is_token(tmp, CT_SEMICOLON))
      {
         break;
      }
   }

   if (  pc->type != CT_SQL_BEGIN
      || tmp == nullptr
      || tmp->type != CT_SEMICOLON)
   {
      return;
   }

   for (tmp = chunk_get_next(tmp);
        tmp != nullptr && tmp->type != CT_SQL_END;
        tmp = chunk_get_next(tmp))
   {
      tmp->level++;
   }
}


chunk_t *skip_template_next(chunk_t *ang_open)
{
   if (chunk_is_token(ang_open, CT_ANGLE_OPEN))
   {
      chunk_t *pc = chunk_get_next_type(ang_open, CT_ANGLE_CLOSE, ang_open->level);
      return(chunk_get_next_ncnl(pc));
   }
   return(ang_open);
}


chunk_t *skip_template_prev(chunk_t *ang_close)
{
   if (chunk_is_token(ang_close, CT_ANGLE_CLOSE))
   {
      chunk_t *pc = chunk_get_prev_type(ang_close, CT_ANGLE_OPEN, ang_close->level);
      return(chunk_get_prev_ncnlni(pc));   // Issue #2279
   }
   return(ang_close);
}


chunk_t *skip_tsquare_next(chunk_t *ary_def)
{
   if (chunk_is_token(ary_def, CT_SQUARE_OPEN) || chunk_is_token(ary_def, CT_TSQUARE))
   {
      return(chunk_get_next_nisq(ary_def));
   }
   return(ary_def);
}


chunk_t *skip_attribute_next(chunk_t *attr)
{
   chunk_t *pc = attr;

   while (chunk_is_token(pc, CT_ATTRIBUTE))
   {
      pc = chunk_get_next_ncnl(pc);

      if (chunk_is_token(pc, CT_FPAREN_OPEN))
      {
         pc = chunk_get_next_type(pc, CT_FPAREN_CLOSE, pc->level);
         pc = chunk_get_next_ncnl(pc);
      }
   }
   return(pc);
}


chunk_t *skip_attribute_prev(chunk_t *fp_close)
{
   chunk_t *pc = fp_close;

   while (true)
   {
      if (  chunk_is_token(pc, CT_FPAREN_CLOSE)
         && get_chunk_parent_type(pc) == CT_ATTRIBUTE)
      {
         pc = chunk_get_prev_type(pc, CT_ATTRIBUTE, pc->level);
      }
      else if (chunk_is_not_token(pc, CT_ATTRIBUTE))
      {
         break;
      }
      pc = chunk_get_prev_ncnlni(pc);   // Issue #2279
   }
   return(pc);
}


static void handle_oc_class(chunk_t *pc)
{
   enum class angle_state_e : unsigned int
   {
      NONE  = 0,
      OPEN  = 1, // '<' found
      CLOSE = 2, // '>' found
   };

   LOG_FUNC_ENTRY();
   chunk_t       *tmp;
   bool          hit_scope     = false;
   bool          passed_name   = false; // Did we pass the name of the class and now there can be only protocols, not generics
   int           generic_level = 0;     // level of depth of generic
   angle_state_e as            = angle_state_e::NONE;

   LOG_FMT(LOCCLASS, "%s(%d): start [%s] [%s] line %zu\n",
           __func__, __LINE__, pc->text(), get_token_name(get_chunk_parent_type(pc)), pc->orig_line);

   if (get_chunk_parent_type(pc) == CT_OC_PROTOCOL)
   {
      tmp = chunk_get_next_ncnl(pc);

      if (chunk_is_semicolon(tmp))
      {
         set_chunk_parent(tmp, get_chunk_parent_type(pc));
         LOG_FMT(LOCCLASS, "%s(%d):   bail on semicolon\n", __func__, __LINE__);
         return;
      }
   }
   tmp = pc;

   while ((tmp = chunk_get_next_nnl(tmp)) != nullptr)
   {
      LOG_FMT(LOCCLASS, "%s(%d):       orig_line is %zu, [%s]\n",
              __func__, __LINE__, tmp->orig_line, tmp->text());

      if (chunk_is_token(tmp, CT_OC_END))
      {
         break;
      }

      if (chunk_is_token(tmp, CT_PAREN_OPEN))
      {
         passed_name = true;
      }

      if (chunk_is_str(tmp, "<", 1))
      {
         set_chunk_type(tmp, CT_ANGLE_OPEN);

         if (passed_name)
         {
            set_chunk_parent(tmp, CT_OC_PROTO_LIST);
         }
         else
         {
            set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);
            generic_level++;
         }
         as = angle_state_e::OPEN;
      }

      if (chunk_is_str(tmp, ">", 1))
      {
         set_chunk_type(tmp, CT_ANGLE_CLOSE);

         if (passed_name)
         {
            set_chunk_parent(tmp, CT_OC_PROTO_LIST);
            as = angle_state_e::CLOSE;
         }
         else
         {
            set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);

            if (generic_level == 0)
            {
               fprintf(stderr, "%s(%d): generic_level is ZERO, cannot be decremented, at line %zu, column %zu\n",
                       __func__, __LINE__, tmp->orig_line, tmp->orig_col);
               log_flush(true);
               exit(EX_SOFTWARE);
            }
            generic_level--;

            if (generic_level == 0)
            {
               as = angle_state_e::CLOSE;
            }
         }
      }

      if (chunk_is_str(tmp, ">>", 2))
      {
         set_chunk_type(tmp, CT_ANGLE_CLOSE);
         set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);
         split_off_angle_close(tmp);
         generic_level -= 1;

         if (generic_level == 0)
         {
            as = angle_state_e::CLOSE;
         }
      }

      if (  chunk_is_token(tmp, CT_BRACE_OPEN)
         && get_chunk_parent_type(tmp) != CT_ASSIGN)
      {
         as = angle_state_e::CLOSE;
         set_chunk_parent(tmp, CT_OC_CLASS);
         tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);

         if (  tmp != nullptr
            && get_chunk_parent_type(tmp) != CT_ASSIGN)
         {
            set_chunk_parent(tmp, CT_OC_CLASS);
         }
      }
      else if (chunk_is_token(tmp, CT_COLON))
      {
         if (as != angle_state_e::OPEN)
         {
            passed_name = true;
         }
         set_chunk_type(tmp, hit_scope ? CT_OC_COLON : CT_CLASS_COLON);

         if (chunk_is_token(tmp, CT_CLASS_COLON))
         {
            set_chunk_parent(tmp, CT_OC_CLASS);
         }
      }
      else if (chunk_is_str(tmp, "-", 1) || chunk_is_str(tmp, "+", 1))
      {
         as = angle_state_e::CLOSE;

         if (chunk_is_newline(chunk_get_prev(tmp)))
         {
            set_chunk_type(tmp, CT_OC_SCOPE);
            chunk_flags_set(tmp, PCF_STMT_START);
            hit_scope = true;
         }
      }

      if (as == angle_state_e::OPEN)
      {
         if (passed_name)
         {
            set_chunk_parent(tmp, CT_OC_PROTO_LIST);
         }
         else
         {
            set_chunk_parent(tmp, CT_OC_GENERIC_SPEC);
         }
      }
   }

   if (chunk_is_token(tmp, CT_BRACE_OPEN))
   {
      tmp = chunk_get_next_type(tmp, CT_BRACE_CLOSE, tmp->level);

      if (tmp != nullptr)
      {
         set_chunk_parent(tmp, CT_OC_CLASS);
      }
   }
} // handle_oc_class


static void handle_oc_block_literal(chunk_t *pc)
{
   LOG_FUNC_ENTRY();
   chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279
   chunk_t *next = chunk_get_next_ncnl(pc);

   if (  pc == nullptr
      || prev == nullptr
      || next == nullptr)
   {
      return; // let's be paranoid
   }
   /*
    * block literal: '^ RTYPE ( ARGS ) { }'
    * RTYPE and ARGS are optional
    */
   LOG_FMT(LOCBLK, "%s(%d): block literal @ orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, pc->orig_line, pc->orig_col);

   chunk_t *apo = nullptr; // arg paren open
   chunk_t *bbo = nullptr; // block brace open
   chunk_t *bbc;           // block brace close

   LOG_FMT(LOCBLK, "%s(%d):  + scan", __func__, __LINE__);
   chunk_t *tmp;

   for (tmp = next; tmp; tmp = chunk_get_next_ncnl(tmp))
   {
      /* handle '< protocol >' */
      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);
      }
      LOG_FMT(LOCBLK, " '%s'", tmp->text());

      if (tmp->level < pc->level || chunk_is_token(tmp, 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 (chunk_is_token(tmp, CT_BRACE_OPEN))
         {
            LOG_FMT(LOCBLK, "[BRACE]");
            bbo = tmp;
            break;
         }
      }
   }

   // make sure we have braces
   bbc = chunk_skip_to_match(bbo);

   if (  bbo == nullptr
      || bbc == nullptr)
   {
      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)
   {
      chunk_t *apc = chunk_skip_to_match(apo);  // arg parenthesis close

      if (chunk_is_paren_close(apc))
      {
         LOG_FMT(LOCBLK, " -- marking parens @ apo->orig_line is %zu, apo->orig_col is %zu and apc->orig_line is %zu, apc->orig_col is %zu\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_ncnlni(apo);   // Issue #2279
   }
   else
   {
      lbp = chunk_get_prev_ncnlni(bbo);   // Issue #2279
   }

   // 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);
      chunk_flags_set(lbp, PCF_OC_RTYPE);
      set_chunk_parent(lbp, CT_OC_BLOCK_EXPR);
      lbp = chunk_get_prev_ncnlni(lbp);   // Issue #2279
   }
   // mark the braces
   set_chunk_parent(bbo, CT_OC_BLOCK_EXPR);
   set_chunk_parent(bbc, CT_OC_BLOCK_EXPR);
} // handle_oc_block_literal


static void handle_oc_block_type(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   if (pc == nullptr)
   {
      return;
   }

   if (pc->flags.test(PCF_IN_TYPEDEF))
   {
      LOG_FMT(LOCBLK, "%s(%d): skip block type @ orig_line is %zu, orig_col is %zu, -- in typedef\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col);
      return;
   }
   // make sure we have '( ^'
   chunk_t *tpo = chunk_get_prev_ncnlni(pc); // type paren open   Issue #2279

   if (chunk_is_paren_open(tpo))
   {
      /*
       * block type: 'RTYPE (^LABEL)(ARGS)'
       * LABEL is optional.
       */
      chunk_t *tpc = chunk_skip_to_match(tpo);   // type close paren (after '^')
      chunk_t *nam = chunk_get_prev_ncnlni(tpc); // name (if any) or '^'   Issue #2279
      chunk_t *apo = chunk_get_next_ncnl(tpc);   // arg open paren
      chunk_t *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));
      }

      // Check apo is '(' or else this might be a block literal. Issue 2643.
      if (!chunk_is_paren_open(apo))
      {
         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(%d): block type @ orig_line is %zu, orig_col is %zu, text() '%s'[%s]\n",
                 __func__, __LINE__, 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_ncnlni(tpo), pt);   // Issue #2279
      }
   }
} // handle_oc_block_type


static chunk_t *handle_oc_md_type(chunk_t *paren_open, c_token_t ptype, pcf_flags_t flags, bool &did_it)
{
   chunk_t *paren_close;

   if (  !chunk_is_paren_open(paren_open)
      || ((paren_close = chunk_skip_to_match(paren_open)) == nullptr))
   {
      did_it = false;
      return(paren_open);
   }
   did_it = true;

   set_chunk_parent(paren_open, ptype);
   chunk_flags_set(paren_open, flags);
   set_chunk_parent(paren_close, ptype);
   chunk_flags_set(paren_close, 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));
      chunk_flags_set(cur, flags);
      make_type(cur);
   }

   // returning the chunk after the paren close
   return(chunk_get_next_ncnl(paren_close));
}


static void handle_oc_message_decl(chunk_t *pc)
{
   LOG_FUNC_ENTRY();

   bool did_it;
   //bool      in_paren  = false;
   //int       paren_cnt = 0;
   //int       arg_cnt   = 0;

   // Figure out if this is a spec or decl
   chunk_t *tmp = pc;

   while ((tmp = chunk_get_next(tmp)) != nullptr)
   {
      if (tmp->level < pc->level)
      {
         // should not happen
         return;
      }

      if (chunk_is_token(tmp, CT_SEMICOLON) || chunk_is_token(tmp, CT_BRACE_OPEN))
      {
         break;
      }
   }

   if (tmp == nullptr)
   {
      return;
   }
   c_token_t 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(%d): %s @ orig_line is %zu, orig_col is %zu -",
           __func__, __LINE__, 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;
   }  // expect the method name/label

   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 (chunk_is_token(pc, CT_COLON) || chunk_is_token(pc, 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): orig_line is %zu, orig_col is %zu expected type\n",
                    __func__, __LINE__, pc->orig_line, pc->orig_col);
            break;
         }
         // attributes for a method parameter sit between the parameter type and the parameter name
         pc = skip_attribute_next(tmp);
         // we should now be on the arg name
         chunk_flags_set(pc, 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 != nullptr)
      {
         set_chunk_parent(pc, pt);
      }
   }
   else if (chunk_is_token(pc, CT_SEMICOLON))
   {
      set_chunk_parent(pc, pt);
   }
   LOG_FMT(LOCMSGD, "\n");
} // handle_oc_message_decl


static void handle_oc_message_send(chunk_t *os)
{
   LOG_FUNC_ENTRY();

   chunk_t *cs = chunk_get_next(os);

   while (cs != nullptr && cs->level > os->level)
   {
      cs = chunk_get_next(cs);
   }

   if (cs == nullptr || cs->type != CT_SQUARE_CLOSE)
   {
      return;
   }
   LOG_FMT(LOCMSG, "%s(%d): orig_line is %zu, orig_col is %zu\n",
           __func__, __LINE__, os->orig_line, os->orig_col);

   chunk_t *tmp = chunk_get_next_ncnl(cs);

   if (chunk_is_semicolon(tmp))
   {
      set_chunk_parent(tmp, CT_OC_MSG);
   }
   // expect a word first thing or [...]
   tmp = chunk_get_next_ncnl(os);

   if (  chunk_is_token(tmp, CT_SQUARE_OPEN) || chunk_is_token(tmp, CT_PAREN_OPEN)
      || (chunk_is_token(tmp, CT_OC_AT)))
   {
      chunk_t *tt = chunk_get_next_ncnl(tmp);

      if ((chunk_is_token(tmp, CT_OC_AT)) && tt)
      {
         if (  (chunk_is_token(tt, CT_PAREN_OPEN))
            || (chunk_is_token(tt, CT_BRACE_OPEN))
            || (chunk_is_token(tt, CT_SQUARE_OPEN)))
         {
            tmp = tt;
         }
         else
         {
            LOG_FMT(LOCMSG, "%s(%d): tmp->orig_line is %zu, tmp->orig_col is %zu, expected identifier, not '%s' [%s]\n",
                    __func__, __LINE__, tmp->orig_line, tmp->orig_col,
                    tmp->text(), get_token_name(tmp->type));
            return;
         }
      }
      tmp = chunk_skip_to_match(tmp);
   }
   else if (  tmp->type != CT_WORD
           && tmp->type != CT_TYPE
           && tmp->type != CT_THIS
           && tmp->type != CT_STAR
           && tmp->type != CT_STRING)
   {
      LOG_FMT(LOCMSG, "%s(%d): orig_line is %zu, orig_col is %zu, expected identifier, not '%s' [%s]\n",
              __func__, __LINE__, tmp->orig_line, tmp->orig_col,
              tmp->text(), get_token_name(tmp->type));
      return;
   }
   else
   {
      if (chunk_is_star(tmp)) // Issue #2722
      {
         set_chunk_type(tmp, CT_PTR_TYPE);
         tmp = chunk_get_next_ncnl(tmp);
      }
      chunk_t *tt = chunk_get_next_ncnl(tmp);

      if (chunk_is_paren_open(tt))
      {
         LOG_FMT(LFCN, "%s(%d): (18) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
                 __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
         set_chunk_type(tmp, CT_FUNC_CALL);
         tmp = chunk_get_prev_ncnlni(set_paren_parent(tt, CT_FUNC_CALL));   // Issue #2279
      }
      else
      {
         set_chunk_type(tmp, CT_OC_MSG_CLASS);
      }
   }
   set_chunk_parent(os, CT_OC_MSG);
   chunk_flags_set(os, PCF_IN_OC_MSG);
   set_chunk_parent(cs, CT_OC_MSG);
   chunk_flags_set(cs, PCF_IN_OC_MSG);

   // 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);
   }
   // handle 'object.property' and 'collection[index]'
   else
   {
      while (tmp)
      {
         if (chunk_is_token(tmp, CT_MEMBER))  // move past [object.prop1.prop2
         {
            chunk_t *typ = chunk_get_next_ncnl(tmp);

            if (chunk_is_token(typ, CT_WORD) || chunk_is_token(typ, CT_TYPE))
            {
               tmp = chunk_get_next_ncnl(typ);
            }
            else
            {
               break;
            }
         }
         else if (chunk_is_token(tmp, CT_SQUARE_OPEN))  // move past [collection[index]
         {
            chunk_t *tcs = chunk_get_next_ncnl(tmp);

            while (tcs != nullptr && tcs->level > tmp->level)
            {
               tcs = chunk_get_next_ncnl(tcs);
            }

            if (chunk_is_token(tcs, CT_SQUARE_CLOSE))
            {
               tmp = chunk_get_next_ncnl(tcs);
            }
            else
            {
               break;
            }
         }
         else
         {
            break;
         }
      }
   }

   // [(self.foo.bar) method]
   if (chunk_is_paren_open(tmp))
   {
      tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp));
   }

   if (chunk_is_token(tmp, CT_WORD) || chunk_is_token(tmp, CT_TYPE))
   {
      set_chunk_type(tmp, CT_OC_MSG_FUNC);
   }
   chunk_t *prev = nullptr;

   for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp))
   {
      chunk_flags_set(tmp, PCF_IN_OC_MSG);

      if (tmp->level == cs->level + 1)
      {
         if (chunk_is_token(tmp, CT_COLON))
         {
            set_chunk_type(tmp, CT_OC_COLON);

            if (chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE))
            {
               // Might be a named param, check previous block
               chunk_t *pp = chunk_get_prev(prev);

               if (  pp != nullptr
                  && 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;
   }
} // handle_oc_message_send


static void handle_oc_available(chunk_t *os)
{
   os = chunk_get_next(os);

   while (os != nullptr)
   {
      c_token_t origType = os->type;
      set_chunk_type(os, CT_OC_AVAILABLE_VALUE);

      if (origType == CT_PAREN_CLOSE)
      {
         break;
      }
      os = chunk_get_next(os);
   }
}


static void handle_oc_property_decl(chunk_t *os)
{
   log_rule_B("mod_sort_oc_properties");

   if (options::mod_sort_oc_properties())
   {
      typedef std::vector<chunk_t *> ChunkGroup;

      chunk_t                 *next       = chunk_get_next(os);
      chunk_t                 *open_paren = nullptr;

      std::vector<ChunkGroup> class_chunks;       // class
      std::vector<ChunkGroup> thread_chunks;      // atomic, nonatomic
      std::vector<ChunkGroup> readwrite_chunks;   // readwrite, readonly
      std::vector<ChunkGroup> ref_chunks;         // retain, copy, assign, weak, strong, unsafe_unretained
      std::vector<ChunkGroup> getter_chunks;      // getter
      std::vector<ChunkGroup> setter_chunks;      // setter
      std::vector<ChunkGroup> nullability_chunks; // nonnull, nullable, null_unspecified, null_resettable
      std::vector<ChunkGroup> other_chunks;       // any words other than above

      if (chunk_is_token(next, CT_PAREN_OPEN))
      {
         open_paren = next;
         next       = chunk_get_next(next);

         /*
          * Determine location of the property attributes
          * NOTE: Did not do this in the combine.cpp do_symbol_check as
          * I was not sure what the ramifications of adding a new type
          * for each of the below types would be. It did break some items
          * when I attempted to add them so this is my hack for now.
          */
         while (next != nullptr && next->type != CT_PAREN_CLOSE)
         {
            if (chunk_is_token(next, CT_OC_PROPERTY_ATTR))
            {
               if (  chunk_is_str(next, "atomic", 6)
                  || chunk_is_str(next, "nonatomic", 9))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  thread_chunks.push_back(chunkGroup);
               }
               else if (  chunk_is_str(next, "readonly", 8)
                       || chunk_is_str(next, "readwrite", 9))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  readwrite_chunks.push_back(chunkGroup);
               }
               else if (  chunk_is_str(next, "assign", 6)
                       || chunk_is_str(next, "retain", 6)
                       || chunk_is_str(next, "copy", 4)
                       || chunk_is_str(next, "strong", 6)
                       || chunk_is_str(next, "weak", 4)
                       || chunk_is_str(next, "unsafe_unretained", 17))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  ref_chunks.push_back(chunkGroup);
               }
               else if (chunk_is_str(next, "getter", 6))
               {
                  ChunkGroup chunkGroup;

                  do
                  {
                     chunkGroup.push_back(next);
                     next = chunk_get_next(next);
                  } while (  next
                          && next->type != CT_COMMA
                          && next->type != CT_PAREN_CLOSE);

                  next = next->prev;

                  // coverity CID 160946
                  if (next == nullptr)
                  {
                     break;
                  }
                  getter_chunks.push_back(chunkGroup);
               }
               else if (chunk_is_str(next, "setter", 6))
               {
                  ChunkGroup chunkGroup;

                  do
                  {
                     chunkGroup.push_back(next);
                     next = chunk_get_next(next);
                  } while (  next
                          && next->type != CT_COMMA
                          && next->type != CT_PAREN_CLOSE);

                  next = chunk_get_prev(next);

                  if (next == nullptr)
                  {
                     break;
                  }
                  setter_chunks.push_back(chunkGroup);
               }
               else if (  chunk_is_str(next, "nullable", 8)
                       || chunk_is_str(next, "nonnull", 7)
                       || chunk_is_str(next, "null_resettable", 15)
                       || chunk_is_str(next, "null_unspecified", 16))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  nullability_chunks.push_back(chunkGroup);
               }
               else if (chunk_is_str(next, "class", 5))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  class_chunks.push_back(chunkGroup);
               }
               else
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  other_chunks.push_back(chunkGroup);
               }
            }
            else if (chunk_is_word(next))
            {
               if (chunk_is_str(next, "class", 5))
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  class_chunks.push_back(chunkGroup);
               }
               else
               {
                  ChunkGroup chunkGroup;
                  chunkGroup.push_back(next);
                  other_chunks.push_back(chunkGroup);
               }
            }
            next = chunk_get_next(next);
         }
         log_rule_B("mod_sort_oc_property_class_weight");
         int class_w = options::mod_sort_oc_property_class_weight();
         log_rule_B("mod_sort_oc_property_thread_safe_weight");
         int thread_w = options::mod_sort_oc_property_thread_safe_weight();
         log_rule_B("mod_sort_oc_property_readwrite_weight");
         int readwrite_w = options::mod_sort_oc_property_readwrite_weight();
         log_rule_B("mod_sort_oc_property_reference_weight");
         int ref_w = options::mod_sort_oc_property_reference_weight();
         log_rule_B("mod_sort_oc_property_getter_weight");
         int getter_w = options::mod_sort_oc_property_getter_weight();
         log_rule_B("mod_sort_oc_property_setter_weight");
         int setter_w = options::mod_sort_oc_property_setter_weight();
         log_rule_B("mod_sort_oc_property_nullability_weight");
         int nullability_w = options::mod_sort_oc_property_nullability_weight();

         //
         std::multimap<int, std::vector<ChunkGroup> > sorted_chunk_map;
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(class_w, class_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(thread_w, thread_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(readwrite_w, readwrite_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(ref_w, ref_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(getter_w, getter_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(setter_w, setter_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(nullability_w, nullability_chunks));
         sorted_chunk_map.insert(pair<int, std::vector<ChunkGroup> >(std::numeric_limits<int>::min(), other_chunks));

         chunk_t *curr_chunk = open_paren;

         for (multimap<int, std::vector<ChunkGroup> >::reverse_iterator it = sorted_chunk_map.rbegin(); it != sorted_chunk_map.rend(); ++it)
         {
            std::vector<ChunkGroup> chunk_groups = (*it).second;

            for (auto chunk_group : chunk_groups)
            {
               for (auto chunk : chunk_group)
               {
                  chunk->orig_prev_sp = 0;

                  if (chunk != curr_chunk)
                  {
                     chunk_move_after(chunk, curr_chunk);
                     curr_chunk = chunk;
                  }
                  else
                  {
                     curr_chunk = chunk_get_next(curr_chunk);
                  }
               }

               // add the parenthesis
               chunk_t endchunk;
               set_chunk_type(&endchunk, CT_COMMA);
               set_chunk_parent(&endchunk, get_chunk_parent_type(curr_chunk));
               endchunk.str         = ",";
               endchunk.level       = curr_chunk->level;
               endchunk.brace_level = curr_chunk->brace_level;
               endchunk.orig_line   = curr_chunk->orig_line;
               endchunk.orig_col    = curr_chunk->orig_col;
               endchunk.column      = curr_chunk->orig_col_end + 1;
               endchunk.flags       = curr_chunk->flags & PCF_COPY_FLAGS;
               chunk_add_after(&endchunk, curr_chunk);
               curr_chunk = curr_chunk->next;
            }
         }

         // Remove the extra comma's that we did not move
         while (curr_chunk && curr_chunk->type != CT_PAREN_CLOSE)
         {
            chunk_t *rm_chunk = curr_chunk;
            curr_chunk = chunk_get_next(curr_chunk);
            chunk_del(rm_chunk);
         }
      }
   }
   chunk_t *tmp = chunk_get_next_ncnl(os);

   if (chunk_is_paren_open(tmp))
   {
      tmp = chunk_get_next_ncnl(chunk_skip_to_match(tmp));
   }
   fix_variable_definition(tmp);
} // handle_oc_property_decl


static void handle_cs_square_stmt(chunk_t *os)
{
   LOG_FUNC_ENTRY();

   chunk_t *cs = chunk_get_next(os);

   while (cs != nullptr && cs->level > os->level)
   {
      cs = chunk_get_next(cs);
   }

   if (cs == nullptr || cs->type != CT_SQUARE_CLOSE)
   {
      return;
   }
   set_chunk_parent(os, CT_CS_SQ_STMT);
   set_chunk_parent(cs, CT_CS_SQ_STMT);

   chunk_t *tmp;

   for (tmp = chunk_get_next(os); tmp != cs; tmp = chunk_get_next(tmp))
   {
      set_chunk_parent(tmp, CT_CS_SQ_STMT);

      if (chunk_is_token(tmp, CT_COLON))
      {
         set_chunk_type(tmp, CT_CS_SQ_COLON);
      }
   }

   tmp = chunk_get_next_ncnl(cs);

   if (tmp != nullptr)
   {
      chunk_flags_set(tmp, PCF_STMT_START | PCF_EXPR_START);
   }
}


static void handle_cs_property(chunk_t *bro)
{
   LOG_FUNC_ENTRY();

   set_paren_parent(bro, CT_CS_PROPERTY);

   bool    did_prop = false;
   chunk_t *pc      = bro;

   while ((pc = chunk_get_prev_ncnlni(pc)) != nullptr)   // Issue #2279
   {
      if (pc->level == bro->level)
      {
         //prevent scanning back past 'new' in expressions like new List<int> {1,2,3}
         // Issue # 1620, UNI-24090.cs
         if (chunk_is_token(pc, CT_NEW))
         {
            break;
         }

         if (  !did_prop
            && (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, 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.test(PCF_STMT_START))
         {
            break;
         }
      }
   }
}


static void handle_cs_array_type(chunk_t *pc)
{
   chunk_t *prev;

   for (prev = chunk_get_prev(pc);
        chunk_is_token(prev, CT_COMMA);
        prev = chunk_get_prev(prev))
   {
      // empty
   }

   if (chunk_is_token(prev, CT_SQUARE_OPEN))
   {
      while (pc != prev)
      {
         set_chunk_parent(pc, CT_TYPE);
         pc = chunk_get_prev(pc);
      }
      set_chunk_parent(prev, 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);

   log_rule_B("sp_func_call_paren");
   log_rule_B("sp_cpp_cast_paren");
   iarf_e pav = (pc->type == CT_FUNC_WRAP) ?
                options::sp_func_call_paren() :
                options::sp_cpp_cast_paren();

   log_rule_B("sp_inside_fparen");
   log_rule_B("sp_inside_paren_cast");
   iarf_e av = (pc->type == CT_FUNC_WRAP) ?
               options::sp_inside_fparen() :
               options::sp_inside_paren_cast();

   if (  chunk_is_token(clp, CT_PAREN_CLOSE)
      && chunk_is_token(opp, CT_PAREN_OPEN)
      && (chunk_is_token(name, CT_WORD) || chunk_is_token(name, CT_TYPE)))
   {
      const char *psp = (pav & IARF_ADD) ? " " : "";
      const char *fsp = (av & IARF_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);
   }
} // handle_wrap


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)
      || opp->type != CT_PAREN_OPEN)
   {
      return;
   }

   if (chunk_is_token(cma, CT_SEMICOLON))
   {
      set_chunk_type(pc, CT_FUNC_PROTO);
   }
   else if (chunk_is_token(cma, CT_BRACE_OPEN))
   {
      LOG_FMT(LFCN, "%s(%d): (19) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
      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);

   if (chunk_is_token(tmp, CT_PAREN_OPEN))
   {
      fix_fcn_def_params(tmp);
   }
   else
   {
      fix_fcn_def_params(opp);
      set_chunk_type(name, CT_WORD);
   }
   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_ncnlni(tmp)) != nullptr)   // Issue #2279
   {
      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);
   }
} // handle_proto_wrap


/**
 * Java assert statements 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)) != nullptr)
   {
      if (tmp->level == pc->level)
      {
         if (!did_colon && chunk_is_token(tmp, CT_COLON))
         {
            did_colon = true;
            set_chunk_parent(tmp, pc->type);
         }

         if (chunk_is_token(tmp, CT_SEMICOLON))
         {
            set_chunk_parent(tmp, pc->type);
            break;
         }
      }
   }
}

[-- Attachment #4: git-diff.diff-Y --]
[-- Type: text/plain, Size: 151204 bytes --]

diff --git a/combine.cpp-2020-04-16-A b/combine.cpp-2020-04-16-R
index bfdbb81..d54bc27 100644
--- a/combine.cpp-2020-04-16-A
+++ b/combine.cpp-2020-04-16-R
@@ -115,6 +115,48 @@ static void process_returns(void);
 static chunk_t *process_return(chunk_t *pc);
 
 
+/**
+ * TODO: add doc cmt
+ *
+ */
+static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags);
+
+
+/**
+ * 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 *pclass);
+
+
+static void mark_cpp_constructor(chunk_t *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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
 /**
  * Process an ObjC 'class'
  * pc is the chunk after '@implementation' or '@interface' or '@protocol'.
@@ -303,6 +345,42 @@ static bool is_oc_block(chunk_t *pc);
 static void handle_java_assert(chunk_t *pc);
 
 
+/**
+ * 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);
+
+
+static bool chunkstack_match(ChunkStack &cs, chunk_t *pc);
+
+
+void make_type(chunk_t *pc)
+{
+   LOG_FUNC_ENTRY();
+
+   if (pc != nullptr)
+   {
+      if (chunk_is_token(pc, CT_WORD))
+      {
+         set_chunk_type(pc, CT_TYPE);
+      }
+      else if (  (  chunk_is_star(pc)
+                 || chunk_is_msref(pc)
+                 || chunk_is_nullable(pc))
+              && chunk_is_type(pc->prev))                              // Issue # 2640
+      {
+         set_chunk_type(pc, CT_PTR_TYPE);
+      }
+      else if (  chunk_is_addr(pc)
+              && !chunk_is_token(pc->prev, CT_SQUARE_OPEN))            // Issue # 2166
+      {
+         set_chunk_type(pc, CT_BYREF);
+      }
+   }
+}
+
+
 void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_t clr_flags, scope_e nav)
 {
    LOG_FUNC_ENTRY();
@@ -326,6 +404,28 @@ void flag_series(chunk_t *start, chunk_t *end, pcf_flags_t set_flags, pcf_flags_
 }
 
 
+chunk_t *set_paren_parent(chunk_t *start, c_token_t parent)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t *end;
+
+   end = chunk_skip_to_match(start, scope_e::PREPROC);
+
+   if (end != nullptr)
+   {
+      LOG_FMT(LFLPAREN, "%s(%d): %zu:%zu '%s' and %zu:%zu '%s' type is %s, parent_type is %s",
+              __func__, __LINE__, 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);
+   }
+   LOG_FMT(LFLPAREN, "%s(%d):\n", __func__, __LINE__);
+   return(chunk_get_next_ncnl(end, scope_e::PREPROC));
+}
+
+
 static void flag_asm(chunk_t *pc)
 {
    LOG_FUNC_ENTRY();
@@ -397,6 +497,79 @@ static void flag_asm(chunk_t *pc)
 } // flag_asm
 
 
+static bool chunk_ends_type(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t *pc       = start;
+   bool    ret       = false;
+   size_t  cnt       = 0;
+   bool    last_expr = false;
+   bool    last_lval = false;
+
+   for ( ; pc != nullptr; pc = chunk_get_prev_ncnlni(pc)) // Issue #2279
+   {
+      LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s', orig_line %zu, orig_col %zu\n   ",
+              __func__, __LINE__, get_token_name(pc->type), pc->text(),
+              pc->orig_line, pc->orig_col);
+      log_pcf_flags(LFTYPE, pc->flags);
+
+      if (  chunk_is_token(pc, CT_WORD)
+         || chunk_is_token(pc, CT_TYPE)
+         || chunk_is_token(pc, CT_PTR_TYPE)
+         || chunk_is_token(pc, CT_STAR)
+         || chunk_is_token(pc, CT_STRUCT)
+         || chunk_is_token(pc, CT_DC_MEMBER)
+         || chunk_is_token(pc, CT_PP)
+         || chunk_is_token(pc, CT_QUALIFIER)
+         || (  language_is_set(LANG_CPP | LANG_OC)                       // Issue #2727
+            && get_chunk_parent_type(pc) == CT_TEMPLATE
+            && (  chunk_is_token(pc, CT_ANGLE_OPEN)
+               || chunk_is_token(pc, CT_ANGLE_CLOSE)))
+         || (  language_is_set(LANG_CS)
+            && (chunk_is_token(pc, CT_MEMBER))))
+      {
+         cnt++;
+         last_expr = pc->flags.test(PCF_EXPR_START)
+                     && !pc->flags.test(PCF_IN_FCN_CALL);
+         last_lval = pc->flags.test(PCF_LVALUE);
+         continue;
+      }
+
+      if (  (  chunk_is_semicolon(pc)
+            && !pc->flags.test(PCF_IN_FOR))
+         || chunk_is_token(pc, CT_TYPEDEF)
+         || chunk_is_token(pc, CT_BRACE_OPEN)
+         || chunk_is_token(pc, CT_BRACE_CLOSE)
+         || chunk_is_token(pc, CT_VBRACE_CLOSE)
+         || chunk_is_token(pc, CT_FPAREN_CLOSE)
+         || chunk_is_forin(pc)
+         || chunk_is_token(pc, CT_MACRO)
+         || chunk_is_token(pc, CT_PP_IF)
+         || chunk_is_token(pc, CT_PP_ELSE)
+         || chunk_is_token(pc, CT_PP_ENDIF)
+         || (  (  chunk_is_token(pc, CT_COMMA)
+               && !pc->flags.test(PCF_IN_FCN_CALL))
+            && last_expr)
+         || (  chunk_is_token(pc, CT_SPAREN_OPEN)
+            && last_lval))
+      {
+         ret = cnt > 0;
+      }
+      break;
+   }
+
+   if (pc == nullptr)
+   {
+      // first token
+      ret = true;
+   }
+   LOG_FMT(LFTYPE, "%s(%d): first token verdict: %s\n",
+           __func__, __LINE__, ret ? "yes" : "no");
+
+   return(ret);
+} // chunk_ends_type
+
+
 void do_symbol_check(chunk_t *prev, chunk_t *pc, chunk_t *next)
 {
    LOG_FUNC_ENTRY();
@@ -2130,1066 +2303,3421 @@ void fix_symbols(void)
 } // fix_symbols
 
 
-static void process_returns(void)
+static void mark_lvalue(chunk_t *pc)
 {
    LOG_FUNC_ENTRY();
-   chunk_t *pc;
+   chunk_t *prev;
 
-   pc = chunk_get_head();
+   if (pc->flags.test(PCF_IN_PREPROC))
+   {
+      return;
+   }
 
-   while (pc != nullptr)
+   for (prev = chunk_get_prev_ncnlni(pc);     // Issue #2279
+        prev != nullptr;
+        prev = chunk_get_prev_ncnlni(prev))   // Issue #2279
    {
-      if (pc->type != CT_RETURN)
+      if (  prev->level < pc->level
+         || chunk_is_token(prev, CT_ASSIGN)
+         || chunk_is_token(prev, CT_COMMA)
+         || chunk_is_token(prev, CT_BOOL)
+         || chunk_is_semicolon(prev)
+         || chunk_is_str(prev, "(", 1)
+         || chunk_is_str(prev, "{", 1)
+         || chunk_is_str(prev, "[", 1)
+         || prev->flags.test(PCF_IN_PREPROC))
       {
-         pc = chunk_get_next_type(pc, CT_RETURN, -1);
-         continue;
+         break;
+      }
+      chunk_flags_set(prev, PCF_LVALUE);
+
+      if (prev->level == pc->level && chunk_is_str(prev, "&", 1))
+      {
+         make_type(prev);
       }
-      pc = process_return(pc);
    }
 }
 
 
-static chunk_t *process_return(chunk_t *pc)
+static void mark_function_return_type(chunk_t *fname, chunk_t *start, c_token_t parent_type)
 {
    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_ppa_get_next_ncnl(pc);
-
-   if (  next == nullptr || chunk_is_semicolon(next)
-      || chunk_is_token(next, CT_NEWLINE))
-   {
-      return(next);
-   }
-   log_rule_B("nl_return_expr");
-
-   if (  options::nl_return_expr() != IARF_IGNORE
-      && !pc->flags.test(PCF_IN_PREPROC))
-   {
-      newline_iarf(pc, options::nl_return_expr());
-   }
+   chunk_t *pc = start;
 
-   if (chunk_is_token(next, CT_PAREN_OPEN))
+   if (pc != nullptr)
    {
-      // See if the return is fully paren'd
-      cpar = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);
-
-      if (cpar == nullptr)
-      {
-         return(nullptr);
-      }
-      semi = chunk_ppa_get_next_ncnl(cpar);
+      // Step backwards from pc and mark the parent of the return type
+      LOG_FMT(LFCNR, "%s(%d): (backwards) return type for '%s' @ orig_line is %zu, orig_col is %zu\n",
+              __func__, __LINE__, fname->text(), fname->orig_line, fname->orig_col);
 
-      if (semi == nullptr)
-      {
-         return(nullptr);
-      }
+      chunk_t *first = pc;
 
-      if (chunk_is_token(semi, CT_NEWLINE) || chunk_is_semicolon(semi))
+      while (pc != nullptr)
       {
-         log_rule_B("mod_paren_on_return");
+         LOG_FMT(LFCNR, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', type is %s, ",
+                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(), get_token_name(pc->type));
+         log_pcf_flags(LFCNR, pc->flags);
 
-         if (options::mod_paren_on_return() == IARF_REMOVE)
+         if (chunk_is_token(pc, CT_ANGLE_CLOSE))
          {
-            LOG_FMT(LRETURN, "%s(%d): removing parens on orig_line %zu\n",
-                    __func__, __LINE__, pc->orig_line);
+            pc = skip_template_prev(pc);
 
-            // lower the level of everything
-            for (temp = next; temp != cpar; temp = chunk_get_next(temp))
+            if (pc == nullptr || chunk_is_token(pc, CT_TEMPLATE))
             {
-               if (temp->level == 0)
-               {
-                  fprintf(stderr, "%s(%d): temp->level is ZERO, cannot be decremented, at line %zu, column %zu\n",
-                          __func__, __LINE__, temp->orig_line, temp->orig_col);
-                  log_flush(true);
-                  exit(EX_SOFTWARE);
-               }
-               temp->level--;
+               //either expression is not complete or this is smth like 'template<T> void func()'
+               //  - we are not interested in 'template<T>' part
+               break;
             }
-
-            // delete the parenthesis
-            chunk_del(next);
-            chunk_del(cpar);
-
-            // back up following chunks
-            temp = semi;
-
-            while (temp != nullptr && temp->type != CT_NEWLINE)
+            else
             {
-               temp->column       = temp->column - 2;
-               temp->orig_col     = temp->orig_col - 2;
-               temp->orig_col_end = temp->orig_col_end - 2;
-               temp               = chunk_get_next(temp);
+               //this is smth like 'vector<int> func()' and 'pc' is currently on 'vector' - just proceed
             }
          }
-         else
+
+         if (  (  !chunk_is_type(pc)
+               && pc->type != CT_OPERATOR
+               && pc->type != CT_WORD
+               && pc->type != CT_ADDR)
+            || pc->flags.test(PCF_IN_PREPROC))
          {
-            LOG_FMT(LRETURN, "%s(%d): keeping parens on orig_line %zu\n",
-                    __func__, __LINE__, pc->orig_line);
+            break;
+         }
 
-            // mark & keep them
-            set_chunk_parent(next, CT_RETURN);
-            set_chunk_parent(cpar, CT_RETURN);
+         if (!chunk_is_ptr_operator(pc))
+         {
+            first = pc;
          }
-         return(semi);
+         pc = chunk_get_prev_ncnlni(pc);   // Issue #2279
       }
-   }
-   // We don't have a fully paren'd return. Should we add some?
-   log_rule_B("mod_paren_on_return");
+      LOG_FMT(LFCNR, "%s(%d): marking returns...", __func__, __LINE__);
 
-   if (!(options::mod_paren_on_return() & IARF_ADD))
-   {
-      return(next);
-   }
+      // Changing words to types into tuple return types in CS.
+      bool is_return_tuple = false;
 
-   // Issue #1917
-   // Never add parens to a braced init list; that breaks the code
-   //   return {args...};    // C++11 type elision; okay
-   //   return ({args...});  // ill-formed
-   if (  language_is_set(LANG_CPP) && chunk_is_token(next, CT_BRACE_OPEN)
-      && get_chunk_parent_type(next) == CT_BRACED_INIT_LIST)
-   {
-      LOG_FMT(LRETURN, "%s(%d): not adding parens around braced initializer"
-              " on orig_line %zd\n",
-              __func__, __LINE__, pc->orig_line);
-      return(next);
-   }
-   // find the next semicolon on the same level
-   semi = next;
+      if (chunk_is_token(pc, CT_PAREN_CLOSE) && !pc->flags.test(PCF_IN_PREPROC))
+      {
+         first           = chunk_skip_to_match_rev(pc);
+         is_return_tuple = true;
+      }
+      pc = first;
 
-   if (pc->flags.test(PCF_IN_PREPROC))
-   {
-      while ((semi = semi->next) != nullptr)
+      while (pc != nullptr)
       {
-         if (!semi->flags.test(PCF_IN_PREPROC))
+         LOG_FMT(LFCNR, " text() '%s', type is %s", pc->text(), get_token_name(pc->type));
+
+         if (parent_type != CT_NONE)
          {
-            break;
+            set_chunk_parent(pc, parent_type);
          }
+         chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279
 
-         if (semi->level < pc->level)
+         if (  !is_return_tuple
+            || pc->type != CT_WORD
+            || (prev != nullptr && prev->type != CT_TYPE))
          {
-            return(semi);
+            make_type(pc);
          }
 
-         if (chunk_is_semicolon(semi) && pc->level == semi->level)
+         if (pc == start)
          {
             break;
          }
-      }
-   }
-   else
-   {
-      while ((semi = chunk_get_next(semi)) != nullptr)
-      {
-         if (semi->level < pc->level)
+         pc = chunk_get_next_ncnl(pc);
+
+         //template angles should keep parent type CT_TEMPLATE
+         if (chunk_is_token(pc, CT_ANGLE_OPEN))
          {
-            return(semi);
+            pc = chunk_get_next_type(pc, CT_ANGLE_CLOSE, pc->level);
+
+            if (pc == start)
+            {
+               break;
+            }
+            pc = chunk_get_next_ncnl(pc);
          }
+      }
+      LOG_FMT(LFCNR, "\n");
 
-         if (chunk_is_semicolon(semi) && pc->level == semi->level)
+      // Back up and mark parent type on friend declarations
+      if (parent_type != CT_NONE && first && first->flags.test(PCF_IN_CLASS))
+      {
+         pc = chunk_get_prev_ncnlni(first);   // Issue #2279
+
+         if (chunk_is_token(pc, CT_FRIEND))
          {
-            break;
+            LOG_FMT(LFCNR, "%s(%d): marking friend\n", __func__, __LINE__);
+            set_chunk_parent(pc, parent_type);
+            // A friend might be preceded by a template specification, as in:
+            //   template <...> friend type func(...);
+            // If so, we need to mark that also
+            pc = chunk_get_prev_ncnlni(pc);   // Issue #2279
+
+            if (chunk_is_token(pc, CT_ANGLE_CLOSE))
+            {
+               pc = skip_template_prev(pc);
+
+               if (chunk_is_token(pc, CT_TEMPLATE))
+               {
+                  LOG_FMT(LFCNR, "%s(%d): marking friend template\n",
+                          __func__, __LINE__);
+                  set_chunk_parent(pc, parent_type);
+               }
+            }
          }
       }
    }
+} // mark_function_return_type
 
-   if (semi)
-   {
-      // add the parenthesis
-      set_chunk_type(&chunk, CT_PAREN_OPEN);
-      set_chunk_parent(&chunk, CT_RETURN);
-      chunk.str         = "(";
-      chunk.level       = pc->level;
-      chunk.brace_level = pc->brace_level;
-      chunk.orig_line   = pc->orig_line;
-      chunk.orig_col    = next->orig_col - 1;
-      chunk.flags       = pc->flags & PCF_COPY_FLAGS;
-      chunk_add_before(&chunk, next);
 
-      set_chunk_type(&chunk, CT_PAREN_CLOSE);
-      chunk.str       = ")";
-      chunk.orig_line = semi->orig_line;
-      chunk.orig_col  = semi->orig_col - 1;
-      cpar            = chunk_add_before(&chunk, semi);
+static bool mark_function_type(chunk_t *pc)
+{
+   LOG_FUNC_ENTRY();
+   LOG_FMT(LFTYPE, "%s(%d): type is %s, text() '%s' @ orig_line is %zu, orig_col is %zu\n",
+           __func__, __LINE__, get_token_name(pc->type), pc->text(),
+           pc->orig_line, pc->orig_col);
 
-      LOG_FMT(LRETURN, "%s(%d): added parens on orig_line %zu\n",
-              __func__, __LINE__, pc->orig_line);
+   size_t    star_count = 0;
+   size_t    word_count = 0;
+   chunk_t   *ptrcnk    = nullptr;
+   chunk_t   *tmp;
+   chunk_t   *apo;
+   chunk_t   *apc;
+   chunk_t   *aft;
+   bool      anon = false;
+   c_token_t pt, ptp;
 
-      for (temp = next; temp != cpar; temp = chunk_get_next(temp))
-      {
-         temp->level++;
-      }
-   }
-   return(semi);
-} // process_return
+   // Scan backwards across the name, which can only be a word and single star
+   chunk_t *varcnk = chunk_get_prev_ncnlni(pc);   // Issue #2279
 
+   varcnk = chunk_get_prev_ssq(varcnk);
 
-static bool is_ucase_str(const char *str, size_t len)
-{
-   while (len-- > 0)
+   if (varcnk != nullptr && !chunk_is_word(varcnk))
    {
-      if (unc_toupper(*str) != *str)
+      if (  language_is_set(LANG_OC)
+         && chunk_is_str(varcnk, "^", 1)
+         && chunk_is_paren_open(chunk_get_prev_ncnlni(varcnk)))   // Issue #2279
       {
-         return(false);
+         // anonymous ObjC block type -- RTYPE (^)(ARGS)
+         anon = true;
+      }
+      else
+      {
+         LOG_FMT(LFTYPE, "%s(%d): not a word: text() '%s', type is %s, @ orig_line is %zu:, orig_col is %zu\n",
+                 __func__, __LINE__, varcnk->text(), get_token_name(varcnk->type),
+                 varcnk->orig_line, varcnk->orig_col);
+         goto nogo_exit;
       }
-      str++;
    }
-   return(true);
-}
+   apo = chunk_get_next_ncnl(pc);
 
+   if (apo == nullptr)
+   {
+      return(false);
+   }
+   apc = chunk_skip_to_match(apo);
 
-static bool is_oc_block(chunk_t *pc)
-{
-   return(  pc != nullptr
-         && (  get_chunk_parent_type(pc) == CT_OC_BLOCK_TYPE
-            || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR
-            || get_chunk_parent_type(pc) == CT_OC_BLOCK_ARG
-            || get_chunk_parent_type(pc) == CT_OC_BLOCK
-            || chunk_is_token(pc, CT_OC_BLOCK_CARET)
-            || (pc->next != nullptr && pc->next->type == CT_OC_BLOCK_CARET)
-            || (pc->prev != nullptr && pc->prev->type == CT_OC_BLOCK_CARET)));
-}
+   if (  apc != nullptr
+      && (  !chunk_is_paren_open(apo)
+         || ((apc = chunk_skip_to_match(apo)) == nullptr)))
+   {
+      LOG_FMT(LFTYPE, "%s(%d): not followed by parens\n", __func__, __LINE__);
+      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(%d): not followed by '{' or ';'\n", __func__, __LINE__);
+      goto nogo_exit;
+   }
+   ptp = pc->flags.test(PCF_IN_TYPEDEF) ? CT_FUNC_TYPE : CT_FUNC_VAR;
 
-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 = nullptr;
-   chunk_t    *paren_close;
-   const char *verb      = "likely";
-   const char *detail    = "";
-   size_t     count      = 0;
-   int        word_count = 0;
-   bool       nope;
-   bool       doubtful_cast = false;
+   tmp = pc;
 
+   while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr)   // Issue #2279
+   {
+      tmp = chunk_get_prev_ssq(tmp);
 
-   LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n",
-           __func__, __LINE__, start->text(), start->orig_line, start->orig_col);
+      LOG_FMT(LFTYPE, " -- type is %s, %s on orig_line %zu, orig_col is %zu",
+              get_token_name(tmp->type), tmp->text(),
+              tmp->orig_line, tmp->orig_col);
 
-   prev = chunk_get_prev_ncnlni(start);   // Issue #2279
+      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)
+              || chunk_is_token(tmp, CT_WORD)
+              || chunk_is_token(tmp, CT_TYPE))
+      {
+         word_count++;
+         LOG_FMT(LFTYPE, " -- TYPE(%s)\n", tmp->text());
+      }
+      else if (chunk_is_token(tmp, 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: type is %s, text() '%s', on orig_line %zu, orig_col %zu\n",
+                 get_token_name(tmp->type), tmp->text(),
+                 tmp->orig_line, tmp->orig_col);
+         goto nogo_exit;
+      }
+   }
 
-   if (prev == nullptr)
+   // Fixes #issue 1577
+   // Allow word count 2 incase of function pointer declaration.
+   // Ex: bool (__stdcall* funcptr)(int, int);
+   if (  star_count > 1
+      || (word_count > 1 && !(word_count == 2 && ptp == CT_FUNC_VAR))
+      || ((star_count + word_count) == 0))
    {
-      return;
+      LOG_FMT(LFTYPE, "%s(%d): bad counts word: %zu, star: %zu\n",
+              __func__, __LINE__, word_count, star_count);
+      goto nogo_exit;
    }
 
-   if (chunk_is_token(prev, CT_PP_DEFINED))
+   // make sure what appears before the first open paren can be a return type
+   if (!chunk_ends_type(chunk_get_prev_ncnlni(tmp)))   // Issue #2279
    {
-      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after defined\n",
-              __func__, __LINE__);
-      return;
+      goto nogo_exit;
    }
 
-   if (chunk_is_token(prev, CT_ANGLE_CLOSE))
+   if (ptrcnk)
    {
-      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after > (template)\n",
-              __func__, __LINE__);
-      return;
+      set_chunk_type(ptrcnk, CT_PTR_TYPE);
    }
-   // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren
-   pc    = chunk_get_next_ncnl(start);
-   first = pc;
 
-   while (  pc != nullptr
-         && (  chunk_is_type(pc)
-            || chunk_is_token(pc, CT_WORD)
-            || chunk_is_token(pc, CT_QUALIFIER)
-            || chunk_is_token(pc, CT_DC_MEMBER)
-            || chunk_is_token(pc, CT_PP)
-            || chunk_is_token(pc, CT_STAR)
-            || chunk_is_token(pc, CT_QUESTION)
-            || chunk_is_token(pc, CT_CARET)
-            || chunk_is_token(pc, CT_TSQUARE)
-            || (  (  chunk_is_token(pc, CT_ANGLE_OPEN)
-                  || chunk_is_token(pc, CT_ANGLE_CLOSE))
-               && language_is_set(LANG_OC | LANG_JAVA))
-            || (  (  chunk_is_token(pc, CT_QUESTION)
-                  || chunk_is_token(pc, CT_COMMA)
-                  || chunk_is_token(pc, CT_MEMBER))
-               && language_is_set(LANG_JAVA))
-            || chunk_is_token(pc, CT_AMP)))
+   if (!anon)
    {
-      LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
-              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
-
-      if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER)))
+      if (pc->flags.test(PCF_IN_TYPEDEF))
       {
-         word_count++;
+         set_chunk_type(varcnk, CT_TYPE);
       }
-      else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP))
+      else
       {
-         // might be negativ, such as with:
-         // a = val + (CFoo::bar_t)7;
-         word_count--;
+         set_chunk_type(varcnk, CT_FUNC_VAR);
+         chunk_flags_set(varcnk, PCF_VAR_1ST_DEF);
       }
-      last = pc;
-      pc   = chunk_get_next_ncnl(pc);
-      count++;
    }
+   set_chunk_type(pc, CT_TPAREN_CLOSE);
+   set_chunk_parent(pc, ptp);
 
-   if (  pc == nullptr
-      || pc->type != CT_PAREN_CLOSE
-      || chunk_is_token(prev, CT_OC_CLASS))
+   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))
    {
-      LOG_FMT(LCASTS, "%s(%d):  -- not a cast, hit type is %s\n",
-              __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type));
-      return;
+      set_chunk_parent(aft, aft->flags.test(PCF_IN_TYPEDEF) ? CT_TYPEDEF : CT_FUNC_VAR);
+   }
+   else if (chunk_is_token(aft, CT_BRACE_OPEN))
+   {
+      flag_parens(aft, PCF_NONE, CT_NONE, pt, false);
+   }
+   // Step backwards to the previous open paren and mark everything a
+   tmp = pc;
+
+   while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr)   // Issue #2279
+   {
+      LOG_FMT(LFTYPE, " ++ type is %s, text() '%s', on orig_line %zu, orig_col %zu\n",
+              get_token_name(tmp->type), tmp->text(),
+              tmp->orig_line, tmp->orig_col);
+
+      if (*tmp->str.c_str() == '(')
+      {
+         if (!pc->flags.test(PCF_IN_TYPEDEF))
+         {
+            chunk_flags_set(tmp, PCF_VAR_1ST_DEF);
+         }
+         set_chunk_type(tmp, CT_TPAREN_OPEN);
+         set_chunk_parent(tmp, ptp);
+
+         tmp = chunk_get_prev_ncnlni(tmp);   // Issue #2279
+
+         if (  chunk_is_token(tmp, CT_FUNCTION)
+            || chunk_is_token(tmp, CT_FUNC_CALL)
+            || chunk_is_token(tmp, CT_FUNC_CALL_USER)
+            || chunk_is_token(tmp, CT_FUNC_DEF)
+            || chunk_is_token(tmp, CT_FUNC_PROTO))
+         {
+            set_chunk_type(tmp, CT_TYPE);
+            chunk_flags_clr(tmp, PCF_VAR_1ST_DEF);
+         }
+         mark_function_return_type(varcnk, tmp, ptp);
+         break;
+      }
    }
+   return(true);
 
-   if (word_count > 1)
+nogo_exit:
+   tmp = chunk_get_next_ncnl(pc);
+
+   if (chunk_is_paren_open(tmp))
    {
-      LOG_FMT(LCASTS, "%s(%d):  -- too many words: %d\n",
-              __func__, __LINE__, word_count);
-      return;
+      LOG_FMT(LFTYPE, "%s(%d): setting FUNC_CALL on orig_line is %zu, orig_col is %zu\n",
+              __func__, __LINE__, tmp->orig_line, tmp->orig_col);
+      flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
    }
-   paren_close = pc;
+   return(false);
+} // mark_function_type
 
-   // If last is a type or star/caret, we have a cast for sure
-   if (  chunk_is_token(last, CT_STAR)
-      || chunk_is_token(last, CT_CARET)
-      || chunk_is_token(last, CT_PTR_TYPE)
-      || chunk_is_token(last, CT_TYPE)
-      || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA)))
+
+static void process_returns(void)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t *pc;
+
+   pc = chunk_get_head();
+
+   while (pc != nullptr)
    {
-      verb = "for sure";
+      if (pc->type != CT_RETURN)
+      {
+         pc = chunk_get_next_type(pc, CT_RETURN, -1);
+         continue;
+      }
+      pc = process_return(pc);
    }
-   else if (count == 1)
+}
+
+
+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_ppa_get_next_ncnl(pc);
+
+   if (  next == nullptr || chunk_is_semicolon(next)
+      || chunk_is_token(next, CT_NEWLINE))
    {
-      /*
-       * 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";
+      return(next);
+   }
+   log_rule_B("nl_return_expr");
 
-      if (  (last->len() > 3)
-         && (last->str[last->len() - 2] == '_')
-         && (last->str[last->len() - 1] == 't'))
+   if (  options::nl_return_expr() != IARF_IGNORE
+      && !pc->flags.test(PCF_IN_PREPROC))
+   {
+      newline_iarf(pc, options::nl_return_expr());
+   }
+
+   if (chunk_is_token(next, CT_PAREN_OPEN))
+   {
+      // See if the return is fully paren'd
+      cpar = chunk_get_next_type(next, CT_PAREN_CLOSE, next->level);
+
+      if (cpar == nullptr)
+      {
+         return(nullptr);
+      }
+      semi = chunk_ppa_get_next_ncnl(cpar);
+
+      if (semi == nullptr)
+      {
+         return(nullptr);
+      }
+
+      if (chunk_is_token(semi, CT_NEWLINE) || chunk_is_semicolon(semi))
+      {
+         log_rule_B("mod_paren_on_return");
+
+         if (options::mod_paren_on_return() == IARF_REMOVE)
+         {
+            LOG_FMT(LRETURN, "%s(%d): removing parens on orig_line %zu\n",
+                    __func__, __LINE__, pc->orig_line);
+
+            // lower the level of everything
+            for (temp = next; temp != cpar; temp = chunk_get_next(temp))
+            {
+               if (temp->level == 0)
+               {
+                  fprintf(stderr, "%s(%d): temp->level is ZERO, cannot be decremented, at line %zu, column %zu\n",
+                          __func__, __LINE__, temp->orig_line, temp->orig_col);
+                  log_flush(true);
+                  exit(EX_SOFTWARE);
+               }
+               temp->level--;
+            }
+
+            // delete the parenthesis
+            chunk_del(next);
+            chunk_del(cpar);
+
+            // back up following chunks
+            temp = semi;
+
+            while (temp != nullptr && temp->type != CT_NEWLINE)
+            {
+               temp->column       = temp->column - 2;
+               temp->orig_col     = temp->orig_col - 2;
+               temp->orig_col_end = temp->orig_col_end - 2;
+               temp               = chunk_get_next(temp);
+            }
+         }
+         else
+         {
+            LOG_FMT(LRETURN, "%s(%d): keeping parens on orig_line %zu\n",
+                    __func__, __LINE__, 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?
+   log_rule_B("mod_paren_on_return");
+
+   if (!(options::mod_paren_on_return() & IARF_ADD))
+   {
+      return(next);
+   }
+
+   // Issue #1917
+   // Never add parens to a braced init list; that breaks the code
+   //   return {args...};    // C++11 type elision; okay
+   //   return ({args...});  // ill-formed
+   if (  language_is_set(LANG_CPP) && chunk_is_token(next, CT_BRACE_OPEN)
+      && get_chunk_parent_type(next) == CT_BRACED_INIT_LIST)
+   {
+      LOG_FMT(LRETURN, "%s(%d): not adding parens around braced initializer"
+              " on orig_line %zd\n",
+              __func__, __LINE__, pc->orig_line);
+      return(next);
+   }
+   // find the next semicolon on the same level
+   semi = next;
+
+   if (pc->flags.test(PCF_IN_PREPROC))
+   {
+      while ((semi = semi->next) != nullptr)
+      {
+         if (!semi->flags.test(PCF_IN_PREPROC))
+         {
+            break;
+         }
+
+         if (semi->level < pc->level)
+         {
+            return(semi);
+         }
+
+         if (chunk_is_semicolon(semi) && pc->level == semi->level)
+         {
+            break;
+         }
+      }
+   }
+   else
+   {
+      while ((semi = chunk_get_next(semi)) != nullptr)
+      {
+         if (semi->level < pc->level)
+         {
+            return(semi);
+         }
+
+         if (chunk_is_semicolon(semi) && pc->level == semi->level)
+         {
+            break;
+         }
+      }
+   }
+
+   if (semi)
+   {
+      // add the parenthesis
+      set_chunk_type(&chunk, CT_PAREN_OPEN);
+      set_chunk_parent(&chunk, CT_RETURN);
+      chunk.str         = "(";
+      chunk.level       = pc->level;
+      chunk.brace_level = pc->brace_level;
+      chunk.orig_line   = pc->orig_line;
+      chunk.orig_col    = next->orig_col - 1;
+      chunk.flags       = pc->flags & PCF_COPY_FLAGS;
+      chunk_add_before(&chunk, next);
+
+      set_chunk_type(&chunk, CT_PAREN_CLOSE);
+      chunk.str       = ")";
+      chunk.orig_line = semi->orig_line;
+      chunk.orig_col  = semi->orig_col - 1;
+      cpar            = chunk_add_before(&chunk, semi);
+
+      LOG_FMT(LRETURN, "%s(%d): added parens on orig_line %zu\n",
+              __func__, __LINE__, pc->orig_line);
+
+      for (temp = next; temp != cpar; temp = chunk_get_next(temp))
+      {
+         temp->level++;
+      }
+   }
+   return(semi);
+} // process_return
+
+
+static bool is_ucase_str(const char *str, size_t len)
+{
+   while (len-- > 0)
+   {
+      if (unc_toupper(*str) != *str)
+      {
+         return(false);
+      }
+      str++;
+   }
+   return(true);
+}
+
+
+static bool is_oc_block(chunk_t *pc)
+{
+   return(  pc != nullptr
+         && (  get_chunk_parent_type(pc) == CT_OC_BLOCK_TYPE
+            || get_chunk_parent_type(pc) == CT_OC_BLOCK_EXPR
+            || get_chunk_parent_type(pc) == CT_OC_BLOCK_ARG
+            || get_chunk_parent_type(pc) == CT_OC_BLOCK
+            || chunk_is_token(pc, CT_OC_BLOCK_CARET)
+            || (pc->next != nullptr && pc->next->type == CT_OC_BLOCK_CARET)
+            || (pc->prev != nullptr && pc->prev->type == CT_OC_BLOCK_CARET)));
+}
+
+
+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 = nullptr;
+   chunk_t    *paren_close;
+   const char *verb      = "likely";
+   const char *detail    = "";
+   size_t     count      = 0;
+   int        word_count = 0;
+   bool       nope;
+   bool       doubtful_cast = false;
+
+
+   LOG_FMT(LCASTS, "%s(%d): start->text() is '%s', orig_line is %zu, orig_col is %zu\n",
+           __func__, __LINE__, start->text(), start->orig_line, start->orig_col);
+
+   prev = chunk_get_prev_ncnlni(start);   // Issue #2279
+
+   if (prev == nullptr)
+   {
+      return;
+   }
+
+   if (chunk_is_token(prev, CT_PP_DEFINED))
+   {
+      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after defined\n",
+              __func__, __LINE__);
+      return;
+   }
+
+   if (chunk_is_token(prev, CT_ANGLE_CLOSE))
+   {
+      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - after > (template)\n",
+              __func__, __LINE__);
+      return;
+   }
+   // Make sure there is only WORD, TYPE, and '*' or '^' before the close paren
+   pc    = chunk_get_next_ncnl(start);
+   first = pc;
+
+   while (  pc != nullptr
+         && (  chunk_is_type(pc)
+            || chunk_is_token(pc, CT_WORD)
+            || chunk_is_token(pc, CT_QUALIFIER)
+            || chunk_is_token(pc, CT_DC_MEMBER)
+            || chunk_is_token(pc, CT_PP)
+            || chunk_is_token(pc, CT_STAR)
+            || chunk_is_token(pc, CT_QUESTION)
+            || chunk_is_token(pc, CT_CARET)
+            || chunk_is_token(pc, CT_TSQUARE)
+            || (  (  chunk_is_token(pc, CT_ANGLE_OPEN)
+                  || chunk_is_token(pc, CT_ANGLE_CLOSE))
+               && language_is_set(LANG_OC | LANG_JAVA))
+            || (  (  chunk_is_token(pc, CT_QUESTION)
+                  || chunk_is_token(pc, CT_COMMA)
+                  || chunk_is_token(pc, CT_MEMBER))
+               && language_is_set(LANG_JAVA))
+            || chunk_is_token(pc, CT_AMP)))
+   {
+      LOG_FMT(LCASTS, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
+
+      if (chunk_is_token(pc, CT_WORD) || (chunk_is_token(last, CT_ANGLE_CLOSE) && chunk_is_token(pc, CT_DC_MEMBER)))
+      {
+         word_count++;
+      }
+      else if (chunk_is_token(pc, CT_DC_MEMBER) || chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_PP))
+      {
+         // might be negativ, such as with:
+         // a = val + (CFoo::bar_t)7;
+         word_count--;
+      }
+      last = pc;
+      pc   = chunk_get_next_ncnl(pc);
+      count++;
+   }
+
+   if (  pc == nullptr
+      || pc->type != CT_PAREN_CLOSE
+      || chunk_is_token(prev, CT_OC_CLASS))
+   {
+      LOG_FMT(LCASTS, "%s(%d):  -- not a cast, hit type is %s\n",
+              __func__, __LINE__, pc == nullptr ? "NULL" : get_token_name(pc->type));
+      return;
+   }
+
+   if (word_count > 1)
+   {
+      LOG_FMT(LCASTS, "%s(%d):  -- too many words: %d\n",
+              __func__, __LINE__, word_count);
+      return;
+   }
+   paren_close = pc;
+
+   // If last is a type or star/caret, we have a cast for sure
+   if (  chunk_is_token(last, CT_STAR)
+      || chunk_is_token(last, CT_CARET)
+      || chunk_is_token(last, CT_PTR_TYPE)
+      || chunk_is_token(last, CT_TYPE)
+      || (chunk_is_token(last, CT_ANGLE_CLOSE) && language_is_set(LANG_OC | LANG_JAVA)))
+   {
+      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 (language_is_set(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 (chunk_is_token(after, CT_PAREN_OPEN));
+
+      if (after == nullptr)
+      {
+         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - hit NULL\n",
+                 __func__, __LINE__);
+         return;
+      }
+      nope = false;
+
+      if (chunk_is_ptr_operator(pc))
+      {
+         // star (*) and address (&) are ambiguous
+         if (  chunk_is_token(after, CT_NUMBER_FP)
+            || chunk_is_token(after, CT_NUMBER)
+            || chunk_is_token(after, CT_STRING)
+            || doubtful_cast)
+         {
+            nope = true;
+         }
+      }
+      else if (chunk_is_token(pc, CT_MINUS))
+      {
+         // (UINT8)-1 or (foo)-1 or (FOO)-'a'
+         if (chunk_is_token(after, CT_STRING) || doubtful_cast)
+         {
+            nope = true;
+         }
+      }
+      else if (chunk_is_token(pc, 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_THIS
+              && pc->type != CT_TYPE
+              && pc->type != CT_PAREN_OPEN
+              && pc->type != CT_STRING
+              && pc->type != CT_DECLTYPE
+              && pc->type != CT_SIZEOF
+              && get_chunk_parent_type(pc) != CT_SIZEOF
+              && pc->type != CT_FUNC_CALL
+              && pc->type != CT_FUNC_CALL_USER
+              && pc->type != CT_FUNCTION
+              && pc->type != CT_BRACE_OPEN
+              && (!(  chunk_is_token(pc, CT_SQUARE_OPEN)
+                   && language_is_set(LANG_OC))))
+      {
+         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by text() '%s', type is %s\n",
+                 __func__, __LINE__, pc->text(), get_token_name(pc->type));
+         return;
+      }
+
+      if (nope)
+      {
+         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - text() '%s' followed by type %s\n",
+                 __func__, __LINE__, pc->text(), get_token_name(after->type));
+         return;
+      }
+   }
+   // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't
+   pc = chunk_get_next_ncnl(paren_close);
+
+   if (pc == nullptr)
+   {
+      return;
+   }
+
+   if (  chunk_is_semicolon(pc)
+      || chunk_is_token(pc, CT_COMMA)
+      || chunk_is_token(pc, CT_BOOL)               // Issue #2151
+      || chunk_is_paren_close(pc))
+   {
+      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by type %s\n",
+              __func__, __LINE__, 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(%d):  -- %s c-cast: (",
+           __func__, __LINE__, verb);
+
+   for (pc = first;
+        pc != nullptr && pc != paren_close;
+        pc = chunk_get_next_ncnl(pc))
+   {
+      set_chunk_parent(pc, CT_C_CAST);
+      make_type(pc);
+      LOG_FMT(LCASTS, " %s", pc->text());
+   }
+
+   LOG_FMT(LCASTS, " )%s\n", detail);
+
+   // Mark the next item as an expression start
+   pc = chunk_get_next_ncnl(paren_close);
+
+   if (pc != nullptr)
+   {
+      chunk_flags_set(pc, PCF_EXPR_START);
+
+      if (chunk_is_opening_brace(pc))
+      {
+         set_paren_parent(pc, get_chunk_parent_type(start));
+      }
+   }
+} // fix_casts
+
+
+static void fix_type_cast(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t *pc;
+
+   pc = chunk_get_next_ncnl(start);
+
+   if (pc == nullptr || pc->type != CT_ANGLE_OPEN)
+   {
+      return;
+   }
+
+   while (  ((pc = chunk_get_next_ncnl(pc)) != nullptr)
+         && pc->level >= start->level)
+   {
+      if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE))
+      {
+         pc = chunk_get_next_ncnl(pc);
+
+         if (pc == nullptr)
+         {
+            return;
+         }
+
+         if (chunk_is_str(pc, "(", 1))
+         {
+            set_paren_parent(pc, CT_TYPE_CAST);
+         }
+         return;
+      }
+      make_type(pc);
+   }
+}
+
+
+static void fix_enum_struct_union(chunk_t *pc)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t     *next;
+   chunk_t     *prev        = nullptr;
+   pcf_flags_t flags        = PCF_VAR_1ST_DEF;
+   auto const  in_fcn_paren = pc->flags & PCF_IN_FCN_DEF;
+
+   // Make sure this wasn't a cast
+   if (get_chunk_parent_type(pc) == CT_C_CAST)
+   {
+      return;
+   }
+   // the next item is either a type or open brace
+   next = chunk_get_next_ncnl(pc);
+
+   // the enum-key might be enum, enum class or enum struct (TODO)
+   if (chunk_is_token(next, CT_ENUM_CLASS))
+   {
+      next = chunk_get_next_ncnl(next); // get the next one
+   }
+
+   if (language_is_set(LANG_CPP))
+   {
+      next = skip_attribute_next(next); // get the next one
+   }
+
+   // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace
+   if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON))
+   {
+      // i.e. "enum xyz : unsigned int { ... };"
+      // i.e. "enum class xyz : unsigned int { ... };"
+      // i.e. "enum : unsigned int { ... };"
+      // xyz is a type
+
+      // save the type if it exists
+      if (!chunk_is_token(next, CT_COLON))
+      {
+         set_chunk_parent(next, pc->type);
+         prev = next;
+         next = chunk_get_next_ncnl(next);
+      }
+
+      if (next == nullptr)
+      {
+         return;
+      }
+      set_chunk_parent(next, pc->type);
+      auto const is_struct_or_class =
+         (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS));
+
+      // next up is either a colon, open brace, or open parenthesis (pawn)
+      if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN))
+      {
+         next = set_paren_parent(next, CT_ENUM);
+      }
+      else if (chunk_is_token(next, CT_COLON))
+      {
+         if (chunk_is_token(pc, CT_ENUM))
+         {
+            // enum TYPE : INT_TYPE { ... };
+            next = chunk_get_next_ncnl(next);
+
+            if (next != nullptr)
+            {
+               make_type(next);
+               next = chunk_get_next_ncnl(next);
+
+               // enum TYPE : unsigned int { ... };
+               if (chunk_is_token(next, CT_TYPE))
+               {
+                  // get the next part of the type
+                  next = chunk_get_next_ncnl(next);
+               }
+            }
+         }
+         else if (is_struct_or_class)
+         {
+            next = skip_parent_types(next);
+         }
+      }
+      else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN))
+      {
+         // Fix #1267 structure attributes
+         // struct __attribute__(align(x)) struct_name;
+         // skip to matching parenclose and make next token as type.
+         next = chunk_skip_to_match(next);
+         next = chunk_get_next_ncnl(next);
+         set_chunk_type(next, CT_TYPE);
+         set_chunk_parent(next, pc->type);
+      }
+
+      if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration
+      {
+         set_chunk_parent(next, pc->type);
+         flag_series(pc, prev, PCF_INCOMPLETE);
+         return;
+      }
+   }
+
+   if (chunk_is_token(next, CT_BRACE_OPEN))
+   {
+      auto const flag = [pc] {
+         switch (pc->type)
+         {
+         case CT_ENUM:
+            return(PCF_IN_ENUM);
+
+         case CT_STRUCT:
+            return(PCF_IN_STRUCT);
+
+         case CT_CLASS:
+            return(PCF_IN_CLASS);
+
+         default:
+            return(PCF_NONE);
+         }
+      }();
+
+      flag_parens(next, flag, CT_NONE, CT_NONE, false);
+
+      if (  chunk_is_token(pc, CT_UNION)
+         || chunk_is_token(pc, CT_STRUCT)
+         || chunk_is_token(pc, CT_CLASS))
+      {
+         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 != nullptr)
+      {
+         set_chunk_parent(next, pc->type);
+         next = chunk_get_next_ncnl(next);
+      }
+      prev = nullptr;
+   }
+   // reset var name parent type
+   else if (next && prev)
+   {
+      set_chunk_parent(prev, CT_NONE);
+   }
+
+   if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE))
+   {
+      return;
+   }
+
+   if (!chunk_is_semicolon(next))
+   {
+      // Pawn does not require a semicolon after an enum
+      if (language_is_set(LANG_PAWN))
+      {
+         return;
+      }
+
+      /*
+       * D does not require a semicolon after an enum, but we add one to make
+       * other code happy.
+       */
+      if (language_is_set(LANG_D))
+      {
+         next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next));   // Issue #2279
+      }
+   }
+
+   // We are either pointing to a ';' or a variable
+   while (  next != nullptr
+         && !chunk_is_semicolon(next)
+         && next->type != CT_ASSIGN
+         && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any())
+   {
+      if (next->level == pc->level)
+      {
+         if (chunk_is_token(next, CT_WORD))
+         {
+            chunk_flags_set(next, flags);
+            flags &= ~PCF_VAR_1ST;   // clear the first flag for the next items
+            LOG_FMT(LCASTS, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
+                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+         }
+
+         if (  chunk_is_token(next, CT_STAR)
+            || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET)))
+         {
+            set_chunk_type(next, CT_PTR_TYPE);
+         }
+
+         // If we hit a comma in a function param, we are done
+         if (  (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE))
+            && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL)))
+         {
+            return;
+         }
+      }
+      next = chunk_get_next_ncnl(next);
+   }
+
+   if (  next != nullptr
+      && chunk_is_token(next, CT_SEMICOLON))
+   {
+      set_chunk_parent(next, pc->type);
+   }
+} // fix_enum_struct_union
+
+
+static void fix_typedef(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+
+   if (start == nullptr)
+   {
+      return;
+   }
+   LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n",
+           __func__, __LINE__, start->orig_line, start->orig_col);
+
+   chunk_t *the_type = nullptr;
+   chunk_t *last_op  = nullptr;
+
+   /*
+    * Mark everything in the typedef and scan for ")(", which makes it a
+    * function type
+    */
+   for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC)
+        ; next != nullptr && next->level >= start->level
+        ; next = chunk_get_next_ncnl(next, scope_e::PREPROC))
+   {
+      chunk_flags_set(next, PCF_IN_TYPEDEF);
+
+      if (start->level == next->level)
+      {
+         if (chunk_is_semicolon(next))
+         {
+            set_chunk_parent(next, CT_TYPEDEF);
+            break;
+         }
+
+         if (chunk_is_token(next, CT_ATTRIBUTE))
+         {
+            break;
+         }
+
+         if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN))
+         {
+            set_chunk_parent(next, CT_TYPEDEF);
+            break;
+         }
+         make_type(next);
+
+         if (chunk_is_token(next, CT_TYPE))
+         {
+            the_type = next;
+         }
+         chunk_flags_clr(next, PCF_VAR_1ST_DEF);
+
+         if (*next->str.c_str() == '(')
+         {
+            last_op = next;
+         }
+      }
+   }
+
+   // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def
+   if (  last_op != nullptr
+      && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM))
+   {
+      flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false);
+      fix_fcn_def_params(last_op);
+
+      the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC);   // Issue #2279
+
+      if (the_type == nullptr)
+      {
+         return;
+      }
+      chunk_t *open_paren = nullptr;
+
+      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_ncnlni(the_type, scope_e::PREPROC);   // Issue #2279
+
+         if (the_type == nullptr)
+         {
+            return;
+         }
+      }
+      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(%d): fcn typedef text() '%s', on orig_line %zu\n",
+              __func__, __LINE__, the_type->text(), the_type->orig_line);
+
+      // If we are aligning on the open parenthesis, grab that instead
+      log_rule_B("align_typedef_func");
+
+      if (open_paren != nullptr && options::align_typedef_func() == 1)
+      {
+         the_type = open_paren;
+      }
+      log_rule_B("align_typedef_func");
+
+      if (options::align_typedef_func() != 0)
+      {
+         LOG_FMT(LTYPEDEF, "%s(%d):  -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n",
+                 __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col);
+         chunk_flags_set(the_type, 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
+    */
+   chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC);
+
+   if (after == nullptr)
+   {
+      return;
+   }
+
+   if (  after->type != CT_ENUM
+      && after->type != CT_STRUCT
+      && after->type != CT_UNION)
+   {
+      if (the_type != nullptr)
+      {
+         // We have just a regular typedef
+         LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n",
+                 __func__, __LINE__, the_type->text(), the_type->orig_line);
+         chunk_flags_set(the_type, PCF_ANCHOR);
+      }
+      return;
+   }
+   // We have a struct/union/enum, next should be either a type or {
+   chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC);
+
+   if (next == nullptr)
+   {
+      return;
+   }
+
+   if (chunk_is_token(next, CT_TYPE))
+   {
+      next = chunk_get_next_ncnl(next, scope_e::PREPROC);
+
+      if (next == nullptr)
+      {
+         return;
+      }
+   }
+
+   if (chunk_is_token(next, CT_BRACE_OPEN))
+   {
+      // Skip to the closing brace
+      chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC);
+
+      if (br_c != nullptr)
+      {
+         const c_token_t tag = after->type;
+         set_chunk_parent(next, tag);
+         set_chunk_parent(br_c, tag);
+
+         if (tag == CT_ENUM)
+         {
+            flag_series(after, br_c, PCF_IN_ENUM);
+         }
+         else if (tag == CT_STRUCT)
+         {
+            flag_series(after, br_c, PCF_IN_STRUCT);
+         }
+      }
+   }
+
+   if (the_type != nullptr)
+   {
+      LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n",
+              __func__, __LINE__, get_token_name(after->type), the_type->text(),
+              the_type->orig_line);
+      chunk_flags_set(the_type, PCF_ANCHOR);
+   }
+} // fix_typedef
+
+
+static void mark_variable_stack(ChunkStack &cs, log_sev_t sev)
+{
+   UNUSED(sev);
+   LOG_FUNC_ENTRY();
+
+   // throw out the last word and mark the rest
+   chunk_t *var_name = cs.Pop_Back();
+
+   if (var_name && var_name->prev->type == CT_DC_MEMBER)
+   {
+      cs.Push_Back(var_name);
+   }
+
+   if (var_name != nullptr)
+   {
+      LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n",
+              __func__, __LINE__, var_name->orig_line, var_name->orig_col);
+
+      size_t  word_cnt = 0;
+      chunk_t *word_type;
+
+      while ((word_type = cs.Pop_Back()) != nullptr)
+      {
+         if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE))
+         {
+            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
+                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text());
+            set_chunk_type(word_type, CT_TYPE);
+            chunk_flags_set(word_type, PCF_VAR_TYPE);
+         }
+         word_cnt++;
+      }
+
+      if (chunk_is_token(var_name, CT_WORD))
+      {
+         if (word_cnt > 0)
+         {
+            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n",
+                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
+            chunk_flags_set(var_name, PCF_VAR_DEF);
+         }
+         else
+         {
+            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
+                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
+            set_chunk_type(var_name, CT_TYPE);
+            chunk_flags_set(var_name, PCF_VAR_TYPE);
+         }
+      }
+   }
+} // mark_variable_stack
+
+
+static void fix_fcn_def_params(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+
+   if (start == nullptr)
+   {
+      return;
+   }
+   LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n",
+           __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level);
+
+   while (start != nullptr && !chunk_is_paren_open(start))
+   {
+      start = chunk_get_next_ncnl(start);
+   }
+
+   if (start == nullptr)// Coverity CID 76003, 1100782
+   {
+      return;
+   }
+   // ensure start chunk holds a single '(' character
+   assert((start->len() == 1) && (start->str[0] == '('));
+
+   ChunkStack cs;
+   size_t     level = start->level + 1;
+   chunk_t    *pc   = start;
+
+   while ((pc = chunk_get_next_ncnl(pc)) != nullptr)
+   {
+      if (  ((start->len() == 1) && (start->str[0] == ')'))
+         || pc->level < level)
+      {
+         LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n",
+                 __func__, __LINE__, pc->text(), pc->orig_line);
+         break;
+      }
+      LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n",
+              __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at",
+              pc->text(), pc->orig_line, pc->level);
+
+      if (pc->level > level)
+      {
+         continue;
+      }
+
+      if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc))
+      {
+         set_chunk_type(pc, CT_PTR_TYPE);
+         cs.Push_Back(pc);
+      }
+      else if (  chunk_is_token(pc, CT_AMP)
+              || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2)))
+      {
+         set_chunk_type(pc, CT_BYREF);
+         cs.Push_Back(pc);
+      }
+      else if (chunk_is_token(pc, CT_TYPE_WRAP))
+      {
+         cs.Push_Back(pc);
+      }
+      else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))
+      {
+         cs.Push_Back(pc);
+      }
+      else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN))
+      {
+         mark_variable_stack(cs, LFCNP);
+
+         if (chunk_is_token(pc, CT_ASSIGN))
+         {
+            // Mark assignment for default param spacing
+            set_chunk_parent(pc, CT_FUNC_PROTO);
+         }
+      }
+   }
+   mark_variable_stack(cs, LFCNP);
+} // fix_fcn_def_params
+
+
+static chunk_t *skip_to_next_statement(chunk_t *pc)
+{
+   while (  pc != nullptr
+         && !chunk_is_semicolon(pc)
+         && pc->type != CT_BRACE_OPEN
+         && pc->type != CT_BRACE_CLOSE)
+   {
+      pc = chunk_get_next_ncnl(pc);
+   }
+   return(pc);
+}
+
+
+static chunk_t *fix_variable_definition(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t    *pc = start;
+   chunk_t    *end;
+   chunk_t    *tmp_pc;
+   ChunkStack cs;
+   int        idx;
+   int        ref_idx;
+
+   LOG_FMT(LFVD, "%s(%d): start at pc->orig_line is %zu, pc->orig_col is %zu\n",
+           __func__, __LINE__, pc->orig_line, pc->orig_col);
+
+   // Scan for words and types and stars oh my!
+   while (  chunk_is_token(pc, CT_TYPE)
+         || chunk_is_token(pc, CT_WORD)
+         || chunk_is_token(pc, CT_QUALIFIER)
+         || chunk_is_token(pc, CT_TYPENAME)
+         || chunk_is_token(pc, CT_DC_MEMBER)
+         || chunk_is_token(pc, CT_MEMBER)
+         || chunk_is_ptr_operator(pc))
+   {
+      LOG_FMT(LFVD, "%s(%d):   1:pc->text() '%s', type is %s\n",
+              __func__, __LINE__, pc->text(), get_token_name(pc->type));
+      cs.Push_Back(pc);
+      pc = chunk_get_next_ncnl(pc);
+
+      if (pc == nullptr)
+      {
+         LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__);
+         return(nullptr);
+      }
+      LOG_FMT(LFVD, "%s(%d):   2:pc->text() '%s', type is %s\n",
+              __func__, __LINE__, pc->text(), get_token_name(pc->type));
+
+      // Skip templates and attributes
+      pc = skip_template_next(pc);
+
+      if (pc == nullptr)
+      {
+         LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__);
+         return(nullptr);
+      }
+      LOG_FMT(LFVD, "%s(%d):   3:pc->text() '%s', type is %s\n",
+              __func__, __LINE__, pc->text(), get_token_name(pc->type));
+
+      pc = skip_attribute_next(pc);
+
+      if (pc == nullptr)
+      {
+         LOG_FMT(LFVD, "%s(%d): pc is nullptr\n", __func__, __LINE__);
+         return(nullptr);
+      }
+      LOG_FMT(LFVD, "%s(%d):   4:pc->text() '%s', type is %s\n",
+              __func__, __LINE__, pc->text(), get_token_name(pc->type));
+
+      if (language_is_set(LANG_JAVA))
+      {
+         pc = skip_tsquare_next(pc);
+         LOG_FMT(LFVD, "%s(%d):   5:pc->text() '%s', type is %s\n", __func__, __LINE__, pc->text(), get_token_name(pc->type));
+      }
+   }
+   end = pc;
+
+   if (end == nullptr)
+   {
+      LOG_FMT(LFVD, "%s(%d): end is nullptr\n", __func__, __LINE__);
+      return(nullptr);
+   }
+   LOG_FMT(LFVD, "%s(%d): end->type is %s\n", __func__, __LINE__, get_token_name(end->type));
+
+   if (  cs.Len() == 1
+      && chunk_is_token(end, CT_BRACE_OPEN)
+      && get_chunk_parent_type(end) == CT_BRACED_INIT_LIST)
+   {
+      set_chunk_type(cs.Get(0)->m_pc, CT_TYPE);
+   }
+
+   // Function defs are handled elsewhere
+   if (  (cs.Len() <= 1)
+      || chunk_is_token(end, CT_FUNC_DEF)
+      || chunk_is_token(end, CT_FUNC_PROTO)
+      || chunk_is_token(end, CT_FUNC_CLASS_DEF)
+      || chunk_is_token(end, CT_FUNC_CLASS_PROTO)
+      || chunk_is_token(end, CT_OPERATOR))
+   {
+      return(skip_to_next_statement(end));
+   }
+   // ref_idx points to the alignable part of the variable definition
+   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;
+         }
+
+         if (idx == 0)
+         {
+            fprintf(stderr, "%s(%d): idx is ZERO, cannot be decremented, at line %zu, column %zu\n",
+                    __func__, __LINE__, tmp_pc->orig_line, tmp_pc->orig_col);
+            log_flush(true);
+            exit(EX_SOFTWARE);
+         }
+         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, "%s(%d): ref_idx(%d) is '%s'\n", __func__, __LINE__, ref_idx, tmp_pc->text());
+
+   // No type part found!
+   if (ref_idx <= 0)
+   {
+      return(skip_to_next_statement(end));
+   }
+   LOG_FMT(LFVD2, "%s(%d): orig_line is %zu, TYPE : ", __func__, __LINE__, start->orig_line);
+
+   for (size_t idxForCs = 0; idxForCs < cs.Len() - 1; idxForCs++)
+   {
+      tmp_pc = cs.Get(idxForCs)->m_pc;
+      make_type(tmp_pc);
+      chunk_flags_set(tmp_pc, PCF_VAR_TYPE);
+      LOG_FMT(LFVD2, " text() is '%s', type is %s", tmp_pc->text(), get_token_name(tmp_pc->type));
+   }
+
+   LOG_FMT(LFVD2, "\n");
+
+   // OK we have two or more items, mark types up to the end.
+   LOG_FMT(LFVD, "%s(%d): pc->orig_line is %zu, pc->orig_col is %zu\n",
+           __func__, __LINE__, pc->orig_line, pc->orig_col);
+   mark_variable_definition(cs.Get(cs.Len() - 1)->m_pc);
+
+   if (chunk_is_token(end, CT_COMMA))
+   {
+      return(chunk_get_next_ncnl(end));
+   }
+   return(skip_to_next_statement(end));
+} // fix_variable_definition
+
+
+static chunk_t *skip_expression(chunk_t *start)
+{
+   chunk_t *pc = start;
+
+   while (pc != nullptr && pc->level >= start->level)
+   {
+      if (  pc->level == start->level
+         && (chunk_is_semicolon(pc) || chunk_is_token(pc, CT_COMMA)))
+      {
+         return(pc);
+      }
+      pc = chunk_get_next_ncnl(pc);
+   }
+   return(pc);
+}
+
+
+bool go_on(chunk_t *pc, chunk_t *start)
+{
+   if (pc == nullptr || pc->level != start->level)
+   {
+      return(false);
+   }
+
+   if (pc->flags.test(PCF_IN_FOR))
+   {
+      return((!chunk_is_semicolon(pc)) && (!(chunk_is_token(pc, CT_COLON))));
+   }
+   return(!chunk_is_semicolon(pc));
+} // go_on
+
+
+static chunk_t *mark_variable_definition(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+
+   if (start == nullptr)
+   {
+      return(nullptr);
+   }
+   chunk_t     *pc   = start;
+   pcf_flags_t flags = PCF_VAR_1ST_DEF;
+
+   LOG_FMT(LVARDEF, "%s(%d): orig_line %zu, orig_col %zu, text() '%s', type is %s\n",
+           __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(),
+           get_token_name(pc->type));
+
+   // Issue #596
+   bool bit_field_colon_is_present = false;
+
+   while (go_on(pc, start))
+   {
+      if (  chunk_is_token(pc, CT_WORD)
+         || chunk_is_token(pc, CT_FUNC_CTOR_VAR))
+      {
+         auto const orig_flags = pc->flags;
+
+         if (!pc->flags.test(PCF_IN_ENUM))
+         {
+            chunk_flags_set(pc, flags);
+         }
+         flags &= ~PCF_VAR_1ST;
+         LOG_FMT(LVARDEF, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
+                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+
+         LOG_FMT(LVARDEF,
+                 "%s(%d): orig_line is %zu, marked text() '%s'[%s] "
+                 "in orig_col %zu, flags: %s -> %s\n",
+                 __func__, __LINE__, pc->orig_line, pc->text(),
+                 get_token_name(pc->type), pc->orig_col,
+                 pcf_flags_str(orig_flags).c_str(),
+                 pcf_flags_str(pc->flags).c_str());
+      }
+      else if (  !bit_field_colon_is_present                      // Issue #2689
+              && (  chunk_is_star(pc)
+                 || chunk_is_msref(pc)))
+      {
+         set_chunk_type(pc, CT_PTR_TYPE);
+      }
+      else if (chunk_is_addr(pc))
+      {
+         set_chunk_type(pc, CT_BYREF);
+      }
+      else if (  chunk_is_token(pc, CT_SQUARE_OPEN)
+              || chunk_is_token(pc, CT_ASSIGN))
+      {
+         pc = skip_expression(pc);
+         continue;
+      }
+      else if (chunk_is_token(pc, CT_COLON))
+      {
+         bit_field_colon_is_present = true;                    // Issue #2689
+      }
+      pc = chunk_get_next_ncnl(pc);
+   }
+   return(pc);
+} // mark_variable_definition
+
+
+static bool can_be_full_param(chunk_t *start, chunk_t *end)
+{
+   LOG_FUNC_ENTRY();
+
+   LOG_FMT(LFPARAM, "%s:", __func__);
+
+   int     word_count = 0;
+   int     type_count = 0;
+   chunk_t *pc;
+
+   for (pc = start;
+        pc != nullptr && pc != end;
+        pc = chunk_get_next_ncnl(pc, scope_e::PREPROC))
+   {
+      LOG_FMT(LFPARAM, " [%s]", pc->text());
+
+      if (  chunk_is_token(pc, CT_QUALIFIER)
+         || chunk_is_token(pc, CT_STRUCT)
+         || chunk_is_token(pc, CT_ENUM)
+         || chunk_is_token(pc, CT_UNION)
+         || chunk_is_token(pc, CT_TYPENAME))
+      {
+         LOG_FMT(LFPARAM, " <== %s! (yes)\n", get_token_name(pc->type));
+         return(true);
+      }
+
+      if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))
+      {
+         ++word_count;
+
+         if (chunk_is_token(pc, CT_TYPE))
+         {
+            ++type_count;
+         }
+      }
+      else if (chunk_is_token(pc, CT_MEMBER) || chunk_is_token(pc, CT_DC_MEMBER))
+      {
+         if (word_count > 0)
+         {
+            --word_count;
+         }
+      }
+      else if (pc != start && chunk_is_ptr_operator(pc))
+      {
+         // chunk is OK
+      }
+      else if (chunk_is_token(pc, CT_ASSIGN))
+      {
+         // chunk is OK (default values)
+         break;
+      }
+      else if (chunk_is_token(pc, CT_ANGLE_OPEN))
+      {
+         LOG_FMT(LFPARAM, " <== template\n");
+         return(true);
+      }
+      else if (chunk_is_token(pc, CT_ELLIPSIS))
+      {
+         LOG_FMT(LFPARAM, " <== elipses\n");
+         return(true);
+      }
+      else if (word_count == 0 && chunk_is_token(pc, CT_PAREN_OPEN))
+      {
+         // Check for old-school func proto param '(type)'
+         chunk_t *tmp1 = chunk_skip_to_match(pc, scope_e::PREPROC);
+
+         if (tmp1 == nullptr)
+         {
+            return(false);
+         }
+         chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC);
+
+         if (tmp2 == nullptr)
+         {
+            return(false);
+         }
+
+         if (chunk_is_token(tmp2, CT_COMMA) || chunk_is_paren_close(tmp2))
+         {
+            do
+            {
+               pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);
+
+               if (pc == nullptr)
+               {
+                  return(false);
+               }
+               LOG_FMT(LFPARAM, " [%s]", pc->text());
+            } while (pc != tmp1);
+
+            // reset some vars to allow [] after parens
+            word_count = 1;
+            type_count = 1;
+         }
+         else
+         {
+            LOG_FMT(LFPARAM, " <== [%s] not fcn type!\n", get_token_name(pc->type));
+            return(false);
+         }
+      }
+      else if (  (word_count == 1 || (word_count == type_count))
+              && chunk_is_token(pc, CT_PAREN_OPEN))
+      {
+         // Check for func proto param 'void (*name)' or 'void (*name)(params)' or 'void (^name)(params)'
+         // <name> can be optional
+         chunk_t *tmp1 = chunk_get_next_ncnl(pc, scope_e::PREPROC);
+
+         if (tmp1 == nullptr)
+         {
+            return(false);
+         }
+         chunk_t *tmp2 = chunk_get_next_ncnl(tmp1, scope_e::PREPROC);
+
+         if (tmp2 == nullptr)
+         {
+            return(false);
+         }
+         chunk_t *tmp3 = (chunk_is_str(tmp2, ")", 1)) ? tmp2 : chunk_get_next_ncnl(tmp2, scope_e::PREPROC);
+
+         if (tmp3 == nullptr)
+         {
+            return(false);
+         }
+
+         if (  !chunk_is_str(tmp3, ")", 1)
+            || !(chunk_is_str(tmp1, "*", 1) || chunk_is_str(tmp1, "^", 1)) // Issue #2656
+            || !(tmp2->type == CT_WORD || chunk_is_str(tmp2, ")", 1)))
+         {
+            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, scope_e::PREPROC);
+
+         if (tmp1 == nullptr)
+         {
+            return(false);
+         }
+
+         if (chunk_is_str(tmp1, "(", 1))
+         {
+            tmp3 = chunk_skip_to_match(tmp1, scope_e::PREPROC);
+         }
+         pc = tmp3;
+
+         // reset some vars to allow [] after parens
+         word_count = 1;
+         type_count = 1;
+      }
+      else if (chunk_is_token(pc, CT_TSQUARE))
+      {
+         // ignore it
+      }
+      else if (word_count == 1 && chunk_is_token(pc, CT_SQUARE_OPEN))
+      {
+         // skip over any array stuff
+         pc = chunk_skip_to_match(pc, scope_e::PREPROC);
+      }
+      else if (word_count == 2 && chunk_is_token(pc, CT_SQUARE_OPEN))
+      {
+         // Bug #671: is it such as: bool foo[FOO_MAX]
+         pc = chunk_skip_to_match(pc, scope_e::PREPROC);
+      }
+      else if (  word_count == 1
+              && language_is_set(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_count);
+         return(false);
+      }
+   }
+
+   chunk_t *last = chunk_get_prev_ncnlni(pc);   // Issue #2279
+
+   if (chunk_is_ptr_operator(last))
+   {
+      LOG_FMT(LFPARAM, " <== [%s] sure!\n", get_token_name(pc->type));
+      return(true);
+   }
+
+   if (word_count < 2 && type_count < 1 && start->brace_level > 0)
+   {
+      LOG_FMT(LFPARAM, " !MVP!");
+      // Oh, joy, we are in Most Vexing Parse territory
+      auto const brace =
+         chunk_get_prev_type(start, CT_BRACE_OPEN, start->brace_level - 1);
+
+      if (brace)
+      {
+         LOG_FMT(LFPARAM, " (matching %s brace at %zu:%zu)",
+                 get_token_name(get_chunk_parent_type(brace)),
+                 brace->orig_line, brace->orig_col);
+      }
+
+      if (  brace
+         && (  get_chunk_parent_type(brace) == CT_CLASS
+            || get_chunk_parent_type(brace) == CT_STRUCT))
+      {
+         // A Most Vexing Parse variable declaration cannot occur in the body
+         // of a struct/class, so we probably have a function prototype
+         LOG_FMT(LFPARAM, " <== [%s] Likely!\n",
+                 (pc == nullptr ? "nullptr" : get_token_name(pc->type)));
+         return(true);
+      }
+   }
+   bool ret = (  word_count >= 2
+              || (word_count == 1 && type_count == 1));
+
+   LOG_FMT(LFPARAM, " <== [%s] %s!\n",
+           (pc == nullptr ? "nullptr" : get_token_name(pc->type)),
+           ret ? "Yup" : "Unlikely");
+   return(ret);
+} // can_be_full_param
+
+
+static void mark_function(chunk_t *pc)
+{
+   LOG_FUNC_ENTRY();
+
+   if (pc == nullptr)
+   {
+      return;
+   }
+   LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
+           __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+   chunk_t *prev = chunk_get_prev_ncnlni(pc);   // Issue #2279
+   chunk_t *next = chunk_get_next_ncnlnp(pc);
+
+   if (next == nullptr)
+   {
+      return;
+   }
+   chunk_t *tmp;
+   chunk_t *semi = nullptr;
+   chunk_t *paren_open;
+   chunk_t *paren_close;
+
+   // Find out what is before the operator
+   if (get_chunk_parent_type(pc) == CT_OPERATOR)
+   {
+      LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s",
+              __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+      log_pcf_flags(LGUY, pc->flags);
+      chunk_t *pc_op = chunk_get_prev_type(pc, CT_OPERATOR, pc->level);
+
+      if (  pc_op != nullptr
+         && pc_op->flags.test(PCF_EXPR_START))
+      {
+         LOG_FMT(LFCN, "%s(%d): (4) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+         set_chunk_type(pc, CT_FUNC_CALL);
+      }
+
+      if (language_is_set(LANG_CPP))
+      {
+         tmp = pc;
+
+         while ((tmp = chunk_get_prev_ncnlni(tmp)) != nullptr)   // Issue #2279
+         {
+            if (  chunk_is_token(tmp, CT_BRACE_CLOSE)
+               || chunk_is_token(tmp, CT_BRACE_OPEN)             // Issue 575
+               || chunk_is_token(tmp, CT_SEMICOLON))
+            {
+               break;
+            }
+
+            if (  chunk_is_paren_open(tmp)
+               && !pc->flags.test(PCF_IN_PREPROC))               // Issue #2703
+            {
+               LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                       __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
+               LOG_FMT(LFCN, "%s(%d): (5) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                       __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+               set_chunk_type(pc, CT_FUNC_CALL);
+               break;
+            }
+
+            if (chunk_is_token(tmp, CT_ASSIGN))
+            {
+               LOG_FMT(LFCN, "%s(%d): (6) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                       __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+               set_chunk_type(pc, CT_FUNC_CALL);
+               break;
+            }
+
+            if (chunk_is_token(tmp, CT_TEMPLATE))
+            {
+               LOG_FMT(LFCN, "%s(%d): (7) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                       __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+               set_chunk_type(pc, CT_FUNC_DEF);
+               break;
+            }
+
+            if (chunk_is_token(tmp, CT_BRACE_OPEN))
+            {
+               if (get_chunk_parent_type(tmp) == CT_FUNC_DEF)
+               {
+                  LOG_FMT(LFCN, "%s(%d): (8) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                          __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+                  set_chunk_type(pc, CT_FUNC_CALL);
+               }
+
+               if (  get_chunk_parent_type(tmp) == CT_CLASS
+                  || get_chunk_parent_type(tmp) == CT_STRUCT)
+               {
+                  LOG_FMT(LFCN, "%s(%d): (9) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                          __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+                  set_chunk_type(pc, CT_FUNC_DEF);
+               }
+               break;
+            }
+         }
+
+         if (  tmp != nullptr
+            && pc->type != CT_FUNC_CALL)
+         {
+            // Mark the return type
+            while (  (tmp = chunk_get_next_ncnl(tmp)) != pc
+                  && tmp != nullptr)
+            {
+               make_type(tmp); // Mark the return type
+            }
+         }
+      }
+   }
+
+   if (chunk_is_ptr_operator(next))
+   {
+      next = chunk_get_next_ncnlnp(next);
+
+      if (next == nullptr)
+      {
+         return;
+      }
+   }
+   LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s, type is %s, parent_type is %s\n",
+           __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text(),
+           get_token_name(pc->type), get_token_name(get_chunk_parent_type(pc)));
+   LOG_FMT(LFCN, "   level is %zu, brace_level is %zu, next->text() '%s', next->type is %s, next->level is %zu\n",
+           pc->level, pc->brace_level,
+           next->text(), get_token_name(next->type), next->level);
+
+   if (pc->flags.test(PCF_IN_CONST_ARGS))
+   {
+      set_chunk_type(pc, CT_FUNC_CTOR_VAR);
+      LOG_FMT(LFCN, "%s(%d):   1) Marked [%s] as FUNC_CTOR_VAR on line %zu col %zu\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+      next = skip_template_next(next);
+
+      if (next == nullptr)
+      {
+         return;
+      }
+      flag_parens(next, PCF_NONE, CT_FPAREN_OPEN, pc->type, true);
+      return;
+   }
+   // Skip over any template and attribute madness
+   next = skip_template_next(next);
+
+   if (next == nullptr)
+   {
+      return;
+   }
+   next = skip_attribute_next(next);
+
+   if (next == nullptr)
+   {
+      return;
+   }
+   // Find the open and close parenthesis
+   paren_open  = chunk_get_next_str(pc, "(", 1, pc->level);
+   paren_close = chunk_get_next_str(paren_open, ")", 1, pc->level);
+
+   if (  paren_open == nullptr
+      || paren_close == nullptr)
+   {
+      LOG_FMT(LFCN, "%s(%d): No parens found for [%s] on orig_line %zu, orig_col %zu\n",
+              __func__, __LINE__, pc->text(), 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 (  tmp != nullptr
+      && chunk_is_str(tmp, "(", 1))
+   {
+      chunk_t *tmp1;
+      chunk_t *tmp2;
+      chunk_t *tmp3;
+
+      // skip over any leading class/namespace in: "T(F::*A)();"
+      tmp1 = chunk_get_next_ncnl(next);
+
+      while (tmp1 != nullptr)
+      {
+         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 = nullptr;
+      }
+      else
+      {
+         tmp3 = chunk_get_next_ncnl(tmp2);
+      }
+      tmp3 = chunk_get_next_ssq(tmp3);
+
+      if (  chunk_is_str(tmp3, ")", 1)
+         && (  chunk_is_star(tmp1)
+            || chunk_is_msref(tmp1)
+            || (language_is_set(LANG_OC) && chunk_is_token(tmp1, CT_CARET)))
+         && (tmp2 == nullptr || chunk_is_token(tmp2, CT_WORD)))
+      {
+         if (tmp2)
+         {
+            LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function variable '%s', changing '%s' into a type\n",
+                    __func__, __LINE__, pc->orig_line, pc->orig_col, tmp2->text(), pc->text());
+            set_chunk_type(tmp2, CT_FUNC_VAR);
+            flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_VAR, false);
+
+            LOG_FMT(LFCN, "%s(%d): paren open @ orig_line %zu, orig_col %zu\n",
+                    __func__, __LINE__, paren_open->orig_line, paren_open->orig_col);
+         }
+         else
+         {
+            LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, function type, changing '%s' into a type\n",
+                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+
+            if (tmp2)
+            {
+               set_chunk_type(tmp2, CT_FUNC_TYPE);
+            }
+            flag_parens(paren_open, PCF_NONE, CT_PAREN_OPEN, CT_FUNC_TYPE, false);
+         }
+         set_chunk_type(pc, CT_TYPE);
+         set_chunk_type(tmp1, CT_PTR_TYPE);
+         chunk_flags_clr(pc, PCF_VAR_1ST_DEF);
+
+         if (tmp2 != nullptr)
+         {
+            chunk_flags_set(tmp2, PCF_VAR_1ST_DEF);
+         }
+         flag_parens(tmp, PCF_NONE, CT_FPAREN_OPEN, CT_FUNC_PROTO, false);
+         fix_fcn_def_params(tmp);
+         return;
+      }
+      LOG_FMT(LFCN, "%s(%d): chained function calls? text() is '%s', orig_line is %zu, orig_col is %zu\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+   }
+
+   // Assume it is a function call if not already labeled
+   if (chunk_is_token(pc, CT_FUNCTION))
+   {
+      LOG_FMT(LFCN, "%s(%d): examine: text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
+      // look for an assigment. Issue #575
+      chunk_t *temp = chunk_get_next_type(pc, CT_ASSIGN, pc->level);
+
+      if (temp != nullptr)
+      {
+         LOG_FMT(LFCN, "%s(%d): assigment found, orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                 __func__, __LINE__, temp->orig_line, temp->orig_col, temp->text());
+         LOG_FMT(LFCN, "%s(%d): (10) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'",
+                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+         set_chunk_type(pc, CT_FUNC_CALL);
+      }
+      else
+      {
+         LOG_FMT(LFCN, "%s(%d): (11) SET TO %s: orig_line is %zu, orig_col is %zu, text() '%s'",
+                 __func__, __LINE__, (get_chunk_parent_type(pc) == CT_OPERATOR) ? "CT_FUNC_DEF" : "CT_FUNC_CALL",
+                 pc->orig_line, pc->orig_col, pc->text());
+         set_chunk_type(pc, (get_chunk_parent_type(pc) == CT_OPERATOR) ? CT_FUNC_DEF : CT_FUNC_CALL);
+      }
+   }
+   LOG_FMT(LFCN, "%s(%d): Check for C++ function def, text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+           __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
+
+   if (prev != nullptr)
+   {
+      LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+              __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type));
+   }
+
+   // Check for C++ function def
+   if (  chunk_is_token(pc, CT_FUNC_CLASS_DEF)
+      || (  prev != nullptr
+         && (  chunk_is_token(prev, CT_INV)
+            || chunk_is_token(prev, CT_DC_MEMBER))))
+   {
+      chunk_t *destr = nullptr;
+
+      if (chunk_is_token(prev, 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;
+         // Point to the item previous to the class name
+         prev = chunk_get_prev_ncnlnp(prev);
+      }
+
+      if (chunk_is_token(prev, CT_DC_MEMBER))
+      {
+         prev = chunk_get_prev_ncnlnp(prev);
+         LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
+                 get_token_name(prev->type));
+         prev = skip_template_prev(prev);
+         LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
+                 get_token_name(prev->type));
+         prev = skip_attribute_prev(prev);
+         LOG_FMT(LFCN, "%s(%d): prev->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
+                 get_token_name(prev->type));
+
+         if (chunk_is_token(prev, CT_WORD) || chunk_is_token(prev, CT_TYPE))
+         {
+            if (pc->str.equals(prev->str))
+            {
+               LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                       __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col,
+                       get_token_name(prev->type));
+               set_chunk_type(pc, CT_FUNC_CLASS_DEF);
+               LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu - FOUND %sSTRUCTOR for '%s', type is %s\n",
+                       __func__, __LINE__,
+                       prev->orig_line, prev->orig_col,
+                       (destr != nullptr) ? "DE" : "CON",
+                       prev->text(), get_token_name(prev->type));
+
+               mark_cpp_constructor(pc);
+               return;
+            }
+            // 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 (  chunk_is_token(pc, CT_FUNC_CALL)
+      && (  pc->level == pc->brace_level
+         || pc->level == 1)
+      && !pc->flags.test(PCF_IN_ARRAY_ASSIGN))
+   {
+      bool isa_def  = false;
+      bool hit_star = false;
+      LOG_FMT(LFCN, "%s(%d): pc->text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col,
+              get_token_name(pc->type));
+
+      if (prev == nullptr)
+      {
+         LOG_FMT(LFCN, "%s(%d): Checking func call: prev is NULL\n",
+                 __func__, __LINE__);
+      }
+      else
+      {
+         LOG_FMT(LFCN, "%s(%d): Checking func call: prev->text() '%s', prev->type is %s\n",
+                 __func__, __LINE__, prev->text(), 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 stuff 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 != nullptr)
+      {
+         LOG_FMT(LFCN, "%s(%d): next step with: prev->orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                 __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text());
+
+         if (get_chunk_parent_type(pc) == CT_FIXED)
+         {
+            isa_def = true;
+         }
+
+         if (prev->flags.test(PCF_IN_PREPROC))
+         {
+            prev = chunk_get_prev_ncnlnp(prev);
+            continue;
+         }
+
+         // Some code slips an attribute between the type and function
+         if (  chunk_is_token(prev, CT_FPAREN_CLOSE)
+            && get_chunk_parent_type(prev) == CT_ATTRIBUTE)
+         {
+            prev = skip_attribute_prev(prev);
+            continue;
+         }
+
+         // skip const(TYPE)
+         if (  chunk_is_token(prev, CT_PAREN_CLOSE)
+            && get_chunk_parent_type(prev) == CT_D_CAST)
+         {
+            LOG_FMT(LFCN, "%s(%d): --> For sure a prototype or definition\n",
+                    __func__, __LINE__);
+            isa_def = true;
+            break;
+         }
+
+         if (get_chunk_parent_type(prev) == CT_DECLSPEC)  // Issue 1289
+         {
+            prev = chunk_skip_to_match_rev(prev);
+            prev = chunk_get_prev(prev);
+
+            if (chunk_is_token(prev, CT_DECLSPEC))
+            {
+               prev = chunk_get_prev(prev);
+            }
+         }
+
+         // if it was determined that this could be a function definition
+         // but one of the preceding tokens is a CT_MEMBER than this is not a
+         // fcn def, issue #1466
+         if (  isa_def
+            && chunk_is_token(prev, CT_MEMBER))
+         {
+            isa_def = false;
+         }
+
+         // get first chunk before: A::B::pc | this.B.pc | this->B->pc
+         if (  chunk_is_token(prev, CT_DC_MEMBER)
+            || chunk_is_token(prev, CT_MEMBER))
+         {
+            while (  chunk_is_token(prev, CT_DC_MEMBER)
+                  || chunk_is_token(prev, CT_MEMBER))
+            {
+               prev = chunk_get_prev_ncnlnp(prev);
+
+               if (  prev == nullptr
+                  || (  prev->type != CT_WORD
+                     && prev->type != CT_TYPE
+                     && prev->type != CT_THIS))
+               {
+                  LOG_FMT(LFCN, "%s(%d): --? skipped MEMBER and landed on %s\n",
+                          __func__, __LINE__, (prev == nullptr) ? "<null>" : get_token_name(prev->type));
+                  break;
+               }
+               LOG_FMT(LFCN, "%s(%d): <skip> '%s'\n",
+                       __func__, __LINE__, prev->text());
+
+               // Issue #1112
+               // clarification: this will skip the CT_WORD, CT_TYPE or CT_THIS landing on either
+               // another CT_DC_MEMBER or CT_MEMBER or a token that indicates the context of the
+               // token in question; therefore, exit loop when not a CT_DC_MEMBER or CT_MEMBER
+               prev = chunk_get_prev_ncnlnp(prev);
+
+               if (prev == nullptr)
+               {
+                  LOG_FMT(LFCN, "%s(%d): prev is nullptr\n",
+                          __func__, __LINE__);
+               }
+               else
+               {
+                  LOG_FMT(LFCN, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                          __func__, __LINE__, prev->orig_line, prev->orig_col, prev->text());
+               }
+            }
+
+            if (prev == nullptr)
+            {
+               break;
+            }
+         }
+
+         // If we are on a TYPE or WORD, then this could be a proto or def
+         if (  chunk_is_token(prev, CT_TYPE)
+            || chunk_is_token(prev, CT_WORD))
+         {
+            if (!hit_star)
+            {
+               LOG_FMT(LFCN, "%s(%d):   --> For sure a prototype or definition\n",
+                       __func__, __LINE__);
+               isa_def = true;
+               break;
+            }
+            chunk_t *prev_prev = chunk_get_prev_ncnlnp(prev);
+
+            if (!chunk_is_token(prev_prev, CT_QUESTION))               // Issue #1753
+            {
+               LOG_FMT(LFCN, "%s(%d):   --> maybe a proto/def\n",
+                       __func__, __LINE__);
+
+               LOG_FMT(LFCN, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s, parent_type is %s\n",
+                       __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col,
+                       get_token_name(prev->type), get_token_name(get_chunk_parent_type(prev)));
+               log_pcf_flags(LFCN, pc->flags);
+               isa_def = true;
+            }
+         }
+
+         if (chunk_is_ptr_operator(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_ptr_operator(prev))
+         {
+            LOG_FMT(LFCN, "%s(%d):  --> Stopping on prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                    __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type));
+
+            // certain tokens are unlikely to precede a prototype or definition
+            if (  chunk_is_token(prev, CT_ARITH)
+               || chunk_is_token(prev, CT_ASSIGN)
+               || chunk_is_token(prev, CT_COMMA)
+               || (chunk_is_token(prev, CT_STRING) && get_chunk_parent_type(prev) != CT_EXTERN)  // fixes issue 1259
+               || chunk_is_token(prev, CT_STRING_MULTI)
+               || chunk_is_token(prev, CT_NUMBER)
+               || chunk_is_token(prev, CT_NUMBER_FP)
+               || chunk_is_token(prev, CT_FPAREN_OPEN)) // issue #1464
+            {
+               isa_def = false;
+            }
+            break;
+         }
+
+         // Skip over template and attribute stuff
+         if (chunk_is_token(prev, CT_ANGLE_CLOSE))
+         {
+            prev = skip_template_prev(prev);
+         }
+         else
+         {
+            prev = chunk_get_prev_ncnlnp(prev);
+         }
+      }
+      //LOG_FMT(LFCN, " -- stopped on %s [%s]\n",
+      //        prev->text(), get_token_name(prev->type));
+
+      // Fixes issue #1634
+      if (chunk_is_paren_close(prev))
+      {
+         chunk_t *preproc = chunk_get_next_ncnl(prev);
+
+         if (chunk_is_token(preproc, CT_PREPROC))
+         {
+            size_t pp_level = preproc->pp_level;
+
+            if (chunk_is_token(chunk_get_next_ncnl(preproc), CT_PP_ELSE))
+            {
+               do
+               {
+                  preproc = chunk_get_prev_ncnlni(preproc);      // Issue #2279
+
+                  if (chunk_is_token(preproc, CT_PP_IF))
+                  {
+                     preproc = chunk_get_prev_ncnlni(preproc);   // Issue #2279
+
+                     if (preproc->pp_level == pp_level)
+                     {
+                        prev = chunk_get_prev_ncnlnp(preproc);
+                        break;
+                     }
+                  }
+               } while (preproc != nullptr);
+            }
+         }
+      }
+
+      if (  isa_def
+         && prev != nullptr
+         && (  (  chunk_is_paren_close(prev)
+               && get_chunk_parent_type(prev) != CT_D_CAST
+               && get_chunk_parent_type(prev) != CT_MACRO_OPEN  // Issue #2726
+               && get_chunk_parent_type(prev) != CT_MACRO_CLOSE)
+            || prev->type == CT_ASSIGN
+            || prev->type == CT_RETURN))
+      {
+         LOG_FMT(LFCN, "%s(%d): -- overriding DEF due to prev is '%s', type is %s\n",
+                 __func__, __LINE__, prev->text(), get_token_name(prev->type));
+         isa_def = false;
+      }
+
+      // Fixes issue #1266, identification of a tuple return type in CS.
+      if (  !isa_def
+         && chunk_is_token(prev, CT_PAREN_CLOSE)
+         && chunk_get_next_ncnl(prev) == pc)
+      {
+         tmp = chunk_skip_to_match_rev(prev);
+
+         while (  tmp != nullptr                     // Issue #2315
+               && tmp != prev)
+         {
+            if (chunk_is_token(tmp, CT_COMMA) && tmp->level == prev->level + 1)
+            {
+               LOG_FMT(LFCN, "%s(%d): -- overriding call due to tuple return type -- prev is '%s', type is %s\n",
+                       __func__, __LINE__, prev->text(), get_token_name(prev->type));
+               isa_def = true;
+               break;
+            }
+            tmp = chunk_get_next_ncnl(tmp);
+         }
+      }
+
+      if (isa_def)
       {
-         detail = " -- '_t'";
+         LOG_FMT(LFCN, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                 __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
+         LOG_FMT(LFCN, "%s(%d): (12) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                 __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+         set_chunk_type(pc, CT_FUNC_DEF);
+
+         if (prev == nullptr)
+         {
+            prev = chunk_get_head();
+         }
+
+         for (  tmp = prev; (tmp != nullptr)
+             && tmp != pc; tmp = chunk_get_next_ncnlnp(tmp))
+         {
+            LOG_FMT(LFCN, "%s(%d): text() is '%s', type is %s\n",
+                    __func__, __LINE__, tmp->text(), get_token_name(tmp->type));
+            make_type(tmp);
+         }
       }
-      else if (is_ucase_str(last->text(), last->len()))
+   }
+
+   if (pc->type != CT_FUNC_DEF)
+   {
+      LOG_FMT(LFCN, "%s(%d):  Detected type %s, text() is '%s', on orig_line %zu, orig_col %zu\n",
+              __func__, __LINE__, get_token_name(pc->type),
+              pc->text(), pc->orig_line, pc->orig_col);
+
+      tmp = flag_parens(next, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CALL, false);
+
+      if (  chunk_is_token(tmp, CT_BRACE_OPEN)
+         && get_chunk_parent_type(tmp) != CT_DOUBLE_BRACE)
       {
-         detail = " -- upper case";
+         set_paren_parent(tmp, pc->type);
       }
-      else if (language_is_set(LANG_OC) && chunk_is_str(last, "id", 2))
+      return;
+   }
+   /*
+    * We have a function definition or prototype
+    * Look for a semicolon or a brace open after the close parenthesis 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)) != nullptr)
+   {
+      // Only care about brace or semicolon on the same level
+      if (tmp->level < pc->level)
       {
-         detail = " -- Objective-C id";
+         // No semicolon - guess that it is a prototype
+         chunk_flags_clr(pc, PCF_VAR_1ST_DEF);
+         set_chunk_type(pc, CT_FUNC_PROTO);
+         break;
       }
-      else
+      else if (tmp->level == pc->level)
       {
-         // If we can't tell for sure whether this is a cast, decide against it
-         detail        = " -- mixed case";
-         doubtful_cast = true;
+         if (chunk_is_token(tmp, CT_BRACE_OPEN))
+         {
+            // its a function def for sure
+            break;
+         }
+         else if (chunk_is_semicolon(tmp))
+         {
+            // Set the parent for the semicolon for later
+            semi = tmp;
+            chunk_flags_clr(pc, PCF_VAR_1ST_DEF);
+            set_chunk_type(pc, CT_FUNC_PROTO);
+            LOG_FMT(LFCN, "%s(%d):   2) Marked text() is '%s', as FUNC_PROTO on orig_line %zu, orig_col %zu\n",
+                    __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+            break;
+         }
+         else if (chunk_is_token(pc, CT_COMMA))
+         {
+            set_chunk_type(pc, CT_FUNC_CTOR_VAR);
+            LOG_FMT(LFCN, "%s(%d):   2) Marked text() is '%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n",
+                    __func__, __LINE__, pc->text(), 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, without being able to accurately determine if an
+    * identifier is a type (which would require us to more or less be a full
+    * compiler), 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 (  language_is_set(LANG_CPP)
+      && chunk_is_token(pc, CT_FUNC_PROTO)
+      && get_chunk_parent_type(pc) != CT_OPERATOR)
+   {
+      LOG_FMT(LFPARAM, "%s(%d):", __func__, __LINE__);
+      LOG_FMT(LFPARAM, "  checking '%s' for constructor variable %s %s\n",
+              pc->text(),
+              get_token_name(paren_open->type),
+              get_token_name(paren_close->type));
+
       /*
-       * 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.
+       * Check the token at the start of the statement. If it's 'extern', we
+       * definitely have a function prototype.
        */
-      pc    = chunk_get_next_ncnl(paren_close);
-      after = pc;
-
-      do
-      {
-         after = chunk_get_next_ncnl(after);
-      } while (chunk_is_token(after, CT_PAREN_OPEN));
+      tmp = pc;
 
-      if (after == nullptr)
+      while (  tmp != nullptr
+            && !tmp->flags.test(PCF_STMT_START))
       {
-         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - hit NULL\n",
-                 __func__, __LINE__);
-         return;
+         tmp = chunk_get_prev_ncnlni(tmp);   // Issue #2279
       }
-      nope = false;
+      const bool is_extern = (tmp && tmp->str.equals("extern"));
 
-      if (chunk_is_ptr_operator(pc))
+      /*
+       * 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)
       {
-         // star (*) and address (&) are ambiguous
-         if (  chunk_is_token(after, CT_NUMBER_FP)
-            || chunk_is_token(after, CT_NUMBER)
-            || chunk_is_token(after, CT_STRING)
-            || doubtful_cast)
+         tmp2 = chunk_get_next_ncnl(tmp);
+
+         if (  chunk_is_token(tmp, CT_COMMA)
+            && (tmp->level == (paren_open->level + 1)))
          {
-            nope = true;
+            if (!can_be_full_param(ref, tmp))
+            {
+               is_param = false;
+               break;
+            }
+            ref = tmp2;
          }
+         tmp = tmp2;
       }
-      else if (chunk_is_token(pc, CT_MINUS))
+
+      if (  !is_extern
+         && is_param && ref != tmp)
       {
-         // (UINT8)-1 or (foo)-1 or (FOO)-'a'
-         if (chunk_is_token(after, CT_STRING) || doubtful_cast)
+         if (!can_be_full_param(ref, tmp))
          {
-            nope = true;
+            is_param = false;
          }
       }
-      else if (chunk_is_token(pc, CT_PLUS))
+
+      if (  !is_extern
+         && !is_param)
       {
-         // (UINT8)+1 or (foo)+1
-         if (  (after->type != CT_NUMBER && after->type != CT_NUMBER_FP)
-            || doubtful_cast)
-         {
-            nope = true;
-         }
+         set_chunk_type(pc, CT_FUNC_CTOR_VAR);
+         LOG_FMT(LFCN, "%s(%d):   3) Marked text() '%s' as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n",
+                 __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
       }
-      else if (  pc->type != CT_NUMBER_FP
-              && pc->type != CT_NUMBER
-              && pc->type != CT_WORD
-              && pc->type != CT_THIS
-              && pc->type != CT_TYPE
-              && pc->type != CT_PAREN_OPEN
-              && pc->type != CT_STRING
-              && pc->type != CT_DECLTYPE
-              && pc->type != CT_SIZEOF
-              && get_chunk_parent_type(pc) != CT_SIZEOF
-              && pc->type != CT_FUNC_CALL
-              && pc->type != CT_FUNC_CALL_USER
-              && pc->type != CT_FUNCTION
-              && pc->type != CT_BRACE_OPEN
-              && (!(  chunk_is_token(pc, CT_SQUARE_OPEN)
-                   && language_is_set(LANG_OC))))
+      else if (pc->brace_level > 0)
       {
-         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by text() '%s', type is %s\n",
-                 __func__, __LINE__, pc->text(), get_token_name(pc->type));
-         return;
-      }
+         chunk_t *br_open = chunk_get_prev_type(pc, CT_BRACE_OPEN, pc->brace_level - 1);
 
-      if (nope)
-      {
-         LOG_FMT(LCASTS, "%s(%d):  -- not a cast - text() '%s' followed by type %s\n",
-                 __func__, __LINE__, pc->text(), get_token_name(after->type));
-         return;
+         if (  br_open != nullptr
+            && get_chunk_parent_type(br_open) != CT_EXTERN
+            && get_chunk_parent_type(br_open) != CT_NAMESPACE)
+         {
+            // Do a check to see if the level is right
+            prev = chunk_get_prev_ncnlni(pc);   // Issue #2279
+
+            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 != nullptr
+                  && get_chunk_parent_type(p_op) != CT_CLASS
+                  && get_chunk_parent_type(p_op) != CT_STRUCT
+                  && get_chunk_parent_type(p_op) != CT_NAMESPACE)
+               {
+                  set_chunk_type(pc, CT_FUNC_CTOR_VAR);
+                  LOG_FMT(LFCN, "%s(%d):   4) Marked text() is'%s', as FUNC_CTOR_VAR on orig_line %zu, orig_col %zu\n",
+                          __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+               }
+            }
+         }
       }
    }
-   // if the 'cast' is followed by a semicolon, comma, bool or close parenthesis, it isn't
-   pc = chunk_get_next_ncnl(paren_close);
 
-   if (pc == nullptr)
+   if (semi != nullptr)
    {
-      return;
+      set_chunk_parent(semi, pc->type);
    }
 
-   if (  chunk_is_semicolon(pc)
-      || chunk_is_token(pc, CT_COMMA)
-      || chunk_is_token(pc, CT_BOOL)               // Issue #2151
-      || chunk_is_paren_close(pc))
+   // Issue # 1403, 2152
+   if (chunk_is_token(paren_open->prev, CT_FUNC_CTOR_VAR))
    {
-      LOG_FMT(LCASTS, "%s(%d):  -- not a cast - followed by type %s\n",
-              __func__, __LINE__, get_token_name(pc->type));
-      return;
+      flag_parens(paren_open, PCF_IN_FCN_CTOR, CT_FPAREN_OPEN, pc->type, false);
    }
-   set_chunk_parent(start, CT_C_CAST);
-   set_chunk_parent(paren_close, CT_C_CAST);
-
-   LOG_FMT(LCASTS, "%s(%d):  -- %s c-cast: (",
-           __func__, __LINE__, verb);
-
-   for (pc = first;
-        pc != nullptr && pc != paren_close;
-        pc = chunk_get_next_ncnl(pc))
+   else
    {
-      set_chunk_parent(pc, CT_C_CAST);
-      make_type(pc);
-      LOG_FMT(LCASTS, " %s", pc->text());
+      flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, false);
    }
+   //flag_parens(paren_open, PCF_IN_FCN_DEF, CT_FPAREN_OPEN, pc->type, true);
 
-   LOG_FMT(LCASTS, " )%s\n", detail);
-
-   // Mark the next item as an expression start
-   pc = chunk_get_next_ncnl(paren_close);
+   if (chunk_is_token(pc, CT_FUNC_CTOR_VAR))
+   {
+      chunk_flags_set(pc, PCF_VAR_1ST_DEF);
+      return;
+   }
 
-   if (pc != nullptr)
+   if (chunk_is_token(next, CT_TSQUARE))
    {
-      chunk_flags_set(pc, PCF_EXPR_START);
+      next = chunk_get_next_ncnl(next);
 
-      if (chunk_is_opening_brace(pc))
+      if (next == nullptr)
       {
-         set_paren_parent(pc, get_chunk_parent_type(start));
+         return;
       }
    }
-} // fix_casts
+   // Mark parameters and return type
+   fix_fcn_def_params(next);
+   mark_function_return_type(pc, chunk_get_prev_ncnlni(pc), pc->type);   // Issue #2279
 
+   /* mark C# where chunk */
+   if (  language_is_set(LANG_CS)
+      && (  (chunk_is_token(pc, CT_FUNC_DEF))
+         || (chunk_is_token(pc, CT_FUNC_PROTO))))
+   {
+      tmp = chunk_get_next_ncnl(paren_close);
+      pcf_flags_t in_where_spec_flags = PCF_NONE;
 
-static void fix_type_cast(chunk_t *start)
-{
-   LOG_FUNC_ENTRY();
-   chunk_t *pc;
-
-   pc = chunk_get_next_ncnl(start);
+      while (  (tmp != nullptr)
+            && (tmp->type != CT_BRACE_OPEN)
+            && (tmp->type != CT_SEMICOLON))
+      {
+         mark_where_chunk(tmp, pc->type, tmp->flags | in_where_spec_flags);
+         in_where_spec_flags = tmp->flags & PCF_IN_WHERE_SPEC;
 
-   if (pc == nullptr || pc->type != CT_ANGLE_OPEN)
-   {
-      return;
+         tmp = chunk_get_next_ncnl(tmp);
+      }
    }
 
-   while (  ((pc = chunk_get_next_ncnl(pc)) != nullptr)
-         && pc->level >= start->level)
+   // Find the brace pair and set the parent
+   if (chunk_is_token(pc, CT_FUNC_DEF))
    {
-      if (pc->level == start->level && chunk_is_token(pc, CT_ANGLE_CLOSE))
+      tmp = chunk_get_next_ncnl(paren_close);
+
+      while (  tmp != nullptr
+            && tmp->type != CT_BRACE_OPEN)
       {
-         pc = chunk_get_next_ncnl(pc);
+         LOG_FMT(LFCN, "%s(%d): (13) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                 __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
+         set_chunk_parent(tmp, CT_FUNC_DEF);
 
-         if (pc == nullptr)
+         if (!chunk_is_semicolon(tmp))
          {
-            return;
+            chunk_flags_set(tmp, PCF_OLD_FCN_PARAMS);
          }
+         tmp = chunk_get_next_ncnl(tmp);
+      }
 
-         if (chunk_is_str(pc, "(", 1))
+      if (chunk_is_token(tmp, CT_BRACE_OPEN))
+      {
+         LOG_FMT(LFCN, "%s(%d): (14) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                 __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
+         set_chunk_parent(tmp, CT_FUNC_DEF);
+         tmp = chunk_skip_to_match(tmp);
+
+         if (tmp != nullptr)
          {
-            set_paren_parent(pc, CT_TYPE_CAST);
+            LOG_FMT(LFCN, "%s(%d): (15) SET TO CT_FUNC_DEF: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                    __func__, __LINE__, tmp->orig_line, tmp->orig_col, tmp->text());
+            set_chunk_parent(tmp, CT_FUNC_DEF);
          }
-         return;
       }
-      make_type(pc);
    }
-}
+} // mark_function
 
 
-static void fix_enum_struct_union(chunk_t *pc)
+static void mark_cpp_constructor(chunk_t *pc)
 {
    LOG_FUNC_ENTRY();
-   chunk_t     *next;
-   chunk_t     *prev        = nullptr;
-   pcf_flags_t flags        = PCF_VAR_1ST_DEF;
-   auto const  in_fcn_paren = pc->flags & PCF_IN_FCN_DEF;
+   chunk_t *paren_open;
+   chunk_t *tmp;
+   chunk_t *after;
+   chunk_t *var;
+   bool    is_destr = false;
 
-   // Make sure this wasn't a cast
-   if (get_chunk_parent_type(pc) == CT_C_CAST)
-   {
-      return;
-   }
-   // the next item is either a type or open brace
-   next = chunk_get_next_ncnl(pc);
+   tmp = chunk_get_prev_ncnlni(pc);   // Issue #2279
 
-   // the enum-key might be enum, enum class or enum struct (TODO)
-   if (chunk_is_token(next, CT_ENUM_CLASS))
+   if (chunk_is_token(tmp, CT_INV) || chunk_is_token(tmp, CT_DESTRUCTOR))
    {
-      next = chunk_get_next_ncnl(next); // get the next one
+      set_chunk_type(tmp, CT_DESTRUCTOR);
+      set_chunk_parent(pc, CT_DESTRUCTOR);
+      is_destr = true;
    }
+   LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, FOUND %sSTRUCTOR for '%s'[%s] prev '%s'[%s]\n",
+           __func__, __LINE__, pc->orig_line, pc->orig_col,
+           is_destr ? "DE" : "CON",
+           pc->text(), get_token_name(pc->type),
+           tmp->text(), get_token_name(tmp->type));
 
-   if (language_is_set(LANG_CPP))
-   {
-      next = skip_attribute_next(next); // get the next one
-   }
+   paren_open = skip_template_next(chunk_get_next_ncnl(pc));
 
-   // the next item is either a type, an attribute (TODO), an identifier, a colon or open brace
-   if (chunk_is_token(next, CT_TYPE) || chunk_is_token(next, CT_WORD) || chunk_is_token(next, CT_COLON))
+   if (!chunk_is_str(paren_open, "(", 1))
    {
-      // i.e. "enum xyz : unsigned int { ... };"
-      // i.e. "enum class xyz : unsigned int { ... };"
-      // i.e. "enum : unsigned int { ... };"
-      // xyz is a type
+      LOG_FMT(LWARN, "%s:%zu Expected '(', got: [%s]\n",
+              cpd.filename.c_str(), paren_open->orig_line,
+              paren_open->text());
+      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);
 
-      // save the type if it exists
-      if (!chunk_is_token(next, CT_COLON))
-      {
-         set_chunk_parent(next, pc->type);
-         prev = next;
-         next = chunk_get_next_ncnl(next);
-      }
+   LOG_FMT(LFTOR, "%s(%d): text() '%s'\n", __func__, __LINE__, after->text());
 
-      if (next == nullptr)
-      {
-         return;
-      }
-      set_chunk_parent(next, pc->type);
-      auto const is_struct_or_class =
-         (chunk_is_token(pc, CT_STRUCT) || chunk_is_token(pc, CT_CLASS));
+   // Scan until the brace open, mark everything
+   tmp = paren_open;
+   bool hit_colon = false;
 
-      // next up is either a colon, open brace, or open parenthesis (pawn)
-      if (language_is_set(LANG_PAWN) && chunk_is_token(next, CT_PAREN_OPEN))
+   while (  tmp != nullptr
+         && (tmp->type != CT_BRACE_OPEN || tmp->level != paren_open->level)
+         && !chunk_is_semicolon(tmp))
+   {
+      LOG_FMT(LFTOR, "%s(%d): tmp is '%s', orig_line is %zu, orig_col is %zu\n",
+              __func__, __LINE__, tmp->text(), tmp->orig_line, tmp->orig_col);
+      chunk_flags_set(tmp, PCF_IN_CONST_ARGS);
+      tmp = chunk_get_next_ncnl(tmp);
+
+      if (chunk_is_str(tmp, ":", 1) && tmp->level == paren_open->level)
       {
-         next = set_paren_parent(next, CT_ENUM);
+         set_chunk_type(tmp, CT_CONSTR_COLON);
+         hit_colon = true;
       }
-      else if (chunk_is_token(next, CT_COLON))
-      {
-         if (chunk_is_token(pc, CT_ENUM))
-         {
-            // enum TYPE : INT_TYPE { ... };
-            next = chunk_get_next_ncnl(next);
 
-            if (next != nullptr)
-            {
-               make_type(next);
-               next = chunk_get_next_ncnl(next);
+      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_ncnlni(tmp));   // Issue #2279
 
-               // enum TYPE : unsigned int { ... };
-               if (chunk_is_token(next, CT_TYPE))
-               {
-                  // get the next part of the type
-                  next = chunk_get_next_ncnl(next);
-               }
-            }
-         }
-         else if (is_struct_or_class)
+         if (chunk_is_token(var, CT_TYPE) || chunk_is_token(var, CT_WORD))
          {
-            next = skip_parent_types(next);
+            set_chunk_type(var, CT_FUNC_CTOR_VAR);
+            flag_parens(tmp, PCF_IN_FCN_CALL, CT_FPAREN_OPEN, CT_FUNC_CTOR_VAR, false);
          }
       }
-      else if (is_struct_or_class && chunk_is_token(next, CT_PAREN_OPEN))
+   }
+
+   if (chunk_is_token(tmp, CT_BRACE_OPEN))
+   {
+      set_paren_parent(paren_open, CT_FUNC_CLASS_DEF);
+      set_paren_parent(tmp, CT_FUNC_CLASS_DEF);
+      LOG_FMT(LFCN, "%s(%d):  Marked '%s' as FUNC_CLASS_DEF on orig_line %zu, orig_col %zu\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+   }
+   else
+   {
+      set_chunk_parent(tmp, CT_FUNC_CLASS_PROTO);
+      set_chunk_type(pc, CT_FUNC_CLASS_PROTO);
+      LOG_FMT(LFCN, "%s(%d):  Marked '%s' as FUNC_CLASS_PROTO on orig_line %zu, orig_col %zu\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+   }
+} // mark_cpp_constructor
+
+
+static pcf_flags_t mark_where_chunk(chunk_t *pc, c_token_t parent_type, pcf_flags_t flags)
+{
+   /* TODO: should have options to control spacing around the ':' as well as newline ability for the
+    * constraint clauses (should it break up a 'where A : B where C : D' on the same line? wrap? etc.) */
+
+   if (chunk_is_token(pc, CT_WHERE))
+   {
+      set_chunk_type(pc, CT_WHERE_SPEC);
+      set_chunk_parent(pc, parent_type);
+      flags |= PCF_IN_WHERE_SPEC;
+      LOG_FMT(LFTOR, "%s: where-spec on line %zu\n",
+              __func__, pc->orig_line);
+   }
+   else if (flags.test(PCF_IN_WHERE_SPEC))
+   {
+      if (chunk_is_str(pc, ":", 1))
       {
-         // Fix #1267 structure attributes
-         // struct __attribute__(align(x)) struct_name;
-         // skip to matching parenclose and make next token as type.
-         next = chunk_skip_to_match(next);
-         next = chunk_get_next_ncnl(next);
-         set_chunk_type(next, CT_TYPE);
-         set_chunk_parent(next, pc->type);
+         set_chunk_type(pc, CT_WHERE_COLON);
+         LOG_FMT(LFTOR, "%s: where-spec colon on line %zu\n",
+                 __func__, pc->orig_line);
       }
-
-      if (chunk_is_token(next, CT_SEMICOLON)) // c++ forward declaration
+      else if ((chunk_is_token(pc, CT_STRUCT)) || (chunk_is_token(pc, CT_CLASS)))
       {
-         set_chunk_parent(next, pc->type);
-         flag_series(pc, prev, PCF_INCOMPLETE);
-         return;
+         /* class/struct inside of a where-clause confuses parser for indentation; set it as a word so it looks like the rest */
+         set_chunk_type(pc, CT_WORD);
       }
    }
 
-   if (chunk_is_token(next, CT_BRACE_OPEN))
+   if (flags.test(PCF_IN_WHERE_SPEC))
    {
-      auto const flag = [pc] {
-         switch (pc->type)
-         {
-         case CT_ENUM:
-            return(PCF_IN_ENUM);
+      chunk_flags_set(pc, PCF_IN_WHERE_SPEC);
+   }
+   return(flags);
+}
 
-         case CT_STRUCT:
-            return(PCF_IN_STRUCT);
 
-         case CT_CLASS:
-            return(PCF_IN_CLASS);
+static void mark_class_ctor(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
 
-         default:
-            return(PCF_NONE);
-         }
-      }();
+   LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, start is '%s', parent_type is %s\n",
+           __func__, __LINE__, start->orig_line, start->orig_col, start->text(),
+           get_token_name(get_chunk_parent_type(start)));
+   log_pcf_flags(LFTOR, start->flags);
 
-      flag_parens(next, flag, CT_NONE, CT_NONE, false);
+   chunk_t *pclass = chunk_get_next_ncnl(start, scope_e::PREPROC);
 
-      if (  chunk_is_token(pc, CT_UNION)
-         || chunk_is_token(pc, CT_STRUCT)
-         || chunk_is_token(pc, CT_CLASS))
+   LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+           __func__, __LINE__, pclass->text());
+   log_pcf_flags(LFTOR, pclass->flags);
+
+   if (language_is_set(LANG_CPP))
+   {
+      pclass = skip_attribute_next(pclass);
+      LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+              __func__, __LINE__, pclass->text());
+   }
+
+   if (get_chunk_parent_type(start) == CT_TEMPLATE)
+   {
+      // look after the class name
+      chunk_t *openingTemplate = chunk_get_next_ncnl(pclass);
+      LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, openingTemplate is '%s', type is %s\n",
+              __func__, __LINE__, openingTemplate->orig_line, openingTemplate->orig_col,
+              openingTemplate->text(), get_token_name(openingTemplate->type));
+
+      if (chunk_is_token(openingTemplate, CT_ANGLE_OPEN))
       {
-         mark_struct_union_body(next);
+         chunk_t *closingTemplate = chunk_skip_to_match(openingTemplate);
+         LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, closingTemplate is '%s', type is %s\n",
+                 __func__, __LINE__, closingTemplate->orig_line, closingTemplate->orig_col,
+                 closingTemplate->text(), get_token_name(closingTemplate->type));
+         chunk_t *thirdToken = chunk_get_next_ncnl(closingTemplate);
+         LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, thirdToken is '%s', type is %s\n",
+                 __func__, __LINE__, thirdToken->orig_line, thirdToken->orig_col,
+                 thirdToken->text(), get_token_name(thirdToken->type));
+
+         if (chunk_is_token(thirdToken, CT_DC_MEMBER))
+         {
+            pclass = chunk_get_next_ncnl(thirdToken);
+            LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, orig_col is %zu, pclass is '%s', type is %s\n",
+                    __func__, __LINE__, pclass->orig_line, pclass->orig_col,
+                    pclass->text(), get_token_name(pclass->type));
+         }
       }
-      // 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;
+   }
+   pclass = skip_attribute_next(pclass);
+   LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+           __func__, __LINE__, pclass->text());
 
-      if (next != nullptr)
+   if (chunk_is_token(pclass, CT_DECLSPEC))  // Issue 1289
+   {
+      pclass = chunk_get_next_ncnl(pclass);
+      LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+              __func__, __LINE__, pclass->text());
+
+      if (chunk_is_token(pclass, CT_PAREN_OPEN))
       {
-         set_chunk_parent(next, pc->type);
-         next = chunk_get_next_ncnl(next);
+         pclass = chunk_get_next_ncnl(chunk_skip_to_match(pclass));
+         LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+                 __func__, __LINE__, pclass->text());
       }
-      prev = nullptr;
    }
-   // reset var name parent type
-   else if (next && prev)
+
+   if (  pclass == nullptr
+      || (pclass->type != CT_TYPE && pclass->type != CT_WORD))
    {
-      set_chunk_parent(prev, CT_NONE);
+      return;
    }
+   chunk_t *next = chunk_get_next_ncnl(pclass, scope_e::PREPROC);
 
-   if (next == nullptr || chunk_is_token(next, CT_PAREN_CLOSE))
+   while (  chunk_is_token(next, CT_TYPE)
+         || chunk_is_token(next, CT_WORD)
+         || chunk_is_token(next, CT_DC_MEMBER))
+   {
+      pclass = next;
+      LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+              __func__, __LINE__, pclass->text());
+      next = chunk_get_next_ncnl(next, scope_e::PREPROC);
+   }
+   chunk_t *pc   = chunk_get_next_ncnl(pclass, scope_e::PREPROC);
+   size_t  level = pclass->brace_level + 1;
+
+   if (pc == nullptr)
    {
+      LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu. Bailed on NULL\n",
+              __func__, __LINE__, pclass->text(), pclass->orig_line);
       return;
    }
+   // Add the class name
+   ChunkStack cs;
 
-   if (!chunk_is_semicolon(next))
+   cs.Push_Back(pclass);
+
+   LOG_FMT(LFTOR, "%s(%d): Called on %s on orig_line %zu (next is '%s')\n",
+           __func__, __LINE__, pclass->text(), pclass->orig_line, pc->text());
+
+   // detect D template class: "class foo(x) { ... }"
+   if (language_is_set(LANG_D) && chunk_is_token(next, CT_PAREN_OPEN))              // Coverity CID 76004
    {
-      // Pawn does not require a semicolon after an enum
-      if (language_is_set(LANG_PAWN))
-      {
-         return;
-      }
+      set_chunk_parent(next, CT_TEMPLATE);
 
-      /*
-       * D does not require a semicolon after an enum, but we add one to make
-       * other code happy.
-       */
-      if (language_is_set(LANG_D))
+      next = get_d_template_types(cs, next);
+
+      if (chunk_is_token(next, CT_PAREN_CLOSE))
       {
-         next = pawn_add_vsemi_after(chunk_get_prev_ncnlni(next));   // Issue #2279
+         set_chunk_parent(next, CT_TEMPLATE);
       }
    }
+   // Find the open brace, abort on semicolon
+   pcf_flags_t flags = PCF_NONE;
 
-   // We are either pointing to a ';' or a variable
-   while (  next != nullptr
-         && !chunk_is_semicolon(next)
-         && next->type != CT_ASSIGN
-         && !(in_fcn_paren ^ (next->flags & PCF_IN_FCN_DEF)).test_any())
+   while (pc != nullptr && pc->type != CT_BRACE_OPEN)
    {
-      if (next->level == pc->level)
-      {
-         if (chunk_is_token(next, CT_WORD))
-         {
-            chunk_flags_set(next, flags);
-            flags &= ~PCF_VAR_1ST;   // clear the first flag for the next items
-            LOG_FMT(LCASTS, "%s(%d): orig_line is %zu, orig_col is %zu, text() '%s', set PCF_VAR_1ST\n",
-                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
-         }
+      LOG_FMT(LFTOR, " [%s]", pc->text());
 
-         if (  chunk_is_token(next, CT_STAR)
-            || (language_is_set(LANG_CPP) && chunk_is_token(next, CT_CARET)))
-         {
-            set_chunk_type(next, CT_PTR_TYPE);
-         }
+      flags = mark_where_chunk(pc, start->type, flags);
 
-         // If we hit a comma in a function param, we are done
-         if (  (chunk_is_token(next, CT_COMMA) || chunk_is_token(next, CT_FPAREN_CLOSE))
-            && (next->flags.test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL)))
-         {
-            return;
-         }
+      if (!flags.test(PCF_IN_WHERE_SPEC) && chunk_is_str(pc, ":", 1))
+      {
+         set_chunk_type(pc, CT_CLASS_COLON);
+         flags |= PCF_IN_CLASS_BASE;
+         LOG_FMT(LFTOR, "%s(%d): class colon on line %zu\n",
+                 __func__, __LINE__, pc->orig_line);
       }
-      next = chunk_get_next_ncnl(next);
-   }
 
-   if (  next != nullptr
-      && chunk_is_token(next, CT_SEMICOLON))
-   {
-      set_chunk_parent(next, pc->type);
+      if (chunk_is_semicolon(pc))
+      {
+         LOG_FMT(LFTOR, "%s(%d): bailed on semicolon on line %zu\n",
+                 __func__, __LINE__, pc->orig_line);
+         return;
+      }
+      chunk_flags_set(pc, flags);
+      pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);
    }
-} // fix_enum_struct_union
-
 
-static void fix_typedef(chunk_t *start)
-{
-   LOG_FUNC_ENTRY();
-
-   if (start == nullptr)
+   if (pc == nullptr)
    {
+      LOG_FMT(LFTOR, "%s(%d): bailed on NULL\n", __func__, __LINE__);
       return;
    }
-   LOG_FMT(LTYPEDEF, "%s(%d): typedef @ orig_line %zu, orig_col %zu\n",
-           __func__, __LINE__, start->orig_line, start->orig_col);
+   set_paren_parent(pc, start->type);
+   chunk_flags_set(pc, PCF_IN_CLASS);
 
-   chunk_t *the_type = nullptr;
-   chunk_t *last_op  = nullptr;
+   pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);
+   LOG_FMT(LFTOR, "%s(%d): pclass is '%s'\n",
+           __func__, __LINE__, pclass->text());
 
-   /*
-    * Mark everything in the typedef and scan for ")(", which makes it a
-    * function type
-    */
-   for (chunk_t *next = chunk_get_next_ncnl(start, scope_e::PREPROC)
-        ; next != nullptr && next->level >= start->level
-        ; next = chunk_get_next_ncnl(next, scope_e::PREPROC))
+   while (pc != nullptr)
    {
-      chunk_flags_set(next, PCF_IN_TYPEDEF);
+      LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n",
+              __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+      chunk_flags_set(pc, PCF_IN_CLASS);
 
-      if (start->level == next->level)
+      if (  pc->brace_level > level
+         || pc->level > pc->brace_level
+         || pc->flags.test(PCF_IN_PREPROC))
       {
-         if (chunk_is_semicolon(next))
-         {
-            set_chunk_parent(next, CT_TYPEDEF);
-            break;
-         }
+         pc = chunk_get_next_ncnl(pc);
+         continue;
+      }
 
-         if (chunk_is_token(next, CT_ATTRIBUTE))
-         {
-            break;
-         }
+      if (chunk_is_token(pc, CT_BRACE_CLOSE) && pc->brace_level < level)
+      {
+         LOG_FMT(LFTOR, "%s(%d): orig_line is %zu, Hit brace close\n",
+                 __func__, __LINE__, pc->orig_line);
+         pc = chunk_get_next_ncnl(pc, scope_e::PREPROC);
 
-         if (language_is_set(LANG_D) && chunk_is_token(next, CT_ASSIGN))
+         if (chunk_is_token(pc, CT_SEMICOLON))
          {
-            set_chunk_parent(next, CT_TYPEDEF);
-            break;
+            set_chunk_parent(pc, start->type);
          }
-         make_type(next);
+         return;
+      }
+      next = chunk_get_next_ncnl(pc, scope_e::PREPROC);
 
-         if (chunk_is_token(next, CT_TYPE))
+      if (chunkstack_match(cs, pc))
+      {
+         LOG_FMT(LFTOR, "%s(%d): pc is '%s', orig_line is %zu, orig_col is %zu\n",
+                 __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col);
+         // Issue #1333 Formatter removes semicolon after variable initializer at class level(C#)
+         // if previous chunk is 'new' operator it is variable initializer not a CLASS_FUNC_DEF.
+         chunk_t *prev = chunk_get_prev_ncnlni(pc, scope_e::PREPROC);   // Issue #2279
+         LOG_FMT(LFTOR, "%s(%d): prev is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                 __func__, __LINE__, prev->text(), prev->orig_line, prev->orig_col, get_token_name(prev->type));
+
+         // Issue #1003, next->type should not be CT_FPAREN_OPEN
+         if (  prev != nullptr
+            && (prev->type != CT_NEW))
          {
-            the_type = next;
+            bool is_func_class_def = false;
+
+            if (chunk_is_token(next, CT_PAREN_OPEN))
+            {
+               is_func_class_def = true;
+            }
+            else if (chunk_is_token(next, CT_ANGLE_OPEN))          // Issue # 1737
+            {
+               chunk_t *closeAngle    = chunk_skip_to_match(next);
+               chunk_t *afterTemplate = chunk_get_next(closeAngle);
+
+               if (chunk_is_token(afterTemplate, CT_PAREN_OPEN))
+               {
+                  is_func_class_def = true;
+               }
+            }
+            else
+            {
+               LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s\n",
+                       __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
+               make_type(pc);
+            }
+
+            if (is_func_class_def)
+            {
+               set_chunk_type(pc, CT_FUNC_CLASS_DEF);
+               LOG_FMT(LFTOR, "%s(%d): text() is '%s', orig_line is %zu, orig_col is %zu, type is %s, Marked CTor/DTor\n",
+                       __func__, __LINE__, pc->text(), pc->orig_line, pc->orig_col, get_token_name(pc->type));
+               mark_cpp_constructor(pc);
+            }
          }
-         chunk_flags_clr(next, PCF_VAR_1ST_DEF);
+      }
+      pc = next;
+   }
+} // mark_class_ctor
 
-         if (*next->str.c_str() == '(')
+
+static chunk_t *skip_align(chunk_t *start)
+{
+   chunk_t *pc = start;
+
+   if (chunk_is_token(pc, CT_ALIGN))
+   {
+      pc = chunk_get_next_ncnl(pc);
+
+      if (chunk_is_token(pc, CT_PAREN_OPEN))
+      {
+         pc = chunk_get_next_type(pc, CT_PAREN_CLOSE, pc->level);
+         pc = chunk_get_next_ncnl(pc);
+
+         if (chunk_is_token(pc, CT_COLON))
          {
-            last_op = next;
+            pc = chunk_get_next_ncnl(pc);
          }
       }
    }
+   return(pc);
+}
 
-   // avoid interpreting typedef NS_ENUM (NSInteger, MyEnum) as a function def
-   if (  last_op != nullptr
-      && !(language_is_set(LANG_OC) && get_chunk_parent_type(last_op) == CT_ENUM))
+
+chunk_t *skip_parent_types(chunk_t *colon)
+{
+   auto pc = chunk_get_next_ncnlnp(colon);
+
+   while (pc)
    {
-      flag_parens(last_op, PCF_NONE, CT_FPAREN_OPEN, CT_TYPEDEF, false);
-      fix_fcn_def_params(last_op);
+      // Skip access specifier
+      if (chunk_is_token(pc, CT_ACCESS))
+      {
+         pc = chunk_get_next_ncnlnp(pc);
+         continue;
+      }
+
+      // Check for a type name
+      if (!(chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE)))
+      {
+         LOG_FMT(LPCU,
+                 "%s is confused; expected a word at %zu:%zu "
+                 "following type list at %zu:%zu\n", __func__,
+                 colon->orig_line, colon->orig_col,
+                 pc->orig_line, pc->orig_col);
+         return(colon);
+      }
+      // Get next token
+      auto next = skip_template_next(chunk_get_next_ncnlnp(pc));
+
+      if (chunk_is_token(next, CT_DC_MEMBER) || chunk_is_token(next, CT_COMMA))
+      {
+         pc = chunk_get_next_ncnlnp(next);
+      }
+      else if (next)
+      {
+         LOG_FMT(LPCU, "%s -> %zu:%zu ('%s')\n", __func__,
+                 next->orig_line, next->orig_col, next->text());
+         return(next);
+      }
+      else
+      {
+         break;
+      }
+   }
+   LOG_FMT(LPCU, "%s: did not find end of type list (start was %zu:%zu)\n",
+           __func__, colon->orig_line, colon->orig_col);
+   return(colon);
+} // skip_parent_types
 
-      the_type = chunk_get_prev_ncnlni(last_op, scope_e::PREPROC);   // Issue #2279
 
-      if (the_type == nullptr)
+static void mark_struct_union_body(chunk_t *start)
+{
+   LOG_FUNC_ENTRY();
+   chunk_t *pc = start;
+
+   while (  pc != nullptr
+         && pc->level >= start->level
+         && !(pc->level == start->level && chunk_is_token(pc, CT_BRACE_CLOSE)))
+   {
+      if (  chunk_is_token(pc, CT_BRACE_OPEN)
+         || chunk_is_token(pc, CT_BRACE_CLOSE)
+         || chunk_is_token(pc, CT_SEMICOLON))
       {
-         return;
+         pc = chunk_get_next_ncnl(pc);
+
+         if (pc == nullptr)
+         {
+            break;
+         }
       }
-      chunk_t *open_paren = nullptr;
 
-      if (chunk_is_paren_close(the_type))
+      if (chunk_is_token(pc, CT_ALIGN))
       {
-         open_paren = chunk_skip_to_match_rev(the_type);
-         mark_function_type(the_type);
-         the_type = chunk_get_prev_ncnlni(the_type, scope_e::PREPROC);   // Issue #2279
+         pc = skip_align(pc); // "align(x)" or "align(x):"
 
-         if (the_type == nullptr)
+         if (pc == nullptr)
          {
-            return;
+            break;
          }
       }
       else
       {
-         // must be: "typedef <return type>func(params);"
-         set_chunk_type(the_type, CT_FUNC_TYPE);
+         pc = fix_variable_definition(pc);
+
+         if (pc == nullptr)
+         {
+            break;
+         }
       }
-      set_chunk_parent(the_type, CT_TYPEDEF);
+   }
+} // mark_struct_union_body
 
-      LOG_FMT(LTYPEDEF, "%s(%d): fcn typedef text() '%s', on orig_line %zu\n",
-              __func__, __LINE__, the_type->text(), the_type->orig_line);
 
-      // If we are aligning on the open parenthesis, grab that instead
-      log_rule_B("align_typedef_func");
+void mark_comments(void)
+{
+   LOG_FUNC_ENTRY();
 
-      if (open_paren != nullptr && options::align_typedef_func() == 1)
-      {
-         the_type = open_paren;
-      }
-      log_rule_B("align_typedef_func");
+   cpd.unc_stage = unc_stage_e::MARK_COMMENTS;
 
-      if (options::align_typedef_func() != 0)
-      {
-         LOG_FMT(LTYPEDEF, "%s(%d):  -- align anchor on text() %s, @ orig_line %zu, orig_col %zu\n",
-                 __func__, __LINE__, the_type->text(), the_type->orig_line, the_type->orig_col);
-         chunk_flags_set(the_type, 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
-    */
-   chunk_t *after = chunk_get_next_ncnl(start, scope_e::PREPROC);
+   bool    prev_nl = true;
+   chunk_t *cur    = chunk_get_head();
 
-   if (after == nullptr)
+   while (cur != nullptr)
    {
-      return;
-   }
+      chunk_t *next   = chunk_get_next_nvb(cur);
+      bool    next_nl = (next == nullptr) || chunk_is_newline(next);
 
-   if (  after->type != CT_ENUM
-      && after->type != CT_STRUCT
-      && after->type != CT_UNION)
-   {
-      if (the_type != nullptr)
+      if (chunk_is_comment(cur))
       {
-         // We have just a regular typedef
-         LOG_FMT(LTYPEDEF, "%s(%d): regular typedef text() %s, on orig_line %zu\n",
-                 __func__, __LINE__, the_type->text(), the_type->orig_line);
-         chunk_flags_set(the_type, PCF_ANCHOR);
+         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);
+         }
       }
-      return;
+      prev_nl = chunk_is_newline(cur);
+      cur     = next;
    }
-   // We have a struct/union/enum, next should be either a type or {
-   chunk_t *next = chunk_get_next_ncnl(after, scope_e::PREPROC);
+}
 
-   if (next == nullptr)
-   {
-      return;
-   }
 
-   if (chunk_is_token(next, CT_TYPE))
-   {
-      next = chunk_get_next_ncnl(next, scope_e::PREPROC);
+static void mark_define_expressions(void)
+{
+   LOG_FUNC_ENTRY();
 
-      if (next == nullptr)
-      {
-         return;
-      }
-   }
+   bool    in_define = false;
+   bool    first     = true;
+   chunk_t *pc       = chunk_get_head();
+   chunk_t *prev     = pc;
 
-   if (chunk_is_token(next, CT_BRACE_OPEN))
+   while (pc != nullptr)
    {
-      // Skip to the closing brace
-      chunk_t *br_c = chunk_get_next_type(next, CT_BRACE_CLOSE, next->level, scope_e::PREPROC);
-
-      if (br_c != nullptr)
+      if (!in_define)
       {
-         const c_token_t tag = after->type;
-         set_chunk_parent(next, tag);
-         set_chunk_parent(br_c, tag);
-
-         if (tag == CT_ENUM)
+         if (  chunk_is_token(pc, CT_PP_DEFINE)
+            || chunk_is_token(pc, CT_PP_IF)
+            || chunk_is_token(pc, CT_PP_ELSE))
          {
-            flag_series(after, br_c, PCF_IN_ENUM);
+            in_define = true;
+            first     = true;
          }
-         else if (tag == CT_STRUCT)
+      }
+      else
+      {
+         if (!pc->flags.test(PCF_IN_PREPROC) || chunk_is_token(pc, CT_PREPROC))
          {
-            flag_series(after, br_c, PCF_IN_STRUCT);
+            in_define = false;
+         }
+         else
+         {
+            if (  pc->type != CT_MACRO
+               && (  first
+                  || chunk_is_token(prev, CT_PAREN_OPEN)
+                  || chunk_is_token(prev, CT_ARITH)
+                  || chunk_is_token(prev, CT_CARET)
+                  || chunk_is_token(prev, CT_ASSIGN)
+                  || chunk_is_token(prev, CT_COMPARE)
+                  || chunk_is_token(prev, CT_RETURN)
+                  || chunk_is_token(prev, CT_GOTO)
+                  || chunk_is_token(prev, CT_CONTINUE)
+                  || chunk_is_token(prev, CT_FPAREN_OPEN)
+                  || chunk_is_token(prev, CT_SPAREN_OPEN)
+                  || chunk_is_token(prev, CT_BRACE_OPEN)
+                  || chunk_is_semicolon(prev)
+                  || chunk_is_token(prev, CT_COMMA)
+                  || chunk_is_token(prev, CT_COLON)
+                  || chunk_is_token(prev, CT_QUESTION)))
+            {
+               chunk_flags_set(pc, PCF_EXPR_START);
+               first = false;
+            }
          }
       }
+      prev = pc;
+      pc   = chunk_get_next(pc);
    }
-
-   if (the_type != nullptr)
-   {
-      LOG_FMT(LTYPEDEF, "%s(%d): %s typedef text() %s, on orig_line %zu\n",
-              __func__, __LINE__, get_token_name(after->type), the_type->text(),
-              the_type->orig_line);
-      chunk_flags_set(the_type, PCF_ANCHOR);
-   }
-} // fix_typedef
-
-
-//static void mark_variable_stack(ChunkStack &cs, log_sev_t sev)
-//{
-//   UNUSED(sev);
-//   LOG_FUNC_ENTRY();
-//
-//   // throw out the last word and mark the rest
-//   chunk_t *var_name = cs.Pop_Back();
-//
-//   if (var_name && var_name->prev->type == CT_DC_MEMBER)
-//   {
-//      cs.Push_Back(var_name);
-//   }
-//
-//   if (var_name != nullptr)
-//   {
-//      LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu:\n",
-//              __func__, __LINE__, var_name->orig_line, var_name->orig_col);
-//
-//      size_t  word_cnt = 0;
-//      chunk_t *word_type;
-//
-//      while ((word_type = cs.Pop_Back()) != nullptr)
-//      {
-//         if (chunk_is_token(word_type, CT_WORD) || chunk_is_token(word_type, CT_TYPE))
-//         {
-//            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
-//                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, word_type->text());
-//            set_chunk_type(word_type, CT_TYPE);
-//            chunk_flags_set(word_type, PCF_VAR_TYPE);
-//         }
-//         word_cnt++;
-//      }
-//
-//      if (chunk_is_token(var_name, CT_WORD))
-//      {
-//         if (word_cnt > 0)
-//         {
-//            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as VAR\n",
-//                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
-//            chunk_flags_set(var_name, PCF_VAR_DEF);
-//         }
-//         else
-//         {
-//            LOG_FMT(LFCNP, "%s(%d): parameter on orig_line %zu, orig_col %zu: <%s> as TYPE\n",
-//                    __func__, __LINE__, var_name->orig_line, var_name->orig_col, var_name->text());
-//            set_chunk_type(var_name, CT_TYPE);
-//            chunk_flags_set(var_name, PCF_VAR_TYPE);
-//         }
-//      }
-//   }
-//} // mark_variable_stack
-
-
-//static void fix_fcn_def_params(chunk_t *start)
-//{
-//   LOG_FUNC_ENTRY();
-//
-//   if (start == nullptr)
-//   {
-//      return;
-//   }
-//   LOG_FMT(LFCNP, "%s(%d): text() '%s', type is %s, on orig_line %zu, level is %zu\n",
-//           __func__, __LINE__, start->text(), get_token_name(start->type), start->orig_line, start->level);
-//
-//   while (start != nullptr && !chunk_is_paren_open(start))
-//   {
-//      start = chunk_get_next_ncnl(start);
-//   }
-//
-//   if (start == nullptr)// Coverity CID 76003, 1100782
-//   {
-//      return;
-//   }
-//   // ensure start chunk holds a single '(' character
-//   assert((start->len() == 1) && (start->str[0] == '('));
-//
-//   ChunkStack cs;
-//   size_t     level = start->level + 1;
-//   chunk_t    *pc   = start;
-//
-//   while ((pc = chunk_get_next_ncnl(pc)) != nullptr)
-//   {
-//      if (  ((start->len() == 1) && (start->str[0] == ')'))
-//         || pc->level < level)
-//      {
-//         LOG_FMT(LFCNP, "%s(%d): bailed on text() '%s', on orig_line %zu\n",
-//                 __func__, __LINE__, pc->text(), pc->orig_line);
-//         break;
-//      }
-//      LOG_FMT(LFCNP, "%s(%d): %s, text() '%s' on orig_line %zu, level %zu\n",
-//              __func__, __LINE__, (pc->level > level) ? "skipping" : "looking at",
-//              pc->text(), pc->orig_line, pc->level);
-//
-//      if (pc->level > level)
-//      {
-//         continue;
-//      }
-//
-//      if (chunk_is_star(pc) || chunk_is_msref(pc) || chunk_is_nullable(pc))
-//      {
-//         set_chunk_type(pc, CT_PTR_TYPE);
-//         cs.Push_Back(pc);
-//      }
-//      else if (  chunk_is_token(pc, CT_AMP)
-//              || (language_is_set(LANG_CPP) && chunk_is_str(pc, "&&", 2)))
-//      {
-//         set_chunk_type(pc, CT_BYREF);
-//         cs.Push_Back(pc);
-//      }
-//      else if (chunk_is_token(pc, CT_TYPE_WRAP))
-//      {
-//         cs.Push_Back(pc);
-//      }
-//      else if (chunk_is_token(pc, CT_WORD) || chunk_is_token(pc, CT_TYPE))
-//      {
-//         cs.Push_Back(pc);
-//      }
-//      else if (chunk_is_token(pc, CT_COMMA) || chunk_is_token(pc, CT_ASSIGN))
-//      {
-//         mark_variable_stack(cs, LFCNP);
-//
-//         if (chunk_is_token(pc, CT_ASSIGN))
-//         {
-//            // Mark assignment for default param spacing
-//            set_chunk_parent(pc, CT_FUNC_PROTO);
-//         }
-//      }
-//   }
-//   mark_variable_stack(cs, LFCNP);
-//} // fix_fcn_def_params
+} // mark_define_expressions
 
 
 static void handle_cpp_template(chunk_t *pc)
@@ -3401,6 +5929,49 @@ static void handle_cpp_lambda(chunk_t *sq_o)
 } // handle_cpp_lambda
 
 
+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)) != nullptr)
+         && tmp->level > open_paren->level)
+   {
+      if (chunk_is_token(tmp, CT_TYPE) || chunk_is_token(tmp, CT_WORD))
+      {
+         if (maybe_type)
+         {
+            make_type(tmp);
+            cs.Push_Back(tmp);
+         }
+         maybe_type = false;
+      }
+      else if (chunk_is_token(tmp, CT_COMMA))
+      {
+         maybe_type = true;
+      }
+   }
+   return(tmp);
+}
+
+
+static bool chunkstack_match(ChunkStack &cs, chunk_t *pc)
+{
+   for (size_t idx = 0; idx < cs.Len(); idx++)
+   {
+      chunk_t *tmp = cs.GetChunk(idx);
+
+      if (pc->str.equals(tmp->str))
+      {
+         return(true);
+      }
+   }
+
+   return(false);
+}
+
+
 static void handle_d_template(chunk_t *pc)
 {
    LOG_FUNC_ENTRY();
@@ -3464,6 +6035,94 @@ static void handle_d_template(chunk_t *pc)
 } // handle_d_template
 
 
+static void mark_template_func(chunk_t *pc, chunk_t *pc_next)
+{
+   LOG_FUNC_ENTRY();
+
+   // We know angle_close must be there...
+   chunk_t *angle_close = chunk_get_next_type(pc_next, CT_ANGLE_CLOSE, pc->level);
+   chunk_t *after       = chunk_get_next_ncnl(angle_close);
+
+   if (after != nullptr)
+   {
+      if (chunk_is_str(after, "(", 1))
+      {
+         if (angle_close->flags.test(PCF_IN_FCN_CALL))
+         {
+            LOG_FMT(LTEMPFUNC, "%s(%d): marking '%s' in line %zu as a FUNC_CALL\n",
+                    __func__, __LINE__, pc->text(), pc->orig_line);
+            LOG_FMT(LFCN, "%s(%d): (16) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+            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(%d): marking '%s' in line %zu as a FUNC_CALL 2\n",
+                    __func__, __LINE__, pc->text(), pc->orig_line);
+            // its a function!!!
+            LOG_FMT(LFCN, "%s(%d): (17) SET TO CT_FUNC_CALL: orig_line is %zu, orig_col is %zu, text() '%s'\n",
+                    __func__, __LINE__, pc->orig_line, pc->orig_col, pc->text());
+            set_chunk_type(pc, CT_FUNC_CALL);
+            mark_function(pc);
+         }
+      }
+      else if (chunk_is_token(after, CT_WORD))
+      {
+         // its a type!
+         set_chunk_type(pc, CT_TYPE);
+         chunk_flags_set(pc, PCF_VAR_TYPE);
+         chunk_flags_set(after, PCF_VAR_DEF);
+      }
+   }
+} // mark_template_func
+
+
+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 != nullptr; tmp = chunk_get_next(tmp))
+   {
+      set_chunk_parent(tmp, pc->type);
+
+      if (chunk_is_token(tmp, CT_WORD))
+      {
+         set_chunk_type(tmp, CT_SQL_WORD);
+      }
+
+      if (chunk_is_token(tmp, CT_SEMICOLON))
+      {
+         break;
+      }
+   }
+
+   if (  pc->type != CT_SQL_BEGIN
+      || tmp == nullptr
+      || tmp->type != CT_SEMICOLON)
+   {
+      return;
+   }
+
+   for (tmp = chunk_get_next(tmp);
+        tmp != nullptr && tmp->type != CT_SQL_END;
+        tmp = chunk_get_next(tmp))
+   {
+      tmp->level++;
+   }
+}
+
+
 chunk_t *skip_template_next(chunk_t *ang_open)
 {
    if (chunk_is_token(ang_open, CT_ANGLE_OPEN))
@@ -3475,6 +6134,66 @@ chunk_t *skip_template_next(chunk_t *ang_open)
 }
 
 
+chunk_t *skip_template_prev(chunk_t *ang_close)
+{
+   if (chunk_is_token(ang_close, CT_ANGLE_CLOSE))
+   {
+      chunk_t *pc = chunk_get_prev_type(ang_close, CT_ANGLE_OPEN, ang_close->level);
+      return(chunk_get_prev_ncnlni(pc));   // Issue #2279
+   }
+   return(ang_close);
+}
+
+
+chunk_t *skip_tsquare_next(chunk_t *ary_def)
+{
+   if (chunk_is_token(ary_def, CT_SQUARE_OPEN) || chunk_is_token(ary_def, CT_TSQUARE))
+   {
+      return(chunk_get_next_nisq(ary_def));
+   }
+   return(ary_def);
+}
+
+
+chunk_t *skip_attribute_next(chunk_t *attr)
+{
+   chunk_t *pc = attr;
+
+   while (chunk_is_token(pc, CT_ATTRIBUTE))
+   {
+      pc = chunk_get_next_ncnl(pc);
+
+      if (chunk_is_token(pc, CT_FPAREN_OPEN))
+      {
+         pc = chunk_get_next_type(pc, CT_FPAREN_CLOSE, pc->level);
+         pc = chunk_get_next_ncnl(pc);
+      }
+   }
+   return(pc);
+}
+
+
+chunk_t *skip_attribute_prev(chunk_t *fp_close)
+{
+   chunk_t *pc = fp_close;
+
+   while (true)
+   {
+      if (  chunk_is_token(pc, CT_FPAREN_CLOSE)
+         && get_chunk_parent_type(pc) == CT_ATTRIBUTE)
+      {
+         pc = chunk_get_prev_type(pc, CT_ATTRIBUTE, pc->level);
+      }
+      else if (chunk_is_not_token(pc, CT_ATTRIBUTE))
+      {
+         break;
+      }
+      pc = chunk_get_prev_ncnlni(pc);   // Issue #2279
+   }
+   return(pc);
+}
+
+
 static void handle_oc_class(chunk_t *pc)
 {
    enum class angle_state_e : unsigned int

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: a problem with git diff
  2020-04-16 16:43 a problem with git diff Guy Maurel
@ 2020-04-16 22:55 ` brian m. carlson
       [not found]   ` <24a180f6-cc7e-fe5b-e810-0fc717d601ea@maurel.de>
  0 siblings, 1 reply; 5+ messages in thread
From: brian m. carlson @ 2020-04-16 22:55 UTC (permalink / raw)
  To: Guy Maurel; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 1556 bytes --]

On 2020-04-16 at 16:43:40, Guy Maurel wrote:
> Hello!
> 
> Using the appended two files:
>   git diff combine.cpp-2020-04-16-A combine.cpp-2020-04-16-R > git-diff.diff-Y
> 
> doesn't show the same differences as with:
>   meld combine.cpp-2020-04-16-R combine.cpp-2020-04-16-A
> 
> Have a look at git-diff.diff-Y at line 210:
> -static void process_returns(void)
> +static void mark_lvalue(chunk_t *pc)
> 
> which is NOT correct.

It looks like there's some additional code that gets inserted before the
process_returns function.  In this case, the diff is accurate in that
applying it to the old file will result in the new file, but it's not
very helpful.

The reason is that the default diff algorithm, myers, looks for common
lines and finds them in the blank lines in both process_returns and the
new code above it.  It then writes the diff as a set of deletions of the
lines in process_returns and an addition of the lines in the new code,
plus a final addition of the process_returns function.  While correct,
this is, as you noted, confusing.

If you're looking for a more helpful output, you can use
--diff-algorithm=patience (or diff.algorithm=patience), which results
in a diff output that more logically matches what most humans would like
to see.  You can also use the histogram diff algorithm, which is based
on patience but has some additional heuristics.  Which one produces
better output differs depending on the circumstance.
-- 
brian m. carlson: Houston, Texas, US
OpenPGP: https://keybase.io/bk2204

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 263 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: a problem with git diff
       [not found]   ` <24a180f6-cc7e-fe5b-e810-0fc717d601ea@maurel.de>
@ 2020-04-17 20:33     ` brian m. carlson
  2020-04-17 21:32       ` Junio C Hamano
  2020-04-22  9:10       ` Pratyush Yadav
  0 siblings, 2 replies; 5+ messages in thread
From: brian m. carlson @ 2020-04-17 20:33 UTC (permalink / raw)
  To: Guy Maurel; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 412 bytes --]

On 2020-04-17 at 12:23:35, Guy Maurel wrote:
> Hello again!
> 
> Using .git/config with
> [diff]
>         algorithm = patience
> 
> doesn't work with "git gui"
> What is to do?

I don't know because I don't use git-gui.  If you would, please keep the
list in CC, so that other folks who do know the answer can chime in.
-- 
brian m. carlson: Houston, Texas, US
OpenPGP: https://keybase.io/bk2204

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 263 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: a problem with git diff
  2020-04-17 20:33     ` brian m. carlson
@ 2020-04-17 21:32       ` Junio C Hamano
  2020-04-22  9:10       ` Pratyush Yadav
  1 sibling, 0 replies; 5+ messages in thread
From: Junio C Hamano @ 2020-04-17 21:32 UTC (permalink / raw)
  To: brian m. carlson; +Cc: Guy Maurel, git

"brian m. carlson" <sandals@crustytoothpaste.net> writes:

> I don't know because I don't use git-gui.  If you would, please keep the
> list in CC, so that other folks who do know the answer can chime in.

I do not use git-gui, either, but IIRC, it stayed away from
scripting around Porcelain commands, so it is not all that
surprising that it does not use diff.algorithm.

There is gui.diffopts configuration used in git-gui/lib/diff.tcl::

	lappend cmd -p
	lappend cmd --color
	set cmd [concat $cmd $repo_config(gui.diffopts)]
	if {$repo_config(gui.diffcontext) >= 1} {
		lappend cmd "-U$repo_config(gui.diffcontext)"
	}

so perhaps you can pass --patience via that configuration?  I.e.

	[gui]
		diffopts = --patience

in .git/config?

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: a problem with git diff
  2020-04-17 20:33     ` brian m. carlson
  2020-04-17 21:32       ` Junio C Hamano
@ 2020-04-22  9:10       ` Pratyush Yadav
  1 sibling, 0 replies; 5+ messages in thread
From: Pratyush Yadav @ 2020-04-22  9:10 UTC (permalink / raw)
  To: brian m. carlson, Guy Maurel, git

On 17/04/20 08:33PM, brian m. carlson wrote:
> On 2020-04-17 at 12:23:35, Guy Maurel wrote:
> > Hello again!
> > 
> > Using .git/config with
> > [diff]
> >         algorithm = patience
> > 
> > doesn't work with "git gui"
> > What is to do?
 
The bug has been reported on this list before. I will try to fix it when 
I can find the time. In the meantime, please use Junio's suggestion as a 
workaround.

> I don't know because I don't use git-gui.  If you would, please keep the
> list in CC, so that other folks who do know the answer can chime in.

-- 
Regards,
Pratyush Yadav

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-04-22  9:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-16 16:43 a problem with git diff Guy Maurel
2020-04-16 22:55 ` brian m. carlson
     [not found]   ` <24a180f6-cc7e-fe5b-e810-0fc717d601ea@maurel.de>
2020-04-17 20:33     ` brian m. carlson
2020-04-17 21:32       ` Junio C Hamano
2020-04-22  9:10       ` Pratyush Yadav

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).