diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng
index 29040a3db..9f343f776 100644
--- a/cfg/cppcheck-cfg.rng
+++ b/cfg/cppcheck-cfg.rng
@@ -447,6 +447,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cfg/std.cfg b/cfg/std.cfg
index ffa17a69f..140f790c3 100644
--- a/cfg/std.cfg
+++ b/cfg/std.cfg
@@ -7993,6 +7993,12 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
+
+
+ std::insert_iterator
+ std::pair
+
+
diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp
index eff8a38be..c8d48fb05 100644
--- a/lib/checkunusedvar.cpp
+++ b/lib/checkunusedvar.cpp
@@ -1185,6 +1185,7 @@ void CheckUnusedVar::checkFunctionVariableUsage()
op1tok = op1tok->astOperand1();
const Variable *op1Var = op1tok ? op1tok->variable() : nullptr;
+ std::string bailoutTypeName;
if (op1Var) {
if (op1Var->isReference() && op1Var->nameToken() != tok->astOperand1())
// todo: check references
@@ -1200,9 +1201,21 @@ void CheckUnusedVar::checkFunctionVariableUsage()
// Bailout for unknown template classes, we have no idea what side effects such assignments have
if (mTokenizer->isCPP() &&
op1Var->isClass() &&
- (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE) &&
- Token::findsimplematch(op1Var->typeStartToken(), "<", op1Var->typeEndToken()))
- continue;
+ (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) {
+ // Check in the library if we should bailout or not..
+ const std::string typeName = op1Var->getTypeName();
+ switch (mSettings->library.getTypeCheck("unusedvar", typeName)) {
+ case Library::TypeCheck::def:
+ if (!mSettings->checkLibrary)
+ continue;
+ bailoutTypeName = typeName;
+ break;
+ case Library::TypeCheck::check:
+ break;
+ case Library::TypeCheck::suppress:
+ continue;
+ };
+ }
}
// Is there a redundant assignment?
@@ -1211,9 +1224,20 @@ void CheckUnusedVar::checkFunctionVariableUsage()
const Token *expr = varDecl ? varDecl : tok->astOperand1();
FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library);
- if (fwdAnalysis.unusedValue(expr, start, scope->bodyEnd))
+ if (fwdAnalysis.unusedValue(expr, start, scope->bodyEnd)) {
+ if (!bailoutTypeName.empty()) {
+ if (mSettings->checkLibrary && mSettings->isEnabled(Settings::INFORMATION)) {
+ reportError(tok,
+ Severity::information,
+ "checkLibraryCheckType",
+ "--check-library: Provide configuration for " + bailoutTypeName);
+ }
+ continue;
+ }
+
// warn
unreadVariableError(tok, expr->expressionString(), false);
+ }
}
// varId, usage {read, write, modified}
diff --git a/lib/library.cpp b/lib/library.cpp
index ec04c5b5a..6015620cd 100644
--- a/lib/library.cpp
+++ b/lib/library.cpp
@@ -511,6 +511,22 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
smartPointers.insert(className);
}
+ else if (nodename == "type-checks") {
+ for (const tinyxml2::XMLElement *checkNode = node->FirstChildElement(); checkNode; checkNode = checkNode->NextSiblingElement()) {
+ const std::string &checkName = checkNode->Name();
+ for (const tinyxml2::XMLElement *checkTypeNode = checkNode->FirstChildElement(); checkTypeNode; checkTypeNode = checkTypeNode->NextSiblingElement()) {
+ const std::string checkTypeName = checkTypeNode->Name();
+ const char *typeName = checkTypeNode->GetText();
+ if (!typeName)
+ continue;
+ if (checkTypeName == "check")
+ mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::check;
+ else if (checkTypeName == "suppress")
+ mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::suppress;
+ }
+ }
+ }
+
else if (nodename == "podtype") {
const char * const name = node->Attribute("name");
if (!name)
@@ -1405,3 +1421,8 @@ CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok)
return tok->valueType()->container;
}
+Library::TypeCheck Library::getTypeCheck(const std::string &check, const std::string &typeName) const
+{
+ auto it = mTypeChecks.find(std::pair(check, typeName));
+ return it == mTypeChecks.end() ? TypeCheck::def : it->second;
+}
diff --git a/lib/library.h b/lib/library.h
index ee2cea17f..e97dffb25 100644
--- a/lib/library.h
+++ b/lib/library.h
@@ -478,6 +478,10 @@ public:
static bool isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback="");
+ /** Suppress/check a type */
+ enum class TypeCheck { def, check, suppress };
+ TypeCheck getTypeCheck(const std::string &check, const std::string &typeName) const;
+
private:
// load a xml node
Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements);
@@ -557,6 +561,7 @@ private:
std::map mPodTypes; // pod types
std::map mPlatformTypes; // platform independent typedefs
std::map mPlatforms; // platform dependent typedefs
+ std::map, TypeCheck> mTypeChecks;
const ArgumentChecks * getarg(const Token *ftok, int argnr) const;
diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp
index 09613c068..cd276370f 100644
--- a/lib/symboldatabase.cpp
+++ b/lib/symboldatabase.cpp
@@ -1857,6 +1857,15 @@ const Type *Variable::smartPointerType() const
return nullptr;
}
+std::string Variable::getTypeName() const
+{
+ std::string ret;
+ // TODO: For known types, generate the full type name
+ for (const Token *typeTok = mTypeStartToken; Token::Match(typeTok, "%name%|::") && typeTok->varId() == 0; typeTok = typeTok->next())
+ ret += typeTok->str();
+ return ret;
+}
+
Function::Function(const Tokenizer *mTokenizer,
const Token *tok,
const Scope *scope,
diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h
index 23847144a..30592243e 100644
--- a/lib/symboldatabase.h
+++ b/lib/symboldatabase.h
@@ -616,6 +616,8 @@ public:
return mAccess;
}
+ std::string getTypeName() const;
+
private:
// only symbol database can change the type
friend class SymbolDatabase;
diff --git a/man/reference-cfg-format.md b/man/reference-cfg-format.md
index 5d688cbca..0c631b435 100644
--- a/man/reference-cfg-format.md
+++ b/man/reference-cfg-format.md
@@ -487,6 +487,24 @@ The first argument that the function takes is a pointer. It must not be a null p
The second argument the function takes is a pointer. It must not be null. And it must point at initialized data. Using `` and `` is correct. Moreover it must point at a zero-terminated string so `` is also used.
+# ``; check or suppress
+
+The ``configuration tells Cppcheck to show or suppress warnings for a certain type.
+
+Example:
+
+
+
+
+
+ foo
+ bar
+
+
+
+
+In the `unusedvar` checking the `foo` type will be checked. Warnings for `bar` type variables will be suppressed.
+
# ``
Libraries can be used to define preprocessor macros as well. For example:
diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp
index 2dc908f86..2a0ba8ffe 100644
--- a/test/testunusedvar.cpp
+++ b/test/testunusedvar.cpp
@@ -33,6 +33,8 @@ private:
void run() OVERRIDE {
settings.addEnabled("style");
+ settings.addEnabled("information");
+ settings.checkLibrary = true;
LOAD_LIB_2(settings.library, "std.cfg");
TEST_CASE(emptyclass); // #5355 - False positive: Variable is not assigned a value.
@@ -3535,13 +3537,18 @@ private:
"}");
ASSERT_EQUALS("", errout.str());
- // FIXME : this is probably inconclusive
- functionVariableUsage("void f() {\n"
+ functionVariableUsage("void f() {\n" // unknown class => library configuration is needed
" Fred fred;\n"
" int *a; a = b;\n"
" fred += a;\n"
"}");
- ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'fred' is assigned a value that is never used.\n", errout.str());
+ ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Provide configuration for Fred\n", errout.str());
+
+ functionVariableUsage("void f() {\n"
+ " std::pair fred;\n" // class with library configuration
+ " fred = x;\n"
+ "}");
+ ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'fred' is assigned a value that is never used.\n", errout.str());
}
void localvarFor() {