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