From 70b407611124838b3c83e8e920100d4771c71f29 Mon Sep 17 00:00:00 2001 From: Greg Hewgill Date: Wed, 9 Mar 2011 21:29:30 +1300 Subject: [PATCH] refactor noMemset so it recursively checks parent classes for non-memset-compatible things --- lib/checkclass.cpp | 146 ++++++++++++++++++++++++++------------------- lib/checkclass.h | 1 + test/testclass.cpp | 12 ++++ 3 files changed, 98 insertions(+), 61 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 569f4ed9d..648c562d1 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -693,6 +693,90 @@ void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string // ClassCheck: Check that memset is not used on classes //--------------------------------------------------------------------------- +void CheckClass::checkMemsetType(const Token *tok, const std::string &type) +{ + // Warn if type is a class or struct that contains any std::* variables + const std::string pattern2(std::string("struct|class ") + type + " :|{"); + const Token *tstruct = Token::findmatch(_tokenizer->tokens(), pattern2.c_str()); + + if (!tstruct) + return; + + const std::string &typeName = tstruct->str(); + + if (tstruct->tokAt(2)->str() == ":") + { + tstruct = tstruct->tokAt(3); + for (; tstruct; tstruct = tstruct->next()) + { + while (Token::Match(tstruct, "public|private|protected|virtual")) + { + tstruct = tstruct->next(); + } + + // recursively check all parent classes + checkMemsetType(tok, tstruct->str()); + + tstruct = tstruct->next(); + if (tstruct->str() != ",") + break; + } + } + + for (; tstruct; tstruct = tstruct->next()) + { + if (tstruct->str() == "}") + break; + + // struct with function? skip function body.. + if (Token::simpleMatch(tstruct, ") {")) + { + tstruct = tstruct->next()->link(); + if (!tstruct) + break; + } + + // before a statement there must be either: + // * private:|protected:|public: + // * { } ; + if (Token::Match(tstruct, "[;{}]") || + tstruct->str().find(":") != std::string::npos) + { + if (Token::Match(tstruct->next(), "std :: %type% %var% ;")) + memsetError(tok, tok->str(), tstruct->strAt(3), typeName); + + else if (Token::Match(tstruct->next(), "std :: %type% <")) + { + // backup the type + const std::string typestr(tstruct->strAt(3)); + + // check if it's a pointer variable.. + unsigned int level = 0; + while (0 != (tstruct = tstruct->next())) + { + if (tstruct->str() == "<") + ++level; + else if (tstruct->str() == ">") + { + if (level <= 1) + break; + --level; + } + else if (tstruct->str() == "(") + tstruct = tstruct->link(); + } + + if (!tstruct) + break; + + // found error => report + if (Token::Match(tstruct, "> %var% ;")) + memsetError(tok, tok->str(), typestr, typeName); + } + } + } +} + void CheckClass::noMemset() { createSymbolDatabase(); @@ -726,67 +810,7 @@ void CheckClass::noMemset() if (type.empty()) continue; - // Warn if type is a class or struct that contains any std::* variables - const std::string pattern2(std::string("struct|class ") + type + " {"); - const Token *tstruct = Token::findmatch(_tokenizer->tokens(), pattern2.c_str()); - - if (!tstruct) - continue; - - const std::string &typeName = tstruct->str(); - - for (; tstruct; tstruct = tstruct->next()) - { - if (tstruct->str() == "}") - break; - - // struct with function? skip function body.. - if (Token::simpleMatch(tstruct, ") {")) - { - tstruct = tstruct->next()->link(); - if (!tstruct) - break; - } - - // before a statement there must be either: - // * private:|protected:|public: - // * { } ; - if (Token::Match(tstruct, "[;{}]") || - tstruct->str().find(":") != std::string::npos) - { - if (Token::Match(tstruct->next(), "std :: %type% %var% ;")) - memsetError(tok, tok->str(), tstruct->strAt(3), typeName); - - else if (Token::Match(tstruct->next(), "std :: %type% <")) - { - // backup the type - const std::string typestr(tstruct->strAt(3)); - - // check if it's a pointer variable.. - unsigned int level = 0; - while (0 != (tstruct = tstruct->next())) - { - if (tstruct->str() == "<") - ++level; - else if (tstruct->str() == ">") - { - if (level <= 1) - break; - --level; - } - else if (tstruct->str() == "(") - tstruct = tstruct->link(); - } - - if (!tstruct) - break; - - // found error => report - if (Token::Match(tstruct, "> %var% ;")) - memsetError(tok, tok->str(), typestr, typeName); - } - } - } + checkMemsetType(tok, type); } } diff --git a/lib/checkclass.h b/lib/checkclass.h index 2dd238f83..1b3b5fa43 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -84,6 +84,7 @@ public: * Important: The checking doesn't work on simplified tokens list. */ void noMemset(); + void checkMemsetType(const Token *tok, const std::string &type); /** @brief 'operator=' should return something and it should not be const. */ void operatorEq(); diff --git a/test/testclass.cpp b/test/testclass.cpp index d49d5abe3..a724285df 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -2949,6 +2949,18 @@ private: " memset(&fred, 0, sizeof(fred));\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'\n", errout.str()); + + checkNoMemset("class Fred\n" + "{\n" + " std::string s;\n" + "};\n" + "class Pebbles: public Fred {};\n" + "void f()\n" + "{\n" + " Pebbles pebbles;\n" + " memset(&pebbles, 0, sizeof(pebbles));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on class that contains a 'std::string'\n", errout.str()); } void memsetOnStruct()