Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/token.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2482,7 +2482,7 @@ Token* findLambdaEndScope(Token* tok)
if (!Token::simpleMatch(tok, ")"))
return nullptr;
tok = tok->next();
while (Token::Match(tok, "mutable|constexpr|constval|noexcept|.")) {
while (Token::Match(tok, "mutable|constexpr|consteval|noexcept|.")) {
if (Token::simpleMatch(tok, "noexcept ("))
tok = tok->linkAt(1);
if (Token::simpleMatch(tok, ".")) {
Expand Down
57 changes: 36 additions & 21 deletions lib/tokenlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,9 +652,11 @@ static bool iscpp11init_impl(const Token * const tok)
return false;
if (Token::Match(nameToken, "else|try|do|const|constexpr|override|volatile|&|&&"))
return false;
if (Token::simpleMatch(nameToken->previous(), ". void {") && nameToken->previous()->originalName() == "->")
return false; // trailing return type. The only function body that can contain no semicolon is a void function.
if (Token::simpleMatch(nameToken->previous(), "namespace"))
return false;
if (Token::Match(nameToken, "%any% {") && !Token::Match(nameToken, "return|:")) {
if (endtok != nullptr && !Token::Match(nameToken, "return|:")) {
// If there is semicolon between {..} this is not a initlist
for (const Token *tok2 = nameToken->next(); tok2 != endtok; tok2 = tok2->next()) {
if (tok2->str() == ";")
Expand All @@ -668,7 +670,7 @@ static bool iscpp11init_impl(const Token * const tok)
if (!Token::simpleMatch(endtok, "} ;"))
return true;
const Token *prev = nameToken;
while (Token::Match(prev, "%name%|::|:|<|>")) {
while (Token::Match(prev, "%name%|::|:|<|>|,")) {
if (Token::Match(prev, "class|struct"))
return false;

Expand Down Expand Up @@ -785,10 +787,15 @@ static void compileTerm(Token *&tok, AST_state& state)
tok = tok->link()->next();

if (Token::Match(tok, "{ . %name% =|{")) {
const Token* end = tok->link();
const int inArrayAssignment = state.inArrayAssignment;
state.inArrayAssignment = 1;
compileBinOp(tok, state, compileExpression);
state.inArrayAssignment = inArrayAssignment;
if (tok == end)
tok = tok->next();
else
throw InternalError(tok, "Syntax error. Unexpected tokens in designated initializer.", InternalError::AST);
} else if (Token::simpleMatch(tok, "{ }")) {
tok->astOperand1(state.op.top());
state.op.pop();
Expand Down Expand Up @@ -834,26 +841,26 @@ static void compileTerm(Token *&tok, AST_state& state)
if (Token::simpleMatch(tok->link(),"} [")) {
tok = tok->next();
} else if (state.cpp && iscpp11init(tok)) {
Token *const end = tok->link();
if (state.op.empty() || Token::Match(tok->previous(), "[{,]") || Token::Match(tok->tokAt(-2), "%name% (")) {
if (Token::Match(tok, "{ !!}")) {
Token *const end = tok->link();
if (Token::Match(tok, "{ . %name% =|{")) {
const int inArrayAssignment = state.inArrayAssignment;
state.inArrayAssignment = 1;
compileBinOp(tok, state, compileExpression);
state.inArrayAssignment = inArrayAssignment;
} else {
compileUnaryOp(tok, state, compileExpression);
}
if (precedes(tok,end))
tok = end;
} else {
if (Token::Match(tok, "{ . %name% =|{")) {
const int inArrayAssignment = state.inArrayAssignment;
state.inArrayAssignment = 1;
compileBinOp(tok, state, compileExpression);
state.inArrayAssignment = inArrayAssignment;
} else if (Token::simpleMatch(tok, "{ }")) {
state.op.push(tok);
tok = tok->tokAt(2);
tok = tok->next();
} else {
compileUnaryOp(tok, state, compileExpression);
if (precedes(tok,end)) // typically for something like `MACRO(x, { if (c) { ... } })`, where end is the last curly, and tok is the open curly for the if
tok = end;
}
} else
compileBinOp(tok, state, compileExpression);
if (Token::Match(tok, "} ,|:|)"))
if (tok != end)
throw InternalError(tok, "Syntax error. Unexpected tokens in initializer.", InternalError::AST);
if (tok->next())
tok = tok->next();
} else if (state.cpp && Token::Match(tok->tokAt(-2), "%name% ( {") && !Token::findsimplematch(tok, ";", tok->link())) {
if (Token::simpleMatch(tok, "{ }"))
Expand Down Expand Up @@ -966,7 +973,7 @@ static void compilePrecedence2(Token *&tok, AST_state& state)
if (Token::simpleMatch(squareBracket->link(), "] (")) {
Token* const roundBracket = squareBracket->link()->next();
Token* curlyBracket = roundBracket->link()->next();
while (Token::Match(curlyBracket, "mutable|const|constexpr"))
while (Token::Match(curlyBracket, "mutable|const|constexpr|consteval"))
curlyBracket = curlyBracket->next();
if (Token::simpleMatch(curlyBracket, "noexcept ("))
curlyBracket = curlyBracket->linkAt(1)->next();
Expand Down Expand Up @@ -1025,12 +1032,20 @@ static void compilePrecedence2(Token *&tok, AST_state& state)
cast->astOperand1(tok1);
tok = tok1->link()->next();
} else if (state.cpp && tok->str() == "{" && iscpp11init(tok)) {
const Token* end = tok->link();
if (Token::simpleMatch(tok, "{ }"))
compileUnaryOp(tok, state, compileExpression);
{
compileUnaryOp(tok, state, nullptr);
tok = tok->next();
}
else
{
compileBinOp(tok, state, compileExpression);
while (Token::simpleMatch(tok, "}"))
tok = tok->next();
}
if (tok == end)
tok = end->next();
else
throw InternalError(tok, "Syntax error. Unexpected tokens in initializer.", InternalError::AST);
} else break;
}
}
Expand Down
8 changes: 4 additions & 4 deletions test/testsimplifytemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4854,14 +4854,14 @@ class TestSimplifyTemplate : public TestFixture {
// "no constructor" false positives
const char code[] = "class Fred {\n"
" template<class T> explicit Fred(T t) { }\n"
"}";
ASSERT_EQUALS("class Fred { template < class T > explicit Fred ( T t ) { } }", tok(code));
"};";
ASSERT_EQUALS("class Fred { template < class T > explicit Fred ( T t ) { } } ;", tok(code));

// #3532
const char code2[] = "class Fred {\n"
" template<class T> Fred(T t) { }\n"
"}";
ASSERT_EQUALS("class Fred { template < class T > Fred ( T t ) { } }", tok(code2));
"};";
ASSERT_EQUALS("class Fred { template < class T > Fred ( T t ) { } } ;", tok(code2));
}

void syntax_error_templates_1() {
Expand Down
4 changes: 2 additions & 2 deletions test/testsimplifytypedef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1427,14 +1427,14 @@ class TestSimplifyTypedef : public TestFixture {
"typedef const Class & Const_Reference;\n"
"void some_method (Const_Reference x) const {}\n"
"void another_method (Const_Reference x) const {}\n"
"}";
"};";

// The expected result..
const char expected[] = "class Class2 { "
""
"void some_method ( const Class & x ) const { } "
"void another_method ( const Class & x ) const { } "
"}";
"} ;";
ASSERT_EQUALS(expected, tok(code));
}

Expand Down
78 changes: 72 additions & 6 deletions test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ class TestTokenizer : public TestFixture {
TEST_CASE(simplifyIfSwitchForInit5);

TEST_CASE(cpp20_default_bitfield_initializer);

TEST_CASE(cpp11init);
}

#define tokenizeAndStringify(...) tokenizeAndStringify_(__FILE__, __LINE__, __VA_ARGS__)
Expand Down Expand Up @@ -6228,8 +6230,8 @@ class TestTokenizer : public TestFixture {
ASSERT_EQUALS("abR{{,P(,((", testAst("a(b(R{},{},P()));"));
ASSERT_EQUALS("f1{2{,3{,{x,(", testAst("f({{1},{2},{3}},x);"));
ASSERT_EQUALS("a1{ b2{", testAst("auto a{1}; auto b{2};"));
ASSERT_EQUALS("var1ab::23,{,4ab::56,{,{,{{", testAst("auto var{{1,a::b{2,3}}, {4,a::b{5,6}}};"));
ASSERT_EQUALS("var{{,{,{{", testAst("auto var{ {{},{}}, {} };"));
ASSERT_EQUALS("var1ab::23,{,{4ab::56,{,{,{", testAst("auto var{{1,a::b{2,3}}, {4,a::b{5,6}}};"));
ASSERT_EQUALS("var{{,{{,{", testAst("auto var{ {{},{}}, {} };"));
ASSERT_EQUALS("fXYabcfalse==CD:?,{,{(", testAst("f({X, {Y, abc == false ? C : D}});"));
ASSERT_EQUALS("stdvector::p0[{(return", testAst("return std::vector<int>({ p[0] });"));

Expand Down Expand Up @@ -6465,7 +6467,7 @@ class TestTokenizer : public TestFixture {
"}"));
ASSERT_EQUALS("{(=[{return ab=",
testAst("return {\n"
" [=]() mutable -> int {\n"
" [=]() mutable consteval -> int {\n"
" a=b;\n"
" }\n"
"}"));
Expand Down Expand Up @@ -6690,12 +6692,31 @@ class TestTokenizer : public TestFixture {
"}; "
"struct poc p = { .port[0] = {.d = 3} };"));

// op op
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { dostuff (x==>y); }"), InternalError, "syntax error: == >");

// Ticket #9664
ASSERT_NO_THROW(tokenizeAndStringify("S s = { .x { 2 }, .y[0] { 3 } };"));

// Ticket #11134
ASSERT_NO_THROW(tokenizeAndStringify("struct my_struct { int x; }; "
"std::string s; "
"func(my_struct{ .x=42 }, s.size());"));
ASSERT_NO_THROW(tokenizeAndStringify("struct my_struct { int x; int y; }; "
"std::string s; "
"func(my_struct{ .x{42}, .y=3 }, s.size());"));
ASSERT_NO_THROW(tokenizeAndStringify("struct my_struct { int x; int y; }; "
"std::string s; "
"func(my_struct{ .x=42, .y{3} }, s.size());"));
ASSERT_NO_THROW(tokenizeAndStringify("struct my_struct { int x; }; "
"void h() { "
" for (my_struct ms : { my_struct{ .x=5 } }) {} "
"}"));
ASSERT_NO_THROW(tokenizeAndStringify("struct my_struct { int x; int y; }; "
"void h() { "
" for (my_struct ms : { my_struct{ .x=5, .y{42} } }) {} "
"}"));

// op op
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { dostuff (x==>y); }"), InternalError, "syntax error: == >");

ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { assert(a==()); }"), InternalError, "syntax error: ==()");
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { assert(a+()); }"), InternalError, "syntax error: +()");

Expand Down Expand Up @@ -7382,6 +7403,51 @@ class TestTokenizer : public TestFixture {
settings.standards.cpp = Standards::CPP17;
ASSERT_THROW(tokenizeAndStringify(code, settings), InternalError);
}

void cpp11init() {
#define testIsCpp11init(...) testIsCpp11init_(__FILE__, __LINE__, __VA_ARGS__)
auto testIsCpp11init_ = [this](const char* file, int line, const char* code, const char* find, TokenImpl::Cpp11init expected) {
Settings settings;
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

const Token* tok = Token::findsimplematch(tokenizer.tokens(), find, strlen(find));
ASSERT_LOC(tok, file, line);
ASSERT_LOC(tok->isCpp11init() == expected, file, line);
};

testIsCpp11init("class X : public A<int>, C::D {};",
"D {",
TokenImpl::Cpp11init::NOINIT);

testIsCpp11init("auto f() -> void {}",
"void {",
TokenImpl::Cpp11init::NOINIT);
testIsCpp11init("auto f() & -> void {}",
"void {",
TokenImpl::Cpp11init::NOINIT);
testIsCpp11init("auto f() const noexcept(false) -> void {}",
"void {",
TokenImpl::Cpp11init::NOINIT);
testIsCpp11init("auto f() -> std::vector<int> { return {}; }",
"{ return",
TokenImpl::Cpp11init::NOINIT);
testIsCpp11init("auto f() -> std::vector<int> { return {}; }",
"vector",
TokenImpl::Cpp11init::NOINIT);
testIsCpp11init("auto f() -> std::vector<int> { return {}; }",
"std ::",
TokenImpl::Cpp11init::NOINIT);

testIsCpp11init("class X{};",
"{ }",
TokenImpl::Cpp11init::NOINIT);
testIsCpp11init("class X{}", // forgotten ; so not properly recognized as a class
"{ }",
TokenImpl::Cpp11init::CPP11INIT);
#undef testIsCpp11init
}
};

REGISTER_TEST(TestTokenizer)
4 changes: 2 additions & 2 deletions test/testvarid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -790,14 +790,14 @@ class TestVarID : public TestFixture {
" : ExecutionPath(c, id)\n"
" {\n"
" }\n"
"}\n";
"};\n";
const char expected3[] = "1: class Nullpointer : public ExecutionPath\n"
"2: {\n"
"3: Nullpointer ( Check * c@1 , const unsigned int id@2 , const std :: string & name@3 )\n"
"4: : ExecutionPath ( c@1 , id@2 )\n"
"5: {\n"
"6: }\n"
"7: }\n";
"7: } ;\n";
ASSERT_EQUALS(expected3, tokenize(code3));
}

Expand Down