diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 5a1d23f43..1b579d0ed 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -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; diff --git a/lib/checkother.cpp b/lib/checkother.cpp index f434848d6..f2920a46f 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -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; }); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index dad5b14f9..efef99e4a 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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 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 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; } } } diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 33c88aa1e..559256488 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -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" diff --git a/test/testother.cpp b/test/testother.cpp index 38a3a9bd6..f9e58b8ef 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -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" diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index 70cd6448c..35857c0d8 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -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 { " diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 767021a81..d540dfffd 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -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;