Skip to content

Commit

Permalink
fix #13561: Syntax error reported when a _Generic is used in if condi…
Browse files Browse the repository at this point in the history
…tion (#7282)
  • Loading branch information
ludviggunne authored Feb 6, 2025
1 parent 83c9a57 commit 103fb2d
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
26 changes: 26 additions & 0 deletions lib/tokenlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down Expand Up @@ -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++;
Expand All @@ -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.
Expand Down Expand Up @@ -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() == "[";
Expand All @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 103fb2d

Please sign in to comment.