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