Skip to content

Commit 1ea89bc

Browse files
authored
add support for template constructors (cppcheck-opensource#2911)
1 parent 22114e3 commit 1ea89bc

4 files changed

Lines changed: 101 additions & 15 deletions

File tree

lib/symboldatabase.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2102,7 +2102,11 @@ Function::Function(const Tokenizer *mTokenizer,
21022102
}
21032103

21042104
// class constructor/destructor
2105-
else if (tokenDef->str() == scope->className && scope->type != Scope::ScopeType::eNamespace) {
2105+
else if (((tokenDef->str() == scope->className) ||
2106+
(tokenDef->str().substr(0, scope->className.size()) == scope->className &&
2107+
tokenDef->str().size() > scope->className.size() + 1 &&
2108+
tokenDef->str()[scope->className.size() + 1] == '<')) &&
2109+
scope->type != Scope::ScopeType::eNamespace) {
21062110
// destructor
21072111
if (tokenDef->previous()->str() == "~")
21082112
type = Function::eDestructor;

lib/templatesimplifier.cpp

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -673,15 +673,6 @@ bool TemplateSimplifier::removeTemplate(Token *tok)
673673
if (tok2->str() == ">")
674674
countgt++;
675675

676-
// don't remove constructor
677-
if (tok2->str() == "explicit" ||
678-
(countgt == 1 && Token::Match(tok2->previous(), "> %type% (") &&
679-
Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) {
680-
eraseTokens(tok, tok2);
681-
deleteToken(tok);
682-
return true;
683-
}
684-
685676
if (tok2->str() == ";") {
686677
tok2 = tok2->next();
687678
eraseTokens(tok, tok2);
@@ -895,8 +886,9 @@ void TemplateSimplifier::getTemplateInstantiations()
895886
// get all declarations with this name
896887
for (auto pos = functionNameMap.lower_bound(tok->str());
897888
pos != functionNameMap.upper_bound(tok->str()); ++pos) {
898-
// look for declaration with same qualification
899-
if (pos->second->fullName() == fullName) {
889+
// look for declaration with same qualification or constructor with same qualification
890+
if (pos->second->fullName() == fullName ||
891+
(pos->second->scope() == fullName && tok->str() == pos->second->name())) {
900892
std::vector<const Token *> templateParams;
901893
getTemplateParametersInDeclaration(pos->second->token()->tokAt(2), templateParams);
902894

@@ -1700,8 +1692,13 @@ void TemplateSimplifier::expandTemplate(
17001692
end = end->next();
17011693
}
17021694

1703-
if (isStatic)
1695+
if (isStatic) {
17041696
dst->insertToken("static", "", true);
1697+
if (start) {
1698+
dst->previous()->linenr(start->linenr());
1699+
dst->previous()->column(start->column());
1700+
}
1701+
}
17051702

17061703
std::map<const Token *, Token *> links;
17071704
bool inAssignment = false;
@@ -1734,6 +1731,8 @@ void TemplateSimplifier::expandTemplate(
17341731
else if (typetok->str() == ")")
17351732
--typeindentlevel;
17361733
dst->insertToken(typetok->str(), typetok->originalName(), true);
1734+
dst->previous()->linenr(start->linenr());
1735+
dst->previous()->column(start->column());
17371736
Token *previous = dst->previous();
17381737
previous->isTemplateArg(true);
17391738
previous->isSigned(typetok->isSigned());
@@ -1760,6 +1759,8 @@ void TemplateSimplifier::expandTemplate(
17601759
}
17611760
if (pointerType && Token::simpleMatch(dst1, "const")) {
17621761
dst->insertToken("const", dst1->originalName(), true);
1762+
dst->previous()->linenr(start->linenr());
1763+
dst->previous()->column(start->column());
17631764
dst1->deleteThis();
17641765
}
17651766
} else {
@@ -1772,10 +1773,14 @@ void TemplateSimplifier::expandTemplate(
17721773
(start->strAt(-1) == "." || Token::simpleMatch(start->tokAt(-2), ". template")))) {
17731774
if (start->strAt(1) != "<" || Token::Match(start, newName.c_str()) || !inAssignment) {
17741775
dst->insertToken(newName, "", true);
1776+
dst->previous()->linenr(start->linenr());
1777+
dst->previous()->column(start->column());
17751778
if (start->strAt(1) == "<")
17761779
start = start->next()->findClosingBracket();
17771780
} else {
17781781
dst->insertToken(start->str(), "", true);
1782+
dst->previous()->linenr(start->linenr());
1783+
dst->previous()->column(start->column());
17791784
newInstantiations.emplace_back(dst->previous(), templateDeclaration.scope());
17801785
}
17811786
} else {
@@ -1797,6 +1802,8 @@ void TemplateSimplifier::expandTemplate(
17971802
if (Token::simpleMatch(inst.token(), name.c_str(), name.size())) {
17981803
// use the instantiated name
17991804
dst->insertToken(name, "", true);
1805+
dst->previous()->linenr(start->linenr());
1806+
dst->previous()->column(start->column());
18001807
start = closing;
18011808
break;
18021809
}
@@ -1805,12 +1812,16 @@ void TemplateSimplifier::expandTemplate(
18051812
// just copy the token if it wasn't instantiated
18061813
if (start != closing) {
18071814
dst->insertToken(start->str(), start->originalName(), true);
1815+
dst->previous()->linenr(start->linenr());
1816+
dst->previous()->column(start->column());
18081817
dst->previous()->isSigned(start->isSigned());
18091818
dst->previous()->isUnsigned(start->isUnsigned());
18101819
dst->previous()->isLong(start->isLong());
18111820
}
18121821
} else {
18131822
dst->insertToken(start->str(), start->originalName(), true);
1823+
dst->previous()->linenr(start->linenr());
1824+
dst->previous()->column(start->column());
18141825
dst->previous()->isSigned(start->isSigned());
18151826
dst->previous()->isUnsigned(start->isUnsigned());
18161827
dst->previous()->isLong(start->isLong());
@@ -1833,6 +1844,8 @@ void TemplateSimplifier::expandTemplate(
18331844
start = start->next();
18341845
}
18351846
dst->insertToken(";", "", true);
1847+
dst->previous()->linenr(dst->tokAt(-2)->linenr());
1848+
dst->previous()->column(dst->tokAt(-2)->column() + 1);
18361849

18371850
if (isVariable || isFunction)
18381851
simplifyTemplateArgs(dstStart, dst);
@@ -3038,7 +3051,9 @@ bool TemplateSimplifier::simplifyTemplateInstantiations(
30383051
if (!Token::Match(instantiation.token(), "%name% <"))
30393052
continue;
30403053

3041-
if (instantiation.fullName() != templateDeclaration.fullName()) {
3054+
if (!((instantiation.fullName() == templateDeclaration.fullName()) ||
3055+
(instantiation.name() == templateDeclaration.name() &&
3056+
instantiation.fullName() == templateDeclaration.scope()))) {
30423057
// FIXME: fallback to not matching scopes until type deduction works
30433058

30443059
// names must match

test/testsimplifytemplate.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ class TestSimplifyTemplate : public TestFixture {
252252
TEST_CASE(templateTypeDeduction2);
253253
TEST_CASE(templateTypeDeduction3);
254254
TEST_CASE(templateTypeDeduction4); // #9983
255+
TEST_CASE(templateTypeDeduction5);
255256

256257
TEST_CASE(simplifyTemplateArgs1);
257258
TEST_CASE(simplifyTemplateArgs2);
@@ -5374,6 +5375,73 @@ class TestSimplifyTemplate : public TestFixture {
53745375
}
53755376
}
53765377

5378+
void templateTypeDeduction5() {
5379+
{
5380+
const char code[] = "class Fred {\n"
5381+
"public:\n"
5382+
" template <class T> Fred(T t) { }\n"
5383+
"};\n"
5384+
"Fred fred1 = Fred(0);\n"
5385+
"Fred fred2 = Fred(0.0);\n"
5386+
"Fred fred3 = Fred(\"zero\");\n"
5387+
"Fred fred4 = Fred(false);";
5388+
const char exp[] = "class Fred { "
5389+
"public: "
5390+
"Fred<int> ( int t ) ; "
5391+
"Fred<double> ( double t ) ; "
5392+
"Fred<constchar*> ( const char * t ) ; "
5393+
"Fred<bool> ( bool t ) ; "
5394+
"} ; "
5395+
"Fred fred1 ; fred1 = Fred<int> ( 0 ) ; "
5396+
"Fred fred2 ; fred2 = Fred<double> ( 0.0 ) ; "
5397+
"Fred fred3 ; fred3 = Fred<constchar*> ( \"zero\" ) ; "
5398+
"Fred fred4 ; fred4 = Fred<bool> ( false ) ; "
5399+
"Fred :: Fred<int> ( int t ) { } "
5400+
"Fred :: Fred<double> ( double t ) { } "
5401+
"Fred :: Fred<constchar*> ( const char * t ) { } "
5402+
"Fred :: Fred<bool> ( bool t ) { }";
5403+
ASSERT_EQUALS(exp, tok(code));
5404+
}
5405+
{
5406+
const char code[] = "namespace NS {\n"
5407+
"class Fred {\n"
5408+
"public:\n"
5409+
" template <class T> Fred(T t) { }\n"
5410+
"};\n"
5411+
"Fred fred1 = Fred(0);\n"
5412+
"Fred fred2 = Fred(0.0);\n"
5413+
"Fred fred3 = Fred(\"zero\");\n"
5414+
"Fred fred4 = Fred(false);\n"
5415+
"}\n"
5416+
"NS::Fred fred1 = NS::Fred(0);\n"
5417+
"NS::Fred fred2 = NS::Fred(0.0);\n"
5418+
"NS::Fred fred3 = NS::Fred(\"zero\");\n"
5419+
"NS::Fred fred4 = NS::Fred(false);\n";
5420+
const char exp[] = "namespace NS { "
5421+
"class Fred { "
5422+
"public: "
5423+
"Fred<int> ( int t ) ; "
5424+
"Fred<double> ( double t ) ; "
5425+
"Fred<constchar*> ( const char * t ) ; "
5426+
"Fred<bool> ( bool t ) ; "
5427+
"} ; "
5428+
"Fred fred1 ; fred1 = Fred<int> ( 0 ) ; "
5429+
"Fred fred2 ; fred2 = Fred<double> ( 0.0 ) ; "
5430+
"Fred fred3 ; fred3 = Fred<constchar*> ( \"zero\" ) ; "
5431+
"Fred fred4 ; fred4 = Fred<bool> ( false ) ; "
5432+
"} "
5433+
"NS :: Fred fred1 ; fred1 = NS :: Fred<int> ( 0 ) ; "
5434+
"NS :: Fred fred2 ; fred2 = NS :: Fred<double> ( 0.0 ) ; "
5435+
"NS :: Fred fred3 ; fred3 = NS :: Fred<constchar*> ( \"zero\" ) ; "
5436+
"NS :: Fred fred4 ; fred4 = NS :: Fred<bool> ( false ) ; "
5437+
"NS :: Fred :: Fred<int> ( int t ) { } "
5438+
"NS :: Fred :: Fred<double> ( double t ) { } "
5439+
"NS :: Fred :: Fred<constchar*> ( const char * t ) { } "
5440+
"NS :: Fred :: Fred<bool> ( bool t ) { }";
5441+
ASSERT_EQUALS(exp, tok(code));
5442+
}
5443+
}
5444+
53775445
void simplifyTemplateArgs1() {
53785446
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < ( 2 ) >;"));
53795447
ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template<int N> foo = N; foo < 1 + 1 >;"));

test/testtokenize.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5292,7 +5292,6 @@ class TestTokenizer : public TestFixture {
52925292
" fn2<int>();\n"
52935293
"}\n";
52945294
ASSERT_EQUALS("void fn2<int> ( int t = [ ] { return 1 ; } ( ) ) ;\n"
5295-
"\n"
52965295
"\n"
52975296
"int main ( )\n"
52985297
"{\n"

0 commit comments

Comments
 (0)