Fixed #2384 ("The function 's::f' can be const" reported for pointer-to-pointer)

Moved check for pointer variables into isVariableDeclaration()

Can now handle multiple scopes and multiple levels of indirection. Simplified check for strucs and unions, too, reducing the size of getVarList().

skipScopeIdentifiers() and skipPointers() should probably be methods on class Token.
This commit is contained in:
Pete Johns 2010-12-31 08:57:43 +11:00
parent 76cf097104
commit 502cfe7243
4 changed files with 133 additions and 68 deletions

View File

@ -1110,48 +1110,17 @@ void SymbolDatabase::SpaceInfo::getVarList()
bool isClass = false;
if (isVariableDeclaration(tok, vartok))
if (Token::Match(tok, "struct|union"))
{
tok = tok->next();
}
if (isVariableDeclaration(tok, vartok, typetok))
{
typetok = vartok->previous();
isClass = (!typetok->isStandardType());
tok = vartok->next();
}
// Structure?
else if (Token::Match(tok, "struct|union %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(2);
typetok = vartok->previous();
tok = vartok->next();
}
// Pointer?
else if (Token::Match(tok, "%type% * %var% ;"))
{
vartok = tok->tokAt(2);
typetok = tok;
tok = vartok->next();
}
else if (Token::Match(tok, "%type% %type% * %var% ;"))
{
vartok = tok->tokAt(3);
typetok = vartok->tokAt(-2);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% * %var% ;"))
{
vartok = tok->tokAt(4);
typetok = vartok->tokAt(-2);
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% :: %type% * %var% ;"))
{
vartok = tok->tokAt(6);
typetok = vartok->tokAt(-2);
tok = vartok->next();
}
// Array?
else if (Token::Match(tok, "%type% %var% [") && tok->next()->str() != "operator")
{
@ -1276,20 +1245,50 @@ void SymbolDatabase::SpaceInfo::getVarList()
}
}
bool SymbolDatabase::SpaceInfo::isVariableDeclaration(const Token* tok, const Token*& vartok) const
const Token* skipScopeIdentifiers(const Token* tok)
{
if (Token::simpleMatch(tok, "::"))
const Token* ret = tok;
if (Token::simpleMatch(ret, "::"))
{
tok = tok->next();
ret = ret->next();
}
while (Token::Match(tok, "%type% :: "))
while (Token::Match(ret, "%type% :: "))
{
tok = tok->tokAt(2);
ret = ret->tokAt(2);
}
if (Token::Match(tok, "%type% %var% ;"))
return ret;
}
const Token* skipPointers(const Token* tok)
{
const Token* ret = tok;
while (Token::simpleMatch(ret, "*"))
{
vartok = tok->next();
ret = ret->next();
}
return ret;
}
bool SymbolDatabase::SpaceInfo::isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok) const
{
tok = skipScopeIdentifiers(tok);
if (Token::Match(tok, "%type%"))
{
const Token* potentialTypetok = tok;
tok = skipPointers(tok->next());
if (Token::Match(tok, "%var% ;"))
{
vartok = tok;
typetok = potentialTypetok;
}
}
return NULL != vartok;
}

View File

@ -240,10 +240,10 @@ public:
* @brief helper function for getVarList()
* @param tok pointer to token to check
* @param vartok populated with pointer to the variable token, if found
* @param typetok populated with pointer to the type token, if found
* @return true if tok points to a variable declaration, false otherwise
*/
bool isVariableDeclaration(const Token* tok, const Token*& vartok) const;
bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok) const;
};
bool isMemberVar(const SpaceInfo *info, const Token *tok);

View File

@ -160,6 +160,7 @@ private:
TEST_CASE(const41); // ticket #2255
TEST_CASE(const42); // ticket #2282
TEST_CASE(const43); // ticket #2377
TEST_CASE(assigningPointerToPointerIsNotAConstOperation);
TEST_CASE(constoperator1); // operator< can often be const
TEST_CASE(constoperator2); // operator<<
TEST_CASE(constoperator3);
@ -4859,6 +4860,20 @@ private:
ASSERT_EQUALS("", errout.str());
}
void assigningPointerToPointerIsNotAConstOperation()
{
checkConst("struct s\n"
"{\n"
" int** v;\n"
" void f()\n"
" {\n"
" v = 0;\n"
" }\n"
"};\n"
);
ASSERT_EQUALS("", errout.str());
}
// increment/decrement => not const
void constincdec()
{

View File

@ -28,11 +28,19 @@ public:
:TestFixture("TestSymbolDatabase")
,si(NULL, NULL, NULL)
,vartok(NULL)
,typetok(NULL)
{}
private:
const SymbolDatabase::SpaceInfo si;
const Token* vartok;
const Token* typetok;
void reset()
{
vartok = NULL;
typetok = NULL;
}
void run()
{
@ -42,89 +50,132 @@ private:
TEST_CASE(test_isVariableDeclarationIdentifiesStdDeclaration);
TEST_CASE(test_isVariableDeclarationIdentifiesScopedStdDeclaration);
TEST_CASE(test_isVariableDeclarationIdentifiesManyScopes);
TEST_CASE(test_isVariableDeclarationDoesNotIdentifyPointers);
TEST_CASE(test_isVariableDeclarationIdentifiesPointers);
TEST_CASE(test_isVariableDeclarationDoesNotIdentifyConstness);
TEST_CASE(test_isVariableDeclarationIdentifiesFirstOfManyVariables);
TEST_CASE(test_isVariableDeclarationIdentifiesScopedPointerDeclaration);
TEST_CASE(test_isVariableDeclarationIdentifiesDeclarationWithIndirection);
TEST_CASE(test_isVariableDeclarationIdentifiesDeclarationWithMultipleIndirection);
}
void test_isVariableDeclarationCanHandleNull()
{
vartok = NULL;
bool result = si.isVariableDeclaration(NULL, vartok);
reset();
bool result = si.isVariableDeclaration(NULL, vartok, typetok);
ASSERT_EQUALS(false, result);
ASSERT(NULL == vartok);
ASSERT(NULL == typetok);
}
void test_isVariableDeclarationIdentifiesSimpleDeclaration()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize simpleDeclaration("int x;");
bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok);
bool result = si.isVariableDeclaration(simpleDeclaration.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("x", vartok->str());
ASSERT_EQUALS("int", typetok->str());
}
void test_isVariableDeclarationIdentifiesScopedDeclaration()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize ScopedDeclaration("::int x;");
bool result = si.isVariableDeclaration(ScopedDeclaration.tokens(), vartok);
bool result = si.isVariableDeclaration(ScopedDeclaration.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("x", vartok->str());
ASSERT_EQUALS("int", typetok->str());
}
void test_isVariableDeclarationIdentifiesStdDeclaration()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize StdDeclaration("std::string x;");
bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok);
bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("x", vartok->str());
ASSERT_EQUALS("string", typetok->str());
}
void test_isVariableDeclarationIdentifiesScopedStdDeclaration()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize StdDeclaration("::std::string x;");
bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok);
bool result = si.isVariableDeclaration(StdDeclaration.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("x", vartok->str());
ASSERT_EQUALS("string", typetok->str());
}
void test_isVariableDeclarationIdentifiesManyScopes()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize manyScopes("AA::BB::CC::DD::EE x;");
bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok);
bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("x", vartok->str());
ASSERT_EQUALS("EE", typetok->str());
}
void test_isVariableDeclarationDoesNotIdentifyPointers()
void test_isVariableDeclarationIdentifiesPointers()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize pointer("int* p;");
bool result = si.isVariableDeclaration(pointer.tokens(), vartok);
ASSERT_EQUALS(false, result);
ASSERT(NULL == vartok);
bool result = si.isVariableDeclaration(pointer.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("p", vartok->str());
ASSERT_EQUALS("int", typetok->str());
}
void test_isVariableDeclarationDoesNotIdentifyConstness()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize constness("const int* cp;");
bool result = si.isVariableDeclaration(constness.tokens(), vartok);
bool result = si.isVariableDeclaration(constness.tokens(), vartok, typetok);
ASSERT_EQUALS(false, result);
ASSERT(NULL == vartok);
ASSERT(NULL == typetok);
}
void test_isVariableDeclarationIdentifiesFirstOfManyVariables()
{
vartok = NULL;
reset();
givenACodeSampleToTokenize multipleDeclaration("int first, second;");
bool result = si.isVariableDeclaration(multipleDeclaration.tokens(), vartok);
bool result = si.isVariableDeclaration(multipleDeclaration.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("first", vartok->str());
ASSERT_EQUALS("int", typetok->str());
}
void test_isVariableDeclarationIdentifiesScopedPointerDeclaration()
{
reset();
givenACodeSampleToTokenize manyScopes("AA::BB::CC::DD::EE* p;");
bool result = si.isVariableDeclaration(manyScopes.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("p", vartok->str());
ASSERT_EQUALS("EE", typetok->str());
}
void test_isVariableDeclarationIdentifiesDeclarationWithIndirection()
{
reset();
givenACodeSampleToTokenize pointerToPointer("int** pp;");
bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("pp", vartok->str());
ASSERT_EQUALS("int", typetok->str());
}
void test_isVariableDeclarationIdentifiesDeclarationWithMultipleIndirection()
{
reset();
givenACodeSampleToTokenize pointerToPointer("int***** p;");
bool result = si.isVariableDeclaration(pointerToPointer.tokens(), vartok, typetok);
ASSERT_EQUALS(true, result);
ASSERT_EQUALS("p", vartok->str());
ASSERT_EQUALS("int", typetok->str());
}
};