Skip to content

Commit 60eb475

Browse files
authored
Fix #13887 (False positive: template argument reported as unused struct member) (danmar#7551)
1 parent 6765ee0 commit 60eb475

File tree

6 files changed

+77
-2
lines changed

6 files changed

+77
-2
lines changed

lib/checkunusedvar.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,9 @@ void CheckUnusedVar::checkStructMemberUsage()
15751575
if (isInherited && !var.isPrivate())
15761576
continue;
15771577

1578+
if (mTokenizer->isVarUsedInTemplate(var.declarationId()))
1579+
continue;
1580+
15781581
// Check if the struct member variable is used anywhere in the file
15791582
bool use = false;
15801583
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {

lib/templatesimplifier.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,12 @@ void TemplateSimplifier::expandTemplate(
16471647

16481648
std::vector<newInstantiation> newInstantiations;
16491649

1650+
for (const Token* tok = templateInstantiation.token()->next()->findClosingBracket();
1651+
tok && tok != templateInstantiation.token(); tok = tok->previous()) {
1652+
if (tok->isName())
1653+
mUsedVariables[newName].insert(tok->str());
1654+
}
1655+
16501656
// add forward declarations
16511657
if (copy && isClass) {
16521658
templateDeclaration.token()->insertTokenBefore(templateDeclarationToken->strAt(1));

lib/templatesimplifier.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ class CPPCHECKLIB TemplateSimplifier {
5454
return mDump;
5555
}
5656

57+
const std::map<std::string, std::set<std::string>>& getUsedVariables() const {
58+
return mUsedVariables;
59+
}
60+
5761
/**
5862
*/
5963
void checkComplicatedSyntaxErrorsInTemplates();
@@ -510,6 +514,8 @@ class CPPCHECKLIB TemplateSimplifier {
510514
std::vector<TokenAndName> mTypesUsedInTemplateInstantiation;
511515
std::unordered_map<const Token*, int> mTemplateNamePos;
512516
std::string mDump;
517+
518+
std::map<std::string, std::set<std::string>> mUsedVariables;
513519
};
514520

515521
/// @}

lib/tokenize.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4410,10 +4410,26 @@ static void setVarIdStructMembers(Token *&tok1,
44104410
tok1 = tok;
44114411
}
44124412

4413+
static void addTemplateVarIdUsage(const std::string &tokstr,
4414+
const std::map<std::string, std::set<std::string>>& templateVarUsage,
4415+
const std::unordered_map<std::string, nonneg int>& variableMap,
4416+
std::set<nonneg int>& templateVarIdUsage) {
4417+
const auto v = templateVarUsage.find(tokstr);
4418+
if (v != templateVarUsage.end()) {
4419+
for (const std::string& varname: v->second) {
4420+
const auto it = variableMap.find(varname);
4421+
if (it != variableMap.end())
4422+
templateVarIdUsage.insert(it->second);
4423+
}
4424+
}
4425+
}
4426+
44134427
static bool setVarIdClassDeclaration(Token* const startToken,
44144428
VariableMap& variableMap,
44154429
const nonneg int scopeStartVarId,
4416-
std::map<nonneg int, std::map<std::string, nonneg int>>& structMembers)
4430+
const std::map<std::string, std::set<std::string>>& templateVarUsage,
4431+
std::map<nonneg int, std::map<std::string, nonneg int>>& structMembers,
4432+
std::set<nonneg int>& templateVarIdUsage)
44174433
{
44184434
// end of scope
44194435
const Token* const endToken = startToken->link();
@@ -4480,6 +4496,8 @@ static bool setVarIdClassDeclaration(Token* const startToken,
44804496
if (it != variableMap.map(false).end()) {
44814497
tok->varId(it->second);
44824498
setVarIdStructMembers(tok, structMembers, variableMap.getVarId());
4499+
} else if (tok->str().back() == '>') {
4500+
addTemplateVarIdUsage(tok->str(), templateVarUsage, variableMap.map(false), templateVarIdUsage);
44834501
}
44844502
}
44854503
}
@@ -4659,7 +4677,9 @@ void Tokenizer::setVarIdPass1()
46594677
if (!setVarIdClassDeclaration(tok->link(),
46604678
variableMap,
46614679
scopeStack.top().startVarid,
4662-
structMembers)) {
4680+
mTemplateSimplifier->getUsedVariables(),
4681+
structMembers,
4682+
mTemplateVarIdUsage)) {
46634683
syntaxError(nullptr);
46644684
}
46654685
}
@@ -4746,6 +4766,16 @@ void Tokenizer::setVarIdPass1()
47464766

47474767
if (decl) {
47484768
if (cpp) {
4769+
for (const Token* tok3 = tok->next(); tok3->isName(); tok3 = tok3->next()) {
4770+
addTemplateVarIdUsage(tok3->str(),
4771+
mTemplateSimplifier->getUsedVariables(),
4772+
variableMap.map(false),
4773+
mTemplateVarIdUsage);
4774+
addTemplateVarIdUsage(tok3->str(),
4775+
mTemplateSimplifier->getUsedVariables(),
4776+
variableMap.map(true),
4777+
mTemplateVarIdUsage);
4778+
}
47494779
if (Token *declTypeTok = Token::findsimplematch(tok, "decltype (", tok2)) {
47504780
for (Token *declTok = declTypeTok->linkAt(1); declTok != declTypeTok; declTok = declTok->previous()) {
47514781
if (declTok->isName() && !Token::Match(declTok->previous(), "::|.") && variableMap.hasVariable(declTok->str()))
@@ -4859,6 +4889,13 @@ void Tokenizer::setVarIdPass1()
48594889
continue;
48604890
}
48614891

4892+
if (tok->str().back() == '>') {
4893+
addTemplateVarIdUsage(tok->str(),
4894+
mTemplateSimplifier->getUsedVariables(),
4895+
variableMap.map(globalNamespace),
4896+
mTemplateVarIdUsage);
4897+
}
4898+
48624899
// function declaration inside executable scope? Function declaration is of form: type name "(" args ")"
48634900
if (scopeStack.top().isExecutable && !scopeStack.top().isStructInit && Token::Match(tok, "%name% [,)[]")) {
48644901
bool par = false;
@@ -6175,6 +6212,12 @@ void Tokenizer::dump(std::ostream &out) const
61756212
outs += dumpTypedefInfo();
61766213

61776214
outs += mTemplateSimplifier->dump();
6215+
if (!mTemplateVarIdUsage.empty()) {
6216+
outs += " <template-varid-usage>\n";
6217+
for (nonneg int id: mTemplateVarIdUsage)
6218+
outs += " <var id=\"" + std::to_string(id) + "\"/>\n";
6219+
outs += " </template-varid-usage>\n";
6220+
}
61786221

61796222
out << outs;
61806223
}

lib/tokenize.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <iosfwd>
2929
#include <list>
3030
#include <map>
31+
#include <set>
3132
#include <string>
3233
#include <vector>
3334

@@ -81,6 +82,10 @@ class CPPCHECKLIB Tokenizer {
8182

8283
bool simplifyTokens1(const std::string &configuration, int fileIndex=0);
8384

85+
bool isVarUsedInTemplate(nonneg int id) const {
86+
return mTemplateVarIdUsage.count(id) != 0;
87+
}
88+
8489
private:
8590
/** Set variable id */
8691
void setVarId();
@@ -641,6 +646,8 @@ class CPPCHECKLIB Tokenizer {
641646

642647
TemplateSimplifier * const mTemplateSimplifier;
643648

649+
std::set<nonneg int> mTemplateVarIdUsage;
650+
644651
/** E.g. "A" for code where "#ifdef A" is true. This is used to
645652
print additional information in error situations. */
646653
std::string mConfiguration;

test/testunusedvar.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class TestUnusedVar : public TestFixture {
7171
TEST_CASE(structmember26); // #13345
7272
TEST_CASE(structmember27); // #13367
7373
TEST_CASE(structmember_macro);
74+
TEST_CASE(structmember_template_argument); // #13887 - do not report that member used in template argument is unused
7475
TEST_CASE(classmember);
7576
TEST_CASE(structmemberStructuredBinding); // #13107
7677

@@ -2003,6 +2004,15 @@ class TestUnusedVar : public TestFixture {
20032004
ASSERT_EQUALS("", errout_str());
20042005
}
20052006

2007+
void structmember_template_argument() { // #13887 - False positive
2008+
checkStructMemberUsage("template <class T, int i> struct A{ T buf[i]; }\n"
2009+
"struct B {\n"
2010+
" constexpr int x = 20;\n" // <- not unused
2011+
" A<uint32_t, x> a;\n" // <- unused
2012+
"};");
2013+
ASSERT_EQUALS("[test.cpp:4:20]: (style) struct member 'B::a' is never used. [unusedStructMember]\n", errout_str());
2014+
}
2015+
20062016
void classmember() {
20072017
checkStructMemberUsage("class C {\n"
20082018
" int i{};\n"

0 commit comments

Comments
 (0)