You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1441 lines
46 KiB

/**
* @file brace_cleanup.cpp
* Determines the brace level and paren level.
* Inserts virtual braces as needed.
* Handles all that preprocessor stuff.
*
* @author Ben Gardner
* @license GPL v2+
*/
#include "brace_cleanup.h"
#include "flag_parens.h"
#include "keywords.h"
#include "lang_pawn.h"
#include "parsing_frame_stack.h"
#include "prototypes.h"
#include <stdexcept> // to get std::invalid_argument
constexpr static auto LCURRENT = LBC;
using namespace uncrustify;
using std::invalid_argument;
using std::string;
using std::to_string;
using std::stringstream;
struct BraceState
{
ParsingFrameStack frames;
E_Token in_preproc = CT_NONE;
int pp_level = 0;
bool consumed = false;
};
/**
* Called when a statement was just closed and the pse_tos was just
* decremented.
*
* - if the TOS is now VBRACE, insert a CT_VBRACE_CLOSE and recurse.
* - if the TOS is a complex statement, call handle_complex_close()
*
* @retval true done with this chunk
* @retval false keep processing
*/
static bool close_statement(ParsingFrame &frm, Chunk *pc, const BraceState &braceState);
static size_t preproc_start(BraceState &braceState, ParsingFrame &frm, Chunk *pc);
static void print_stack(log_sev_t logsev, const char *str, const ParsingFrame &frm);
/**
* pc is a CT_WHILE.
* Scan backwards to see if we find a brace/vbrace with the parent set to CT_DO
*/
static bool maybe_while_of_do(Chunk *pc);
/**
* @param after determines: true - insert_vbrace_close_after(pc, frm)
* false - insert_vbrace_open_before(pc, frm)
*/
static Chunk *insert_vbrace(Chunk *pc, bool after, const ParsingFrame &frm);
#define insert_vbrace_close_after(pc, frm) insert_vbrace(pc, true, frm)
#define insert_vbrace_open_before(pc, frm) insert_vbrace(pc, false, frm)
static void parse_cleanup(BraceState &braceState, ParsingFrame &frm, Chunk *pc);
/**
* Checks the progression of complex statements.
* - checks for else after if
* - checks for if after else
* - checks for while after do
* - checks for open brace in BRACE2 and BRACE_DO stages, inserts open VBRACE
* - checks for open paren in PAREN1 and BRACE2 stages, complains
*
* @param frm The parse frame
* @param pc The current chunk
*
* @return true - done with this chunk, false - keep processing
*/
static bool check_complex_statements(ParsingFrame &frm, Chunk *pc, const BraceState &braceState);
/**
* Handles a close paren or brace - just progress the stage, if the end
* of the statement is hit, call close_statement()
*
* @param frm The parse frame
* @param pc The current chunk
*
* @return true - done with this chunk, false - keep processing
*/
static bool handle_complex_close(ParsingFrame &frm, Chunk *pc, const BraceState &braceState);
//! We're on a 'namespace' skip the word and then set the parent of the braces.
static void mark_namespace(Chunk *pns);
static size_t preproc_start(BraceState &braceState, ParsingFrame &frm, Chunk *pc)
{
LOG_FUNC_ENTRY();
const size_t pp_level = braceState.pp_level;
Chunk *next = pc->GetNextNcNnl();
if (next->IsNullChunk())
{
return(pp_level);
}
// Get the type of preprocessor and handle it
braceState.in_preproc = next->GetType();
// If we are not in a define, check for #if, #else, #endif, etc
if (braceState.in_preproc != CT_PP_DEFINE)
{
int pp_indent = braceState.frames.check(frm, braceState.pp_level, pc);
return(pp_indent);
}
// else push the frame stack
braceState.frames.push(frm);
// a preproc body starts a new, blank frame
frm = ParsingFrame();
frm.SetParenLevel(1);
frm.SetBraceLevel(1);
// TODO: not sure about the next 3 lines
frm.push(Chunk::NullChunkPtr, __func__, __LINE__);
frm.top().SetOpenToken(CT_PP_DEFINE);
return(pp_level);
}
static void print_stack(log_sev_t logsev, const char *str,
const ParsingFrame &frm)
{
LOG_FUNC_ENTRY();
if (!log_sev_on(logsev))
{
return;
}
log_fmt(logsev, "%s(%d): str is '%s'", __func__, __LINE__, str);
for (size_t idx = 1; idx < frm.size(); idx++)
{
if (frm.at(idx).GetStage() != E_BraceStage::NONE)
{
LOG_FMT(logsev, " [%s - %u]", get_token_name(frm.at(idx).GetOpenToken()),
(unsigned int)frm.at(idx).GetStage());
}
else
{
LOG_FMT(logsev, " [%s]", get_token_name(frm.at(idx).GetOpenToken()));
}
}
log_fmt(logsev, "\n");
}
//TODO: This can be cleaned up and simplified - we can look both forward and backward!
void brace_cleanup()
{
LOG_FUNC_ENTRY();
BraceState braceState;
ParsingFrame frm;
Chunk *pc = Chunk::GetHead();
while (pc->IsNotNullChunk())
{
LOG_CHUNK(LTOK, pc);
// Check for leaving a #define body
if ( braceState.in_preproc != CT_NONE
&& !pc->TestFlags(PCF_IN_PREPROC))
{
if (braceState.in_preproc == CT_PP_DEFINE)
{
// out of the #define body, restore the frame
size_t brace_level = frm.GetBraceLevel();
if ( options::pp_warn_unbalanced_if()
&& brace_level != 1)
{
LOG_FMT(LWARN, "%s(%d): orig line is %zu, unbalanced #define block braces, out-level is %zu\n",
__func__, __LINE__, pc->GetOrigLine(), brace_level);
}
braceState.frames.pop(frm);
}
braceState.in_preproc = CT_NONE;
}
// Check for a preprocessor start
size_t pp_level;
if (pc->Is(CT_PREPROC))
{
pp_level = preproc_start(braceState, frm, pc);
}
else
{
pp_level = braceState.pp_level;
}
LOG_FMT(LTOK, "%s(%d): pp level is %zu\n",
__func__, __LINE__, pp_level);
// Do before assigning stuff from the frame
if ( language_is_set(LANG_PAWN)
&& frm.top().GetOpenToken() == CT_VBRACE_OPEN
&& pc->Is(CT_NEWLINE))
{
pc = pawn_check_vsemicolon(pc);
if (pc->IsNullChunk())
{
return;
}
}
// Issue #1813
if (pc->Is(CT_NAMESPACE))
{
mark_namespace(pc);
}
// Assume the level won't change
pc->SetLevel(frm.GetParenLevel());
pc->SetBraceLevel(frm.GetBraceLevel());
pc->SetPpLevel(pp_level);
/*
* #define bodies get the full formatting treatment
* Also need to pass in the initial '#' to close out any virtual braces.
*/
if ( !pc->IsCommentOrNewline()
&& !pc->Is(CT_ATTRIBUTE)
&& !pc->Is(CT_IGNORED) // Issue #2279
&& ( braceState.in_preproc == CT_PP_DEFINE
|| braceState.in_preproc == CT_NONE))
{
braceState.consumed = false;
parse_cleanup(braceState, frm, pc);
print_stack(LBCSAFTER, (pc->Is(CT_VBRACE_CLOSE)) ? "Virt-}\n" : pc->GetStr().c_str(), frm);
}
pc = pc->GetNext();
}
} // brace_cleanup
static bool maybe_while_of_do(Chunk *pc)
{
LOG_FUNC_ENTRY();
Chunk *prev = pc->GetPrevNcNnl();
if ( prev->IsNullChunk()
|| !prev->TestFlags(PCF_IN_PREPROC))
{
return(false);
}
// Find the chunk before the preprocessor
while ( prev->IsNullChunk()
&& prev->TestFlags(PCF_IN_PREPROC))
{
prev = prev->GetPrevNcNnl();
}
if ( ( prev->Is(CT_VBRACE_CLOSE)
|| prev->Is(CT_BRACE_CLOSE))
&& prev->GetParentType() == CT_DO)
{
return(true);
}
return(false);
}
/**
* At the heart of this algorithm are two stacks.
* There is the Paren Stack (PS) and the Frame stack.
*
* The PS (m_parenStack in the code) keeps track of braces, parens,
* if/else/switch/do/while/etc items -- anything that is nestable.
* Complex statements go through stages.
* Take this simple if statement as an example:
* if ( x ) { x--; }
*
* The stack would change like so: 'token' stack afterwards
* 'if' [IF - 1]
* '(' [IF - 1] [PAREN OPEN]
* 'x' [IF - 1] [PAREN OPEN]
* ')' [IF - 2] <- note that the state was incremented
* '{' [IF - 2] [BRACE OPEN]
* 'x' [IF - 2] [BRACE OPEN]
* '--' [IF - 2] [BRACE OPEN]
* ';' [IF - 2] [BRACE OPEN]
* '}' [IF - 3]
* <- lack of else kills the IF, closes statement
*
* Virtual braces example:
* if ( x ) x--; else x++;
*
* 'if' [IF - 1]
* '(' [IF - 1] [PAREN OPEN]
* 'x' [IF - 1] [PAREN OPEN]
* ')' [IF - 2]
* 'x' [IF - 2] [VBRACE OPEN] <- VBrace open inserted before because '{' was not next
* '--' [IF - 2] [VBRACE OPEN]
* ';' [IF - 3] <- VBrace close inserted after semicolon
* 'else' [ELSE - 0] <- IF changed into ELSE
* 'x' [ELSE - 0] [VBRACE OPEN] <- lack of '{' -> VBrace
* '++' [ELSE - 0] [VBRACE OPEN]
* ';' [ELSE - 0] <- VBrace close inserted after semicolon
* <- ELSE removed after statement close
*
* The m_parenStack stack is kept on a frame stack.
* The frame stack is need for languages that support preprocessors (C, C++, C#)
* that can arbitrarily change code flow. It also isolates #define macros so
* that they are indented independently and do not affect the rest of the program.
*
* When an #if is hit, a copy of the current frame is push on the frame stack.
* When an #else/#elif is hit, a copy of the current stack is pushed under the
* #if frame and the original (pre-#if) frame is copied to the current frame.
* When #endif is hit, the top frame is popped.
* This has the following effects:
* - a simple #if / #endif does not affect program flow
* - #if / #else /#endif - continues from the #if clause
*
* When a #define is entered, the current frame is pushed and cleared.
* When a #define is exited, the frame is popped.
*/
static void parse_cleanup(BraceState &braceState, ParsingFrame &frm, Chunk *pc)
{
LOG_FUNC_ENTRY();
LOG_FMT(LTOK, "%s(%d): orig line is %zu, orig col is %zu, type is %s, tos is %zu, TOS.type is %s, TOS.stage is %s, ",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()),
frm.size() - 1, get_token_name(frm.top().GetOpenToken()),
get_brace_stage_name(frm.top().GetStage()));
log_pcf_flags(LTOK, pc->GetFlags());
// Mark statement starts
LOG_FMT(LTOK, "%s(%d): orig line is %zu, type is %s, Text() is '%s'\n",
__func__, __LINE__, pc->GetOrigLine(), get_token_name(pc->GetType()), pc->Text());
LOG_FMT(LTOK, "%s(%d): frame statement count is %zu, expression count is %zu\n",
__func__, __LINE__, frm.GetStmtCount(), frm.GetExprCount());
if ( ( frm.GetStmtCount() == 0
|| frm.GetExprCount() == 0)
&& !pc->IsSemicolon()
&& pc->IsNot(CT_BRACE_CLOSE)
&& pc->IsNot(CT_VBRACE_CLOSE)
&& !pc->IsString(")")
&& !pc->IsString("]"))
{
pc->SetFlagBits(PCF_EXPR_START | ((frm.GetStmtCount() == 0) ? PCF_STMT_START : PCF_NONE));
LOG_FMT(LSTMT, "%s(%d): orig line is %zu, 1.marked '%s' as %s, start statement count is %zu, expression count is %zu\n",
__func__, __LINE__, pc->GetOrigLine(), pc->Text(),
pc->TestFlags(PCF_STMT_START) ? "statement" : "expression", frm.GetStmtCount(),
frm.GetExprCount());
pc->TestFlags(PCF_STMT_START) ? log_ruleStart("start statement", pc) : log_ruleStart("start expression", pc);
}
frm.SetStmtCount(frm.GetStmtCount() + 1);
frm.SetExprCount(frm.GetExprCount() + 1);
LOG_FMT(LTOK, "%s(%d): frame statement count is %zu, expression count is %zu\n",
__func__, __LINE__, frm.GetStmtCount(), frm.GetExprCount());
if (frm.GetSParenCount() > 0)
{
pc->SetFlagBits(PCF_IN_SPAREN);
// Mark everything in the for statement
for (int tmp = static_cast<int>(frm.size()) - 2; tmp >= 0; tmp--)
{
if (frm.at(tmp).GetOpenToken() == CT_FOR)
{
pc->SetFlagBits(PCF_IN_FOR);
break;
}
}
// Mark the parent on semicolons in for() statements
if ( pc->Is(CT_SEMICOLON)
&& frm.size() > 2
&& frm.prev().GetOpenToken() == CT_FOR)
{
pc->SetParentType(CT_FOR);
}
}
// Check the progression of complex statements
if ( frm.top().GetStage() != E_BraceStage::NONE
&& !pc->Is(CT_AUTORELEASEPOOL)
&& check_complex_statements(frm, pc, braceState))
{
return;
}
/*
* Check for a virtual brace statement close due to a semicolon.
* The virtual brace will get handled the next time through.
* The semicolon isn't handled at all.
* TODO: may need to float VBRACE past comments until newline?
*/
if (frm.top().GetOpenToken() == CT_VBRACE_OPEN)
{
if (pc->IsSemicolon())
{
braceState.consumed = true;
close_statement(frm, pc, braceState);
}
else if ( language_is_set(LANG_PAWN)
&& pc->Is(CT_BRACE_CLOSE))
{
close_statement(frm, pc, braceState);
}
else if ( language_is_set(LANG_D)
&& pc->Is(CT_BRACE_CLOSE))
{
close_statement(frm, pc, braceState);
}
}
// Handle close parenthesis, vbrace, brace, and square
if ( pc->Is(CT_PAREN_CLOSE)
|| pc->Is(CT_BRACE_CLOSE)
|| pc->Is(CT_VBRACE_CLOSE)
|| pc->Is(CT_ANGLE_CLOSE)
|| pc->Is(CT_MACRO_CLOSE)
|| pc->Is(CT_SQUARE_CLOSE))
{
// Change CT_PAREN_CLOSE into CT_SPAREN_CLOSE or CT_FPAREN_CLOSE
if ( pc->Is(CT_PAREN_CLOSE)
&& ( (frm.top().GetOpenToken() == CT_FPAREN_OPEN)
|| (frm.top().GetOpenToken() == CT_SPAREN_OPEN)))
{
// TODO: fix enum hack
pc->SetType(static_cast<E_Token>(frm.top().GetOpenToken() + 1));
if (pc->Is(CT_SPAREN_CLOSE))
{
frm.SetSParenCount(frm.GetSParenCount() - 1);
pc->ResetFlagBits(PCF_IN_SPAREN);
}
}
// Make sure the open / close match
if (pc->IsNot((E_Token)(frm.top().GetOpenToken() + 1)))
{
if (pc->TestFlags(PCF_IN_PREPROC)) // Issue #3113, #3283
{
// do nothing
}
else
{
LOG_FMT(LWARN, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
const ParenStackEntry &AA = frm.top(); // Issue #3055
if (AA.GetOpenToken() != CT_EOF)
{
LOG_FMT(LWARN, "%s(%d): (frm.top().type + 1) is %s\n",
__func__, __LINE__, get_token_name((E_Token)(frm.top().GetOpenToken() + 1)));
}
if ( frm.top().GetOpenToken() != CT_EOF
&& frm.top().GetOpenToken() != CT_PP_DEFINE)
{
LOG_FMT(LWARN, "%s(%d): File: %s, orig line is %zu, orig col is %zu, Error: Unexpected '%s' for '%s', which was on line %zu\n",
__func__, __LINE__, cpd.filename.c_str(), pc->GetOrigLine(), pc->GetOrigCol(),
pc->Text(), get_token_name(frm.top().GetOpenChunk()->GetType()),
frm.top().GetOpenChunk()->GetOrigLine());
print_stack(LBCSPOP, "=Error ", frm);
exit(EXIT_FAILURE);
}
}
}
else
{
braceState.consumed = true;
// Copy the parent, update the parenthesis/brace levels
pc->SetParentType(frm.top().GetParent());
frm.SetParenLevel(frm.GetParenLevel() - 1);
if ( pc->Is(CT_BRACE_CLOSE)
|| pc->Is(CT_VBRACE_CLOSE)
|| pc->Is(CT_MACRO_CLOSE))
{
frm.SetBraceLevel(frm.GetBraceLevel() - 1);
LOG_FMT(LBCSPOP, "%s(%d): frame brace level decreased to %zu",
__func__, __LINE__, frm.GetBraceLevel());
log_pcf_flags(LBCSPOP, pc->GetFlags());
}
pc->SetLevel(frm.GetParenLevel());
pc->SetBraceLevel(frm.GetBraceLevel());
// Pop the entry
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-Close ", frm);
if ( frm.top().GetStage() == E_BraceStage::NONE
&& ( pc->Is(CT_VBRACE_CLOSE)
|| pc->Is(CT_BRACE_CLOSE)
|| pc->Is(CT_SEMICOLON))
&& frm.top().GetOpenChunk()->Is(CT_VBRACE_OPEN))
{
// frames for functions are not created as they are for an if
// this here is a hackish solution to close a vbrace of a block that
// contains the function
frm.push(Chunk::NullChunkPtr, __func__, __LINE__); // <- dummy frame for the function
frm.top().SetStage(E_BraceStage::BRACE2);
}
// See if we are in a complex statement
if (frm.top().GetStage() != E_BraceStage::NONE)
{
handle_complex_close(frm, pc, braceState);
}
}
}
/*
* In this state, we expect a semicolon, but we'll also hit the closing
* sparen, so we need to check braceState.consumed to see if the close sparen
* was already handled.
*/
if (frm.top().GetStage() == E_BraceStage::WOD_SEMI)
{
if (braceState.consumed)
{
/*
* If consumed, then we are on the close sparen.
* PAWN: Check the next chunk for a semicolon. If it isn't, then
* add a virtual semicolon, which will get handled on the next pass.
*/
if (language_is_set(LANG_PAWN))
{
Chunk *tmp = pc->GetNextNcNnl();
if (!tmp->IsSemicolon())
{
pawn_add_vsemi_after(pc);
}
}
}
else
{
// Complain if this ISN'T a semicolon, but close out WHILE_OF_DO anyway
if (pc->IsSemicolon())
{
braceState.consumed = true;
pc->SetParentType(CT_WHILE_OF_DO);
}
else
{
LOG_FMT(LWARN, "%s: %s(%d): %zu: Error: Expected a semicolon for WHILE_OF_DO, but got '%s'\n",
cpd.filename.c_str(), __func__, __LINE__, pc->GetOrigLine(),
get_token_name(pc->GetType()));
exit(EX_SOFTWARE);
}
handle_complex_close(frm, pc, braceState);
}
}
// Get the parent type for brace and parenthesis open
E_Token parentType = pc->GetParentType();
if ( pc->Is(CT_PAREN_OPEN)
|| pc->Is(CT_FPAREN_OPEN)
|| pc->Is(CT_SPAREN_OPEN)
|| pc->Is(CT_BRACE_OPEN))
{
Chunk *prev = pc->GetPrevNcNnl();
if (prev->IsNotNullChunk())
{
if ( pc->Is(CT_PAREN_OPEN)
|| pc->Is(CT_FPAREN_OPEN)
|| pc->Is(CT_SPAREN_OPEN))
{
// Set the parent for parenthesis and change parenthesis type
if ( prev->Is(CT_IF)
|| prev->Is(CT_CONSTEXPR)
|| prev->Is(CT_ELSEIF)
|| prev->Is(CT_WHILE)
|| prev->Is(CT_WHILE_OF_DO)
|| prev->Is(CT_DO)
|| prev->Is(CT_FOR)
|| prev->Is(CT_SWITCH)
|| prev->Is(CT_CATCH)
|| prev->Is(CT_SYNCHRONIZED)
|| prev->Is(CT_D_VERSION)
|| prev->Is(CT_D_VERSION_IF)
|| prev->Is(CT_D_SCOPE)
|| prev->Is(CT_D_SCOPE_IF))
{
pc->SetType(CT_SPAREN_OPEN);
parentType = frm.top().GetOpenToken();
frm.SetSParenCount(frm.GetSParenCount() + 1);
}
else if (prev->Is(CT_FUNCTION))
{
pc->SetType(CT_FPAREN_OPEN);
parentType = CT_FUNCTION;
}
// NS_ENUM and NS_OPTIONS are followed by a (type, name) pair
else if ( prev->Is(CT_ENUM)
&& language_is_set(LANG_OC))
{
// Treat both as CT_ENUM since the syntax is identical
pc->SetType(CT_FPAREN_OPEN);
parentType = CT_ENUM;
}
else if (prev->Is(CT_DECLSPEC)) // Issue 1289
{
parentType = CT_DECLSPEC;
}
// else: no need to set parent
}
else // must be CT_BRACE_OPEN
{
// Set the parent for open braces
if (frm.top().GetStage() != E_BraceStage::NONE)
{
parentType = frm.top().GetOpenToken();
}
else if ( prev->Is(CT_ASSIGN)
&& (prev->GetStr()[0] == '='))
{
parentType = CT_ASSIGN;
}
else if ( prev->Is(CT_RETURN)
&& language_is_set(LANG_CPP))
{
parentType = CT_RETURN;
}
// Carry through CT_ENUM parent in NS_ENUM (type, name) {
// only to help the vim command }
else if ( prev->Is(CT_FPAREN_CLOSE)
&& language_is_set(LANG_OC)
&& prev->GetParentType() == CT_ENUM)
{
parentType = CT_ENUM;
}
else if (prev->Is(CT_FPAREN_CLOSE))
{
parentType = CT_FUNCTION;
}
// else: no need to set parent
}
}
}
/*
* Adjust the level for opens & create a stack entry
* Note that CT_VBRACE_OPEN has already been handled.
*/
if ( pc->Is(CT_BRACE_OPEN)
|| pc->Is(CT_PAREN_OPEN)
|| pc->Is(CT_FPAREN_OPEN)
|| pc->Is(CT_SPAREN_OPEN)
|| pc->Is(CT_ANGLE_OPEN)
|| pc->Is(CT_MACRO_OPEN)
|| pc->Is(CT_SQUARE_OPEN))
{
frm.SetParenLevel(frm.GetParenLevel() + 1);
if ( pc->Is(CT_BRACE_OPEN)
|| pc->Is(CT_MACRO_OPEN))
{
// Issue #1813
bool single = false;
if (pc->GetParentType() == CT_NAMESPACE)
{
LOG_FMT(LBCSPOP, "%s(%d): parent type is NAMESPACE\n",
__func__, __LINE__);
Chunk *tmp = frm.top().GetOpenChunk();
if (tmp->GetParentType() == CT_NAMESPACE)
{
LOG_FMT(LBCSPOP, "%s(%d): tmp->GetParentType() is NAMESPACE\n",
__func__, __LINE__);
log_rule_B("indent_namespace");
log_rule_B("indent_namespace_single_indent");
if ( options::indent_namespace()
&& options::indent_namespace_single_indent())
{
LOG_FMT(LBCSPOP, "%s(%d): Options are SINGLE\n",
__func__, __LINE__);
single = true;
}
}
}
LOG_FMT(LBCSPOP, "%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->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()), get_token_name(pc->GetParentType()));
if (!single)
{
frm.SetBraceLevel(frm.GetBraceLevel() + 1);
LOG_FMT(LBCSPOP, "%s(%d): frame brace level increased to %zu\n",
__func__, __LINE__, frm.GetBraceLevel());
}
}
frm.push(pc, __func__, __LINE__);
frm.top().SetParent(parentType);
pc->SetParentType(parentType);
}
// Issue #2281
if ( pc->Is(CT_BRACE_OPEN)
&& pc->GetParentType() == CT_SWITCH)
{
size_t idx = frm.size();
LOG_FMT(LBCSPOP, "%s(%d): idx is %zu\n",
__func__, __LINE__, idx);
Chunk *saved = frm.at(idx - 2).GetOpenChunk();
if (saved->IsNotNullChunk())
{
// set parent member
pc->SetParent(saved);
}
}
if ( pc->Is(CT_CASE)
|| pc->Is(CT_DEFAULT))
{
Chunk *prev = pc->GetPrevNcNnl(); // Issue #3176
if ( pc->Is(CT_CASE)
|| ( pc->Is(CT_DEFAULT)
&& prev->IsNot(CT_ASSIGN)))
{
// it is a CT_DEFAULT from a switch
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());
pc->SetParentType(CT_SWITCH);
size_t idx = frm.size();
LOG_FMT(LBCSPOP, "%s(%d): idx is %zu\n",
__func__, __LINE__, idx);
Chunk *saved = frm.at(idx - 2).GetOpenChunk();
if (saved->IsNotNullChunk())
{
// set parent member
pc->SetParent(saved);
}
}
}
if (pc->Is(CT_BREAK))
{
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());
size_t idx = frm.size();
LOG_FMT(LBCSPOP, "%s(%d): idx is %zu\n",
__func__, __LINE__, idx);
Chunk *saved = frm.at(idx - 2).GetOpenChunk();
if (saved->IsNotNullChunk())
{
// set parent member
pc->SetParent(saved);
}
}
const pattern_class_e patcls = get_token_pattern_class(pc->GetType());
/*
* Create a stack entry for complex statements:
* if, elseif, switch, for, while, synchronized, using, lock, with,
* version, CT_D_SCOPE_IF
*/
if (patcls == pattern_class_e::BRACED)
{
frm.push(pc, __func__, __LINE__, (pc->Is(CT_DO) ? E_BraceStage::BRACE_DO
: E_BraceStage::BRACE2));
// "+ComplexBraced"
}
else if (patcls == pattern_class_e::PBRACED)
{
E_BraceStage bs = E_BraceStage::PAREN1;
if ( pc->Is(CT_WHILE)
&& maybe_while_of_do(pc))
{
pc->SetType(CT_WHILE_OF_DO);
bs = E_BraceStage::WOD_PAREN;
}
frm.push(pc, __func__, __LINE__, bs);
// "+ComplexParenBraced"
}
else if (patcls == pattern_class_e::OPBRACED)
{
frm.push(pc, __func__, __LINE__, E_BraceStage::OP_PAREN1);
// "+ComplexOpParenBraced");
}
else if (patcls == pattern_class_e::ELSE)
{
frm.push(pc, __func__, __LINE__, E_BraceStage::ELSEIF);
// "+ComplexElse");
}
/*
* Mark simple statement/expression starts
* - after { or }
* - after ';', but not if the paren stack top is a paren
* - after '(' that has a parent type of CT_FOR
*/
if ( pc->Is(CT_SQUARE_OPEN)
|| ( pc->Is(CT_BRACE_OPEN)
&& pc->GetParentType() != CT_ASSIGN)
|| pc->Is(CT_BRACE_CLOSE)
|| pc->Is(CT_VBRACE_CLOSE)
|| ( pc->Is(CT_SPAREN_OPEN)
&& pc->GetParentType() == CT_FOR)
|| pc->Is(CT_COLON)
|| pc->Is(CT_OC_END)
|| ( pc->IsSemicolon()
&& frm.top().GetOpenToken() != CT_PAREN_OPEN
&& frm.top().GetOpenToken() != CT_FPAREN_OPEN
&& frm.top().GetOpenToken() != CT_SPAREN_OPEN)
|| pc->Is(CT_MACRO)) // Issue #2742
{
LOG_FMT(LSTMT, "%s(%d): orig line is %zu, reset1 stmt on '%s'\n",
__func__, __LINE__, pc->GetOrigLine(), pc->Text());
frm.SetStmtCount(0);
frm.SetExprCount(0);
LOG_FMT(LTOK, "%s(%d): frame statement count is %zu, expression count is %zu\n",
__func__, __LINE__, frm.GetStmtCount(), frm.GetExprCount());
}
// Mark expression starts
LOG_FMT(LSTMT, "%s(%d): Mark expression starts: orig line is %zu, orig col is %zu, Text() is '%s'\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text());
Chunk *tmp = pc->GetNextNcNnl();
if ( pc->Is(CT_ARITH)
|| pc->Is(CT_SHIFT)
|| pc->Is(CT_ASSIGN)
|| pc->Is(CT_CASE)
|| pc->Is(CT_COMPARE)
|| ( pc->Is(CT_STAR)
&& tmp->IsNot(CT_STAR))
|| pc->Is(CT_BOOL)
|| pc->Is(CT_MINUS)
|| pc->Is(CT_PLUS)
|| pc->Is(CT_CARET)
|| pc->Is(CT_ANGLE_OPEN)
|| pc->Is(CT_ANGLE_CLOSE)
|| pc->Is(CT_RETURN)
|| pc->Is(CT_THROW)
|| pc->Is(CT_GOTO)
|| pc->Is(CT_CONTINUE)
|| pc->Is(CT_PAREN_OPEN)
|| pc->Is(CT_FPAREN_OPEN)
|| pc->Is(CT_SPAREN_OPEN)
|| pc->Is(CT_BRACE_OPEN)
|| pc->IsSemicolon()
|| pc->Is(CT_COMMA)
|| pc->Is(CT_NOT)
|| pc->Is(CT_INV)
|| pc->Is(CT_COLON)
|| pc->Is(CT_QUESTION))
{
frm.SetExprCount(0);
LOG_FMT(LSTMT, "%s(%d): orig line is %zu, orig col is %zu, reset expr on '%s'\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text());
}
} // parse_cleanup
static bool check_complex_statements(ParsingFrame &frm, Chunk *pc, const BraceState &braceState)
{
LOG_FUNC_ENTRY();
E_BraceStage atest = frm.top().GetStage();
LOG_FMT(LBCSPOP, "%s(%d): atest is %s\n",
__func__, __LINE__, get_brace_stage_name(atest));
// Turn an optional parenthesis into either a real parenthesis or a brace
if (frm.top().GetStage() == E_BraceStage::OP_PAREN1)
{
frm.top().SetStage(pc->IsNot(CT_PAREN_OPEN)
? E_BraceStage::BRACE2
: E_BraceStage::PAREN1);
LOG_FMT(LBCSPOP, "%s(%d): frm.top().stage is now %s\n",
__func__, __LINE__, get_brace_stage_name(frm.top().GetStage()));
}
// Check for CT_ELSE after CT_IF
while (frm.top().GetStage() == E_BraceStage::ELSE)
{
if (pc->Is(CT_ELSE))
{
// Replace CT_IF with CT_ELSE on the stack & we are done
frm.top().SetOpenToken(CT_ELSE);
frm.top().SetStage(E_BraceStage::ELSEIF);
print_stack(LBCSSWAP, "=Swap ", frm);
return(true);
}
// Remove the CT_IF and close the statement
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-IF-CCS ", frm);
if (close_statement(frm, pc, braceState))
{
return(true);
}
}
// Check for CT_IF after CT_ELSE
if (frm.top().GetStage() == E_BraceStage::ELSEIF)
{
log_rule_B("indent_else_if");
if ( pc->Is(CT_IF)
&& ( !options::indent_else_if()
|| !pc->GetPrevNc()->IsNewline()))
{
// Replace CT_ELSE with CT_IF
pc->SetType(CT_ELSEIF);
frm.top().SetOpenToken(CT_ELSEIF);
frm.top().SetStage(E_BraceStage::PAREN1);
return(true);
}
// Jump to the 'expecting brace' stage
frm.top().SetStage(E_BraceStage::BRACE2);
}
// Check for CT_CATCH or CT_FINALLY after CT_TRY or CT_CATCH
while (frm.top().GetStage() == E_BraceStage::CATCH)
{
if ( pc->Is(CT_CATCH)
|| pc->Is(CT_FINALLY))
{
// Replace CT_TRY with CT_CATCH or CT_FINALLY on the stack & we are done
frm.top().SetOpenToken(pc->GetType());
if (language_is_set(LANG_CS))
{
frm.top().SetStage((pc->Is(CT_CATCH)) ? E_BraceStage::CATCH_WHEN : E_BraceStage::BRACE2);
}
else
{
// historically this used OP_PAREN1; however, to my knowledge the expression after a catch clause
// is only optional for C# which has been handled above; therefore, this should now always expect
// a parenthetical expression after the catch keyword and brace after the finally keyword
frm.top().SetStage((pc->Is(CT_CATCH)) ? E_BraceStage::PAREN1 : E_BraceStage::BRACE2);
}
print_stack(LBCSSWAP, "=Swap ", frm);
return(true);
}
// Remove the CT_TRY and close the statement
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-TRY-CCS ", frm);
if (close_statement(frm, pc, braceState))
{
return(true);
}
}
// Check for optional parenthesis and optional CT_WHEN after CT_CATCH
if (frm.top().GetStage() == E_BraceStage::CATCH_WHEN)
{
if (pc->Is(CT_PAREN_OPEN)) // this is for the paren after "catch"
{
// Replace CT_PAREN_OPEN with CT_SPAREN_OPEN
pc->SetType(CT_SPAREN_OPEN);
frm.top().SetOpenToken(pc->GetType());
frm.top().SetStage(E_BraceStage::PAREN1);
return(false);
}
if (pc->Is(CT_WHEN))
{
frm.top().SetOpenToken(pc->GetType());
frm.top().SetStage(E_BraceStage::OP_PAREN1);
return(true);
}
if (pc->Is(CT_BRACE_OPEN))
{
frm.top().SetStage(E_BraceStage::BRACE2);
return(false);
}
}
// Check for CT_WHILE after the CT_DO
if (frm.top().GetStage() == E_BraceStage::WHILE)
{
if (pc->Is(CT_WHILE))
{
pc->SetType(CT_WHILE_OF_DO);
frm.top().SetOpenToken(CT_WHILE_OF_DO);
frm.top().SetStage(E_BraceStage::WOD_PAREN);
return(true);
}
LOG_FMT(LWARN, "%s(%d): %s, orig line is %zu, Error: Expected 'while', got '%s'\n",
__func__, __LINE__, cpd.filename.c_str(), pc->GetOrigLine(),
pc->Text());
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-Error ", frm);
exit(EX_SOFTWARE);
}
// Insert a CT_VBRACE_OPEN, if needed
// but not in a preprocessor
atest = frm.top().GetStage();
if ( pc->IsNot(CT_BRACE_OPEN)
&& !pc->TestFlags(PCF_IN_PREPROC)
&& ( (frm.top().GetStage() == E_BraceStage::BRACE2)
|| (frm.top().GetStage() == E_BraceStage::BRACE_DO)))
{
log_rule_B("indent_using_block");
if ( language_is_set(LANG_CS)
&& pc->Is(CT_USING_STMT)
&& (!options::indent_using_block()))
{
// don't indent the using block
}
else
{
const E_Token parentType = frm.top().GetOpenToken();
Chunk *vbrace = insert_vbrace_open_before(pc, frm);
vbrace->SetParentType(parentType);
frm.SetParenLevel(frm.GetParenLevel() + 1);
frm.SetBraceLevel(frm.GetBraceLevel() + 1);
LOG_FMT(LBCSPOP, "%s(%d): frame brace level increased to %zu\n",
__func__, __LINE__, frm.GetBraceLevel());
log_pcf_flags(LBCSPOP, pc->GetFlags());
frm.push(vbrace, __func__, __LINE__, E_BraceStage::NONE);
// "+VBrace");
frm.top().SetParent(parentType);
// update the level of pc
pc->SetLevel(frm.GetParenLevel());
pc->SetBraceLevel(frm.GetBraceLevel());
// Mark as a start of a statement
frm.SetStmtCount(0);
frm.SetExprCount(0);
LOG_FMT(LTOK, "%s(%d): frame statement count is %zu, expression count is %zu\n",
__func__, __LINE__, frm.GetStmtCount(), frm.GetExprCount());
pc->SetFlagBits(PCF_STMT_START | PCF_EXPR_START);
frm.SetStmtCount(1);
frm.SetExprCount(1);
LOG_FMT(LSTMT, "%s(%d): orig line is %zu, 2.marked '%s' as stmt start\n",
__func__, __LINE__, pc->GetOrigLine(), pc->Text());
}
}
// Check for "constexpr" after CT_IF or CT_ELSEIF
if ( frm.top().GetStage() == E_BraceStage::PAREN1
&& ( frm.top().GetOpenToken() == CT_IF
|| frm.top().GetOpenToken() == CT_ELSEIF)
&& pc->Is(CT_CONSTEXPR))
{
return(false);
}
// Verify open parenthesis in complex statement
if ( pc->IsNot(CT_PAREN_OPEN)
&& ( (frm.top().GetStage() == E_BraceStage::PAREN1)
|| (frm.top().GetStage() == E_BraceStage::WOD_PAREN)))
{
LOG_FMT(LWARN, "%s(%d): %s, orig line is %zu, Error: Expected '(', got '%s' for '%s'\n",
__func__, __LINE__, cpd.filename.c_str(), pc->GetOrigLine(), pc->Text(),
get_token_name(frm.top().GetOpenToken()));
// Throw out the complex statement
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-Error ", frm);
exit(EX_SOFTWARE);
}
return(false);
} // check_complex_statements
static bool handle_complex_close(ParsingFrame &frm, Chunk *pc, const BraceState &braceState)
{
LOG_FUNC_ENTRY();
if (frm.top().GetStage() == E_BraceStage::PAREN1)
{
if (pc->GetNext()->GetType() == CT_WHEN)
{
frm.top().SetOpenToken(pc->GetType());
frm.top().SetStage(E_BraceStage::CATCH_WHEN);
return(true);
}
// PAREN1 always => BRACE2
frm.top().SetStage(E_BraceStage::BRACE2);
}
else if (frm.top().GetStage() == E_BraceStage::BRACE2)
{
// BRACE2: IF => ELSE, anything else => close
if ( (frm.top().GetOpenToken() == CT_IF)
|| (frm.top().GetOpenToken() == CT_ELSEIF))
{
frm.top().SetStage(E_BraceStage::ELSE);
// If the next chunk isn't CT_ELSE, close the statement
Chunk *next = pc->GetNextNcNnl();
if ( next->IsNullChunk()
|| next->IsNot(CT_ELSE))
{
LOG_FMT(LBCSPOP, "%s(%d): no CT_ELSE, pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-IF-HCS ", frm);
return(close_statement(frm, pc, braceState));
}
}
else if ( (frm.top().GetOpenToken() == CT_TRY)
|| (frm.top().GetOpenToken() == CT_CATCH))
{
frm.top().SetStage(E_BraceStage::CATCH);
// If the next chunk isn't CT_CATCH or CT_FINALLY, close the statement
Chunk *next = pc->GetNextNcNnl();
if ( next->IsNot(CT_CATCH)
&& next->IsNot(CT_FINALLY))
{
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-TRY-HCS ", frm);
return(close_statement(frm, pc, braceState));
}
}
else
{
LOG_FMT(LNOTE, "%s(%d): close_statement on %s E_BraceStage::BRACE2\n",
__func__, __LINE__, get_token_name(frm.top().GetOpenToken()));
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-HCC B2 ", frm);
return(close_statement(frm, pc, braceState));
}
}
else if (frm.top().GetStage() == E_BraceStage::BRACE_DO)
{
frm.top().SetStage(E_BraceStage::WHILE);
}
else if (frm.top().GetStage() == E_BraceStage::WOD_PAREN)
{
LOG_FMT(LNOTE, "%s(%d): close_statement on %s E_BraceStage::WOD_PAREN\n",
__func__, __LINE__, get_token_name(frm.top().GetOpenToken()));
frm.top().SetStage(E_BraceStage::WOD_SEMI);
print_stack(LBCSPOP, "-HCC WoDP ", frm);
}
else if (frm.top().GetStage() == E_BraceStage::WOD_SEMI)
{
LOG_FMT(LNOTE, "%s(%d): close_statement on %s E_BraceStage::WOD_SEMI\n",
__func__, __LINE__, get_token_name(frm.top().GetOpenToken()));
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
print_stack(LBCSPOP, "-HCC WoDS ", frm);
return(close_statement(frm, pc, braceState));
}
else
{
// PROBLEM
LOG_FMT(LWARN, "%s(%d): %s:%zu Error: TOS.type='%s' TOS.stage=%u\n",
__func__, __LINE__, cpd.filename.c_str(), pc->GetOrigLine(),
get_token_name(frm.top().GetOpenToken()),
(unsigned int)frm.top().GetStage());
exit(EX_SOFTWARE);
}
return(false);
} // handle_complex_close
static void mark_namespace(Chunk *pns)
{
LOG_FUNC_ENTRY();
// Issue #1813
Chunk *br_close;
bool is_using = false;
Chunk *pc = pns->GetPrevNcNnl();
if (pc->Is(CT_USING))
{
is_using = true;
pns->SetParentType(CT_USING);
}
pc = pns->GetNextNcNnl();
while (pc->IsNotNullChunk())
{
pc->SetParentType(CT_NAMESPACE);
if (pc->IsNot(CT_BRACE_OPEN))
{
if (pc->Is(CT_SEMICOLON))
{
if (is_using)
{
pc->SetParentType(CT_USING);
}
return;
}
pc = pc->GetNextNcNnl();
continue;
}
log_rule_B("indent_namespace_limit");
if ( (options::indent_namespace_limit() > 0)
&& ((br_close = pc->GetClosingParen())->IsNotNullChunk()))
{
// br_close->GetOrigLine() is always >= pc->GetOrigLine();
size_t numberOfLines = br_close->GetOrigLine() - pc->GetOrigLine() - 1; // Issue #2345
LOG_FMT(LTOK, "%s(%d): br_close orig line is %zu, pc orig line is %zu\n",
__func__, __LINE__, br_close->GetOrigLine(), pc->GetOrigLine());
LOG_FMT(LTOK, "%s(%d): numberOfLines is %zu, indent_namespace_limit() is %d\n",
__func__, __LINE__, numberOfLines, options::indent_namespace_limit());
log_rule_B("indent_namespace_limit");
if (numberOfLines > options::indent_namespace_limit())
{
LOG_FMT(LTOK, "%s(%d): PCF_LONG_BLOCK is set\n", __func__, __LINE__);
pc->SetFlagBits(PCF_LONG_BLOCK);
br_close->SetFlagBits(PCF_LONG_BLOCK);
}
}
flag_parens(pc, PCF_IN_NAMESPACE, CT_NONE, CT_NAMESPACE, false);
return;
}
} // mark_namespace
static Chunk *insert_vbrace(Chunk *pc, bool after, const ParsingFrame &frm)
{
LOG_FUNC_ENTRY();
Chunk chunk;
chunk.SetParentType(frm.top().GetOpenToken());
chunk.SetOrigLine(pc->GetOrigLine());
chunk.SetLevel(frm.GetParenLevel());
chunk.SetPpLevel(frm.GetPpLevel());
chunk.SetBraceLevel(frm.GetBraceLevel());
chunk.SetFlags(pc->GetFlags() & PCF_COPY_FLAGS);
chunk.Str() = "";
if (after)
{
chunk.SetOrigCol(pc->GetOrigCol());
chunk.SetType(CT_VBRACE_CLOSE);
return(chunk.CopyAndAddAfter(pc));
}
Chunk *ref = pc->GetPrev();
if (ref->IsNullChunk())
{
return(Chunk::NullChunkPtr);
}
if (!ref->TestFlags(PCF_IN_PREPROC))
{
chunk.ResetFlagBits(PCF_IN_PREPROC);
}
bool ref_is_comment = ref->IsComment(); // Issue #3351
while (ref->IsCommentOrNewline())
{
ref->SetLevel(ref->GetLevel() + 1);
ref->SetBraceLevel(ref->GetBraceLevel() + 1);
ref = ref->GetPrev();
}
if (ref->IsNullChunk())
{
return(Chunk::NullChunkPtr);
}
// Don't back into a preprocessor
if ( !pc->TestFlags(PCF_IN_PREPROC)
&& ref->TestFlags(PCF_IN_PREPROC))
{
if (ref->Is(CT_PREPROC_BODY))
{
while ( ref->IsNotNullChunk()
&& ref->TestFlags(PCF_IN_PREPROC))
{
ref = ref->GetPrev();
}
}
else
{
ref = ref->GetNext();
if (ref->Is(CT_COMMENT)) // Issue #3034
{
ref = ref->GetNextNc();
}
}
}
if (ref_is_comment) // Issue #3351
{
ref = ref->GetNext();
}
if (ref->IsNullChunk())
{
return(Chunk::NullChunkPtr);
}
chunk.SetOrigLine(ref->GetOrigLine());
chunk.SetOrigCol(ref->GetOrigCol());
chunk.SetColumn(ref->GetColumn() + ref->Len() + 1);
chunk.SetPpLevel(ref->GetPpLevel()); // Issue #3055
chunk.SetType(CT_VBRACE_OPEN);
return(chunk.CopyAndAddAfter(ref));
} // insert_vbrace
bool close_statement(ParsingFrame &frm, Chunk *pc, const BraceState &braceState)
{
LOG_FUNC_ENTRY();
if (pc->IsNullChunk())
{
throw invalid_argument(string(__func__) + ":" + to_string(__LINE__)
+ "args cannot be null chunk");
}
LOG_FMT(LTOK, "%s(%d): orig line is %zu, type is %s, '%s' type is %s, stage is %u\n",
__func__, __LINE__, pc->GetOrigLine(),
get_token_name(pc->GetType()), pc->Text(),
get_token_name(frm.top().GetOpenToken()),
(unsigned int)frm.top().GetStage());
if (braceState.consumed)
{
frm.SetStmtCount(0);
frm.SetExprCount(0);
LOG_FMT(LSTMT, "%s(%d): orig line is %zu> reset2 stmt on '%s'\n",
__func__, __LINE__, pc->GetOrigLine(), pc->Text());
}
/*
* Insert a CT_VBRACE_CLOSE, if needed:
* If we are in a virtual brace and we are not ON a CT_VBRACE_CLOSE add one
*/
Chunk *vbc = pc;
if (frm.top().GetOpenToken() == CT_VBRACE_OPEN)
{
// If the current token has already been consumed, then add after it
if (braceState.consumed)
{
insert_vbrace_close_after(pc, frm);
}
else
{
// otherwise, add before it and consume the vbrace
vbc = pc->GetPrevNcNnl();
frm.SetParenLevel(frm.GetParenLevel() - 1);
frm.SetBraceLevel(frm.GetBraceLevel() - 1);
vbc = insert_vbrace_close_after(vbc, frm);
vbc->SetParentType(frm.top().GetParent());
LOG_FMT(LBCSPOP, "%s(%d): frame brace level decreased to %zu\n",
__func__, __LINE__, frm.GetBraceLevel());
log_pcf_flags(LBCSPOP, pc->GetFlags());
LOG_FMT(LBCSPOP, "%s(%d): pc orig line is %zu, orig col is %zu, Text() is '%s', type is %s\n",
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), pc->Text(), get_token_name(pc->GetType()));
frm.pop(__func__, __LINE__, pc);
// Update the token level
pc->SetLevel(frm.GetParenLevel());
pc->SetBraceLevel(frm.GetBraceLevel());
print_stack(LBCSPOP, "-CS VB ", frm);
// And repeat the close
close_statement(frm, pc, braceState);
return(true);
}
}
// See if we are done with a complex statement
if (frm.top().GetStage() != E_BraceStage::NONE)
{
if (handle_complex_close(frm, vbc, braceState))
{
return(true);
}
}
return(false);
} // close_statement