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.

2949 lines
84 KiB

/**
* @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->IsNotNullChunk()
&& next->IsNotNullChunk())
{
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->IsNotNullChunk()
&& next->IsNotNullChunk())
{
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->IsNotNullChunk())
{
if ( test_equal
&& pc == after)
{
return(true);
}
else if (after->IsNotNullChunk())
{
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->IsNotNullChunk())
{
if ( test_equal
&& pc == before)
{
return(true);
}
else if (before->IsNotNullChunk())
{
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->IsNotNullChunk()
&& start->IsNotNullChunk())
{
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(Chunk::NullChunkPtr, Chunk::NullChunkPtr));
} // 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->IsNotNullChunk() ? 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->IsNotNullChunk()
|| identifier->GetPrevNcNnlNi()->Is(CT_WORD)))
{
return(std::make_tuple(start, identifier, end));
}
return(std::make_tuple(Chunk::NullChunkPtr, Chunk::NullChunkPtr, Chunk::NullChunkPtr));
} // 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 = Chunk::NullChunkPtr;
while (pc->IsNotNullChunk())
{
/**
* skip any right-hand side assignments
*/
Chunk *rhs_exp_end = Chunk::NullChunkPtr;
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->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(Chunk::NullChunkPtr, Chunk::NullChunkPtr));
} // 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;
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->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->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(Chunk::NullChunkPtr)
, m_parse_error(false)
, m_start(Chunk::NullChunkPtr)
, m_type(Chunk::NullChunkPtr)
{
} // 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();
PcfFlags flags = PCF_VAR_1ST_DEF;
auto *inheritance_start = get_inheritance_start();
Chunk *pc = body_end->IsNotNullChunk() ? 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->IsNotNullChunk())
{
pc = body_end;
}
else if (template_end->IsNotNullChunk())
{
pc = template_end;
}
}
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->IsNotNullChunk())
{
/**
* 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->IsNotNullChunk()
&& identifier->IsNotNullChunk())
{
if (end->IsNotNullChunk())
{
mark_variable(identifier, flags);
if (flags & PCF_VAR_1ST)
{
flags &= ~PCF_VAR_1ST; // clear the first flag for the next items
}
}
}
if (end->IsNotNullChunk())
{
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->IsNotNullChunk()
&& body_start->IsNotNullChunk());
} // 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(Chunk::NullChunkPtr);
} // 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(Chunk::NullChunkPtr);
} // 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(Chunk::NullChunkPtr);
} // 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(Chunk::NullChunkPtr);
} // EnumStructUnionParser::get_first_top_level_comma
Chunk *EnumStructUnionParser::get_inheritance_end() const
{
LOG_FUNC_ENTRY();
Chunk *brace_open = Chunk::NullChunkPtr;
auto *inheritance_start = get_inheritance_start();
if (inheritance_start->IsNotNullChunk())
{
brace_open = get_body_start();
if (brace_open->IsNullChunk())
{
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(Chunk::NullChunkPtr);
} // 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(Chunk::NullChunkPtr);
} // 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(Chunk::NullChunkPtr);
} // 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 = Chunk::NullChunkPtr;
auto *where_start = get_where_start();
if (where_start->IsNotNullChunk())
{
brace_open = get_body_start();
if (brace_open->IsNullChunk())
{
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(Chunk::NullChunkPtr);
} // 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 = Chunk::NullChunkPtr;
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->TestFlags(PCF_IN_CLASS_BASE))
{
return(true);
}
auto *inheritance_end = get_inheritance_end();
auto *inheritance_start = get_inheritance_start();
if ( inheritance_end->IsNotNullChunk()
&& inheritance_start->IsNotNullChunk())
{
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->TestFlags(PCF_IN_WHERE_SPEC))
{
return(true);
}
auto *where_end = get_where_end();
auto *where_start = get_where_start();
if ( where_end->IsNotNullChunk()
&& where_start->IsNotNullChunk())
{
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();
PcfFlags flags = PCF_VAR_1ST_DEF;
while (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();
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->IsNotNullChunk())
{
/**
* 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();
// Issue #4040
LOG_FMT(LFTOR,
"%s(%d): orig line is %zu, orig col is %zu\n",
__unqualified_func__, __LINE__,
colon->GetOrigLine(), colon->GetOrigCol());
colon->SetType(CT_ENUM_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->IsNotNullChunk())
{
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->IsNotNullChunk()
&& start->IsNotNullChunk())
{
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());
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->IsNotNullChunk())
{
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, PcfFlags flags)
{
LOG_FUNC_ENTRY();
if (variable->IsNotNullChunk())
{
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->IsNotNullChunk())
{
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);
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->IsNotNullChunk())
{
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->IsNotNullChunk()
&& 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->IsNullChunk()
|| identifier->IsNullChunk()
|| start->IsNullChunk())
{
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->IsNotNullChunk()
&& template_start->IsNotNullChunk());
} // 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->IsNullChunk())
{
/**
* 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 = Chunk::NullChunkPtr;
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->IsNotNullChunk())
{
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->IsNullChunk())
{
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->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->IsNotNullChunk());
} // 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->IsNotNullChunk()
&& where_start->IsNotNullChunk());
} // EnumStructUnionParser::where_clause_detected