Fixed #11013 (FP overlappingWriteUnion with anonymous struct in union) (#5067)

This commit is contained in:
Daniel Marjamäki 2023-05-20 10:34:42 +02:00 committed by GitHub
parent e6576dd949
commit f72db74817
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 98 deletions

View File

@ -221,13 +221,15 @@ void CheckClass::constructors()
if (!usage.assign && !usage.init)
continue;
const Scope* varScope1 = var.nameToken()->scope();
while (varScope1->type == Scope::ScopeType::eStruct)
varScope1 = varScope1->nestedIn;
if (varScope1->type == Scope::ScopeType::eUnion) {
for (Usage &usage2 : usageList) {
const Variable& var2 = *usage2.var;
if (usage2.assign || usage2.init || var2.isStatic())
continue;
const Scope* varScope2 = var2.nameToken()->scope();
if (varScope2->type == Scope::ScopeType::eStruct)
while (varScope2->type == Scope::ScopeType::eStruct)
varScope2 = varScope2->nestedIn;
if (varScope1 == varScope2)
usage2.assign = true;

View File

@ -3842,6 +3842,12 @@ void CheckOther::checkOverlappingWrite()
return ChildrenToVisit::none;
if (lhsmember->str() == rhs->astOperand2()->str())
return ChildrenToVisit::none;
const Variable* rhsmembervar = rhs->astOperand2()->variable();
const Scope* varscope1 = lhsmember->variable() ? lhsmember->variable()->typeStartToken()->scope() : nullptr;
const Scope* varscope2 = rhsmembervar ? rhsmembervar->typeStartToken()->scope() : nullptr;
if (varscope1 && varscope1 == varscope2 && varscope1 != lhsvar->typeScope())
// lhsmember and rhsmember are declared in same anonymous scope inside union
return ChildrenToVisit::none;
errorToken = rhs->astOperand2();
return ChildrenToVisit::done;
});

View File

@ -8550,10 +8550,6 @@ void Tokenizer::simplifyStructDecl()
// A counter that is used when giving unique names for anonymous structs.
int count = 0;
// Skip simplification of unions in class definition
std::stack<bool> skip; // true = in function, false = not in function
skip.push(false);
// Add names for anonymous structs
for (Token *tok = list.front(); tok; tok = tok->next()) {
if (!tok->isName())
@ -8585,15 +8581,26 @@ void Tokenizer::simplifyStructDecl()
}
}
// "{" token for current scope
std::stack<const Token*> scopeStart;
const Token* functionEnd = nullptr;
for (Token *tok = list.front(); tok; tok = tok->next()) {
// check for start of scope and determine if it is in a function
if (tok->str() == "{")
skip.push(Token::Match(tok->previous(), "const|)"));
if (tok->str() == "{") {
scopeStart.push(tok);
if (!functionEnd && Token::Match(tok->previous(), "const|)"))
functionEnd = tok->link();
}
// end of scope
else if (tok->str() == "}" && !skip.empty())
skip.pop();
else if (tok->str() == "}") {
if (!scopeStart.empty())
scopeStart.pop();
if (tok == functionEnd)
functionEnd = nullptr;
}
// check for named struct/union
else if (Token::Match(tok, "class|struct|union|enum %type% :|{")) {
@ -8607,120 +8614,82 @@ void Tokenizer::simplifyStructDecl()
next = next->next();
if (!next)
continue;
skip.push(false);
tok = next->link();
if (!tok)
Token* after = next->link();
if (!after)
break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code)
Token *restart = next;
// check for named type
if (Token::Match(tok->next(), "const|static|volatile| *|&| const| (| %type% )| ,|;|[|=|(|{")) {
tok->insertToken(";");
tok = tok->next();
if (Token::Match(after->next(), "const|static|volatile| *|&| const| (| %type% )| ,|;|[|=|(|{")) {
after->insertToken(";");
after = after->next();
while (!Token::Match(start, "struct|class|union|enum")) {
tok->insertToken(start->str());
tok = tok->next();
after->insertToken(start->str());
after = after->next();
start->deleteThis();
}
if (!tok)
tok = start;
if (!after)
break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code)
tok->insertToken(type->str());
after->insertToken(type->str());
if (start->str() != "class") {
tok->insertToken(start->str());
tok = tok->next();
after->insertToken(start->str());
after = after->next();
}
tok = tok->tokAt(2);
after = after->tokAt(2);
if (Token::Match(tok, "( %type% )")) {
tok->link()->deleteThis();
tok->deleteThis();
if (Token::Match(after, "( %type% )")) {
after->link()->deleteThis();
after->deleteThis();
}
// check for initialization
if (tok && (tok->next()->str() == "(" || tok->next()->str() == "{")) {
tok->insertToken("=");
tok = tok->next();
if (Token::Match(after, "%any% (|{")) {
after->insertToken("=");
after = after->next();
const bool isEnum = start->str() == "enum";
if (!isEnum && cpp) {
tok->insertToken(type->str());
tok = tok->next();
after->insertToken(type->str());
after = after->next();
}
if (isEnum) {
if (tok->next()->str() == "{" && tok->next()->link() != tok->tokAt(2)) {
tok->next()->str("(");
tok->linkAt(1)->str(")");
if (Token::Match(after->next(), "{ !!}")) {
after->next()->str("(");
after->linkAt(1)->str(")");
}
}
}
}
tok = restart;
}
// check for anonymous struct/union
else if (Token::Match(tok, "struct|union {")) {
const bool inFunction = skip.top();
skip.push(false);
Token *tok1 = tok;
Token *restart = tok->next();
tok = tok->next()->link();
else {
// unnamed anonymous struct/union so possibly remove it
if (tok && tok->next() && tok->next()->str() == ";") {
if (inFunction && tok1->str() == "union") {
// Try to create references in the union..
Token *tok2 = tok1->tokAt(2);
while (tok2) {
if (Token::Match(tok2, "%type% %name% ;"))
tok2 = tok2->tokAt(3);
else
bool done = false;
while (!done && Token::Match(tok, "struct|union {") && Token::simpleMatch(tok->linkAt(1), "} ;")) {
done = true;
// is this a class/struct/union scope?
bool isClassStructUnionScope = false;
if (!scopeStart.empty()) {
for (const Token* tok2 = scopeStart.top()->previous(); tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->previous()) {
if (Token::Match(tok2, "class|struct|union")) {
isClassStructUnionScope = true;
break;
}
if (!Token::simpleMatch(tok2, "} ;"))
continue;
Token *vartok = nullptr;
tok2 = tok1->tokAt(2);
while (Token::Match(tok2, "%type% %name% ;")) {
if (!vartok) {
vartok = tok2->next();
tok2 = tok2->tokAt(3);
} else {
tok2->insertToken("&");
tok2 = tok2->tokAt(2);
tok2->insertToken(vartok->str());
tok2->next()->varId(vartok->varId());
tok2->insertToken("=");
tok2 = tok2->tokAt(4);
}
}
}
// don't remove unnamed anonymous unions from a class, struct or union
if (!(!inFunction && tok1->str() == "union") && !Token::Match(tok1->tokAt(-3), "using %name% =")) {
skip.pop();
tok1->deleteThis();
if (tok1->next() == tok) {
tok1->deleteThis();
tok = tok1;
} else
tok1->deleteThis();
restart = tok1->previous();
// remove unnamed anonymous struct/union
// * not in class/struct/union scopes
if (Token::simpleMatch(tok->linkAt(1), "} ;") && !isClassStructUnionScope && tok->str() != "union") {
tok->linkAt(1)->previous()->deleteNext(2);
tok->deleteNext();
tok->deleteThis();
if (tok->next())
tok->deleteThis();
done = false;
}
}
if (!restart) {
simplifyStructDecl();
return;
} else if (!restart->next())
return;
tok = restart;
}
}
}

View File

@ -1473,6 +1473,22 @@ private:
"};");
ASSERT_EQUALS("", errout.str());
check("struct S {\n"
" union {\n"
" unsigned short all;\n"
" struct {\n"
" unsigned char flag1;\n"
" unsigned char flag2;\n"
" };\n"
" };\n"
"};\n"
"\n"
"class C : public S {\n"
"public:\n"
" C() { flag1 = flag2 = 0; tick = 0; }\n"
"};");
ASSERT_EQUALS("", errout.str());
check("struct S {\n"
" union {\n"
" unsigned short all;\n"

View File

@ -11059,6 +11059,12 @@ private:
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Overlapping read/write of union is undefined behavior\n", errout.str());
check("void foo() {\n" // #11013
" union { struct { uint8_t a; uint8_t b; }; uint16_t c; } u;\n"
" u.a = u.b = 0;\n"
"}");
ASSERT_EQUALS("", errout.str());
// memcpy
check("void foo() {\n"
" char a[10];\n"

View File

@ -1200,12 +1200,6 @@ private:
const char expected[] = "class { } ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "class { struct { struct { } ; } ; };";
const char expected[] = "class { } ;";
ASSERT_EQUALS(expected, tok(code));
}
}
void simplifyStructDecl4() {
@ -1224,8 +1218,7 @@ private:
"} abc;\n";
const char expected[] = "class ABC { "
"void foo ( ) { "
"int i ; "
"float & f = i ; "
"union { int i ; float f ; } ; "
"struct Fee { } ; struct Fee fee ; "
"} "
"union { "

View File

@ -1973,6 +1973,11 @@ private:
const char code3[] = "struct a { int b; } static e[1];";
const char expected3[] = "struct a { int b ; } ; struct a static e [ 1 ] ;";
ASSERT_EQUALS(expected3, tokenizeAndStringify(code3));
// #11013 - Do not remove unnamed struct in union
const char code4[] = "union U { struct { int a; int b; }; int ab[2]; };";
const char expected4[] = "union U { struct { int a ; int b ; } ; int ab [ 2 ] ; } ;";
ASSERT_EQUALS(expected4, tokenizeAndStringify(code4));
}
void vardecl1() {
@ -2090,7 +2095,7 @@ private:
" long y;\n"
" };\n"
"}";
ASSERT_EQUALS("void f ( ) {\n\nint x ;\nlong & y = x ;\n\n}", tokenizeAndStringify(code2));
ASSERT_EQUALS("void f ( ) {\nunion {\nint x ;\nlong y ;\n} ;\n}", tokenizeAndStringify(code2));
// ticket #3927
const char code3[] = "union xy *p = NULL;";
@ -4539,7 +4544,7 @@ private:
}
void bitfields13() { // ticket #3502 (segmentation fault)
ASSERT_EQUALS("x y ;", tokenizeAndStringify("struct{x y:};\n"));
ASSERT_NO_THROW(tokenizeAndStringify("struct{x y:};\n"));
}
void bitfields15() { // #7747 - enum Foo {A,B}:4;