# Re: a problem with git diff

5 messages from 2020-04-16 to 2020-04-22. Participants: brian m. carlson, Junio C Hamano, Guy Maurel, Pratyush Yadav.
Thread: https://gitlist.dev/t/53248

## Guy Maurel, 2020-04-16 16:43

Subject: a problem with git diff
Message-ID: <65cbee30-fa37-1422-98f2-2c2b5aa094bd@maurel.de>
URL: https://gitlist.dev/e/65cbee30-fa37-1422-98f2-2c2b5aa094bd%40maurel.de

```
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


/**
 * @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;
         }
      }
   }
}


/**
 * @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",
         
```

## brian m. carlson, 2020-04-16 22:55

Subject: Re: a problem with git diff
Message-ID: <20200416225502.GJ2751707@camp.crustytoothpaste.net>
URL: https://gitlist.dev/e/20200416225502.GJ2751707%40camp.crustytoothpaste.net
In-Reply-To: <65cbee30-fa37-1422-98f2-2c2b5aa094bd@maurel.de>

```
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

```

## brian m. carlson, 2020-04-17 20:33

Subject: Re: a problem with git diff
Message-ID: <20200417203323.GK2751707@camp.crustytoothpaste.net>
URL: https://gitlist.dev/e/20200417203323.GK2751707%40camp.crustytoothpaste.net
In-Reply-To: <24a180f6-cc7e-fe5b-e810-0fc717d601ea@maurel.de>

```
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

```

## Junio C Hamano, 2020-04-17 21:32

Subject: Re: a problem with git diff
Message-ID: <xmqqh7xhvlvg.fsf@gitster.c.googlers.com>
URL: https://gitlist.dev/e/xmqqh7xhvlvg.fsf%40gitster.c.googlers.com
In-Reply-To: <20200417203323.GK2751707@camp.crustytoothpaste.net>

```
"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?

```

## Pratyush Yadav, 2020-04-22 09:10

Subject: Re: a problem with git diff
Message-ID: <20200422091040.dmmdqih5ni3gfaac@yadavpratyush.com>
URL: https://gitlist.dev/e/20200422091040.dmmdqih5ni3gfaac%40yadavpratyush.com
In-Reply-To: <20200417203323.GK2751707@camp.crustytoothpaste.net>

```
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

```
