diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index f4ef10c49d9..ce3e2a60af4 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -451,6 +451,7 @@ namespace { bool cpp; int assign{}; bool inCase{}; // true from case to : + bool inGeneric{}; bool stopAtColon{}; // help to properly parse ternary operators const Token* functionCallEndPar{}; explicit AST_state(bool cpp) : cpp(cpp) {} @@ -706,11 +707,31 @@ static void compileUnaryOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, state.op.push(unaryop); } +static void skipGenericType(Token *&tok) +{ + Token *skip = tok; + while (Token::Match(skip, "%name%|*|:|(")) { + if (skip->link()) { + skip = skip->link()->next(); + continue; + } + if (Token::simpleMatch(skip, ":")) { + tok = skip->next(); + return; + } + skip = skip->next(); + } +} + static void compileBinOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, AST_state& state)) { Token *binop = tok; if (f) { tok = tok->next(); + if (Token::simpleMatch(binop, ",") && state.inGeneric) + skipGenericType(tok); + const bool inGenericSaved = state.inGeneric; + state.inGeneric = false; if (Token::Match(binop, "::|. ~")) tok = tok->next(); state.depth++; @@ -719,6 +740,7 @@ static void compileBinOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, A if (state.depth > AST_MAX_DEPTH) throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); state.depth--; + state.inGeneric = inGenericSaved; } // TODO: Should we check if op is empty. @@ -1048,6 +1070,9 @@ static void compilePrecedence2(Token *&tok, AST_state& state) continue; } else if (tok->str() == "(" && (!iscast(tok, state.cpp) || Token::Match(tok->previous(), "if|while|for|switch|catch"))) { + const bool inGenericSaved = state.inGeneric; + if (Token::simpleMatch(tok->previous(), "_Generic")) + state.inGeneric = true; Token* tok2 = tok; tok = tok->next(); const bool opPrevTopSquare = !state.op.empty() && state.op.top() && state.op.top()->str() == "["; @@ -1066,6 +1091,7 @@ static void compilePrecedence2(Token *&tok, AST_state& state) else compileUnaryOp(tok, state, nullptr); } + state.inGeneric = inGenericSaved; tok = tok->link()->next(); if (Token::simpleMatch(tok, "::")) compileBinOp(tok, state, compileTerm); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index aa3dfff6aaf..9e0b3e71757 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -464,6 +464,8 @@ class TestTokenizer : public TestFixture { TEST_CASE(funcnameInParenthesis1); // #13554 TEST_CASE(funcnameInParenthesis2); // #13578 TEST_CASE(funcnameInParenthesis3); // #13585 + + TEST_CASE(genericInIf); // #13561 } #define tokenizeAndStringify(...) tokenizeAndStringify_(__FILE__, __LINE__, __VA_ARGS__) @@ -8389,6 +8391,12 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS(par->astOperand1(), f->astParent() /* :: */); ASSERT(Token::simpleMatch(par->astOperand2(), ",")); } + + void genericInIf() { // #13561 + const char code[] = " if (_Generic(s, char * : 1, const float * : (a ? b, c : d), volatile int * : 3, default : 0)) {}"; + const char ast[] = "(( if (( _Generic (, (, (, (, s 1) (? a (: (, b c) d))) 3) 0)))"; + ASSERT_EQUALS(ast, testAst(code, AstStyle::Z3)); + } }; REGISTER_TEST(TestTokenizer)