diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index ef825309f..04dd96477 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -3550,6 +3550,17 @@ static const Token* skipPointers(const Token* tok) return tok; } +static const Token* skipPointersAndQualifiers(const Token* tok) +{ + tok = skipPointers(tok); + while (Token::Match(tok, "const|volatile")) { + tok = tok->next(); + tok = skipPointers(tok); + } + + return tok; +} + bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const { const bool isCPP = check && check->mTokenizer->isCPP(); @@ -3582,7 +3593,7 @@ bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, } } } else if (Token::Match(localTypeTok, "%type%")) { - localVarTok = skipPointers(localTypeTok->strAt(1)=="const"?localTypeTok->tokAt(2):localTypeTok->next()); + localVarTok = skipPointersAndQualifiers(localTypeTok->next()); } if (!localVarTok) diff --git a/test/testclass.cpp b/test/testclass.cpp index 07f23e8d6..3d8842d5b 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -196,6 +196,7 @@ private: TEST_CASE(constArrayOperator); // #4406 TEST_CASE(constRangeBasedFor); // #5514 TEST_CASE(const_shared_ptr); + TEST_CASE(constPtrToConstPtr); TEST_CASE(initializerListOrder); TEST_CASE(initializerListUsage); @@ -6215,6 +6216,14 @@ private: ASSERT_EQUALS("", errout.str()); } + void constPtrToConstPtr() { + checkConst("class Fred {\n" + "public:\n" + " const char *const *data;\n" + " const char *const *getData() { return data; }\n}"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::getData' can be const.\n", errout.str()); + } + void checkInitializerListOrder(const char code[]) { // Clear the error log errout.str(""); diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 66a7b0cb8..4aead430e 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -141,6 +141,10 @@ private: TEST_CASE(isVariableDeclarationRValueRef); TEST_CASE(isVariableDeclarationDoesNotIdentifyCase); TEST_CASE(isVariableStlType); + TEST_CASE(isVariablePointerToConstPointer); + TEST_CASE(isVariablePointerToVolatilePointer); + TEST_CASE(isVariablePointerToConstVolatilePointer); + TEST_CASE(isVariableMultiplePointersAndQualifiers); TEST_CASE(VariableValueType1); TEST_CASE(VariableValueType2); @@ -954,6 +958,50 @@ private: } } + void isVariablePointerToConstPointer() { + reset(); + givenACodeSampleToTokenize var("char* const * s;"); + bool result = nullScope.isVariableDeclaration(var.tokens(), vartok, typetok); + ASSERT_EQUALS(true, result); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1); + ASSERT(false == v.isArray()); + ASSERT(true == v.isPointer()); + ASSERT(false == v.isReference()); + } + + void isVariablePointerToVolatilePointer() { + reset(); + givenACodeSampleToTokenize var("char* volatile * s;"); + bool result = nullScope.isVariableDeclaration(var.tokens(), vartok, typetok); + ASSERT_EQUALS(true, result); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1); + ASSERT(false == v.isArray()); + ASSERT(true == v.isPointer()); + ASSERT(false == v.isReference()); + } + + void isVariablePointerToConstVolatilePointer() { + reset(); + givenACodeSampleToTokenize var("char* const volatile * s;"); + bool result = nullScope.isVariableDeclaration(var.tokens(), vartok, typetok); + ASSERT_EQUALS(true, result); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1); + ASSERT(false == v.isArray()); + ASSERT(true == v.isPointer()); + ASSERT(false == v.isReference()); + } + + void isVariableMultiplePointersAndQualifiers() { + reset(); + givenACodeSampleToTokenize var("const char* const volatile * const volatile * const volatile * const volatile s;"); + bool result = nullScope.isVariableDeclaration(var.tokens()->next(), vartok, typetok); + ASSERT_EQUALS(true, result); + Variable v(vartok, typetok, vartok->previous(), 0, Public, 0, 0, &settings1); + ASSERT(false == v.isArray()); + ASSERT(true == v.isPointer()); + ASSERT(false == v.isReference()); + } + void arrayMemberVar1() { GET_SYMBOL_DB("struct Foo {\n" " int x;\n"