Fixed #1343 (simplifyTypedef doesn't support deeply nested classes)

This commit is contained in:
Robert Reif 2010-02-03 07:58:36 +01:00 committed by Daniel Marjamäki
parent 4ec94116f5
commit f2eac901c0
2 changed files with 197 additions and 44 deletions

View File

@ -366,29 +366,65 @@ void Tokenizer::createTokens(std::istream &code)
} }
} }
// check if this statement is a typedef definition
static bool duplicateTypedef(const Token * tok)
{
// check for a typedef end of definition
if (tok && tok->next() && Token::Match(tok->next(), ";|,|["))
{
// scan backwards for the end of the previous statement
while (tok && tok->previous() && !Token::Match(tok->previous(), ";|{"))
{
if (tok->str() == "typedef")
return true;
tok = tok->previous();
}
}
return false;
}
struct SpaceInfo
{
bool isNamespace;
std::string className;
const Token * classEnd;
};
void Tokenizer::simplifyTypedef() void Tokenizer::simplifyTypedef()
{ {
std::vector<SpaceInfo> spaceInfo;
bool isNamespace = false;
std::string className; std::string className;
int classLevel = 0; bool hasClass = false;
for (Token *tok = _tokens; tok; tok = tok->next()) for (Token *tok = _tokens; tok; tok = tok->next())
{ {
if (Token::Match(tok, "class|struct|namespace %any%")) if (Token::Match(tok, "class|struct|namespace %any%"))
{ {
isNamespace = (tok->str() == "namespace");
hasClass = true;
className = tok->next()->str(); className = tok->next()->str();
classLevel = 0;
continue; continue;
} }
else if (tok->str() == "}") else if (hasClass && tok->str() == ";")
{ {
--classLevel; hasClass = false;
if (classLevel < 0) continue;
className = ""; }
else if (hasClass && tok->str() == "{")
{
SpaceInfo info;
info.isNamespace = isNamespace;
info.className = className;
info.classEnd = tok->link();
spaceInfo.push_back(info);
hasClass = false;
continue; continue;
} }
else if (tok->str() == "{") else if (!spaceInfo.empty() && tok->str() == "}" && spaceInfo.back().classEnd == tok)
{ {
++classLevel; spaceInfo.pop_back();
continue; continue;
} }
else if (tok->str() != "typedef") else if (tok->str() != "typedef")
@ -594,55 +630,88 @@ void Tokenizer::simplifyTypedef()
while (!done) while (!done)
{ {
const std::string pattern(className.empty() ? "" : (className + " :: " + typeName).c_str()); std::string pattern;
int level = 0; int scope = 0;
bool inScope = true; bool inScope = true;
bool exitThisScope = false; bool exitThisScope = false;
int exitScope = 0; int exitScope = 0;
bool simplifyType = false; bool simplifyType = false;
unsigned int classLevel = spaceInfo.size();
for (unsigned int i = classLevel; i < spaceInfo.size(); i++)
pattern += (spaceInfo[i].className + " :: ");
pattern += typeName;
for (Token *tok2 = tok; tok2; tok2 = tok2->next()) for (Token *tok2 = tok; tok2; tok2 = tok2->next())
{ {
if (tok2->str() == "}") if (tok2->str() == "}")
{ {
--level; if (classLevel > 0 && tok2 == spaceInfo[classLevel - 1].classEnd)
if (level < 0) {
inScope = false; --classLevel;
pattern.clear();
if (exitThisScope) for (unsigned int i = classLevel; i < spaceInfo.size(); i++)
{ pattern += (spaceInfo[i].className + " :: ");
if (level < exitScope)
exitThisScope = false; pattern += typeName;
}
}
else if (tok2->str() == "{")
++level;
else if (!pattern.empty() && Token::Match(tok2, pattern.c_str()))
{
tok2->deleteNext();
tok2->deleteNext();
simplifyType = true;
}
else if (inScope && !exitThisScope && tok2->str() == typeName)
{
if (Token::simpleMatch(tok2->previous(), "::"))
{
// Don't replace this typename if it's preceded by "::"
}
else if (Token::Match(tok2->tokAt(-2), "!!typedef") &&
Token::Match(tok2->tokAt(-3), "!!typedef"))
{
// Check for enum and typedef with same name.
if (!hasTemplate && tok2->tokAt(-1)->str() != typeStart->str())
simplifyType = true;
else if (hasTemplate)
simplifyType = true;
} }
else else
{ {
// Typedef with the same name. scope--;
exitThisScope = true; if (scope < 0)
exitScope = level; inScope = false;
if (exitThisScope)
{
if (scope < exitScope)
exitScope = false;
}
}
}
else if (tok2->str() == "{")
scope++;
else if (Token::Match(tok2, pattern.c_str()))
{
if (pattern != typeName) // has a "something ::"
{
for (unsigned int i = classLevel; i < spaceInfo.size(); i++)
{
tok2->deleteNext();
tok2->deleteNext();
}
simplifyType = true;
}
else if (inScope && !exitThisScope)
{
if (Token::simpleMatch(tok2->previous(), "::"))
{
// Don't replace this typename if it's preceded by "::" unless it's a namespace
if (!spaceInfo.empty() && (tok2->tokAt(-2)->str() == spaceInfo[0].className) && spaceInfo[0].isNamespace)
{
tok2 = tok2->tokAt(-3);
tok2->deleteNext();
tok2->deleteNext();
tok2 = tok2->next();
simplifyType = true;
}
}
else if (!duplicateTypedef(tok2))
{
// Check for enum and typedef with same name.
if (!hasTemplate && tok2->tokAt(-1)->str() != typeStart->str())
simplifyType = true;
else if (hasTemplate)
simplifyType = true;
}
else
{
// Typedef with the same name.
exitThisScope = true;
exitScope = scope;
}
} }
} }

View File

@ -168,6 +168,8 @@ private:
TEST_CASE(simplifyTypedef29); TEST_CASE(simplifyTypedef29);
TEST_CASE(simplifyTypedef30); TEST_CASE(simplifyTypedef30);
TEST_CASE(simplifyTypedef31); TEST_CASE(simplifyTypedef31);
TEST_CASE(simplifyTypedef32);
TEST_CASE(simplifyTypedef33);
TEST_CASE(reverseArraySyntax) TEST_CASE(reverseArraySyntax)
TEST_CASE(simplify_numeric_condition) TEST_CASE(simplify_numeric_condition)
@ -3072,6 +3074,88 @@ private:
} }
} }
void simplifyTypedef32()
{
const char code[] = "typedef char CHAR;\n"
"typedef CHAR * LPSTR;\n"
"typedef const CHAR * LPCSTR;\n"
"CHAR c;\n"
"LPSTR cp;\n"
"LPCSTR ccp;";
const char expected[] =
"; "
"; "
"; "
"char c ; "
"char * cp ; "
"const char * ccp ;";
ASSERT_EQUALS(expected, tok(code, false));
}
void simplifyTypedef33()
{
const char code[] = "class A {\n"
"public:\n"
" typedef char CHAR_A;\n"
" CHAR_A funA();\n"
" class B {\n"
" public:\n"
" typedef short SHRT_B;\n"
" SHRT_B funB();\n"
" class C {\n"
" public:\n"
" typedef int INT_C;\n"
" INT_C funC();\n"
" struct D {\n"
" typedef long LONG_D;\n"
" LONG_D funD();\n"
" LONG_D d;\n"
" };\n"
" INT_C c;\n"
" };\n"
" SHRT_B b;\n"
" };\n"
" CHAR_A a;\n"
"};\n"
"A::CHAR_A A::funA() { return a; }\n"
"A::B::SHRT_B A::B::funB() { return b; }\n"
"A::B::C::INT_C A::B::C::funC() { return c; }"
"A::B::C::D::LONG_D A::B::C::D::funD() { return d; }";
const char expected[] =
"class A { "
"public: "
"; "
"char funA ( ) ; "
"class B { "
"public: "
"; "
"short funB ( ) ; "
"class C { "
"public: "
"; "
"int funC ( ) ; "
"struct D { "
"; "
"long funD ( ) ; "
"long d ; "
"} ; "
"int c ; "
"} ; "
"short b ; "
"} ; "
"char a ; "
"} ; "
"char A :: funA ( ) { return a ; } "
"short A :: B :: funB ( ) { return b ; } "
"int A :: B :: C :: funC ( ) { return c ; } "
"long A :: B :: C :: D :: funD ( ) { return d ; }";
ASSERT_EQUALS(expected, tok(code, false));
}
void reverseArraySyntax() void reverseArraySyntax()
{ {
ASSERT_EQUALS("a [ 13 ]", tok("13[a]")); ASSERT_EQUALS("a [ 13 ]", tok("13[a]"));