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) if (!usage.assign && !usage.init)
continue; continue;
const Scope* varScope1 = var.nameToken()->scope(); const Scope* varScope1 = var.nameToken()->scope();
while (varScope1->type == Scope::ScopeType::eStruct)
varScope1 = varScope1->nestedIn;
if (varScope1->type == Scope::ScopeType::eUnion) { if (varScope1->type == Scope::ScopeType::eUnion) {
for (Usage &usage2 : usageList) { for (Usage &usage2 : usageList) {
const Variable& var2 = *usage2.var; const Variable& var2 = *usage2.var;
if (usage2.assign || usage2.init || var2.isStatic()) if (usage2.assign || usage2.init || var2.isStatic())
continue; continue;
const Scope* varScope2 = var2.nameToken()->scope(); const Scope* varScope2 = var2.nameToken()->scope();
if (varScope2->type == Scope::ScopeType::eStruct) while (varScope2->type == Scope::ScopeType::eStruct)
varScope2 = varScope2->nestedIn; varScope2 = varScope2->nestedIn;
if (varScope1 == varScope2) if (varScope1 == varScope2)
usage2.assign = true; usage2.assign = true;

View File

@ -3842,6 +3842,12 @@ void CheckOther::checkOverlappingWrite()
return ChildrenToVisit::none; return ChildrenToVisit::none;
if (lhsmember->str() == rhs->astOperand2()->str()) if (lhsmember->str() == rhs->astOperand2()->str())
return ChildrenToVisit::none; 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(); errorToken = rhs->astOperand2();
return ChildrenToVisit::done; return ChildrenToVisit::done;
}); });

View File

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

View File

@ -1473,6 +1473,22 @@ private:
"};"); "};");
ASSERT_EQUALS("", errout.str()); 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" check("struct S {\n"
" union {\n" " union {\n"
" unsigned short all;\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()); 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 // memcpy
check("void foo() {\n" check("void foo() {\n"
" char a[10];\n" " char a[10];\n"

View File

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

View File

@ -1973,6 +1973,11 @@ private:
const char code3[] = "struct a { int b; } static e[1];"; const char code3[] = "struct a { int b; } static e[1];";
const char expected3[] = "struct a { int b ; } ; struct a static e [ 1 ] ;"; const char expected3[] = "struct a { int b ; } ; struct a static e [ 1 ] ;";
ASSERT_EQUALS(expected3, tokenizeAndStringify(code3)); 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() { void vardecl1() {
@ -2090,7 +2095,7 @@ private:
" long y;\n" " long y;\n"
" };\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 // ticket #3927
const char code3[] = "union xy *p = NULL;"; const char code3[] = "union xy *p = NULL;";
@ -4539,7 +4544,7 @@ private:
} }
void bitfields13() { // ticket #3502 (segmentation fault) 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; void bitfields15() { // #7747 - enum Foo {A,B}:4;