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.
537 lines
15 KiB
537 lines
15 KiB
/**
|
|
* @file check_template.cpp
|
|
*
|
|
* splitted from tokenize_cleanup.cpp
|
|
*
|
|
* @author Guy Maurel 2022
|
|
* @license GPL v2+
|
|
*/
|
|
|
|
#include "tokenize_cleanup.h"
|
|
|
|
#include "check_template.h"
|
|
#include "chunk.h"
|
|
#include "combine.h"
|
|
#include "flag_braced_init_list.h"
|
|
#include "flag_decltype.h"
|
|
#include "prototypes.h"
|
|
|
|
|
|
using namespace uncrustify;
|
|
|
|
|
|
bool invalid_open_angle_template(Chunk *prev)
|
|
{
|
|
if (prev->IsNullChunk())
|
|
{
|
|
return(false);
|
|
}
|
|
// A template requires a word/type right before the open angle
|
|
return( prev->IsNot(CT_WORD)
|
|
&& prev->IsNot(CT_TYPE)
|
|
&& prev->IsNot(CT_COMMA)
|
|
&& prev->IsNot(CT_QUALIFIER)
|
|
&& prev->IsNot(CT_OPERATOR_VAL)
|
|
&& prev->GetParentType() != CT_OPERATOR);
|
|
}
|
|
|
|
|
|
Chunk *handle_double_angle_close(Chunk *pc)
|
|
{
|
|
Chunk *next = pc->GetNext();
|
|
|
|
if (next->IsNotNullChunk())
|
|
{
|
|
if ( pc->Is(CT_ANGLE_CLOSE)
|
|
&& next->Is(CT_ANGLE_CLOSE)
|
|
&& pc->GetParentType() == CT_NONE
|
|
&& (pc->GetOrigColEnd() + 1) == next->GetOrigCol()
|
|
&& next->GetParentType() == CT_NONE)
|
|
{
|
|
pc->Str().append('>');
|
|
pc->SetType(CT_SHIFT);
|
|
pc->SetOrigColEnd(next->GetOrigColEnd());
|
|
|
|
Chunk *tmp = next->GetNextNcNnl();
|
|
Chunk::Delete(next);
|
|
next = tmp;
|
|
}
|
|
else
|
|
{
|
|
// bug #663
|
|
pc->SetType(CT_COMPARE);
|
|
}
|
|
}
|
|
return(next);
|
|
}
|
|
|
|
|
|
void check_template(Chunk *start, bool in_type_cast)
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): orig line %zu, orig col %zu:\n",
|
|
__func__, __LINE__, start->GetOrigLine(), start->GetOrigCol());
|
|
|
|
Chunk *prev = start->GetPrevNcNnl(E_Scope::PREPROC);
|
|
|
|
if (prev->IsNullChunk())
|
|
{
|
|
return;
|
|
}
|
|
Chunk *end;
|
|
Chunk *pc;
|
|
|
|
if (prev->Is(CT_TEMPLATE))
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): CT_TEMPLATE:\n", __func__, __LINE__);
|
|
|
|
// We have: "template< ... >", which is a template declaration
|
|
size_t level = 1;
|
|
size_t parens = 0;
|
|
|
|
for (pc = start->GetNextNcNnl(E_Scope::PREPROC);
|
|
pc->IsNotNullChunk();
|
|
pc = pc->GetNextNcNnl(E_Scope::PREPROC))
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): type is %s, level is %zu\n",
|
|
__func__, __LINE__, get_token_name(pc->GetType()), level);
|
|
|
|
if ( (pc->GetStr()[0] == '>')
|
|
&& (pc->Len() > 1))
|
|
{
|
|
if (pc->GetStr()[1] == '=') // Issue #1462 and #2565
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): do not split '%s' at orig line %zu, orig col %zu\n",
|
|
__func__, __LINE__, pc->Text(), pc->GetOrigLine(), pc->GetOrigCol());
|
|
}
|
|
else
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): {split '%s' at orig line %zu, orig col %zu}\n",
|
|
__func__, __LINE__, pc->Text(), pc->GetOrigLine(), pc->GetOrigCol());
|
|
split_off_angle_close(pc);
|
|
}
|
|
}
|
|
|
|
if (pc->Is(CT_DECLTYPE))
|
|
{
|
|
flag_cpp_decltype(pc);
|
|
}
|
|
else if (pc->Is(CT_PAREN_OPEN))
|
|
{
|
|
++parens;
|
|
}
|
|
else if (pc->Is(CT_PAREN_CLOSE))
|
|
{
|
|
--parens;
|
|
}
|
|
|
|
if (parens == 0)
|
|
{
|
|
if (pc->IsString("<"))
|
|
{
|
|
level++;
|
|
}
|
|
else if (pc->IsString(">"))
|
|
{
|
|
if (level == 0)
|
|
{
|
|
fprintf(stderr, "%s(%d): level is ZERO, cannot be decremented, at line %zu, column %zu\n",
|
|
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());
|
|
log_flush(true);
|
|
exit(EX_SOFTWARE);
|
|
}
|
|
level--;
|
|
|
|
if (level == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
end = pc;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We may have something like "a< ... >", which is a template where
|
|
* '...' may consist of anything except a semicolon, unbalanced
|
|
* parens, or braces (with one exception being braced initializers
|
|
* embedded within decltypes).
|
|
*
|
|
* For example, braces may be encountered as such in the following
|
|
* snippet of valid C++ code:
|
|
*
|
|
* template<typename T,
|
|
* typename = enable_if_t<is_same<typename decay<T>::type,
|
|
* decltype (make_index_sequence<5> { })>::value>>
|
|
* void foo(T &&arg)
|
|
* {
|
|
*
|
|
* }
|
|
*
|
|
* Finally, if we are inside an 'if' statement and hit a CT_BOOL,
|
|
* then it isn't a template.
|
|
*/
|
|
|
|
if (invalid_open_angle_template(prev))
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): - after type %s + ( - Not a template\n",
|
|
__func__, __LINE__, get_token_name(prev->GetType()));
|
|
start->SetType(CT_COMPARE);
|
|
return;
|
|
}
|
|
LOG_FMT(LTEMPL, "%s(%d): - prev->GetType() is %s -\n",
|
|
__func__, __LINE__, get_token_name(prev->GetType()));
|
|
|
|
// Scan back and make sure we aren't inside square parenthesis
|
|
bool in_if = false;
|
|
bool hit_semicolon = false;
|
|
pc = start->GetPrevNcNnl(E_Scope::PREPROC);
|
|
|
|
while (pc->IsNotNullChunk())
|
|
{
|
|
if ( ( pc->Is(CT_SEMICOLON)
|
|
&& hit_semicolon)
|
|
|| pc->Is(CT_SQUARE_CLOSE))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pc->Is(CT_DECLTYPE))
|
|
{
|
|
flag_cpp_decltype(pc);
|
|
}
|
|
|
|
if (pc->Is(CT_BRACE_OPEN))
|
|
{
|
|
if ( !pc->TestFlags(PCF_IN_DECLTYPE)
|
|
|| !detect_cpp_braced_init_list(pc->GetPrev(), pc))
|
|
{
|
|
break;
|
|
}
|
|
flag_cpp_braced_init_list(pc->GetPrev(), pc);
|
|
}
|
|
|
|
if ( pc->Is(CT_BRACE_CLOSE)
|
|
&& pc->GetParentType() != CT_BRACED_INIT_LIST
|
|
&& !pc->TestFlags(PCF_IN_DECLTYPE))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( pc->Is(CT_SEMICOLON)
|
|
&& !hit_semicolon)
|
|
{
|
|
hit_semicolon = true;
|
|
}
|
|
|
|
if ( ( ( pc->Is(CT_IF)
|
|
|| pc->Is(CT_RETURN)
|
|
|| pc->Is(CT_WHILE)
|
|
|| pc->Is(CT_WHILE_OF_DO))
|
|
&& !hit_semicolon)
|
|
|| ( pc->Is(CT_FOR)
|
|
&& hit_semicolon))
|
|
{
|
|
in_if = true;
|
|
break;
|
|
}
|
|
pc = pc->GetPrevNcNnl(E_Scope::PREPROC);
|
|
}
|
|
/*
|
|
* Scan forward to the angle close
|
|
* If we have a comparison in there, then it can't be a template.
|
|
*/
|
|
const int max_token_count = 1024;
|
|
E_Token tokens[max_token_count];
|
|
size_t num_tokens = 1;
|
|
|
|
tokens[0] = CT_ANGLE_OPEN;
|
|
|
|
for (pc = start->GetNextNcNnl(E_Scope::PREPROC);
|
|
pc->IsNotNullChunk();
|
|
pc = pc->GetNextNcNnl(E_Scope::PREPROC))
|
|
{
|
|
constexpr static auto LCURRENT = LTEMPL;
|
|
|
|
LOG_FMT(LTEMPL, "%s(%d): pc orig line is %zu, orig col is %zu, type is %s, num_tokens is %zu\n",
|
|
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol(), get_token_name(pc->GetType()), num_tokens);
|
|
|
|
log_rule_B("tok_split_gte");
|
|
|
|
if (pc->Is(CT_BRACE_OPEN)) // Issue #2886
|
|
{
|
|
// look for the closing brace
|
|
Chunk *A = pc->GetClosingParen();
|
|
LOG_FMT(LTEMPL, "%s(%d): A orig line is %zu, orig col is %zu, type is %s\n",
|
|
__func__, __LINE__, A->GetOrigLine(), A->GetOrigCol(), get_token_name(A->GetType()));
|
|
pc = A->GetNext();
|
|
}
|
|
|
|
if ( (tokens[num_tokens - 1] == CT_ANGLE_OPEN)
|
|
&& (pc->GetStr()[0] == '>')
|
|
&& (pc->Len() > 1)
|
|
&& ( options::tok_split_gte()
|
|
|| ( ( pc->IsString(">>")
|
|
|| pc->IsString(">>>"))
|
|
&& ( num_tokens >= 2
|
|
|| ( num_tokens >= 1
|
|
&& in_type_cast)))))
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): {split '%s' at orig line %zu, orig col %zu}\n",
|
|
__func__, __LINE__, pc->Text(), pc->GetOrigLine(), pc->GetOrigCol());
|
|
|
|
split_off_angle_close(pc);
|
|
}
|
|
|
|
if (pc->IsString("<"))
|
|
{
|
|
if ( num_tokens > 0 && (tokens[num_tokens - 1] == CT_PAREN_OPEN)
|
|
&& invalid_open_angle_template(pc->GetPrev()))
|
|
{
|
|
pc->SetType(CT_COMPARE); // Issue #3127
|
|
}
|
|
else
|
|
{
|
|
tokens[num_tokens] = CT_ANGLE_OPEN;
|
|
num_tokens++;
|
|
}
|
|
}
|
|
else if (pc->IsString(">"))
|
|
{
|
|
if (num_tokens > 0 && (tokens[num_tokens - 1] == CT_PAREN_OPEN))
|
|
{
|
|
handle_double_angle_close(pc);
|
|
}
|
|
else if (--num_tokens <= 0)
|
|
{
|
|
break;
|
|
}
|
|
else if (tokens[num_tokens] != CT_ANGLE_OPEN)
|
|
{
|
|
break; // unbalanced parentheses
|
|
}
|
|
}
|
|
else if ( in_if
|
|
&& ( pc->Is(CT_BOOL)
|
|
|| pc->Is(CT_COMPARE)))
|
|
{
|
|
break;
|
|
}
|
|
else if (pc->Is(CT_BRACE_OPEN))
|
|
{
|
|
if ( !pc->TestFlags(PCF_IN_DECLTYPE)
|
|
|| !detect_cpp_braced_init_list(pc->GetPrev(), pc))
|
|
{
|
|
break;
|
|
}
|
|
auto brace_open = pc->GetNextNcNnl();
|
|
auto brace_close = brace_open->GetClosingParen();
|
|
|
|
brace_open->SetParentType(CT_BRACED_INIT_LIST);
|
|
brace_close->SetParentType(CT_BRACED_INIT_LIST);
|
|
}
|
|
else if ( pc->Is(CT_BRACE_CLOSE)
|
|
&& pc->GetParentType() != CT_BRACED_INIT_LIST
|
|
&& !pc->TestFlags(PCF_IN_DECLTYPE))
|
|
{
|
|
break;
|
|
}
|
|
else if (pc->Is(CT_SEMICOLON))
|
|
{
|
|
break;
|
|
}
|
|
else if (pc->Is(CT_PAREN_OPEN))
|
|
{
|
|
if (num_tokens >= max_token_count - 1)
|
|
{
|
|
break;
|
|
}
|
|
tokens[num_tokens] = CT_PAREN_OPEN;
|
|
num_tokens++;
|
|
}
|
|
else if ( pc->Is(CT_QUESTION) // Issue #2949
|
|
&& language_is_set(LANG_CPP))
|
|
{
|
|
break;
|
|
}
|
|
else if (pc->Is(CT_PAREN_CLOSE))
|
|
{
|
|
if (num_tokens == 0)
|
|
{
|
|
fprintf(stderr, "%s(%d): num_tokens is ZERO, cannot be decremented, at line %zu, column %zu\n",
|
|
__func__, __LINE__, pc->GetOrigLine(), pc->GetOrigCol());
|
|
log_flush(true);
|
|
exit(EX_SOFTWARE);
|
|
}
|
|
num_tokens--;
|
|
|
|
if (tokens[num_tokens] != CT_PAREN_OPEN)
|
|
{
|
|
break; // unbalanced parentheses
|
|
}
|
|
}
|
|
}
|
|
|
|
end = pc;
|
|
}
|
|
|
|
if (end->Is(CT_ANGLE_CLOSE))
|
|
{
|
|
pc = end->GetNextNcNnl(E_Scope::PREPROC);
|
|
|
|
if ( pc->IsNullChunk()
|
|
|| pc->IsNot(CT_NUMBER))
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): Template detected\n", __func__, __LINE__);
|
|
LOG_FMT(LTEMPL, "%s(%d): from orig line %zu, orig col %zu\n",
|
|
__func__, __LINE__, start->GetOrigLine(), start->GetOrigCol());
|
|
LOG_FMT(LTEMPL, "%s(%d): to orig line %zu, orig col %zu\n",
|
|
__func__, __LINE__, end->GetOrigLine(), end->GetOrigCol());
|
|
start->SetParentType(CT_TEMPLATE);
|
|
|
|
check_template_args(start, end);
|
|
|
|
end->SetParentType(CT_TEMPLATE);
|
|
end->SetFlagBits(PCF_IN_TEMPLATE);
|
|
return;
|
|
}
|
|
}
|
|
LOG_FMT(LTEMPL, "%s(%d): - Not a template: end = %s\n",
|
|
__func__, __LINE__, (end->IsNotNullChunk() ? get_token_name(end->GetType()) : "<null>"));
|
|
start->SetType(CT_COMPARE);
|
|
} // check_template
|
|
|
|
|
|
void check_template_arg(Chunk *start, Chunk *end)
|
|
{
|
|
LOG_FMT(LTEMPL, "%s(%d): Template argument detected\n", __func__, __LINE__);
|
|
LOG_FMT(LTEMPL, "%s(%d): from orig line %zu, orig col %zu\n",
|
|
__func__, __LINE__, start->GetOrigLine(), start->GetOrigCol());
|
|
LOG_FMT(LTEMPL, "%s(%d): to orig line %zu, orig col %zu\n",
|
|
__func__, __LINE__, end->GetOrigLine(), end->GetOrigCol());
|
|
|
|
// Issue #1127
|
|
// MyFoo<mySize * 2> foo1;
|
|
// MyFoo<2*mySize * 2> foo1;
|
|
// Issue #1346
|
|
// use it as ONE line:
|
|
// typename std::enable_if<!std::is_void<T>::value,
|
|
// QVector<T> >::type dummy(const std::function<T*(const S&)>&
|
|
// pFunc, const QVector<S>& pItems)
|
|
// we need two runs
|
|
// 1. run to test if expression is numeric
|
|
bool expressionIsNumeric = false;
|
|
Chunk *pc = start;
|
|
|
|
while (pc != end)
|
|
{
|
|
Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC);
|
|
pc->SetFlagBits(PCF_IN_TEMPLATE);
|
|
|
|
if ( pc->Is(CT_DECLTYPE)
|
|
|| pc->Is(CT_SIZEOF))
|
|
{
|
|
expressionIsNumeric = true;
|
|
break;
|
|
}
|
|
|
|
if (next->IsNot(CT_PAREN_OPEN))
|
|
{
|
|
if ( pc->Is(CT_NUMBER)
|
|
|| pc->Is(CT_ARITH)
|
|
|| pc->Is(CT_SHIFT))
|
|
{
|
|
expressionIsNumeric = true;
|
|
break;
|
|
}
|
|
}
|
|
pc = next;
|
|
}
|
|
LOG_FMT(LTEMPL, "%s(%d): expressionIsNumeric is %s\n",
|
|
__func__, __LINE__, expressionIsNumeric ? "TRUE" : "FALSE");
|
|
|
|
// 2. run to do the work
|
|
if (!expressionIsNumeric)
|
|
{
|
|
pc = start;
|
|
|
|
while (pc != end)
|
|
{
|
|
Chunk *next = pc->GetNextNcNnl(E_Scope::PREPROC);
|
|
pc->SetFlagBits(PCF_IN_TEMPLATE);
|
|
|
|
Chunk *prev = pc->GetPrevNcNnl(E_Scope::PREPROC);
|
|
Chunk *prev2 = prev->GetPrevNcNnl(E_Scope::PREPROC);
|
|
|
|
if ( prev->Is(CT_ELLIPSIS) // Issue #3309
|
|
&& prev2->Is(CT_TYPENAME))
|
|
{
|
|
pc->SetType(CT_PARAMETER_PACK);
|
|
}
|
|
else
|
|
{
|
|
make_type(pc);
|
|
}
|
|
pc = next;
|
|
}
|
|
}
|
|
} // check_template_arg
|
|
|
|
|
|
void check_template_args(Chunk *start, Chunk *end)
|
|
{
|
|
std::vector<E_Token> tokens;
|
|
|
|
// Scan for commas
|
|
Chunk *pc;
|
|
|
|
for (pc = start->GetNextNcNnl(E_Scope::PREPROC);
|
|
pc->IsNotNullChunk() && pc != end;
|
|
pc = pc->GetNextNcNnl(E_Scope::PREPROC))
|
|
{
|
|
switch (pc->GetType())
|
|
{
|
|
case CT_COMMA:
|
|
|
|
if (tokens.empty())
|
|
{
|
|
// Check current argument
|
|
check_template_args(start, pc);
|
|
start = pc;
|
|
}
|
|
break;
|
|
|
|
case CT_ANGLE_OPEN:
|
|
case CT_PAREN_OPEN:
|
|
tokens.push_back(pc->GetType());
|
|
break;
|
|
|
|
case CT_ANGLE_CLOSE:
|
|
|
|
if ( !tokens.empty()
|
|
&& tokens.back() == CT_ANGLE_OPEN)
|
|
{
|
|
tokens.pop_back();
|
|
}
|
|
break;
|
|
|
|
case CT_PAREN_CLOSE:
|
|
|
|
if ( !tokens.empty()
|
|
&& tokens.back() == CT_PAREN_OPEN)
|
|
{
|
|
tokens.pop_back();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check whatever is left
|
|
check_template_arg(start, end);
|
|
} // check_template_args
|