Fixed #1275 (Cppcheck hangs when analysing the code 'typedef struct { } A, *B, **C;')
This commit is contained in:
parent
2851e76414
commit
70dbb8ba17
490
lib/tokenize.cpp
490
lib/tokenize.cpp
|
@ -447,255 +447,294 @@ void Tokenizer::simplifyTypedef()
|
|||
const char *type2 = 0;
|
||||
const char *typeName = 0;
|
||||
bool pointer = false;
|
||||
bool toPointer = false;
|
||||
Token *start = 0;
|
||||
Token *end = 0;
|
||||
Token *num = 0;
|
||||
Token *typeDef = tok;
|
||||
|
||||
if (Token::Match(tok->next(), "%type% <") ||
|
||||
Token::Match(tok->next(), "%type% %type% <") ||
|
||||
Token::Match(tok->next(), "%type% :: %type% <") ||
|
||||
Token::Match(tok->next(), "%type% *| %type% ;|,") ||
|
||||
Token::Match(tok->next(), "%type% %type% *| %type% ;|,") ||
|
||||
Token::Match(tok->next(), "%type% *| %type% [ %num% ] ;|,") ||
|
||||
Token::Match(tok->next(), "%type% %type% *| %type% [ %num% ] ;|,"))
|
||||
Token::Match(tok->next(), "%type% %type% :: %type% <"))
|
||||
{
|
||||
if ((tok->tokAt(2)->str() == "<") ||
|
||||
(tok->tokAt(4) && (tok->tokAt(4)->str() == "<")))
|
||||
// template
|
||||
int level = 1;
|
||||
start = tok->next();
|
||||
|
||||
if (tok->tokAt(2)->str() == "<")
|
||||
end = tok->tokAt(3);
|
||||
else if (tok->tokAt(3)->str() == "<")
|
||||
end = tok->tokAt(4);
|
||||
else if (tok->tokAt(4)->str() == "<")
|
||||
end = tok->tokAt(5);
|
||||
else
|
||||
end = tok->tokAt(6);
|
||||
|
||||
int paren = 0;
|
||||
for (; end ; end = end->next())
|
||||
{
|
||||
int level = 1;
|
||||
start = tok->next();
|
||||
|
||||
if (tok->tokAt(2)->str() == "<")
|
||||
end = tok->tokAt(3);
|
||||
else
|
||||
end = tok->tokAt(5);
|
||||
|
||||
for (; end ; end = end->next())
|
||||
if (end->str() == ">")
|
||||
{
|
||||
if (end->str() == ">")
|
||||
if (paren == 0)
|
||||
{
|
||||
level--;
|
||||
if (level == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (end->str() == "<")
|
||||
}
|
||||
else if (end->str() == "<")
|
||||
{
|
||||
if (paren == 0)
|
||||
level++;
|
||||
}
|
||||
|
||||
while (end && end->next() && Token::Match(end->next(), ":: %type%"))
|
||||
end = end->tokAt(2);
|
||||
|
||||
if (end && end->next() && Token::Match(end->next(), "%type% ;|,"))
|
||||
{
|
||||
typeName = end->strAt(1);
|
||||
tok = end->tokAt(2);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
else if (end->str() == "(")
|
||||
paren++;
|
||||
else if (end->str() == ")")
|
||||
paren--;
|
||||
}
|
||||
else if ((tok->tokAt(3) && tok->tokAt(3)->str() == "[") ||
|
||||
(tok->tokAt(4) && tok->tokAt(4)->str() == "[") ||
|
||||
(tok->tokAt(5) && tok->tokAt(5)->str() == "["))
|
||||
{
|
||||
type1 = tok->strAt(1);
|
||||
|
||||
if ((tok->tokAt(4) && tok->tokAt(4)->str() == "[" && tok->tokAt(2)->str() != "*") ||
|
||||
(tok->tokAt(5) && tok->tokAt(5)->str() == "["))
|
||||
{
|
||||
type2 = tok->strAt(2);
|
||||
pointer = (tok->tokAt(3)->str() == "*");
|
||||
while (end && end->next() && Token::Match(end->next(), ":: %type%"))
|
||||
end = end->tokAt(2);
|
||||
|
||||
if (pointer)
|
||||
{
|
||||
typeName = tok->strAt(4);
|
||||
num = tok->tokAt(6);
|
||||
tok = tok->tokAt(8);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = tok->strAt(3);
|
||||
num = tok->tokAt(5);
|
||||
tok = tok->tokAt(7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pointer = (tok->tokAt(2)->str() == "*");
|
||||
if (pointer)
|
||||
{
|
||||
typeName = tok->strAt(3);
|
||||
num = tok->tokAt(5);
|
||||
tok = tok->tokAt(7);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = tok->strAt(2);
|
||||
num = tok->tokAt(4);
|
||||
tok = tok->tokAt(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tok->tokAt(3)->str() == ";" ||
|
||||
tok->tokAt(3)->str() == ",")
|
||||
{
|
||||
type1 = tok->strAt(1);
|
||||
type2 = 0;
|
||||
typeName = tok->strAt(2);
|
||||
tok = tok->tokAt(3);
|
||||
}
|
||||
else if (tok->tokAt(2)->str() == "*")
|
||||
if (end && end->next() && end->next()->str() == "*")
|
||||
{
|
||||
pointer = true;
|
||||
type1 = tok->strAt(1);
|
||||
type2 = 0;
|
||||
typeName = tok->strAt(3);
|
||||
tok = tok->tokAt(4);
|
||||
end = end->next();
|
||||
}
|
||||
|
||||
if (end && end->next() && end->next()->str() == "*")
|
||||
{
|
||||
toPointer = true;
|
||||
end = end->next();
|
||||
}
|
||||
|
||||
if (end && end->next() && (Token::Match(end->next(), "%type% ;|,") || Token::Match(end->next(), "%type% [ %num% ] ;|,")))
|
||||
{
|
||||
typeName = end->strAt(1);
|
||||
|
||||
if (end->tokAt(2)->str() == "[")
|
||||
{
|
||||
num = end->tokAt(3);
|
||||
tok = end->tokAt(5);
|
||||
}
|
||||
else
|
||||
tok = end->tokAt(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
pointer = (tok->tokAt(3)->str() == "*");
|
||||
// unhandled template typedef, skip it and continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (Token::Match(tok->next(), "%type% *| *| %type% [ %num% ] ;|,") ||
|
||||
Token::Match(tok->next(), "%type% %type% *| *| %type% [ %num% ] ;|,"))
|
||||
{
|
||||
// array
|
||||
int offset = 2;
|
||||
type1 = tok->strAt(1);
|
||||
|
||||
type1 = tok->strAt(1);
|
||||
if (tok->tokAt(2)->str() != "*" && tok->tokAt(3)->str() != "[")
|
||||
{
|
||||
type2 = tok->strAt(2);
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (pointer)
|
||||
if (tok->tokAt(offset) && (tok->tokAt(offset)->str() == "*"))
|
||||
{
|
||||
pointer = true;
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (tok->tokAt(offset) && (tok->tokAt(offset)->str() == "*"))
|
||||
{
|
||||
toPointer = true;
|
||||
offset++;
|
||||
}
|
||||
|
||||
typeName = tok->strAt(offset);
|
||||
num = tok->tokAt(offset + 2);
|
||||
tok = tok->tokAt(offset + 4);
|
||||
}
|
||||
else if (Token::Match(tok->next(), "%type% *| *| %type% ;|,") ||
|
||||
Token::Match(tok->next(), "%type% %type% *| *| %type% ;|,"))
|
||||
{
|
||||
int offset = 2;
|
||||
type1 = tok->strAt(1);
|
||||
|
||||
if (tok->tokAt(2)->str() != "*" && (tok->tokAt(3)->str() != ";" && tok->tokAt(3)->str() != ","))
|
||||
{
|
||||
type2 = tok->strAt(2);
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (tok->tokAt(offset) && (tok->tokAt(offset)->str() == "*"))
|
||||
{
|
||||
pointer = true;
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (tok->tokAt(offset) && (tok->tokAt(offset)->str() == "*"))
|
||||
{
|
||||
toPointer = true;
|
||||
offset++;
|
||||
}
|
||||
|
||||
typeName = tok->strAt(offset++);
|
||||
tok = tok->tokAt(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unhandled typedef, skip it and continue
|
||||
continue;
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
bool ok = true;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
const std::string pattern(className.empty() ? "" : (className + " :: " + typeName).c_str());
|
||||
int level = 0;
|
||||
bool inScope = true;
|
||||
|
||||
bool exitThisScope = false;
|
||||
int exitScope = 0;
|
||||
bool simplifyType = false;
|
||||
for (Token *tok2 = tok; tok2; tok2 = tok2->next())
|
||||
{
|
||||
if (tok2->str() == "}")
|
||||
{
|
||||
typeName = tok->strAt(4);
|
||||
tok = tok->tokAt(5);
|
||||
--level;
|
||||
if (level < 0)
|
||||
inScope = false;
|
||||
|
||||
if (exitThisScope)
|
||||
{
|
||||
if (level < exitScope)
|
||||
exitThisScope = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (tok2->str() == "{")
|
||||
++level;
|
||||
else if (!pattern.empty() && Token::Match(tok2, pattern.c_str()))
|
||||
{
|
||||
typeName = tok->strAt(3);
|
||||
tok = tok->tokAt(4);
|
||||
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 (type1 && (tok2->tokAt(-1)->str() != type1))
|
||||
simplifyType = true;
|
||||
else if (!type1)
|
||||
simplifyType = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Typedef with the same name.
|
||||
exitThisScope = true;
|
||||
exitScope = level;
|
||||
}
|
||||
}
|
||||
|
||||
if (simplifyType)
|
||||
{
|
||||
if (start && end)
|
||||
{
|
||||
tok2->str(start->str());
|
||||
Token * nextToken;
|
||||
std::stack<Token *> links;
|
||||
for (nextToken = start->next(); nextToken != end->next(); nextToken = nextToken->next())
|
||||
{
|
||||
tok2->insertToken(nextToken->strAt(0));
|
||||
tok2 = tok2->next();
|
||||
|
||||
// Check for links and fix them up
|
||||
if (tok2->str() == "(" || tok2->str() == "[")
|
||||
links.push(tok2);
|
||||
if (tok2->str() == ")" || tok2->str() == "]")
|
||||
{
|
||||
Token * link = links.top();
|
||||
|
||||
tok2->link(link);
|
||||
link->link(tok2);
|
||||
|
||||
links.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tok2->str(type1);
|
||||
if (type2)
|
||||
{
|
||||
tok2->insertToken(type2);
|
||||
tok2 = tok2->next();
|
||||
}
|
||||
}
|
||||
|
||||
if (pointer)
|
||||
{
|
||||
tok2->insertToken("*");
|
||||
tok2 = tok2->next();
|
||||
}
|
||||
if (toPointer)
|
||||
{
|
||||
tok2->insertToken("*");
|
||||
tok2 = tok2->next();
|
||||
}
|
||||
if (num)
|
||||
{
|
||||
tok2 = tok2->next();
|
||||
tok2->insertToken("[");
|
||||
tok2 = tok2->next();
|
||||
Token *tok3 = tok2;
|
||||
tok2->insertToken(num->strAt(0));
|
||||
tok2 = tok2->next();
|
||||
tok2->insertToken("]");
|
||||
tok2 = tok2->next();
|
||||
Token::createMutualLinks(tok2, tok3);
|
||||
}
|
||||
|
||||
simplifyType = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
|
||||
while (!done)
|
||||
if (tok->str() == ";")
|
||||
done = true;
|
||||
else if (tok->str() == ",")
|
||||
{
|
||||
const std::string pattern(className.empty() ? "" : (className + " :: " + typeName).c_str());
|
||||
int level = 0;
|
||||
bool inScope = true;
|
||||
|
||||
bool exitThisScope = false;
|
||||
int exitScope = 0;
|
||||
bool simplifyType = false;
|
||||
for (Token *tok2 = tok; tok2; tok2 = tok2->next())
|
||||
if (Token::Match(tok->next(), "*| *| %type% ;|,") ||
|
||||
Token::Match(tok->next(), "*| *| %type% [ %num% ] ;|,"))
|
||||
{
|
||||
if (tok2->str() == "}")
|
||||
{
|
||||
--level;
|
||||
if (level < 0)
|
||||
inScope = false;
|
||||
num = 0;
|
||||
pointer = (tok->tokAt(1)->str() == "*");
|
||||
toPointer = (tok->tokAt(2)->str() == "*");
|
||||
|
||||
if (exitThisScope)
|
||||
{
|
||||
if (level < exitScope)
|
||||
exitThisScope = false;
|
||||
}
|
||||
}
|
||||
else if (tok2->str() == "{")
|
||||
++level;
|
||||
else if (!pattern.empty() && Token::Match(tok2, pattern.c_str()))
|
||||
if (pointer)
|
||||
{
|
||||
tok2->deleteNext();
|
||||
tok2->deleteNext();
|
||||
simplifyType = true;
|
||||
}
|
||||
else if (inScope && !exitThisScope && tok2->str() == typeName)
|
||||
{
|
||||
if (Token::simpleMatch(tok2->previous(), "::"))
|
||||
if (toPointer)
|
||||
{
|
||||
// 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 (type1 && (tok2->tokAt(-1)->str() != type1))
|
||||
simplifyType = true;
|
||||
else if (!type1)
|
||||
simplifyType = true;
|
||||
typeName = tok->strAt(3);
|
||||
|
||||
if (tok->tokAt(4)->str() == "[")
|
||||
{
|
||||
num = tok->tokAt(5);
|
||||
tok = tok->tokAt(7);
|
||||
}
|
||||
else
|
||||
tok = tok->tokAt(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Typedef with the same name.
|
||||
exitThisScope = true;
|
||||
exitScope = level;
|
||||
}
|
||||
}
|
||||
|
||||
if (simplifyType)
|
||||
{
|
||||
if (start && end)
|
||||
{
|
||||
tok2->str(start->str());
|
||||
Token * nextToken;
|
||||
std::stack<Token *> links;
|
||||
for (nextToken = start->next(); nextToken != end->next(); nextToken = nextToken->next())
|
||||
{
|
||||
tok2->insertToken(nextToken->strAt(0));
|
||||
tok2 = tok2->next();
|
||||
|
||||
// Check for links and fix them up
|
||||
if (tok2->str() == "(" || tok2->str() == "[")
|
||||
links.push(tok2);
|
||||
if (tok2->str() == ")" || tok2->str() == "]")
|
||||
{
|
||||
Token * link = links.top();
|
||||
|
||||
tok2->link(link);
|
||||
link->link(tok2);
|
||||
|
||||
links.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tok2->str(type1);
|
||||
if (type2)
|
||||
{
|
||||
tok2->insertToken(type2);
|
||||
tok2 = tok2->next();
|
||||
}
|
||||
if (pointer)
|
||||
{
|
||||
tok2->insertToken("*");
|
||||
tok2 = tok2->next();
|
||||
}
|
||||
|
||||
if (num)
|
||||
{
|
||||
tok2 = tok2->next();
|
||||
tok2->insertToken("[");
|
||||
tok2 = tok2->next();
|
||||
Token *tok3 = tok2;
|
||||
tok2->insertToken(num->strAt(0));
|
||||
tok2 = tok2->next();
|
||||
tok2->insertToken("]");
|
||||
tok2 = tok2->next();
|
||||
Token::createMutualLinks(tok2, tok3);
|
||||
}
|
||||
}
|
||||
|
||||
simplifyType = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tok->str() == ";")
|
||||
done = true;
|
||||
else
|
||||
{
|
||||
if (Token::Match(tok->next(), "*| %type% ;|,") ||
|
||||
Token::Match(tok->next(), "*| %type% [ %num% ] ;|,"))
|
||||
{
|
||||
num = 0;
|
||||
pointer = (tok->tokAt(1)->str() == "*");
|
||||
|
||||
if (pointer)
|
||||
{
|
||||
typeName = tok->strAt(2);
|
||||
|
||||
|
@ -707,22 +746,37 @@ void Tokenizer::simplifyTypedef()
|
|||
else
|
||||
tok = tok->tokAt(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = tok->strAt(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = tok->strAt(1);
|
||||
|
||||
if (tok->tokAt(2)->str() == "[")
|
||||
{
|
||||
num = tok->tokAt(3);
|
||||
tok = tok->tokAt(5);
|
||||
}
|
||||
else
|
||||
tok = tok->tokAt(2);
|
||||
if (tok->tokAt(2)->str() == "[")
|
||||
{
|
||||
num = tok->tokAt(3);
|
||||
tok = tok->tokAt(5);
|
||||
}
|
||||
else
|
||||
tok = tok->tokAt(2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we encountered a typedef we don't support yet so just continue
|
||||
done = true;
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// something is really wrong (internal error)
|
||||
done = true;
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
// remove typedef but leave ;
|
||||
while (typeDef->next() && typeDef->next() != tok)
|
||||
typeDef->deleteNext();
|
||||
|
|
|
@ -153,6 +153,7 @@ private:
|
|||
TEST_CASE(simplifyTypedef16);
|
||||
TEST_CASE(simplifyTypedef17);
|
||||
TEST_CASE(simplifyTypedef18); // typedef vector<int[4]> a;
|
||||
TEST_CASE(simplifyTypedef19);
|
||||
TEST_CASE(reverseArraySyntax)
|
||||
TEST_CASE(simplify_numeric_condition)
|
||||
|
||||
|
@ -2555,6 +2556,62 @@ private:
|
|||
ASSERT_EQUALS(true, tokenizer.validate());
|
||||
}
|
||||
|
||||
void simplifyTypedef19()
|
||||
{
|
||||
{
|
||||
// ticket #1275
|
||||
const char code[] = "typedef struct {} A, *B, **C;\n"
|
||||
"A a;\n"
|
||||
"B b;\n"
|
||||
"C c;";
|
||||
|
||||
const char expected[] =
|
||||
"struct A { } ; ; "
|
||||
"struct A a ; "
|
||||
"struct A * b ; "
|
||||
"struct A * * c ;";
|
||||
|
||||
ASSERT_EQUALS(expected, tok(code, false));
|
||||
}
|
||||
|
||||
{
|
||||
// This doesn't work yet.
|
||||
// The first name gets substituted successfully.
|
||||
// The second doesn't so the typedef is left alone.
|
||||
// The variable declaration simplification code then splits the typedef into two.
|
||||
const char code[] = "typedef struct {} A, ***B;\n"
|
||||
"A a;\n"
|
||||
"B b;";
|
||||
|
||||
const char expected[] =
|
||||
"struct A { } ; typedef struct A A ; typedef struct A * * * B ; "
|
||||
"struct A a ; "
|
||||
"B b ;";
|
||||
|
||||
const char todo[] =
|
||||
"struct A { } ; ; "
|
||||
"struct A a ; "
|
||||
"struct A * * * b ;";
|
||||
ASSERT_EQUALS(expected, tok(code, false));
|
||||
TODO_ASSERT_EQUALS(todo, tok(code, false));
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "typedef struct {} **A, *B, C;\n"
|
||||
"A a;\n"
|
||||
"B b;\n"
|
||||
"C c;";
|
||||
|
||||
const char expected[] =
|
||||
"struct Unnamed2 { } ; ; "
|
||||
"struct Unnamed2 * * a ; "
|
||||
"struct Unnamed2 * b ; "
|
||||
"struct Unnamed2 c ;";
|
||||
|
||||
ASSERT_EQUALS(expected, tok(code, false));
|
||||
}
|
||||
}
|
||||
|
||||
void reverseArraySyntax()
|
||||
{
|
||||
ASSERT_EQUALS("a [ 13 ]", tok("13[a]"));
|
||||
|
|
Loading…
Reference in New Issue