Fixed #8971 ("(debug) Unknown type 'x'." using alias in class members) (#1653)

* Fixed #8971 ("(debug) Unknown type 'x'." using alias in class members)

* template simplifier: partial fix for #8972

Add support for multi-token default template parameters.

* template simplifier: fix for #8971

Remove typename outside of templates.
This commit is contained in:
IOBYTE 2019-02-09 02:34:59 -05:00 committed by Daniel Marjamäki
parent 851b537d15
commit 155e4ce912
5 changed files with 340 additions and 211 deletions

View File

@ -2415,6 +2415,10 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
getTemplateParametersInDeclaration(forwardDecl.token->tokAt(2), params1); getTemplateParametersInDeclaration(forwardDecl.token->tokAt(2), params1);
for (auto & decl : mTemplateDeclarations) { for (auto & decl : mTemplateDeclarations) {
// skip partializations
if (decl.isPartialSpecialization())
continue;
std::vector<const Token *> params2; std::vector<const Token *> params2;
getTemplateParametersInDeclaration(decl.token->tokAt(2), params2); getTemplateParametersInDeclaration(decl.token->tokAt(2), params2);
@ -2432,8 +2436,18 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
for (size_t k = 0; k < params1.size(); k++) { for (size_t k = 0; k < params1.size(); k++) {
// copy default value to declaration if not present // copy default value to declaration if not present
if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") { if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") {
const_cast<Token *>(params2[k])->insertToken(params1[k]->strAt(2)); int level = 0;
const_cast<Token *>(params2[k])->insertToken(params1[k]->strAt(1)); const Token *end = params1[k]->next();
while (end && !(level == 0 && Token::Match(end, ",|>"))) {
if (Token::Match(end, "{|(|<"))
level++;
else if (Token::Match(end, "}|)|>"))
level--;
end = end->next();
}
if (end)
TokenList::copyTokens(const_cast<Token *>(params2[k]), params1[k]->next(), end->previous());
break;
} }
} }
@ -2629,6 +2643,97 @@ static Token *findSemicolon(Token *tok)
return tok; return tok;
} }
static bool usingMatch(
const Token *nameToken,
const std::string &scope,
Token **tok,
const std::string &scope1,
const std::list<ScopeInfo2> &scopeList1)
{
Token *tok1 = *tok;
if (tok1 && tok1->str() != nameToken->str())
return false;
// skip this using
if (tok1 == nameToken) {
*tok = findSemicolon(tok1);
return false;
}
// skip other using with this name
if (tok1->strAt(-1) == "using") {
// fixme: this is wrong
// skip to end of scope
if (scopeList1.back().bodyEnd)
*tok = scopeList1.back().bodyEnd->previous();
return false;
}
if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) {
// fixme
return false;
}
// get qualification
std::string qualification;
const Token* tok2 = tok1;
std::string::size_type index = scope.size();
std::string::size_type new_index = std::string::npos;
bool match = true;
while (tok2->strAt(-1) == "::") {
std::string last;
if (match && !scope1.empty()) {
new_index = scope1.rfind(' ', index - 1);
if (new_index != std::string::npos)
last = scope1.substr(new_index, index - new_index);
else if (!qualification.empty())
last.clear();
else
last = scope1;
} else
match = false;
if (match && tok2->strAt(-2) == last)
index = new_index;
else {
if (!qualification.empty())
qualification = " :: " + qualification;
qualification = tok2->strAt(-2) + qualification;
}
tok2 = tok2->tokAt(-2);
}
// todo: check using namespace
std::string fullScope1 = scope1;
if (!scope1.empty() && !qualification.empty())
fullScope1 += " :: ";
fullScope1 += qualification;
if (scope == fullScope1)
return true;
std::string newScope1 = scope1;
// scopes didn't match so try higher scopes
while (!newScope1.empty()) {
std::string::size_type separator = newScope1.rfind(" :: ", index - 1);
if (separator != std::string::npos)
newScope1 = newScope1.substr(0, separator);
else
newScope1.clear();
std::string newFullScope1 = newScope1;
if (!newScope1.empty() && !qualification.empty())
newFullScope1 += " :: ";
newFullScope1 += qualification;
if (scope == newFullScope1)
return true;
}
return false;
}
bool TemplateSimplifier::simplifyUsing() bool TemplateSimplifier::simplifyUsing()
{ {
bool substitute = false; bool substitute = false;
@ -2755,203 +2860,152 @@ bool TemplateSimplifier::simplifyUsing()
scope1 = getScopeName(scopeList1); scope1 = getScopeName(scopeList1);
continue; continue;
} }
if (tok1 && tok1->str() == name) {
// skip this using if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1))
if (tok1 == nameToken) { continue;
tok1 = findSemicolon(tok1);
continue; // remove the qualification
} while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) {
// skip other using with this name tok1->deletePrevious();
if (tok1->strAt(-1) == "using") { tok1->deletePrevious();
// fixme: this is wrong }
// skip to end of scope
if (scopeList1.back().bodyEnd) Token * arrayStart = nullptr;
tok1 = scopeList1.back().bodyEnd->previous();
continue; // parse the type
} Token *type = start;
if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) { if (type->str() == "::") {
// fixme type = type->next();
continue; while (Token::Match(type, "%type% ::"))
type = type->tokAt(2);
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type% ::")) {
do {
type = type->tokAt(2);
} while (Token::Match(type, "%type% ::"));
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type%")) {
while (Token::Match(type, "const|struct|union|enum %type%") ||
(type->next() && type->next()->isStandardType()))
type = type->next();
type = type->next();
while (Token::Match(type, "%type%") &&
(type->isStandardType() || Token::Match(type, "unsigned|signed"))) {
type = type->next();
} }
// get qualification bool atEnd = false;
std::string qualification; while (!atEnd) {
const Token* tok2 = tok1; if (type && type->str() == "::") {
std::string::size_type index = scope.size(); type = type->next();
std::string::size_type new_index = std::string::npos; }
bool match = true;
while (tok2->strAt(-1) == "::") { if (Token::Match(type, "%type%") &&
std::string last; type->next() && !Token::Match(type->next(), "[|;|,|(")) {
if (match && !scope1.empty()) { type = type->next();
new_index = scope1.rfind(' ', index - 1); } else if (Token::simpleMatch(type, "const (")) {
if (new_index != std::string::npos) type = type->next();
last = scope1.substr(new_index, index - new_index); atEnd = true;
else if (!qualification.empty()) } else
last.clear(); atEnd = true;
}
} else
syntaxError(type);
// check for invalid input
if (!type)
syntaxError(tok1);
// check for template
if (type->str() == "<") {
type = type->findClosingBracket();
while (type && Token::Match(type->next(), ":: %type%"))
type = type->tokAt(2);
if (!type) {
syntaxError(tok1);
}
while (Token::Match(type->next(), "const|volatile"))
type = type->next();
type = type->next();
}
// check for pointers and references
std::list<std::string> pointers;
while (Token::Match(type, "*|&|&&|const")) {
pointers.push_back(type->str());
type = type->next();
}
// check for array
if (type && type->str() == "[") {
do {
if (!arrayStart)
arrayStart = type;
bool atEnd = false;
while (!atEnd) {
while (type->next() && !Token::Match(type->next(), ";|,")) {
type = type->next();
}
if (!type->next())
syntaxError(type); // invalid input
else if (type->next()->str() == ";")
atEnd = true;
else if (type->str() == "]")
atEnd = true;
else else
last = scope1; type = type->next();
} else
match = false;
if (match && tok2->strAt(-2) == last)
index = new_index;
else {
if (!qualification.empty())
qualification = " :: " + qualification;
qualification = tok2->strAt(-2) + qualification;
} }
tok2 = tok2->tokAt(-2);
type = type->next();
} while (type && type->str() == "[");
}
Token* after = tok1->next();
// check if type was parsed
if (type && type == usingEnd) {
// check for array syntax and add type around variable
if (arrayStart) {
if (Token::Match(tok1->next(), "%name%")) {
mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous());
mTokenList.copyTokens(tok1, start, arrayStart->previous());
tok1->deleteThis();
substitute = true;
}
} else {
// just replace simple type aliases
mTokenList.copyTokens(tok1, start, usingEnd->previous());
tok1->deleteThis();
substitute = true;
} }
} else {
// todo: check using namespace skip = true;
std::string fullScope1 = scope1; if (mSettings->debugwarnings && mErrorLogger) {
if (!scope1.empty() && !qualification.empty()) std::string str;
fullScope1 += " :: "; for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
fullScope1 += qualification; if (!str.empty())
str += ' ';
if (scope == fullScope1) { str += tok3->str();
// remove the qualification
while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) {
tok1->deletePrevious();
tok1->deletePrevious();
} }
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
Token * arrayStart = nullptr; "Failed to parse \'" + str + "\'. The checking continues anyway.", false));
// parse the type
Token *type = start;
if (type->str() == "::") {
type = type->next();
while (Token::Match(type, "%type% ::"))
type = type->tokAt(2);
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type% ::")) {
do {
type = type->tokAt(2);
} while (Token::Match(type, "%type% ::"));
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type%")) {
while (Token::Match(type, "const|struct|union|enum %type%") ||
(type->next() && type->next()->isStandardType()))
type = type->next();
type = type->next();
while (Token::Match(type, "%type%") &&
(type->isStandardType() || Token::Match(type, "unsigned|signed"))) {
type = type->next();
}
bool atEnd = false;
while (!atEnd) {
if (type && type->str() == "::") {
type = type->next();
}
if (Token::Match(type, "%type%") &&
type->next() && !Token::Match(type->next(), "[|;|,|(")) {
type = type->next();
} else if (Token::simpleMatch(type, "const (")) {
type = type->next();
atEnd = true;
} else
atEnd = true;
}
} else
syntaxError(type);
// check for invalid input
if (!type)
syntaxError(tok1);
// check for template
if (type->str() == "<") {
type = type->findClosingBracket();
while (type && Token::Match(type->next(), ":: %type%"))
type = type->tokAt(2);
if (!type) {
syntaxError(tok1);
}
while (Token::Match(type->next(), "const|volatile"))
type = type->next();
type = type->next();
}
// check for pointers and references
std::list<std::string> pointers;
while (Token::Match(type, "*|&|&&|const")) {
pointers.push_back(type->str());
type = type->next();
}
// check for array
if (type && type->str() == "[") {
do {
if (!arrayStart)
arrayStart = type;
bool atEnd = false;
while (!atEnd) {
while (type->next() && !Token::Match(type->next(), ";|,")) {
type = type->next();
}
if (!type->next())
syntaxError(type); // invalid input
else if (type->next()->str() == ";")
atEnd = true;
else if (type->str() == "]")
atEnd = true;
else
type = type->next();
}
type = type->next();
} while (type && type->str() == "[");
}
Token* after = tok1->next();
// check if type was parsed
if (type && type == usingEnd) {
// check for array syntax and add type around variable
if (arrayStart) {
if (Token::Match(tok1->next(), "%name%")) {
mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous());
mTokenList.copyTokens(tok1, start, arrayStart->previous());
tok1->deleteThis();
substitute = true;
}
} else {
// just replace simple type aliases
mTokenList.copyTokens(tok1, start, usingEnd->previous());
tok1->deleteThis();
substitute = true;
}
} else {
skip = true;
if (mSettings->debugwarnings && mErrorLogger) {
std::string str;
for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
if (!str.empty())
str += ' ';
str += tok3->str();
}
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug",
"Failed to parse \'" + str + "\'. The checking continues anyway.", false));
}
}
tok1 = after;
} }
} }
tok1 = after;
} }
if (!skip) if (!skip)
usingList.emplace_back(usingStart, usingEnd); usingList.emplace_back(usingStart, usingEnd);
} }
@ -2988,6 +3042,19 @@ void TemplateSimplifier::simplifyTemplates(
const std::time_t maxtime, const std::time_t maxtime,
bool &codeWithTemplates) bool &codeWithTemplates)
{ {
// Remove "typename" unless used in template arguments..
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "typename %name%"))
tok->deleteThis();
if (Token::simpleMatch(tok, "template <")) {
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
// TODO: 2 is not the ideal number of loops. // TODO: 2 is not the ideal number of loops.
// We should loop until the number of declarations is 0 but we can't // We should loop until the number of declarations is 0 but we can't
// do that until we instantiate unintstantiated templates with their symbolic types. // do that until we instantiate unintstantiated templates with their symbolic types.
@ -3014,24 +3081,8 @@ void TemplateSimplifier::simplifyTemplates(
bool hasTemplates = getTemplateDeclarations(); bool hasTemplates = getTemplateDeclarations();
if (i == 0) { if (i == 0)
codeWithTemplates = hasTemplates; codeWithTemplates = hasTemplates;
if (hasTemplates) {
// There are templates..
// Remove "typename" unless used in template arguments..
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (tok->str() == "typename")
tok->deleteThis();
if (Token::simpleMatch(tok, "template <")) {
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
}
}
// Make sure there is something to simplify. // Make sure there is something to simplify.
if (mTemplateDeclarations.empty()) if (mTemplateDeclarations.empty())

View File

@ -138,6 +138,7 @@ private:
TEST_CASE(template98); // #8959 TEST_CASE(template98); // #8959
TEST_CASE(template99); // #8960 TEST_CASE(template99); // #8960
TEST_CASE(template100); // #8967 TEST_CASE(template100); // #8967
TEST_CASE(template101); // #8968
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..}; TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -2117,6 +2118,24 @@ private:
ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS(exp, tok(code));
} }
void template101() { // #8968
const char code[] = "class A {\n"
"public:\n"
" using ArrayType = std::vector<int>;\n"
" void func(typename ArrayType::size_type i) {\n"
" }\n"
"};";
const char exp[] = "class A { "
"public: "
"void func ( std :: vector < int > :: size_type i ) { "
"} "
"} ;";
ASSERT_EQUALS(exp, tok(code));
ASSERT_EQUALS("", errout.str());
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..}; void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n" const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n" "template <typename T> struct S {a};\n"
@ -2334,6 +2353,25 @@ private:
"{ int ar [ 3 ] ; } ;"; "{ int ar [ 3 ] ; } ;";
ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS(exp, tok(code));
} }
{
const char code[] = "template<typename Lhs, int TriangularPart = (int(Lhs::Flags) & LowerTriangularBit)>\n"
"struct ei_solve_triangular_selector;\n"
"template<typename Lhs, int UpLo>\n"
"struct ei_solve_triangular_selector<Lhs,UpLo> {\n"
"};\n"
"template<typename Lhs, int TriangularPart>\n"
"struct ei_solve_triangular_selector { };";
const char exp[] = "template < typename Lhs , int TriangularPart = ( int ( Lhs :: Flags ) & LowerTriangularBit ) > "
"struct ei_solve_triangular_selector ; "
"template < typename Lhs , int UpLo > "
"struct ei_solve_triangular_selector < Lhs , UpLo > { "
"} ; "
"template < typename Lhs , int TriangularPart = ( int ( Lhs :: Flags ) & LowerTriangularBit ) > "
"struct ei_solve_triangular_selector { } ;";
ASSERT_EQUALS(exp, tok(code));
}
} }
void template_default_type() { void template_default_type() {

View File

@ -2307,25 +2307,25 @@ private:
const char code1[] = "typedef typename A B;\n" const char code1[] = "typedef typename A B;\n"
"typedef typename B C;\n" "typedef typename B C;\n"
"typename C c;\n"; "typename C c;\n";
const char expected1[] = "typename A c ;"; const char expected1[] = "A c ;";
ASSERT_EQUALS(expected1, tok(code1)); ASSERT_EQUALS(expected1, tok(code1));
const char code2[] = "typedef typename A B;\n" const char code2[] = "typedef typename A B;\n"
"typedef typename B C;\n" "typedef typename B C;\n"
"C c;\n"; "C c;\n";
const char expected2[] = "typename A c ;"; const char expected2[] = "A c ;";
ASSERT_EQUALS(expected2, tok(code2)); ASSERT_EQUALS(expected2, tok(code2));
const char code3[] = "typedef typename A B;\n" const char code3[] = "typedef typename A B;\n"
"typedef B C;\n" "typedef B C;\n"
"C c;\n"; "C c;\n";
const char expected3[] = "typename A c ;"; const char expected3[] = "A c ;";
ASSERT_EQUALS(expected3, tok(code3)); ASSERT_EQUALS(expected3, tok(code3));
const char code4[] = "typedef A B;\n" const char code4[] = "typedef A B;\n"
"typedef typename B C;\n" "typedef typename B C;\n"
"C c;\n"; "C c;\n";
const char expected4[] = "typename A c ;"; const char expected4[] = "A c ;";
ASSERT_EQUALS(expected4, tok(code4)); ASSERT_EQUALS(expected4, tok(code4));
const char code5[] = "typedef A B;\n" const char code5[] = "typedef A B;\n"

View File

@ -58,6 +58,9 @@ private:
TEST_CASE(simplifyUsing14); TEST_CASE(simplifyUsing14);
TEST_CASE(simplifyUsing15); TEST_CASE(simplifyUsing15);
TEST_CASE(simplifyUsing16); TEST_CASE(simplifyUsing16);
TEST_CASE(simplifyUsing8970);
TEST_CASE(simplifyUsing8971);
} }
std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) { std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) {
@ -412,6 +415,43 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void simplifyUsing8970() {
const char code[] = "using V = std::vector<int>;\n"
"struct A {\n"
" V p;\n"
"};";
const char expected[] = "struct A { "
"std :: vector < int > p ; "
"} ;";
ASSERT_EQUALS(expected, tok(code, false));
}
void simplifyUsing8971() {
const char code[] = "class A {\n"
"public:\n"
" using V = std::vector<double>;\n"
"};\n"
"using V = std::vector<int>;\n"
"class I {\n"
"private:\n"
" A::V v_;\n"
" V v2_;\n"
"};";
const char expected[] = "class A { "
"public: "
"} ; "
"class I { "
"private: "
"std :: vector < double > v_ ; "
"std :: vector < int > v2_ ; "
"} ;";
ASSERT_EQUALS(expected, tok(code, false));
}
}; };
REGISTER_TEST(TestSimplifyUsing) REGISTER_TEST(TestSimplifyUsing)

View File

@ -2187,7 +2187,7 @@ private:
ASSERT_EQUALS("1: template < int d , typename A , typename B > struct S { } ;\n", tokenize("template<int d, typename A, typename B> struct S {};")); ASSERT_EQUALS("1: template < int d , typename A , typename B > struct S { } ;\n", tokenize("template<int d, typename A, typename B> struct S {};"));
ASSERT_EQUALS("1: typename A a@1 ;\n", tokenize("typename A a;")); ASSERT_EQUALS("1: A a@1 ;\n", tokenize("typename A a;"));
} }
void varid_rvalueref() { void varid_rvalueref() {