summaryrefslogtreecommitdiffstats
path: root/debian/uncrustify-trinity/uncrustify-trinity-0.76.0/src/EnumStructUnionParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.76.0/src/EnumStructUnionParser.cpp')
-rw-r--r--debian/uncrustify-trinity/uncrustify-trinity-0.76.0/src/EnumStructUnionParser.cpp2968
1 files changed, 2968 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.76.0/src/EnumStructUnionParser.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.76.0/src/EnumStructUnionParser.cpp
new file mode 100644
index 00000000..d1c7bc67
--- /dev/null
+++ b/debian/uncrustify-trinity/uncrustify-trinity-0.76.0/src/EnumStructUnionParser.cpp
@@ -0,0 +1,2968 @@
+/**
+ * @file EnumStructUnionParser.cpp
+ *
+ * @author
+ * @license GPL v2+
+ */
+
+#include "EnumStructUnionParser.h"
+
+#include "combine_fix_mark.h"
+#include "combine_skip.h"
+#include "combine_tools.h"
+#include "flag_parens.h"
+#include "lang_pawn.h"
+
+
+/**
+ * Extern declarations
+ */
+extern const char *get_token_name(E_Token);
+
+
+/**
+ * Forward declarations
+ */
+static std::pair<Chunk *, Chunk *> match_variable_end(Chunk *, std::size_t);
+static std::pair<Chunk *, Chunk *> match_variable_start(Chunk *, std::size_t);
+static Chunk *skip_scope_resolution_and_nested_name_specifiers(Chunk *);
+static Chunk *skip_scope_resolution_and_nested_name_specifiers_rev(Chunk *);
+
+
+/**
+ * Returns true if two adjacent chunks potentially match a pattern consistent
+ * with that of a qualified identifier
+ */
+static bool adj_tokens_match_qualified_identifier_pattern(Chunk *prev, Chunk *next)
+{
+ LOG_FUNC_ENTRY();
+
+ if ( prev != nullptr
+ && next != nullptr)
+ {
+ auto prev_token_type = prev->GetType();
+ auto next_token_type = next->GetType();
+
+ switch (prev_token_type)
+ {
+ case CT_ANGLE_CLOSE:
+ /**
+ * assuming the previous token is possibly the closing angle of a
+ * templated type, the next token may be a scope resolution operator ("::")
+ */
+ return(next_token_type == CT_DC_MEMBER);
+
+ case CT_ANGLE_OPEN:
+ /**
+ * assuming the previous token is possibly the opening angle of a
+ * templated type, just check to see if there's a matching closing
+ * angle
+ */
+ return(prev->GetClosingParen(E_Scope::PREPROC)->IsNotNullChunk());
+
+ case CT_DC_MEMBER:
+ /**
+ * if the previous token is a double colon ("::"), it is likely part
+ * of a chain of scope-resolution qualifications preceding a word or
+ * type
+ */
+ return( next_token_type == CT_TYPE
+ || next_token_type == CT_WORD);
+
+ case CT_TYPE:
+ case CT_WORD:
+ /**
+ * if the previous token is an identifier, the next token may be
+ * one of the following:
+ * - an opening angle, which may indicate a templated type as part of a
+ * scope resolution preceding the actual variable identifier
+ * - a double colon ("::")
+ */
+ return( next_token_type == CT_ANGLE_OPEN
+ || next_token_type == CT_DC_MEMBER);
+
+ default:
+ // do nothing
+ break;
+ } // switch
+ }
+ return(false);
+} // adj_tokens_match_qualified_identifier_pattern
+
+
+/**
+ * Returns true if two adjacent chunks potentially match a pattern consistent
+ * with that of a variable definition
+ */
+static bool adj_tokens_match_var_def_pattern(Chunk *prev, Chunk *next)
+{
+ LOG_FUNC_ENTRY();
+
+ if ( prev != nullptr
+ && next != nullptr)
+ {
+ auto prev_token_type = prev->GetType();
+ auto next_token_type = next->GetType();
+
+ switch (prev_token_type)
+ {
+ case CT_ANGLE_CLOSE:
+ /**
+ * assuming the previous token is possibly the closing angle of a
+ * templated type, the next token may be one of the following:
+ * - a pointer symbol ('*', '^')
+ * - a double colon ("::")
+ * - a reference symbol ('&')
+ * - a qualifier (const, etc.)
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_DC_MEMBER
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD);
+
+
+ case CT_ANGLE_OPEN:
+ /**
+ * assuming the previous token is possibly the opening angle of a
+ * templated type, just check to see if there's a matching closing
+ * angle
+ */
+ return(prev->GetClosingParen(E_Scope::PREPROC)->IsNotNullChunk());
+
+ case CT_BRACE_CLOSE:
+ /**
+ * assuming the previous token is possibly the closing brace of a
+ * class/enum/struct/union definition, one or more inline variable
+ * definitions may follow; in that case, the next token may be one of
+ * the following:
+ * - a pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - a qualifier (const, etc.)
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD);
+
+ case CT_BRACE_OPEN:
+ /**
+ * if the previous token is an opening brace, it may indicate the
+ * start of a braced initializer list - skip ahead to find a matching
+ * closing brace
+ */
+ return(prev->GetClosingParen(E_Scope::PREPROC)->IsNotNullChunk());
+
+ case CT_BYREF:
+ /**
+ * if the previous token is a reference symbol ('&'), the next token
+ * may be an identifier
+ */
+ return(next_token_type == CT_WORD);
+
+ case CT_CARET:
+ /**
+ * if the previous token is a managed C++/CLI pointer symbol ('^'),
+ * the next token may be one of the following:
+ * - a pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - a qualifier (const, etc.)
+ * - an identifier
+ */
+ return( language_is_set(LANG_CPP)
+ && ( next->IsPointerOrReference()
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD));
+
+ case CT_COMMA:
+ /**
+ * if the previous token is a comma, this may indicate a variable
+ * declaration trailing a prior declaration; in that case, the next
+ * token may be one of the following:
+ * - a pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_WORD);
+
+ case CT_DC_MEMBER:
+ /**
+ * if the previous token is a double colon ("::"), it is likely part
+ * of a chain of scope-resolution qualifications preceding a word or
+ * type
+ */
+ return( next_token_type == CT_TYPE
+ || next_token_type == CT_WORD);
+
+ case CT_PAREN_OPEN:
+ /**
+ * if the previous token is an opening paren, it may indicate the
+ * start of a constructor call parameter list - skip ahead to find a
+ * matching closing paren
+ */
+ next = prev->GetClosingParen(E_Scope::PREPROC);
+
+ if (next->IsNotNullChunk())
+ {
+ next_token_type = next->GetType();
+ }
+ return(next_token_type == CT_PAREN_CLOSE);
+
+ case CT_PTR_TYPE:
+ /**
+ * if the previous token is a pointer type, ('*', '^'), the next token
+ * may be one of the following:
+ * - another pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - a qualifier (const, etc.)
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD);
+
+ case CT_QUALIFIER:
+ /**
+ * if the previous token is a qualifier (const, etc.), the next token
+ * may be one of the following:
+ * - a pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - another qualifier
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD);
+
+ case CT_SQUARE_CLOSE:
+ /**
+ * if the previous token is a closing bracket, the next token may be
+ * an assignment following an array variable declaration
+ */
+ return(next_token_type == CT_ASSIGN);
+
+ case CT_SQUARE_OPEN:
+ /**
+ * if the previous token is an opening bracket, it may indicate an
+ * array declaration - skip ahead to find a matching closing bracket
+ */
+ return(prev->GetClosingParen(E_Scope::PREPROC)->IsNotNullChunk());
+
+ case CT_STAR:
+ /**
+ * if the previous token is a pointer symbol, ('*'), the next token
+ * may be one of the following:
+ * - another pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - a qualifier (const, etc.)
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD);
+
+ case CT_TSQUARE:
+ /**
+ * if the previous token is a set of brackets, the next token may be
+ * an assignment following an array variable declaration
+ */
+ return(next_token_type == CT_ASSIGN);
+
+ case CT_TYPE:
+ /**
+ * if the previous token is marked as a type, the next token may be
+ * one of the following:
+ * - a pointer symbol ('*', '^')
+ * - a reference symbol ('&')
+ * - an opening angle, which may indicate a templated type as part of a
+ * scope resolution preceding the actual variable identifier
+ * - a double colon ("::")
+ * - a qualifier (const, etc.)
+ * - an identifier
+ */
+ return( next->IsPointerOrReference()
+ || next_token_type == CT_ANGLE_OPEN
+ || next_token_type == CT_DC_MEMBER
+ || next_token_type == CT_QUALIFIER
+ || next_token_type == CT_WORD);
+
+ case CT_WORD:
+ /**
+ * if the previous token is an identifier, the next token may be one
+ * of the following:
+ * - an assignment symbol ('=')
+ * - an opening angle, which may indicate a templated type as part of a
+ * scope resolution preceding the actual variable identifier
+ * - an opening brace, which may indicate a braced-initializer list
+ * - a double colon ("::")
+ * - an opening paren, which may indicate a constructor call parameter
+ * list
+ * - an opening square bracket, which may indicate an array variable
+ * - an set of empty square brackets, which also may indicate an array
+ * variable
+ */
+ return( next_token_type == CT_ANGLE_OPEN
+ || next_token_type == CT_ASSIGN
+ || next_token_type == CT_BRACE_OPEN
+ || next_token_type == CT_DC_MEMBER
+ || next_token_type == CT_PAREN_OPEN
+ || next_token_type == CT_SQUARE_OPEN
+ || next_token_type == CT_TSQUARE);
+
+ default:
+ // do nothing
+ break;
+ } // switch
+ }
+ return(false);
+} // adj_tokens_match_var_def_pattern
+
+
+/**
+ * Returns true if the first chunk occurs AFTER the second chunk in the argument
+ * list
+ * @param pc points to the first chunk
+ * @param after points to the second chunk
+ * @param test_equal if true, returns true when both chunks refer to the same chunk
+ */
+static bool chunk_is_after(Chunk *pc, Chunk *after, bool test_equal = true)
+{
+ LOG_FUNC_ENTRY();
+
+ if ( pc != nullptr
+ && pc->IsNotNullChunk())
+ {
+ if ( test_equal
+ && pc == after)
+ {
+ return(true);
+ }
+ else if (after != nullptr)
+ {
+ auto pc_column = pc->GetOrigCol();
+ auto pc_line = pc->GetOrigLine();
+ auto after_column = after->GetOrigCol();
+ auto after_line = after->GetOrigLine();
+
+ return( pc_line > after_line
+ || ( pc_line == after_line
+ && pc_column > after_column));
+ }
+ }
+ return(false);
+} // chunk_is_after
+
+
+/**
+ * Returns true if the first chunk occurs BEFORE the second chunk in the argument
+ * list
+ * @param pc points to the first chunk
+ * @param before points to the second chunk
+ * @param test_equal if true, returns true when both chunks refer to the same chunk
+ */
+static bool chunk_is_before(Chunk *pc, Chunk *before, bool test_equal = true)
+{
+ LOG_FUNC_ENTRY();
+
+ if ( pc != nullptr
+ && pc->IsNotNullChunk())
+ {
+ if ( test_equal
+ && pc == before)
+ {
+ return(true);
+ }
+ else if (before != nullptr)
+ {
+ auto pc_column = pc->GetOrigCol();
+ auto pc_line = pc->GetOrigLine();
+ auto before_column = before->GetOrigCol();
+ auto before_line = before->GetOrigLine();
+
+ return( pc_line < before_line
+ || ( pc_line == before_line
+ && pc_column < before_column));
+ }
+ }
+ return(false);
+} // chunk_is_before
+
+
+/**
+ * Returns true if the first chunk occurs both AFTER and BEFORE
+ * the second and third chunks, respectively, in the argument list
+ * @param pc points to the first chunk
+ * @param after points to the second chunk
+ * @param before points to the third chunk
+ * @param test_equal if true, returns true when the first chunk tests equal to
+ * either the second or third chunk
+ */
+static bool chunk_is_between(Chunk *pc, Chunk *after, Chunk *before, bool test_equal = true)
+{
+ LOG_FUNC_ENTRY();
+
+ return( chunk_is_before(pc, before, test_equal)
+ && chunk_is_after(pc, after, test_equal));
+} // chunk_is_between
+
+
+/**
+ * Returns true if the chunk under test is a reference to a macro defined elsewhere in
+ * the source file currently being processed. Note that a macro may be defined in
+ * another source or header file, for which this function does not currently account
+ */
+static bool chunk_is_macro_reference(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *next = Chunk::GetHead();
+
+ if ( ( language_is_set(LANG_CPP)
+ || language_is_set(LANG_C))
+ && pc->Is(CT_WORD)
+ && !pc->TestFlags(PCF_IN_PREPROC))
+ {
+ while (next->IsNotNullChunk())
+ {
+ if ( next->TestFlags(PCF_IN_PREPROC)
+ && std::strcmp(pc->GetStr().c_str(), next->GetStr().c_str()) == 0)
+ {
+ return(true);
+ }
+ next = next->GetNextType(CT_MACRO);
+ }
+ }
+ return(false);
+} // chunk_is_macro_reference
+
+
+bool Chunk::IsPointerReferenceOrQualifier() const
+{
+ LOG_FUNC_ENTRY();
+
+ return( IsPointerOrReference()
+ || ( Is(CT_QUALIFIER)
+ && !IsCppInheritanceAccessSpecifier()));
+}
+
+
+/**
+ * This function attempts to match the starting and ending chunks of a qualified
+ * identifier, which consists of one or more scope resolution operator(s) and
+ * zero or more nested name specifiers
+ * specifiers
+ * @param pc the starting chunk
+ * @return an std::pair, where the first chunk indicates the starting chunk of the
+ * match and second indicates the ending chunk. Upon finding a successful
+ * match, the starting chunk may consist of an identifier or a scope
+ * resolution operator, while the ending chunk may consist of identifier
+ * or the closing angle bracket of a template. If no match is found, a
+ * pair of null chunks is returned
+ */
+static std::pair<Chunk *, Chunk *> match_qualified_identifier(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ auto *end = skip_scope_resolution_and_nested_name_specifiers(pc);
+ auto *start = skip_scope_resolution_and_nested_name_specifiers_rev(pc);
+
+ if ( end != nullptr
+ && start != nullptr)
+ {
+ auto *double_colon = start->GetNextType(CT_DC_MEMBER);
+
+ if ( double_colon->IsNotNullChunk()
+ && chunk_is_between(double_colon, start, end))
+ {
+ return(std::make_pair(start, end));
+ }
+ }
+ return(std::make_pair(nullptr, nullptr));
+} // match_qualified_identifier
+
+
+/**
+ * Starting from the input chunk, this function attempts to match a variable
+ * declaration/definition in both the forward and reverse directions; each pair of
+ * consecutive chunks is tested to determine if a potential match is satisfied.
+ * @param pc the starting chunk
+ * @param level the brace level
+ * @return upon successful match, function returns an std::tuple, where the
+ * first chunk indicates the starting chunk, the second chunk indicates
+ * the identifier name, and the third chunk indicates the end associated
+ * with the variable declaration/definition
+ */
+static std::tuple<Chunk *, Chunk *, Chunk *> match_variable(Chunk *pc, std::size_t level)
+{
+ LOG_FUNC_ENTRY();
+
+ auto identifier_end_pair = match_variable_end(pc, level);
+ auto start_identifier_pair = match_variable_start(pc, level);
+ auto *end = identifier_end_pair.second;
+ auto *identifier = identifier_end_pair.first != nullptr ? identifier_end_pair.first : start_identifier_pair.second;
+ auto *start = start_identifier_pair.first;
+
+ /**
+ * a forward search starting at the chunk under test will fail if two consecutive chunks marked as CT_WORD
+ * are encountered; in that case, it's likely that the preceding chunk indicates a type and the subsequent
+ * chunk indicates a variable declaration/definition
+ */
+
+ if ( identifier->IsNotNullChunk()
+ && start->IsNotNullChunk()
+ && ( end != nullptr
+ || identifier->GetPrevNcNnlNi()->Is(CT_WORD)))
+ {
+ return(std::make_tuple(start, identifier, end));
+ }
+ return(std::make_tuple(nullptr, nullptr, nullptr));
+} // match_variable
+
+
+/**
+ * Starting from the input chunk, this function attempts to match a variable in the
+ * forward direction, and tests each pair of consecutive chunks to determine if a
+ * potential variable declaration/definition match is satisfied. Secondly, the
+ * function attempts to identify the end chunk associated with the candidate variable
+ * match. For scalar variables (simply declared and not defined), both the end chunk
+ * and identifier chunk should be one in the same
+ * @param pc the starting chunk
+ * @param level the brace level
+ * @return an std::pair, where the first chunk indicates the identifier
+ * (if non-null) and the second chunk indicates the end associated with
+ * the variable declaration/definition; assuming a valid match, the first
+ * chunk may be null if the function is called with a starting chunk
+ * that occurs after the identifier
+ */
+static std::pair<Chunk *, Chunk *> match_variable_end(Chunk *pc, std::size_t level)
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *identifier = nullptr;
+
+ while ( pc != nullptr
+ && pc->IsNotNullChunk())
+ {
+ /**
+ * skip any right-hand side assignments
+ */
+ Chunk *rhs_exp_end = nullptr;
+
+ if (pc->Is(CT_ASSIGN))
+ {
+ /**
+ * store a pointer to the end chunk of the rhs expression;
+ * use it later to test against setting the identifier
+ */
+ rhs_exp_end = skip_to_expression_end(pc);
+ pc = rhs_exp_end;
+ }
+
+ /**
+ * skip current and preceding chunks if at a higher brace level
+ */
+ while ( pc != nullptr
+ && pc->IsNotNullChunk()
+ && pc->GetLevel() > level)
+ {
+ pc = pc->GetNextNcNnl();
+ }
+
+ /**
+ * skip to any following match for angle brackets, braces, parens,
+ * or square brackets
+ */
+ if ( pc->Is(CT_ANGLE_OPEN)
+ || pc->Is(CT_BRACE_OPEN)
+ || pc->IsParenOpen()
+ || pc->Is(CT_SQUARE_OPEN))
+ {
+ pc = pc->GetClosingParen(E_Scope::PREPROC);
+ }
+ /**
+ * call a separate function to validate adjacent tokens as potentially
+ * matching a variable declaration/definition
+ */
+
+ Chunk *next = pc->GetNextNcNnl();
+
+ if ( next->IsNot(CT_COMMA)
+ && next->IsNot(CT_FPAREN_CLOSE)
+ && !next->IsSemicolon()
+ && !adj_tokens_match_var_def_pattern(pc, next))
+ {
+ /**
+ * error, pattern is not consistent with a variable declaration/definition
+ */
+ break;
+ }
+
+ if ( pc->Is(CT_WORD)
+ && pc != rhs_exp_end)
+ {
+ /**
+ * we've encountered a candidate for the variable name
+ */
+
+ identifier = pc;
+ }
+
+ /**
+ * we're done searching if we've previously identified a variable name
+ * and then encounter a comma or semicolon
+ */
+ if ( next->Is(CT_COMMA)
+ || next->Is(CT_FPAREN_CLOSE)
+ || next->IsSemicolon())
+ {
+ return(std::make_pair(identifier, pc));
+ }
+ pc = next;
+ }
+ return(std::make_pair(nullptr, nullptr));
+} // match_variable_end
+
+
+/**
+ * Starting from the input chunk, this function attempts to match a variable in the
+ * reverse direction, and tests each pair of consecutive chunks to determine if a
+ * potential variable declaration/definition match is satisfied. Secondly, the
+ * function attempts to identify the starting chunk associated with the candidate
+ * variable match. The start and identifier chunks may refer to each other in cases
+ * where the identifier is not preceded by pointer or reference operators or qualifiers,
+ * etc.
+ * @param pc the starting chunk
+ * @param level the brace level
+ * @return an std::pair, where the first chunk indicates the starting chunk and
+ * the second chunk indicates the identifier associated with the variable
+ * match; assuming a valid match, the second chunk may be null if the
+ * function is called with a starting chunk that occurs before the
+ * identifier
+ */
+static std::pair<Chunk *, Chunk *> match_variable_start(Chunk *pc, std::size_t level)
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *identifier = Chunk::NullChunkPtr;
+
+ if (pc == nullptr)
+ {
+ pc = Chunk::NullChunkPtr;
+ }
+
+ while (pc->IsNotNullChunk())
+ {
+ /**
+ * skip any right-hand side assignments
+ */
+ Chunk *before_rhs_exp_start = skip_expression_rev(pc);
+ Chunk *prev = Chunk::NullChunkPtr;
+ Chunk *next = pc;
+
+ while ( chunk_is_after(next, before_rhs_exp_start)
+ && pc != prev)
+ {
+ next = prev;
+ prev = next->GetPrevNcNnlNi();
+
+ if (next->Is(CT_ASSIGN))
+ {
+ pc = prev;
+ }
+ }
+ /**
+ * skip current and preceding chunks if at a higher brace level
+ */
+
+ while ( pc->IsNotNullChunk()
+ && pc->GetLevel() > level)
+ {
+ pc = pc->GetPrevNcNnlNi();
+ }
+
+ /**
+ * skip to any preceding match for angle brackets, braces, parens,
+ * or square brackets
+ */
+ if ( pc->Is(CT_ANGLE_CLOSE)
+ || pc->Is(CT_BRACE_CLOSE)
+ || pc->IsParenClose()
+ || pc->Is(CT_SQUARE_CLOSE))
+ {
+ pc = pc->GetOpeningParen(E_Scope::PREPROC);
+ }
+ /**
+ * call a separate function to validate adjacent tokens as potentially
+ * matching a variable declaration/definition
+ */
+
+ prev = pc->GetPrevNcNnlNi();
+
+ if (!adj_tokens_match_var_def_pattern(prev, pc))
+ {
+ /**
+ * perhaps the previous chunk possibly indicates a type that yet to be
+ * marked? if not, then break
+ */
+ if ( prev->IsNot(CT_WORD)
+ || ( !pc->IsPointerOrReference()
+ && pc->IsNot(CT_WORD)))
+ {
+ /**
+ * error, pattern is not consistent with a variable declaration/definition
+ */
+
+ break;
+ }
+ }
+
+ if ( identifier->IsNullChunk()
+ && pc->Is(CT_WORD))
+ {
+ /**
+ * we've encountered a candidate for the variable name
+ */
+
+ identifier = pc;
+ }
+
+ /**
+ * we're done searching if we've previously identified a variable name
+ * and then encounter another identifier, or we encounter a closing
+ * brace (which would likely indicate an inline variable definition)
+ */
+ if ( prev->Is(CT_ANGLE_CLOSE)
+ || prev->Is(CT_BRACE_CLOSE)
+ || prev->Is(CT_COMMA)
+ || prev->Is(CT_TYPE)
+ || prev->Is(CT_WORD))
+ {
+ return(std::make_pair(pc, identifier));
+ }
+ pc = prev;
+ }
+ return(std::make_pair(Chunk::NullChunkPtr, Chunk::NullChunkPtr));
+} // match_variable_start
+
+
+/**
+ * Skip forward past any scope resolution operators and nested name specifiers and return
+ * just the qualified identifier name; while similar to the existing skip_dc_member()
+ * function, this function also takes into account templates that may comprise any
+ * nested name specifiers
+ */
+static Chunk *skip_scope_resolution_and_nested_name_specifiers(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ if ( ( pc != nullptr
+ && pc->TestFlags(PCF_IN_TEMPLATE))
+ || pc->Is(CT_DC_MEMBER)
+ || pc->Is(CT_TYPE)
+ || pc->Is(CT_WORD))
+ {
+ while (pc->IsNotNullChunk())
+ {
+ /**
+ * skip to any following match for angle brackets
+ */
+ if (pc->Is(CT_ANGLE_OPEN))
+ {
+ pc = pc->GetClosingParen(E_Scope::PREPROC);
+ }
+ Chunk *next = pc->GetNextNcNnl();
+
+ /**
+ * call a separate function to validate adjacent tokens as potentially
+ * matching a qualified identifier
+ */
+ if (!adj_tokens_match_qualified_identifier_pattern(pc, next))
+ {
+ break;
+ }
+ pc = next;
+ }
+ }
+ return(pc);
+} // skip_scope_resolution_and_nested_name_specifiers
+
+
+/**
+ * Skip in reverse to the beginning chunk of a qualified identifier; while similar to
+ * the existing skip_dc_member_rev() function, this function also takes into account
+ * templates that may comprise any nested name specifiers
+ */
+static Chunk *skip_scope_resolution_and_nested_name_specifiers_rev(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ if (pc == nullptr)
+ {
+ pc = Chunk::NullChunkPtr;
+ }
+
+ if ( ( pc->IsNotNullChunk()
+ && pc->TestFlags(PCF_IN_TEMPLATE))
+ || pc->Is(CT_DC_MEMBER)
+ || pc->Is(CT_TYPE)
+ || pc->Is(CT_WORD))
+ {
+ while (pc->IsNotNullChunk())
+ {
+ /**
+ * skip to any preceding match for angle brackets
+ */
+ if (pc->Is(CT_ANGLE_CLOSE))
+ {
+ pc = pc->GetOpeningParen(E_Scope::PREPROC);
+ }
+ Chunk *prev = pc->GetPrevNcNnlNi();
+
+ /**
+ * call a separate function to validate adjacent tokens as potentially
+ * matching a qualified identifier
+ */
+ if (!adj_tokens_match_qualified_identifier_pattern(prev, pc))
+ {
+ break;
+ }
+ pc = prev;
+ }
+ }
+ return(pc);
+} // skip_scope_resolution_and_nested_name_specifiers_rev
+
+
+EnumStructUnionParser::EnumStructUnionParser()
+ : m_end(nullptr)
+ , m_parse_error(false)
+ , m_start(nullptr)
+ , m_type(nullptr)
+{
+} // EnumStructUnionParser::EnumStructUnionParser
+
+
+EnumStructUnionParser::~EnumStructUnionParser()
+{
+} // EnumStructUnionParser::~EnumStructUnionParser
+
+
+void EnumStructUnionParser::analyze_identifiers()
+{
+ LOG_FUNC_ENTRY();
+
+ /**
+ * the enum (and variable declarations thereof) could be of
+ * the following forms:
+ *
+ * "enum type [: integral_type] { ... } [x, ...]"
+ * "enum type : integral_type"
+ * "enum type x, ..."
+ * "enum class type [: integral_type] { ... } [x, ...]"
+ * "enum class type [: integral_type]"
+ * "enum [: integral_type] { ... } x, ..."
+ */
+
+ /**
+ * the class/struct (and variable declarations thereof) could be of
+ * the following forms:
+ *
+ * "template<...> class/struct[<...>] [macros/attributes ...] type [: bases ...] { }"
+ * "template<...> class/struct[<...>] [macros/attributes ...] type"
+ * "class/struct [macros/attributes ...] type [: bases ...] { } [x, ...]"
+ * "class/struct [macros/attributes ...] type [x, ...]"
+ * "class/struct [macros/attributes ...] [: bases] { } x, ..."
+ */
+
+ Chunk *template_end = get_template_end();
+ auto *body_end = get_body_end();
+ auto *body_start = get_body_start();
+ T_PcfFlags flags = PCF_VAR_1ST_DEF;
+ auto *inheritance_start = get_inheritance_start();
+ Chunk *pc = body_end ? body_end : m_start;
+
+ /**
+ * first, try a simple approach to identify any associated type
+ */
+ if (try_pre_identify_type())
+ {
+ /**
+ * a type was identified, meaning a pair of braces, angle brackets, or
+ * a colon was found; if a colon was found, then there should be a
+ * balanced set of braces that follow; therefore, start the search for
+ * variable identifiers after the closing brace or close angle bracket
+ */
+
+ if (body_end != nullptr)
+ {
+ pc = body_end;
+ }
+ else if (template_end != nullptr)
+ {
+ pc = template_end;
+ }
+ }
+
+ if (pc == nullptr)
+ {
+ pc = Chunk::NullChunkPtr;
+ }
+
+ if (pc->GetNextNcNnl() == m_end)
+ {
+ /**
+ * we're likely at the end of a class/enum/struct/union body which lacks
+ * any trailing inline definitions
+ */
+
+ pc = m_end->GetNextNcNnl();
+ }
+
+ if ( type_identified()
+ || pc->IsClassEnumStructOrUnion()
+ || pc == m_end)
+ {
+ /**
+ * in case we're pointing at the end chunk, advance the chunk pointer
+ * by one more so that we don't perform a variable identifier search
+ * below
+ */
+ pc = pc->GetNextNcNnl();
+ }
+
+ if (body_end != nullptr)
+ {
+ /**
+ * a closing brace was found, so any identifiers trailing the closing
+ * brace are probably inline variable declarations following a
+ * class/enum/struct/union definition
+ */
+ flags |= PCF_VAR_INLINE;
+ }
+ else if (!type_identified())
+ {
+ /**
+ * skip any chain of one or more function-like macro calls,
+ * declspecs, and attributes
+ */
+
+ Chunk *tmp = pc;
+
+ do
+ {
+ pc = tmp;
+ tmp = skip_attribute_next(tmp);
+ tmp = skip_declspec_next(tmp);
+ } while (tmp != pc);
+ }
+ /**
+ * try to match some variable identifiers in the loop below
+ */
+
+ while (chunk_is_between(pc, m_start, m_end, false))
+ {
+ auto match = match_variable(pc, m_start->GetLevel());
+ auto *start = std::get<0>(match);
+ auto *identifier = std::get<1>(match);
+ auto *end = std::get<2>(match);
+
+ if ( start != nullptr
+ && identifier != nullptr)
+ {
+ if (end != nullptr)
+ {
+ mark_variable(identifier, flags);
+
+ if (flags & PCF_VAR_1ST)
+ {
+ flags &= ~PCF_VAR_1ST; // clear the first flag for the next items
+ }
+ }
+ }
+
+ if (end != nullptr)
+ {
+ pc = end;
+ }
+ pc = pc->GetNextNcNnl();
+
+ /**
+ * skip any right-hand side assignments
+ */
+ if (pc->Is(CT_ASSIGN))
+ {
+ pc = skip_to_expression_end(pc);
+ }
+
+ /**
+ * if we're sitting at a comma or semicolon, skip it
+ */
+ if ( pc->IsSemicolon()
+ || ( pc->Is(CT_COMMA)
+ && !pc->GetFlags().test_any(PCF_IN_FCN_DEF | PCF_IN_FCN_CALL | PCF_IN_TEMPLATE)
+ && !chunk_is_between(pc, inheritance_start, body_start)))
+ {
+ pc = pc->GetNextNcNnl();
+ }
+ }
+ /**
+ * if we still haven't identified a type, try doing so now that the
+ * variables, if any, have been marked
+ */
+ try_post_identify_type();
+
+ /**
+ * identify possible macros preceding the type name
+ */
+ try_post_identify_macro_calls();
+
+ if ( m_start->IsClassOrStruct()
+ && ( m_start->IsNot(CT_STRUCT)
+ || !language_is_set(LANG_C)))
+ {
+ /**
+ * if a type has been identified, mark any constructor matching constructor
+ * declarations/definitions
+ */
+ mark_constructors();
+ }
+
+ if (type_identified())
+ {
+ if (~flags & PCF_VAR_1ST)
+ {
+ /**
+ * PCF_VAR_1ST was cleared and a type was identified; therefore, set
+ * PCF_VAR_TYPE for the identified type
+ */
+ m_type->SetFlagBits(PCF_VAR_TYPE);
+ }
+ else if (~flags & PCF_VAR_INLINE)
+ {
+ /**
+ * if a type was identified but no braced-enclosed body was found and no
+ * identifiers were marked as variables, then we're likely we're likely
+ * dealing with a forward declaration
+ */
+ flag_series(m_start, m_type, PCF_INCOMPLETE);
+ }
+ }
+} // EnumStructUnionParser::analyze_identifiers
+
+
+bool EnumStructUnionParser::body_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto *body_end = get_body_end();
+ auto *body_start = get_body_start();
+
+ return( body_end != nullptr
+ && body_start != nullptr);
+} // EnumStructUnionParser::body_detected
+
+
+bool EnumStructUnionParser::comma_separated_values_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ return(!get_top_level_commas().empty());
+} // EnumStructUnionParser::comma_separated_values_detected
+
+
+bool EnumStructUnionParser::enum_base_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ return(m_chunk_map.find(CT_BIT_COLON) != m_chunk_map.cend());
+} // EnumStructUnionParser::enum_base_detected
+
+
+Chunk *EnumStructUnionParser::get_body_end() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_BRACE_CLOSE);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_body_end
+
+
+Chunk *EnumStructUnionParser::get_body_start() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_BRACE_OPEN);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_body_start
+
+
+Chunk *EnumStructUnionParser::get_enum_base_start() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_BIT_COLON);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_enum_base_start
+
+
+Chunk *EnumStructUnionParser::get_first_top_level_comma() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_COMMA);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_first_top_level_comma
+
+
+Chunk *EnumStructUnionParser::get_inheritance_end() const
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *brace_open = nullptr;
+ auto *inheritance_start = get_inheritance_start();
+
+ if (inheritance_start != nullptr)
+ {
+ brace_open = get_body_start();
+
+ if (brace_open == nullptr)
+ {
+ brace_open = inheritance_start->GetNextType(CT_BRACE_OPEN, m_start->GetLevel(), E_Scope::ALL);
+ }
+ }
+ return(brace_open);
+} // EnumStructUnionParser::get_inheritance_end
+
+
+Chunk *EnumStructUnionParser::get_inheritance_start() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_COLON);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_inheritance_start
+
+
+std::map<std::size_t, Chunk *> EnumStructUnionParser::get_question_operators() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_QUESTION);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second);
+ }
+ return(std::map<std::size_t, Chunk *>());
+} // EnumStructUnionParser::get_question_operators
+
+
+Chunk *EnumStructUnionParser::get_template_end() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_ANGLE_CLOSE);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_template_end
+
+
+Chunk *EnumStructUnionParser::get_template_start() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_ANGLE_OPEN);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_template_start
+
+
+std::map<std::size_t, Chunk *> EnumStructUnionParser::get_top_level_commas() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_COMMA);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second);
+ }
+ return(std::map<std::size_t, Chunk *>());
+} // EnumStructUnionParser::get_top_level_commas
+
+
+Chunk *EnumStructUnionParser::get_where_end() const
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *brace_open = nullptr;
+ auto *where_start = get_where_start();
+
+ if (where_start != nullptr)
+ {
+ brace_open = get_body_start();
+
+ if (brace_open == nullptr)
+ {
+ brace_open = where_start->GetNextType(CT_BRACE_OPEN, m_start->GetLevel(), E_Scope::ALL);
+ }
+ }
+ return(brace_open);
+} // EnumStructUnionParser::get_where_end
+
+
+Chunk *EnumStructUnionParser::get_where_start() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto &&it_token_chunk_map_pair = m_chunk_map.find(CT_WHERE);
+
+ if (it_token_chunk_map_pair != m_chunk_map.cend())
+ {
+ return(it_token_chunk_map_pair->second.at(0));
+ }
+ return(nullptr);
+} // EnumStructUnionParser::get_where_start
+
+
+bool EnumStructUnionParser::inheritance_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ return(m_chunk_map.find(CT_COLON) != m_chunk_map.cend());
+} // EnumStructUnionParser::inheritance_detected
+
+
+void EnumStructUnionParser::initialize(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ parse_error_detected(false);
+ m_chunk_map.clear();
+
+ m_start = pc;
+ m_type = nullptr;
+ pc = try_find_end_chunk(pc);
+
+ if (parse_error_detected())
+ {
+ return;
+ }
+ m_end = refine_end_chunk(pc);
+} // EnumStructUnionParser::initialize
+
+
+bool EnumStructUnionParser::is_potential_end_chunk(Chunk *pc) const
+{
+ LOG_FUNC_ENTRY();
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+
+ /**
+ * test for a semicolon or closing brace at the level of the starting chunk
+ */
+ if ( pc->IsNullChunk()
+ || parse_error_detected()
+ || ( ( pc->IsSemicolon()
+ || pc->Is(CT_BRACE_CLOSE))
+ && pc->GetLevel() == m_start->GetLevel()))
+ {
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ return(true);
+ }
+ /**
+ * check for the following:
+ * 1) did we encounter a closing paren, which may indicate the end of cast?
+ * 2) did we cross a preprocessor boundary?
+ * 3) did we cross the closing paren of a function signature?
+ */
+
+ auto const pc_in_funcdef = pc->GetFlags() & PCF_IN_FCN_DEF;
+ auto const pc_in_preproc = pc->GetFlags() & PCF_IN_PREPROC;
+ auto const start_in_funcdef = m_start->GetFlags() & PCF_IN_FCN_DEF;
+ auto const start_in_preproc = m_start->GetFlags() & PCF_IN_PREPROC;
+
+ /**
+ * the following may identify cases where we've reached the
+ * end of a cast terminated by a closing paren
+ */
+ if ( ( pc->IsParenClose() // Issue #3538
+ && pc->GetLevel() < m_start->GetLevel())
+ || (start_in_funcdef ^ pc_in_funcdef).test_any()
+ || (start_in_preproc ^ pc_in_preproc).test_any())
+ {
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ return(true);
+ }
+ /**
+ * check whether the current chunk's nest level is less than that
+ * of the starting chunk
+ */
+
+ std::size_t pc_template_nest = get_cpp_template_angle_nest_level(pc);
+ std::size_t start_template_nest = get_cpp_template_angle_nest_level(m_start);
+
+ if (start_template_nest > pc_template_nest)
+ {
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ return(true);
+ }
+ /**
+ * assuming the chunk is within a function call/definition, check the following:
+ * 1) chunk is a closing function paren at a lower level than the starting chunk
+ * 2) chunk is an assignment ('=') or comma at the level of the starting chunk
+ */
+
+ auto const pc_in_funccall = pc->GetFlags() & PCF_IN_FCN_CALL;
+ auto const start_in_funccall = m_start->GetFlags() & PCF_IN_FCN_CALL;
+
+ if ( ( pc_in_funccall.test_any()
+ && start_in_funccall.test_any()
+ && pc->Is(CT_COMMA)
+ && pc->GetLevel() == m_start->GetLevel())
+ || ( pc_in_funcdef.test_any()
+ && ( ( pc->Is(CT_FPAREN_CLOSE)
+ && pc->GetLevel() < m_start->GetLevel())
+ || ( ( pc->Is(CT_ASSIGN)
+ || pc->Is(CT_COMMA))
+ && pc->GetLevel() == m_start->GetLevel()))))
+ {
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ return(true);
+ }
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ return(false);
+} // EnumStructUnionParser::is_potential_end_chunk
+
+
+bool EnumStructUnionParser::is_within_conditional(Chunk *pc) const
+{
+ LOG_FUNC_ENTRY();
+
+ auto question_operators = get_question_operators();
+
+ if (!question_operators.empty())
+ {
+ auto &&it_token_chunk_pair = question_operators.cbegin();
+
+ while (it_token_chunk_pair != question_operators.cend())
+ {
+ auto *question = it_token_chunk_pair->second;
+ auto *end = skip_to_expression_end(question);
+ auto *start = skip_to_expression_start(question);
+
+ if (chunk_is_between(pc, start, end))
+ {
+ return(true);
+ }
+ ++it_token_chunk_pair;
+ }
+ }
+ return(false);
+} // EnumStructUnionParser::is_within_conditional
+
+
+bool EnumStructUnionParser::is_within_inheritance_list(Chunk *pc) const
+{
+ LOG_FUNC_ENTRY();
+
+ if ( pc != nullptr
+ && pc->TestFlags(PCF_IN_CLASS_BASE))
+ {
+ return(true);
+ }
+ auto *inheritance_end = get_inheritance_end();
+ auto *inheritance_start = get_inheritance_start();
+
+ if ( inheritance_end != nullptr
+ && inheritance_start != nullptr)
+ {
+ return(chunk_is_between(pc, inheritance_start, inheritance_end));
+ }
+ return(false);
+} // EnumStructUnionParser::is_within_inheritance_list
+
+
+bool EnumStructUnionParser::is_within_where_clause(Chunk *pc) const
+{
+ LOG_FUNC_ENTRY();
+
+ if ( pc != nullptr
+ && pc->TestFlags(PCF_IN_WHERE_SPEC))
+ {
+ return(true);
+ }
+ auto *where_end = get_where_end();
+ auto *where_start = get_where_start();
+
+ if ( where_end != nullptr
+ && where_start != nullptr)
+ {
+ return(chunk_is_between(pc, where_start, where_end));
+ }
+ return(false);
+} // EnumStructUnionParser::is_within_where_clause
+
+
+void EnumStructUnionParser::mark_base_classes(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ T_PcfFlags flags = PCF_VAR_1ST_DEF;
+
+ while ( pc != nullptr
+ && pc->IsNotNullChunk())
+ {
+ pc->SetFlagBits(PCF_IN_CLASS_BASE);
+ /**
+ * clear the PCF_VAR_TYPE flag for all chunks within the inheritance list
+ * TODO: this may not be necessary in the future once code outside this
+ * class is improved such that PCF_VAR_TYPE is not set for these chunks
+ */
+ pc->ResetFlagBits(PCF_VAR_TYPE);
+
+ Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC);
+
+ if (next->Is(CT_DC_MEMBER))
+ {
+ /**
+ * just in case it's a templated type
+ */
+ pc = skip_template_prev(pc);
+
+ if (pc->Is(CT_WORD))
+ {
+ /**
+ * TODO:
+ * To comply with conventions used elsewhere in the code, we're going
+ * to change chunks marked CT_WORD to CT_TYPE if followed by a scope-
+ * resolution operator; if a chunk marked CT_WORD is followed by a set
+ * of angle brackets, then it's obviously a templated type. However,
+ * in the absence of a pair trailing angle brackets, the chunk may be
+ * a namespace rather than a type. Need to revisit this!
+ */
+ pc->SetType(CT_TYPE);
+ }
+ }
+ else if ( ( next->Is(CT_BRACE_OPEN)
+ || ( next->Is(CT_COMMA)
+ && !is_within_where_clause(next)))
+ && next->GetLevel() == m_start->GetLevel())
+ {
+ /**
+ * just in case it's a templated type
+ */
+ pc = skip_template_prev(pc);
+
+ if (pc->Is(CT_WORD))
+ {
+ pc->SetFlagBits(flags);
+
+ if (flags & PCF_VAR_1ST)
+ {
+ flags &= ~PCF_VAR_1ST; // clear the first flag for the next items
+ }
+ }
+
+ if (next->Is(CT_BRACE_OPEN))
+ {
+ break;
+ }
+ }
+ pc = next;
+ }
+ pc->SetFlagBits(PCF_IN_CLASS_BASE);
+} // EnumStructUnionParser::mark_base_classes
+
+
+void EnumStructUnionParser::mark_braces(Chunk *brace_open)
+{
+ LOG_FUNC_ENTRY();
+
+ T_PcfFlags flags = PCF_NONE;
+
+ if (m_start->Is(CT_CLASS))
+ {
+ flags = PCF_IN_CLASS;
+ }
+ else if (m_start->IsEnum())
+ {
+ flags = PCF_IN_ENUM;
+ }
+ else if (m_start->Is(CT_STRUCT))
+ {
+ flags = PCF_IN_STRUCT;
+ }
+ /**
+ * TODO: why does flag_parens() flag the closing paren,
+ * but it doesn't flag the opening paren?
+ */
+
+ flag_parens(brace_open,
+ flags,
+ CT_NONE,
+ CT_NONE,
+ false);
+
+ if (m_start->IsClassStructOrUnion())
+ {
+ mark_struct_union_body(brace_open);
+
+ auto *inheritance_start = get_inheritance_start();
+
+ if (inheritance_start != nullptr)
+ {
+ /**
+ * the class/struct/union is a derived class; mark the base
+ * classes between the colon/java "implements" keyword and the
+ * opening brace
+ */
+
+ mark_base_classes(inheritance_start);
+ }
+ }
+ brace_open->SetParentType(m_start->GetType());
+
+ auto *brace_close = brace_open->GetClosingParen(E_Scope::PREPROC);
+
+ if (brace_close->IsNotNullChunk())
+ {
+ brace_close->SetParentType(m_start->GetType());
+ }
+} // EnumStructUnionParser::mark_braces
+
+
+void EnumStructUnionParser::mark_class_colon(Chunk *colon)
+{
+ LOG_FUNC_ENTRY();
+
+ LOG_FMT(LFTOR,
+ "%s(%d): Class colon detected: orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ colon->GetOrigLine(),
+ colon->GetOrigCol());
+
+ colon->SetType(CT_CLASS_COLON);
+ colon->SetParentType(m_start->GetType());
+} // EnumStructUnionParser::mark_class_colon
+
+
+void EnumStructUnionParser::mark_conditional_colon(Chunk *colon)
+{
+ colon->SetType(CT_COND_COLON);
+} // EnumStructUnionParser::mark_conditional_colon
+
+
+void EnumStructUnionParser::mark_constructors()
+{
+ LOG_FUNC_ENTRY();
+
+ /**
+ * if a type was previously identified, then look for
+ * class/struct constructors in the body
+ */
+ if ( body_detected()
+ && type_identified()
+ && m_start->IsClassOrStruct())
+ {
+ LOG_FMT(LFTOR,
+ "%s(%d): orig line is %zu, orig col is %zu, start is '%s', parent type is %s\n",
+ __unqualified_func__,
+ __LINE__,
+ m_start->GetOrigLine(),
+ m_start->GetOrigCol(),
+ m_start->Text(),
+ get_token_name(m_start->GetParentType()));
+
+ log_pcf_flags(LFTOR, m_start->GetFlags());
+
+ /**
+ * get the name of the type
+ */
+ auto *body_end = get_body_end();
+ auto *body_start = get_body_start();
+ auto *name = m_type->Text();
+
+ LOG_FMT(LFTOR,
+ "%s(%d): Name of type is '%s'\n",
+ __unqualified_func__,
+ __LINE__,
+ name);
+ log_pcf_flags(LFTOR, m_type->GetFlags());
+
+ Chunk *next = Chunk::NullChunkPtr;
+ std::size_t level = m_type->GetBraceLevel() + 1;
+
+ for (auto *prev = body_start; next != body_end; prev = next)
+ {
+ prev->SetFlagBits(PCF_IN_CLASS);
+
+ next = skip_template_next(prev->GetNextNcNnl(E_Scope::PREPROC)); // Issue #3368
+
+ /**
+ * find a chunk within the class/struct body that
+ */
+ if ( prev->IsNotNullChunk()
+ && std::strcmp(prev->Text(), name) == 0
+ && prev->GetLevel() == level
+ && next->IsParenOpen())
+ {
+ prev->SetType(CT_FUNC_CLASS_DEF);
+
+ LOG_FMT(LFTOR,
+ "%s(%d): Constructor/destructor detected: '%s' at orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__,
+ __LINE__,
+ name,
+ prev->GetOrigLine(),
+ prev->GetOrigCol(),
+ get_token_name(prev->GetType()));
+
+ mark_cpp_constructor(prev);
+ }
+ }
+
+ next->SetFlagBits(PCF_IN_CLASS);
+ }
+} // EnumStructUnionParser::mark_constructor
+
+
+void EnumStructUnionParser::mark_enum_integral_type(Chunk *colon)
+{
+ LOG_FUNC_ENTRY();
+
+ colon->SetType(CT_BIT_COLON);
+ colon->SetParentType(m_start->GetType());
+
+ auto *body_start = get_body_start();
+ auto *pc = colon->GetNextNcNnl();
+
+ /**
+ * the chunk(s) between the colon and opening
+ * brace (if present) should specify the enum's
+ * integral type
+ */
+
+ while ( chunk_is_between(pc, m_start, m_end)
+ && pc != body_start
+ && pc->IsNot(CT_BRACE_OPEN)
+ && !pc->IsSemicolon())
+ {
+ /**
+ * clear the PCF_VAR_TYPE flag for all chunks within the enum integral base
+ * TODO: this may not be necessary in the future once code outside this
+ * class is improved such that PCF_VAR_TYPE is not set for these chunks
+ */
+ if (pc->IsNot(CT_DC_MEMBER)) // Issue #3198
+ {
+ pc->ResetFlagBits(PCF_VAR_TYPE);
+ pc->SetType(CT_TYPE);
+ pc->SetParentType(colon->GetType());
+ }
+ pc = pc->GetNextNcNnl();
+ }
+} // EnumStructUnionParser::mark_enum_integral_type
+
+
+void EnumStructUnionParser::mark_extracorporeal_lvalues()
+{
+ /**
+ * clear the PCF_LVALUE flag for all chunks outside the body definition,
+ * as this flag may have been set elsewhere by code outside this class
+ * TODO: the mark_lvalue() function needs some improvement so that the
+ * following isn't necessary
+ */
+ Chunk *next = m_start;
+ Chunk *prev = Chunk::NullChunkPtr;
+
+ /**
+ * if the class is a template, go the extra step and correct the
+ * erroneously marked chunks - as previously mentioned, this likely
+ * won't be necessary with improvements to the mark_lvalue() function
+ */
+ if (next->GetParentType() == CT_TEMPLATE)
+ {
+ while (true)
+ {
+ prev = next->GetPrevNcNnlNi();
+
+ if ( prev->IsNullChunk()
+ || ( !prev->TestFlags(PCF_IN_TEMPLATE)
+ && prev->IsNot(CT_TEMPLATE)))
+ {
+ break;
+ }
+ next = prev;
+ }
+ }
+ Chunk *body_end = get_body_end();
+ Chunk *body_start = get_body_start();
+
+ while (next != m_end)
+ {
+ if ( !chunk_is_between(next, body_start, body_end)
+ && next->TestFlags(PCF_LVALUE))
+ {
+ next->ResetFlagBits(PCF_LVALUE);
+ }
+ else if ( ( next->Is(CT_ASSIGN)
+ || next->Is(CT_BRACE_OPEN))
+ && prev->Is(CT_WORD)
+ && prev->GetFlags().test_any(PCF_VAR_DEF | PCF_VAR_1ST | PCF_VAR_INLINE))
+ {
+ prev->SetFlagBits(PCF_LVALUE);
+ }
+ prev = next;
+ next = next->GetNextNcNnl();
+ }
+} // EnumStructUnionParser::mark_extracorporeal_lavlues
+
+
+void EnumStructUnionParser::mark_nested_name_specifiers(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ auto start_end_pair = match_qualified_identifier(pc);
+ auto start = start_end_pair.first;
+ auto end = start_end_pair.second;
+
+ for (pc = start; chunk_is_between(pc, start, end); pc = pc->GetNextNcNnl())
+ {
+ if (pc->Is(CT_WORD))
+ {
+ /**
+ * if the next token is an opening angle, then we can safely
+ * mark the current identifier as a type
+ */
+ auto *next = pc->GetNextNcNnl();
+
+ if (next->Is(CT_ANGLE_OPEN))
+ {
+ /**
+ * the template may have already been previously marked elsewhere...
+ */
+ auto *angle_open = next;
+ auto *angle_close = angle_open->GetClosingParen(E_Scope::PREPROC);
+
+ if (angle_close->IsNullChunk())
+ {
+ // parse error
+ parse_error_detected(true);
+
+ // TODO: should this be just a warning or an error (with exit condition?)
+ LOG_FMT(LWARN,
+ "%s(%d): Unmatched '<' at orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ angle_open->GetOrigLine(),
+ angle_open->GetOrigCol());
+
+ break;
+ }
+ pc->SetType(CT_TYPE);
+ mark_template(next);
+ pc = angle_close;
+ }
+ else if ( is_within_inheritance_list(pc)
+ && ( next->Is(CT_COMMA)
+ || next->Is(CT_BRACE_OPEN)))
+ {
+ pc->SetType(CT_TYPE);
+ }
+ }
+ }
+} // EnumStructUnionParser::mark_nested_name_specifiers
+
+
+void EnumStructUnionParser::mark_pointer_types(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ if (pc->Is(CT_WORD))
+ {
+ do
+ {
+ // TODO: should there be a CT_BYREF_TYPE?
+ pc = pc->GetPrevNcNnlNi();
+
+ if (pc->IsPointerOperator())
+ {
+ pc->SetParentType(m_start->GetType());
+ pc->SetType(CT_PTR_TYPE);
+ }
+ } while (pc->IsPointerReferenceOrQualifier());
+ }
+} // EnumStructUnionParser::mark_pointer_types
+
+
+void EnumStructUnionParser::mark_template(Chunk *start) const
+{
+ LOG_FUNC_ENTRY();
+
+ if (start != nullptr)
+ {
+ LOG_FMT(LTEMPL,
+ "%s(%d): Template detected: '%s' at orig line %zu, orig col %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ start->Text(),
+ start->GetOrigLine(),
+ start->GetOrigCol());
+ }
+ start->SetParentType(CT_TEMPLATE);
+
+ auto *end = start->GetClosingParen(E_Scope::PREPROC);
+
+ if (end->IsNotNullChunk())
+ {
+ end->SetParentType(CT_TEMPLATE);
+
+ mark_template_args(start, end);
+ }
+} // EnumStructUnionParser::mark_template
+
+
+void EnumStructUnionParser::mark_template_args(Chunk *start, Chunk *end) const
+{
+ LOG_FUNC_ENTRY();
+
+ if ( end != nullptr
+ && start != nullptr)
+ {
+ LOG_FMT(LTEMPL,
+ "%s(%d): Start of template detected: '%s' at orig line %zu, orig col %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ start->Text(),
+ start->GetOrigLine(),
+ start->GetOrigCol());
+
+ T_PcfFlags flags = PCF_IN_TEMPLATE;
+ Chunk *next = start;
+
+ /**
+ * TODO: for now, just mark the chunks within the template as PCF_IN_TEMPLATE;
+ * we probably need to create a TemplateParser class to handle all
+ * things template-related
+ */
+
+ while (true)
+ {
+ next = next->GetNextNcNnl();
+
+ if (next == end)
+ {
+ break;
+ }
+ next->SetFlagBits(flags);
+ }
+ LOG_FMT(LTEMPL,
+ "%s(%d): End of template detected: '%s' at orig line %zu, orig col %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ end->Text(),
+ end->GetOrigLine(),
+ end->GetOrigCol());
+ }
+} // EnumStructUnionParser::mark_template_args
+
+
+void EnumStructUnionParser::mark_type(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ if (pc != nullptr)
+ {
+ m_type = pc;
+
+ do
+ {
+ make_type(pc);
+ pc->SetParentType(m_start->GetType());
+ pc = pc->GetNextNcNnl(E_Scope::PREPROC);
+ } while (pc->IsPointerOrReference());
+ }
+} // EnumStructUnionParser::mark_type
+
+
+void EnumStructUnionParser::mark_variable(Chunk *variable, T_PcfFlags flags)
+{
+ LOG_FUNC_ENTRY();
+
+ if (variable != nullptr)
+ {
+ LOG_FMT(LVARDEF,
+ "%s(%d): Variable definition detected: '%s' at orig line is %zu, orig col is %zu, set %s\n",
+ __unqualified_func__,
+ __LINE__,
+ variable->Text(),
+ variable->GetOrigLine(),
+ variable->GetOrigCol(),
+ flags & PCF_VAR_1ST_DEF ? "PCF_VAR_1ST_DEF" : "PCF_VAR_1ST");
+
+ variable->SetFlagBits(flags);
+ variable->SetType(CT_WORD);
+ mark_pointer_types(variable);
+ }
+} // EnumStructUnionParser::mark_variable
+
+
+void EnumStructUnionParser::mark_where_clause(Chunk *where)
+{
+ LOG_FUNC_ENTRY();
+
+ if (where != nullptr)
+ {
+ LOG_FMT(LFTOR,
+ "%s(%d): Where clause detected: orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ where->GetOrigLine(),
+ where->GetOrigCol());
+ }
+ set_where_start(where);
+
+ auto *where_end = get_where_end();
+ auto *where_start = get_where_start();
+
+ set_where_end(where_end);
+
+ T_PcfFlags flags;
+
+ for (auto *pc = where_start; pc != where_end; pc = pc->GetNextNcNnl())
+ {
+ flags = mark_where_chunk(pc, m_start->GetType(), flags);
+ }
+} // EnumStructUnionParser::mark_where_clause
+
+
+void EnumStructUnionParser::mark_where_colon(Chunk *colon)
+{
+ LOG_FUNC_ENTRY();
+
+ if (colon != nullptr)
+ {
+ LOG_FMT(LFTOR,
+ "%s(%d): Where colon detected: orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ colon->GetOrigLine(),
+ colon->GetOrigCol());
+ }
+ colon->SetType(CT_WHERE_COLON);
+ colon->SetParentType(m_start->GetType());
+} // EnumStructUnionParser::mark_where_colon
+
+
+void EnumStructUnionParser::parse(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ initialize(pc);
+
+ if (parse_error_detected())
+ {
+ return;
+ }
+
+ /**
+ * make sure this wasn't a cast, and also make sure we're
+ * actually dealing with a class/enum/struct/union type
+ */
+ if ( m_start->GetParentType() == CT_C_CAST
+ || !m_start->IsClassEnumStructOrUnion())
+ {
+ return;
+ }
+ Chunk *prev = m_start;
+ Chunk *next = prev->GetNextNcNnl();
+
+ /**
+ * the enum-key might be enum, enum class or enum struct
+ */
+ if (next->IsEnum())
+ {
+ prev = next;
+ next = prev->GetNextNcNnl();
+ }
+ else if (prev->IsEnum())
+ {
+ Chunk *prev_prev = prev->GetPrevNcNnlNi();
+
+ if ( prev_prev->IsEnum()
+ && prev->IsEnum())
+ {
+ m_start = prev_prev;
+ }
+ }
+ /**
+ * pre-process all chunks between the starting and ending chunks identified
+ * in the initial pass
+ */
+
+ while (chunk_is_between(next, m_start, m_end))
+ {
+ /**
+ * skip attributes
+ */
+ next = skip_attribute(next);
+
+ /**
+ * skip declspec
+ */
+ next = skip_declspec(next);
+
+ /**
+ * skip any right-hand side assignments
+ */
+ if (next->Is(CT_ASSIGN))
+ {
+ next = skip_to_expression_end(next);
+ }
+
+ if ( next->Is(CT_ANGLE_OPEN)
+ && !template_detected())
+ {
+ next = parse_angles(next);
+ }
+ else if ( next->Is(CT_BRACE_OPEN)
+ && !body_detected())
+ {
+ next = parse_braces(next);
+ }
+ else if (next->IsColon())
+ {
+ parse_colon(next);
+ }
+ else if (next->Is(CT_COMMA))
+ {
+ record_top_level_comma(next);
+ }
+ else if (next->Is(CT_DC_MEMBER))
+ {
+ next = parse_double_colon(next);
+ }
+ else if ( next->IsParenOpen()
+ && ( language_is_set(LANG_D)
+ || ( language_is_set(LANG_PAWN)
+ && m_start->IsEnum())))
+ {
+ set_paren_parent(next, m_start->GetType());
+
+ if ( prev->Is(CT_WORD)
+ && language_is_set(LANG_D))
+ {
+ mark_template(next);
+ }
+ next = next->GetClosingParen(E_Scope::PREPROC);
+ }
+ else if ( next->Is(CT_QUALIFIER)
+ && language_is_set(LANG_JAVA)
+ && std::strncmp(next->GetStr().c_str(), "implements", 10) == 0)
+ {
+ mark_base_classes(next);
+ }
+ else if (next->Is(CT_QUESTION))
+ {
+ record_question_operator(next);
+ }
+ else if ( next->Is(CT_WHERE)
+ && !where_clause_detected())
+ {
+ mark_where_clause(next);
+ }
+ prev = next;
+
+ do
+ {
+ next = next->GetNextNcNnl();
+ } while ( next->IsNotNullChunk()
+ && next->GetLevel() > m_start->GetLevel());
+ }
+ /**
+ * identify the type and/or variable(s)
+ */
+ analyze_identifiers();
+
+ /**
+ * identify and mark lvalues occurring outside the body definition
+ */
+ mark_extracorporeal_lvalues();
+
+ if ( prev->IsNotNullChunk()
+ && prev->IsSemicolon()
+ && prev->GetLevel() == m_start->GetLevel()
+ && !prev->TestFlags(PCF_IN_FOR))
+ {
+ prev->SetParentType(m_start->GetType());
+ }
+} // EnumStructUnionParser::parse
+
+
+Chunk *EnumStructUnionParser::parse_angles(Chunk *angle_open)
+{
+ LOG_FUNC_ENTRY();
+
+ /**
+ * first check to see if the open angle occurs within an inheritance list
+ */
+ auto *pc = angle_open;
+
+ if (!is_within_inheritance_list(pc))
+ {
+ /**
+ * check to see if there's a matching closing angle bracket
+ */
+ auto *angle_close = angle_open->GetClosingParen(E_Scope::PREPROC);
+
+ if (angle_close->IsNullChunk())
+ {
+ // parse error
+ parse_error_detected(true);
+
+ // TODO: should this be just a warning or an error (with exit condition?)
+ LOG_FMT(LWARN,
+ "%s(%d): Unmatched '<' at orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ angle_open->GetOrigLine(),
+ angle_open->GetOrigCol());
+ }
+ else
+ {
+ /**
+ * check to make sure that the template is the final chunk in a list
+ * of scope-resolution qualifications
+ */
+ auto *next = angle_close->GetNextNcNnl();
+
+ if (next->IsNot(CT_DC_MEMBER))
+ {
+ set_template_start(angle_open);
+
+ /**
+ * we could be dealing with a template type; if so, the opening angle
+ * bracket should be preceded by a CT_WORD token and we should have
+ * found a closing angle bracket
+ */
+ auto *prev = angle_open->GetPrevNcNnlNi();
+
+ if (prev->IsNot(CT_WORD))
+ {
+ // parse error
+ parse_error_detected(true);
+
+ // TODO: should this be just a warning or an error (with exit condition?)
+ LOG_FMT(LWARN,
+ "%s(%d): Identifier missing before '<' at orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ angle_open->GetOrigLine(),
+ angle_open->GetOrigCol());
+ }
+ else
+ {
+ set_template_end(angle_close);
+ mark_template(angle_open);
+ }
+ }
+ /**
+ * update input argument to point to the closing angle bracket
+ */
+ pc = angle_close;
+ }
+ }
+ return(pc);
+} // EnumStructUnionParser::parse_angles
+
+
+Chunk *EnumStructUnionParser::parse_braces(Chunk *brace_open)
+{
+ LOG_FUNC_ENTRY();
+
+ /**
+ * check to see if there's a matching closing brace
+ */
+
+ auto *pc = brace_open;
+ auto *brace_close = pc->GetClosingParen(E_Scope::PREPROC);
+
+ if (brace_close->IsNotNullChunk())
+ {
+ /**
+ * we could be dealing with a variable definition preceded by
+ * the class/struct keyword. It's possible that the variable is
+ * assigned via direct-list initialization, hence the open brace
+ * is NOT part of a class/struct type definition.
+ */
+ auto *first_comma = get_first_top_level_comma();
+
+ if (chunk_is_after(pc, first_comma))
+ {
+ /**
+ * the open brace occurs after a top-level comma was encountered, which
+ * likely implies a direct-initialization or braced initializer list in
+ * the midst of a list of variable definitions
+ */
+
+ return(pc);
+ }
+ set_body_end(brace_close);
+ set_body_start(brace_open);
+
+ auto *enum_base_start = get_enum_base_start();
+ auto *inheritance_start = get_inheritance_start();
+ auto *prev = pc->GetPrevNcNnlNi();
+
+ /**
+ * check to see if the open brace was preceded by a closing paren;
+ * it could possibly be a function-like macro call preceding the
+ * open brace, but it's more likely that we're dealing with a
+ * signature associated with a function definition
+ */
+ bool is_potential_function_definition = false;
+
+ if ( ( language_is_set(LANG_C)
+ || language_is_set(LANG_CPP))
+ && prev->IsParenClose())
+ {
+ /**
+ * we may be dealing with a c/cpp function definition, where the 'struct'
+ * or 'class' keywords appear as the return type preceding a pair of braces
+ * and therefore may be associated with a function definition body
+ */
+ auto *paren_close = prev;
+
+ // skip in reverse to the matching open paren
+ auto *paren_open = paren_close->GetOpeningParen();
+
+ if (paren_open->IsNotNullChunk())
+ {
+ /**
+ * determine if there's an identifier preceding the open paren;
+ * if so, the identifier is very likely to be associated with
+ * a function definition
+ */
+ auto *type = m_start->GetNextNcNnl();
+ auto *identifier = paren_open->GetPrevNcNnlNi(E_Scope::PREPROC);
+ is_potential_function_definition = ( ( identifier->Is(CT_FUNCTION)
+ || identifier->Is(CT_FUNC_DEF)
+ || identifier->Is(CT_WORD))
+ && type != identifier);
+ }
+ }
+
+ if ( language_is_set(LANG_D)
+ || language_is_set(LANG_PAWN)
+ || !prev->IsParenClose()
+ || is_potential_function_definition
+ || chunk_is_between(prev, enum_base_start, brace_open)
+ || chunk_is_between(prev, inheritance_start, brace_open))
+ {
+ mark_braces(brace_open);
+
+ /**
+ * D does not require a semicolon after an enum, but we add one to make
+ * other code happy.
+ */
+ if ( language_is_set(LANG_D)
+ && m_start->IsEnum())
+ {
+ pawn_add_vsemi_after(brace_close); // Issue #2279
+ }
+ pc = brace_close;
+ }
+ else
+ {
+ // TODO: should this be just a warning or an error (with exit condition?)
+ LOG_FMT(LWARN,
+ "%s(%d): Parsing error precedes start of body '{' at orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ brace_open->GetOrigLine(),
+ brace_open->GetOrigCol());
+
+ // parse error
+ parse_error_detected(true);
+ }
+ }
+ return(pc);
+} // EnumStructUnionParser::parse_braces
+
+
+void EnumStructUnionParser::parse_colon(Chunk *colon)
+{
+ LOG_FUNC_ENTRY();
+
+ if (m_start->Is(CT_UNION))
+ {
+ /**
+ * unions do not implement inheritance
+ */
+
+ // TODO: should this be just a warning or an error (with exit condition?)
+ LOG_FMT(LWARN,
+ "%s(%d): Colon follows union declaration at orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ colon->GetOrigLine(),
+ colon->GetOrigCol());
+
+ // parse error
+ parse_error_detected(true);
+ }
+ else if (is_within_conditional(colon))
+ {
+ mark_conditional_colon(colon);
+ }
+ else if (is_within_where_clause(colon))
+ {
+ mark_where_colon(colon);
+ }
+ else if (!inheritance_detected())
+ {
+ if (m_start->IsClassOrStruct())
+ {
+ /**
+ * the colon likely specifies an inheritance list for a struct
+ * or class type
+ */
+
+ set_inheritance_start(colon);
+ mark_class_colon(colon);
+ }
+ else if (m_start->IsEnum())
+ {
+ set_enum_base_start(colon);
+ mark_enum_integral_type(colon);
+ }
+ }
+} // EnumStructUnionParser::parse_colon
+
+
+Chunk *EnumStructUnionParser::parse_double_colon(Chunk *double_colon)
+{
+ LOG_FUNC_ENTRY();
+
+ auto *pc = double_colon;
+
+ if ( language_is_set(LANG_CPP)
+ && pc->Is(CT_DC_MEMBER))
+ {
+ mark_nested_name_specifiers(pc);
+ pc = skip_scope_resolution_and_nested_name_specifiers(pc);
+ }
+ return(pc);
+} // EnumStructUnionParser::parse_double_colon
+
+
+bool EnumStructUnionParser::parse_error_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ return(m_parse_error);
+} // EnumStructUnionParser::parse_error_detected
+
+
+void EnumStructUnionParser::parse_error_detected(bool status)
+{
+ LOG_FUNC_ENTRY();
+
+ m_parse_error = status;
+} // EnumStructUnionParser::parse_error_detected
+
+
+void EnumStructUnionParser::record_question_operator(Chunk *question)
+{
+ LOG_FUNC_ENTRY();
+
+ if (question->Is(CT_QUESTION))
+ {
+ std::size_t index = m_chunk_map[CT_QUESTION].size();
+
+ m_chunk_map[CT_QUESTION][index] = question;
+ }
+} // EnumStructUnionParser::record_question_operator
+
+
+void EnumStructUnionParser::record_top_level_comma(Chunk *comma)
+{
+ if ( comma != nullptr
+ && comma->GetLevel() == m_start->GetLevel()
+ && !is_within_conditional(comma)
+ && !is_within_inheritance_list(comma))
+ {
+ std::size_t index = m_chunk_map[CT_COMMA].size();
+
+ m_chunk_map[CT_COMMA][index] = comma;
+ }
+} // EnumStructUnionParser::record_top_level_comma
+
+
+Chunk *EnumStructUnionParser::refine_end_chunk(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+
+ if ( ( language_is_set(LANG_C)
+ || language_is_set(LANG_CPP))
+ && pc->Is(CT_BRACE_CLOSE))
+ {
+ /**
+ * if dealing with C/C++, one or more trailing variable definitions may
+ * follow the closing brace; a semi-colon should've been good enough to
+ * indicate the terminating condition, however some of the classes defined
+ * in the input tests cases for Continuous Integration DO NOT correctly
+ * terminate classes/struct with a semicolon (which is compilation error).
+ * As a consequence, more checks must be performed to determine where
+ * the terminating chunk is located. For instance, see operator.cpp and
+ * enum_comma.h for examples of offenders
+ */
+ auto *next = pc->GetNextNcNnl();
+
+ while (true)
+ {
+ if (next->IsSemicolon())
+ {
+ pc = next;
+
+ break;
+ }
+ else
+ {
+ /**
+ * if we're sitting at a comma, skip it
+ */
+ if (next->Is(CT_COMMA))
+ {
+ next = next->GetNextNcNnl();
+ }
+ auto match = match_variable(next, m_start->GetLevel());
+ auto *start = std::get<0>(match);
+ auto *identifier = std::get<1>(match);
+ auto *end = std::get<2>(match);
+
+ if ( end == nullptr
+ || identifier == nullptr
+ || start == nullptr)
+ {
+ break;
+ }
+ else
+ {
+ pc = end->GetNextNcNnl();
+
+ /**
+ * skip any right-hand side assignments
+ */
+ if (pc->Is(CT_ASSIGN))
+ {
+ pc = skip_to_expression_end(pc);
+ }
+ next = pc;
+ }
+ }
+ }
+ }
+ return(pc);
+} // EnumStructUnionParser::refine_end_chunk
+
+
+void EnumStructUnionParser::set_body_end(Chunk *body_end)
+{
+ LOG_FUNC_ENTRY();
+
+ if (body_end->Is(CT_BRACE_CLOSE))
+ {
+ m_chunk_map[CT_BRACE_CLOSE][0] = body_end;
+ }
+} // EnumStructUnionParser::set_body_end
+
+
+void EnumStructUnionParser::set_body_start(Chunk *body_start)
+{
+ LOG_FUNC_ENTRY();
+
+ if (body_start->Is(CT_BRACE_OPEN))
+ {
+ m_chunk_map[CT_BRACE_OPEN][0] = body_start;
+ }
+} // EnumStructUnionParser::set_body_start
+
+
+void EnumStructUnionParser::set_enum_base_start(Chunk *enum_base_start)
+{
+ LOG_FUNC_ENTRY();
+
+ if (enum_base_start->IsColon())
+ {
+ m_chunk_map[CT_BIT_COLON][0] = enum_base_start;
+ }
+} // EnumStructUnionParser::set_enum_base_start
+
+
+void EnumStructUnionParser::set_inheritance_start(Chunk *inheritance_start)
+{
+ LOG_FUNC_ENTRY();
+
+ if (inheritance_start->IsColon())
+ {
+ m_chunk_map[CT_COLON][0] = inheritance_start;
+ }
+} // EnumStructUnionParser::set_inheritance_start
+
+
+void EnumStructUnionParser::set_template_end(Chunk *template_end)
+{
+ LOG_FUNC_ENTRY();
+
+ if (template_end->Is(CT_ANGLE_CLOSE))
+ {
+ m_chunk_map[CT_ANGLE_CLOSE][0] = template_end;
+ }
+} // EnumStructUnionParser::set_template_end
+
+
+void EnumStructUnionParser::set_template_start(Chunk *template_start)
+{
+ LOG_FUNC_ENTRY();
+
+ if (template_start->Is(CT_ANGLE_OPEN))
+ {
+ m_chunk_map[CT_ANGLE_OPEN][0] = template_start;
+ }
+} // EnumStructUnionParser::set_template_start
+
+
+void EnumStructUnionParser::set_where_end(Chunk *where_end)
+{
+ LOG_FUNC_ENTRY();
+
+ if (where_end->Is(CT_BRACE_OPEN))
+ {
+ m_chunk_map[CT_WHERE][0] = where_end;
+ }
+} // EnumStructUnionParser::set_where_end
+
+
+void EnumStructUnionParser::set_where_start(Chunk *where_start)
+{
+ LOG_FUNC_ENTRY();
+
+ if (where_start->Is(CT_WHERE))
+ {
+ m_chunk_map[CT_WHERE][0] = where_start;
+ }
+} // EnumStructUnionParser::set_where_start
+
+
+bool EnumStructUnionParser::template_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto *template_end = get_template_end();
+ auto *template_start = get_template_start();
+
+ return( template_end != nullptr
+ && template_start != nullptr);
+} // EnumStructUnionParser::template_detected
+
+
+Chunk *EnumStructUnionParser::try_find_end_chunk(Chunk *pc)
+{
+ LOG_FUNC_ENTRY();
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+
+ do
+ {
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+
+ /**
+ * clear some previously marked token types, some of which have likely
+ * been erroneously marked up to this point; a good example of this
+ * arises when macro variables and/or macro function calls follow the
+ * class/enum/struct/union keyword and precede the actual type name
+ */
+ if ( pc->Is(CT_TYPE)
+ || pc->Is(CT_WORD))
+ {
+ pc->SetType(CT_WORD);
+ pc->SetParentType(CT_NONE);
+ }
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+
+ do
+ {
+ pc = pc->GetNextNcNnl(E_Scope::PREPROC);
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ } while ( pc->IsNotNullChunk()
+ && pc->GetLevel() > m_start->GetLevel());
+
+ if (pc->IsNullChunk())
+ {
+ LOG_FMT(LFTOR, "%s(%d): IsNullChunk\n",
+ __unqualified_func__, __LINE__);
+ // parse error
+ parse_error_detected(true);
+ return(Chunk::NullChunkPtr);
+ }
+ else
+ {
+ LOG_FMT(LFTOR, "%s(%d): orig line is %zu, orig col is %zu, type is %s\n",
+ __unqualified_func__, __LINE__,
+ pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()));
+ }
+ } while (!is_potential_end_chunk(pc));
+
+ /**
+ * perform a second pass for c++ that
+ */
+ pc = refine_end_chunk(pc);
+
+ return(pc);
+} // EnumStructUnionParser::try_find_end_chunk
+
+
+void EnumStructUnionParser::try_post_identify_macro_calls()
+{
+ LOG_FUNC_ENTRY();
+
+ if ( language_is_set(LANG_CPP)
+ && type_identified())
+ {
+ /**
+ * for all chunks at class/enum/struct/union level, identify function-like
+ * macro calls and mark them as CT_MACRO_FUNC_CALL. The reason for doing
+ * so is to avoid mis-interpretation by code executed at a later time
+ */
+
+ auto *body_start = get_body_start();
+ auto *inheritance_start = get_inheritance_start();
+ Chunk *pc = m_start;
+ Chunk *prev = Chunk::NullChunkPtr;
+
+ do
+ {
+ if ( !chunk_is_between(prev, inheritance_start, body_start)
+ && ( prev->Is(CT_WORD)
+ || prev->Is(CT_FUNCTION)
+ || prev->Is(CT_FUNC_DEF))
+ && !prev->GetFlags().test_any(PCF_VAR_DEF | PCF_VAR_1ST | PCF_VAR_INLINE)
+ && prev->GetLevel() == m_start->GetLevel())
+ {
+ if (pc->IsParenOpen())
+ {
+ auto *paren_open = pc;
+ auto *paren_close = paren_open->GetClosingParen(E_Scope::PREPROC);
+
+ if (paren_close->IsNotNullChunk())
+ {
+ paren_open->SetType(CT_FPAREN_OPEN);
+ paren_open->SetParentType(CT_MACRO_FUNC_CALL);
+ paren_close->SetType(CT_FPAREN_CLOSE);
+ paren_close->SetParentType(CT_MACRO_FUNC_CALL);
+ prev->SetType(CT_MACRO_FUNC_CALL);
+ }
+ }
+ }
+ prev = pc;
+ pc = prev->GetNextNcNnl();
+ } while (chunk_is_between(pc, m_start, m_end));
+ }
+} // EnumStructUnionParser::try_post_identify_macro_calls
+
+
+void EnumStructUnionParser::try_post_identify_type()
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *body_end = get_body_end();
+
+ if ( !type_identified()
+ && body_end == nullptr)
+ {
+ /**
+ * a type wasn't identified and no closing brace is present; we're
+ * likely not dealing with an anonymous enum/class/struct
+ */
+
+ /**
+ * a type has yet to be identified, so search for the last word
+ * that hasn't been marked as a variable
+ */
+ Chunk *type = nullptr;
+ Chunk *pc = m_start;
+
+ do
+ {
+ /**
+ * in case it's a qualified identifier, skip scope-resolution and
+ * nested name specifiers and return just the qualified identifier name
+ */
+ pc = skip_scope_resolution_and_nested_name_specifiers(pc);
+
+ if (pc->GetFlags().test_any(PCF_VAR_DEF | PCF_VAR_1ST | PCF_VAR_INLINE))
+ {
+ break;
+ }
+ else if ( pc->Is(CT_WORD)
+ || pc->Is(CT_ANGLE_CLOSE))
+ {
+ type = skip_template_prev(pc);
+ }
+ pc = pc->GetNextNcNnl();
+ } while (chunk_is_between(pc, m_start, m_end));
+
+ if (type != nullptr)
+ {
+ mark_type(type);
+ }
+ }
+} // EnumStructUnionParser::try_post_identify_type
+
+
+bool EnumStructUnionParser::try_pre_identify_type()
+{
+ LOG_FUNC_ENTRY();
+
+ Chunk *pc = get_body_start();
+
+ if ( language_is_set(LANG_PAWN)
+ && m_start->IsEnum())
+ {
+ set_paren_parent(pc, m_start->GetType());
+ }
+ else if (template_detected())
+ {
+ pc = get_template_start();
+ }
+ else if (enum_base_detected())
+ {
+ pc = get_enum_base_start();
+ }
+ else if (inheritance_detected())
+ {
+ pc = get_inheritance_start();
+
+ if (m_start->Is(CT_UNION))
+ {
+ /**
+ * unions do not implement inheritance
+ */
+
+ // TODO: should this be just a warning or an error (with exit condition?)
+ LOG_FMT(LWARN,
+ "%s(%d): Bad union declaration detected at orig line is %zu, orig col is %zu\n",
+ __unqualified_func__,
+ __LINE__,
+ m_start->GetOrigLine(),
+ m_start->GetOrigCol());
+
+ parse_error_detected(true);
+
+ return(false);
+ }
+ }
+
+ if (pc == nullptr)
+ {
+ Chunk *next = m_start->GetNextNcNnl();
+
+ /**
+ * in case it's a qualified identifier, skip scope-resolution and
+ * nested name specifiers and return just the qualified identifier name
+ */
+ next = skip_scope_resolution_and_nested_name_specifiers(next);
+
+ Chunk *next_next = next->GetNextNcNnl();
+
+ /**
+ * in case it's a qualified identifier, skip scope-resolution and
+ * nested name specifiers and return just the qualified identifier name
+ */
+ next_next = skip_scope_resolution_and_nested_name_specifiers(next_next);
+
+ /**
+ * if there is one word between the start and end chunks, then we've likely
+ * identified the type; if there are two words, then the first is likely a
+ * type and the second is an instantiation thereof; however, it is possible
+ * that the first word is actually a reference to a macro definition, in which
+ * the second word would be the type
+ */
+ if (next_next == m_end)
+ {
+ pc = next_next;
+ }
+ else if ( next->IsNotNullChunk()
+ && next->Is(CT_WORD)
+ && next_next->Is(CT_WORD)
+ && m_end->GetPrevNcNnlNi() == next_next)
+ {
+ /**
+ * check to see if we've got a macro reference preceding the last word chunk;
+ * this won't work in all cases, because a macro may be defined in another header
+ * file, but this is an attempt to increase the chances of identifying the correct
+ * chunk as the type
+ */
+ if ( chunk_is_macro_reference(next)
+ || m_start->GetParentType() == CT_TEMPLATE)
+ {
+ pc = m_end;
+ }
+ else
+ {
+ pc = next_next;
+ }
+ }
+ else
+ {
+ /**
+ * search for some common patterns that may indicate a type
+ */
+ Chunk *prev = m_start;
+
+ while ( chunk_is_between(next, m_start, m_end)
+ && ( ( next->IsNot(CT_ASSIGN)
+ && next->IsNot(CT_COMMA))
+ || next->GetLevel() != m_start->GetLevel())
+ && !next->IsSemicolon())
+ {
+ prev = next;
+ next = next->GetNextNcNnl();
+
+ /**
+ * in case it's a qualified identifier, skip scope-resolution and
+ * nested name specifiers and return just the qualified identifier name
+ */
+ next = skip_scope_resolution_and_nested_name_specifiers(next);
+
+ /**
+ * skip array brackets, as the type cannot be located within;
+ * also skip a set of parens - there may be a type embedded within,
+ * but it's not the type with which we're concerned
+ */
+ if ( next->IsSquareBracket() // Issue #3601
+ || next->IsParenOpen())
+ {
+ prev = next->GetClosingParen(E_Scope::PREPROC);
+ next = prev->GetNextNcNnl(E_Scope::PREPROC);
+ }
+
+ if ( prev->Is(CT_WORD)
+ && next->IsPointerOrReference())
+ {
+ pc = next;
+
+ break;
+ }
+ }
+ }
+ }
+
+ if ( pc != nullptr
+ && pc->IsNotNullChunk())
+ {
+ /**
+ * the chunk preceding the previously selected chunk should indicate the type
+ */
+
+ pc = pc->GetPrevNcNnlNi(E_Scope::PREPROC);
+
+ if ( pc->Is(CT_QUALIFIER)
+ && std::strncmp(pc->GetStr().c_str(), "final", 5) == 0)
+ {
+ pc = pc->GetPrevNcNnlNi(E_Scope::PREPROC);
+ }
+
+ if ( language_is_set(LANG_D)
+ && pc->IsParenClose())
+ {
+ pc = pc->GetOpeningParen();
+ pc = pc->GetPrevNcNnlNi();
+ }
+
+ if (pc->Is(CT_WORD))
+ {
+ mark_type(pc);
+
+ return(true);
+ }
+ }
+ return(false);
+} // EnumStructUnionParser::try_pre_identify_type
+
+
+bool EnumStructUnionParser::type_identified() const
+{
+ LOG_FUNC_ENTRY();
+
+ return(m_type != nullptr);
+} // EnumStructUnionParser::type_identified
+
+
+/**
+ * Returns true if a where clause was detected during parsing
+ */
+bool EnumStructUnionParser::where_clause_detected() const
+{
+ LOG_FUNC_ENTRY();
+
+ auto *where_end = get_where_end();
+ auto *where_start = get_where_start();
+
+ return( where_end != nullptr
+ && where_start != nullptr);
+} // EnumStructUnionParser::where_clause_detected