bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::string &name, std::size_t numberOfArguments, const char patternAfter[])
{
    if (!Token::simpleMatch(instance, (name + " <").c_str()))
        return false;

    if (numberOfArguments != TemplateSimplifier::templateParameters(instance->next()))
        return false;

    if (patternAfter) {
        const Token *tok = instance;
        unsigned int indentlevel = 0;
        for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0) && (tok->str() != ">>" || indentlevel > 1); tok = tok->next()) {
            if (Token::Match(tok, "[<,] %var% <") && templateParameters(tok->tokAt(2)) > 0)
                ++indentlevel;
            if (indentlevel > 0 && tok->str() == ">")
                --indentlevel;
            if (indentlevel > 0 && tok->str() == ">>")
                indentlevel -= (indentlevel > 1) ? 2 : 1;
        }
        if (!tok || !Token::Match(tok->next(), patternAfter))
            return false;
    }

    // nothing mismatching was found..
    return true;
}
Beispiel #2
0
QString ClassItem::buildDisplayName() const
{
    auto diagramClass = dynamic_cast<DClass *>(object());
    QMT_CHECK(diagramClass);

    QString name;
    if (templateDisplay() == DClass::TemplateName && !diagramClass->templateParameters().isEmpty()) {
        name = object()->name();
        name += QLatin1Char('<');
        bool first = true;
        foreach (const QString &p, diagramClass->templateParameters()) {
            if (!first)
                name += QLatin1Char(',');
            name += p;
            first = false;
        }
        name += QLatin1Char('>');
    } else {
void DUpdateVisitor::visitMClass(const MClass *klass)
{
    auto dclass = dynamic_cast<DClass *>(m_target);
    QMT_CHECK(dclass);
    if (isUpdating(klass->umlNamespace() != dclass->umlNamespace()))
        dclass->setUmlNamespace(klass->umlNamespace());
    if (isUpdating(klass->templateParameters() != dclass->templateParameters()))
        dclass->setTemplateParameters(klass->templateParameters());
    if (isUpdating(klass->members() != dclass->members()))
        dclass->setMembers(klass->members());
    visitMObject(klass);
}
void TemplateSimplifier::expandTemplate(
    TokenList& tokenlist,
    const Token *tok,
    const std::string &name,
    std::vector<const Token *> &typeParametersInDeclaration,
    const std::string &newName,
    std::vector<const Token *> &typesUsedInTemplateInstantiation,
    std::list<Token *> &templateInstantiations)
{
    for (const Token *tok3 = tokenlist.front(); tok3; tok3 = tok3->next()) {
        if (tok3->str() == "{" || tok3->str() == "(" || tok3->str() == "[")
            tok3 = tok3->link();

        // Start of template..
        if (tok3 == tok) {
            tok3 = tok3->next();
        }

        // member function implemented outside class definition
        else if (TemplateSimplifier::instantiateMatch(tok3, name, typeParametersInDeclaration.size(), ":: ~| %var% (")) {
            tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex());
            while (tok3->str() != "::")
                tok3 = tok3->next();
        }

        // not part of template.. go on to next token
        else
            continue;

        int indentlevel = 0;
        std::stack<Token *> brackets; // holds "(", "[" and "{" tokens

        for (; tok3; tok3 = tok3->next()) {
            if (tok3->isName()) {
                // search for this token in the type vector
                unsigned int itype = 0;
                while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str())
                    ++itype;

                // replace type with given type..
                if (itype < typeParametersInDeclaration.size()) {
                    unsigned int typeindentlevel = 0;
                    for (const Token *typetok = typesUsedInTemplateInstantiation[itype];
                         typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>|>>"));
                         typetok = typetok->next()) {
                        if (Token::Match(typetok, "%var% <") && templateParameters(typetok->next()) > 0)
                            ++typeindentlevel;
                        else if (typeindentlevel > 0 && typetok->str() == ">")
                            --typeindentlevel;
                        else if (typeindentlevel > 0 && typetok->str() == ">>") {
                            if (typeindentlevel == 1)
                                break;
                            typeindentlevel -= 2;
                        }
                        tokenlist.addtoken(typetok, tok3->linenr(), tok3->fileIndex());
                    }
                    continue;
                }
            }

            // replace name..
            if (Token::Match(tok3, (name + " !!<").c_str())) {
                tokenlist.addtoken(newName, tok3->linenr(), tok3->fileIndex());
                continue;
            }

            // copy
            tokenlist.addtoken(tok3, tok3->linenr(), tok3->fileIndex());
            if (Token::Match(tok3, "%type% <")) {
                //if (!Token::simpleMatch(tok3, (name + " <").c_str()))
                //done = false;
                templateInstantiations.push_back(tokenlist.back());
            }

            // link() newly tokens manually
            else if (tok3->str() == "{") {
                brackets.push(tokenlist.back());
                indentlevel++;
            } else if (tok3->str() == "(") {
                brackets.push(tokenlist.back());
            } else if (tok3->str() == "[") {
                brackets.push(tokenlist.back());
            } else if (tok3->str() == "}") {
                assert(brackets.empty() == false && brackets.top()->str() == "{");
                Token::createMutualLinks(brackets.top(), tokenlist.back());
                if (tok3->strAt(1) == ";") {
                    const Token * tokSemicolon = tok3->next();
                    tokenlist.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex());
                }
                brackets.pop();
                if (indentlevel <= 1 && brackets.empty()) {
                    // there is a bug if indentlevel is 0
                    // the "}" token should only be added if indentlevel is 1 but I add it always intentionally
                    // if indentlevel ever becomes 0, cppcheck will write:
                    // ### Error: Invalid number of character {
                    break;
                }
                --indentlevel;
            } else if (tok3->str() == ")") {
                assert(brackets.empty() == false && brackets.top()->str() == "(");
                Token::createMutualLinks(brackets.top(), tokenlist.back());
                brackets.pop();
            } else if (tok3->str() == "]") {
                assert(brackets.empty() == false && brackets.top()->str() == "[");
                Token::createMutualLinks(brackets.top(), tokenlist.back());
                brackets.pop();
            }
        }

        assert(brackets.empty());
    }
}
void TemplateSimplifier::useDefaultArgumentValues(const std::list<Token *> &templates,
        std::list<Token *> * const templateInstantiations)
{
    for (std::list<Token *>::const_iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) {
        // template parameters with default value has syntax such as:
        //     x = y
        // this list will contain all the '=' tokens for such arguments
        std::list<Token *> eq;

        // parameter number. 1,2,3,..
        std::size_t templatepar = 1;

        // the template classname. This will be empty for template functions
        std::string classname;

        // Scan template declaration..
        for (Token *tok = *iter1; tok; tok = tok->next()) {
            // end of template parameters?
            if (tok->str() == ">") {
                if (Token::Match(tok, "> class|struct %var%"))
                    classname = tok->strAt(2);
                break;
            }

            // next template parameter
            if (tok->str() == ",")
                ++templatepar;

            // default parameter value
            else if (tok->str() == "=")
                eq.push_back(tok);
        }
        if (eq.empty() || classname.empty())
            continue;

        // iterate through all template instantiations
        for (std::list<Token *>::const_iterator iter2 = templateInstantiations->begin(); iter2 != templateInstantiations->end(); ++iter2) {
            Token *tok = *iter2;

            if (!Token::Match(tok, (classname + " < %any%").c_str()))
                continue;

            // count the parameters..
            unsigned int usedpar = 1;
            for (tok = tok->tokAt(3); tok; tok = tok->tokAt(2)) {
                if (tok->str() == ">")
                    break;

                if (tok->str() == ",")
                    ++usedpar;

                else
                    break;
            }
            if (tok && tok->str() == ">") {
                tok = tok->previous();
                std::list<Token *>::const_iterator it = eq.begin();
                for (std::size_t i = (templatepar - eq.size()); it != eq.end() && i < usedpar; ++i)
                    ++it;
                while (it != eq.end()) {
                    tok->insertToken(",");
                    tok = tok->next();
                    const Token *from = (*it)->next();
                    std::stack<Token *> links;
                    while (from && (!links.empty() || (from->str() != "," && from->str() != ">"))) {
                        tok->insertToken(from->str(), from->originalName());
                        tok = tok->next();
                        if (Token::Match(tok, "(|["))
                            links.push(tok);
                        else if (!links.empty() && Token::Match(tok, ")|]")) {
                            Token::createMutualLinks(links.top(), tok);
                            links.pop();
                        }
                        from = from->next();
                    }
                    ++it;
                }
            }
        }

        for (std::list<Token *>::iterator it = eq.begin(); it != eq.end(); ++it) {
            Token * const eqtok = *it;
            Token *tok2;
            int indentlevel = 0;
            for (tok2 = eqtok->next(); tok2; tok2 = tok2->next()) {
                if (tok2->str() == "(")
                    tok2 = tok2->link();
                else if (Token::Match(tok2, "%type% <") && templateParameters(tok2->next())) {
                    std::list<Token*>::iterator ti = std::find(templateInstantiations->begin(),
                                                     templateInstantiations->end(),
                                                     tok2);
                    if (ti != templateInstantiations->end())
                        templateInstantiations->erase(ti);
                    ++indentlevel;
                } else if (indentlevel > 0 && tok2->str() == ">")
                    --indentlevel;
                else if (indentlevel > 0 && tok2->str() == ">>") {
                    indentlevel -= 2;
                    if (indentlevel < 0)
                        tok2->str(">");
                } else if (indentlevel == 0 && Token::Match(tok2, ",|>|>>"))
                    break;
                if (indentlevel < 0)
                    break;
            }
            Token::eraseTokens(eqtok, tok2);
            eqtok->deleteThis();
        }
    }
}
bool TemplateSimplifier::simplifyTemplateInstantiations(
    TokenList& tokenlist,
    ErrorLogger& errorlogger,
    const Settings *_settings,
    const Token *tok,
    std::list<Token *> &templateInstantiations,
    std::set<std::string> &expandedtemplates)
{
    // this variable is not used at the moment. The intention was to
    // allow continuous instantiations until all templates has been expanded
    //bool done = false;

    // Contains tokens such as "T"
    std::vector<const Token *> typeParametersInDeclaration;
    for (tok = tok->tokAt(2); tok && tok->str() != ">"; tok = tok->next()) {
        if (Token::Match(tok, "%var% ,|>"))
            typeParametersInDeclaration.push_back(tok);
    }

    // bail out if the end of the file was reached
    if (!tok)
        return false;

    // get the position of the template name
    int namepos = TemplateSimplifier::getTemplateNamePosition(tok);
    if (namepos == -1) {
        // debug message that we bail out..
        if (_settings->debugwarnings) {
            std::list<const Token *> callstack(1, tok);
            errorlogger.reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug", "simplifyTemplates: bailing out", false));
        }
        return false;
    }

    // name of template function/class..
    const std::string name(tok->strAt(namepos));

    const bool isfunc(tok->strAt(namepos + 1) == "(");

    // locate template usage..
    std::string::size_type amountOftemplateInstantiations = templateInstantiations.size();
    unsigned int recursiveCount = 0;

    bool instantiated = false;

    for (std::list<Token *>::const_iterator iter2 = templateInstantiations.begin(); iter2 != templateInstantiations.end(); ++iter2) {
        if (amountOftemplateInstantiations != templateInstantiations.size()) {
            amountOftemplateInstantiations = templateInstantiations.size();
            simplifyCalculations(tokenlist.front());
            ++recursiveCount;
            if (recursiveCount > 100) {
                // bail out..
                break;
            }
        }

        Token * const tok2 = *iter2;
        if (tok2->str() != name)
            continue;

        if (Token::Match(tok2->previous(), "[;{}=]") &&
            !TemplateSimplifier::instantiateMatch(*iter2, name, typeParametersInDeclaration.size(), isfunc ? "(" : "*| %var%"))
            continue;

        // New type..
        std::vector<const Token *> typesUsedInTemplateInstantiation;
        std::string typeForNewNameStr;
        std::string templateMatchPattern(name + " < ");
        unsigned int indentlevel = 0;
        for (const Token *tok3 = tok2->tokAt(2); tok3 && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) {
            // #2648 - unhandled parentheses => bail out
            // #2721 - unhandled [ => bail out
            if (tok3->str() == "(" || tok3->str() == "[") {
                typeForNewNameStr.clear();
                break;
            }
            if (!tok3->next()) {
                typeForNewNameStr.clear();
                break;
            }
            if (Token::Match(tok3->tokAt(-2), "[<,] %var% <") && templateParameters(tok3) > 0)
                ++indentlevel;
            else if (indentlevel > 0 && Token::Match(tok3, "> [,>]"))
                --indentlevel;
            else if (indentlevel > 0 && tok3->str() == ">>") {
                if (indentlevel == 1) {
                    templateMatchPattern += '>';
                    typeForNewNameStr += '>';
                    break;
                }
                indentlevel -= 2;
            }
            templateMatchPattern += (tok3->str() == ">>") ? std::string("> >") : tok3->str();
            templateMatchPattern += ' ';
            if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]"))
                typesUsedInTemplateInstantiation.push_back(tok3);
            // add additional type information
            if (tok3->str() != "class") {
                if (tok3->isUnsigned())
                    typeForNewNameStr += "unsigned";
                else if (tok3->isSigned())
                    typeForNewNameStr += "signed";
                if (tok3->isLong())
                    typeForNewNameStr += "long";
                typeForNewNameStr += tok3->str();
            }
        }
        templateMatchPattern += ">";
        const std::string typeForNewName(typeForNewNameStr);

        if (typeForNewName.empty() || typeParametersInDeclaration.size() != typesUsedInTemplateInstantiation.size()) {
            if (_settings->debugwarnings) {
                std::list<const Token *> callstack(1, tok);
                errorlogger.reportErr(ErrorLogger::ErrorMessage(callstack, &tokenlist, Severity::debug, "debug",
                                      "Failed to instantiate template. The checking continues anyway.", false));
            }
            if (typeForNewName.empty())
                continue;
            break;
        }

        // New classname/funcname..
        const std::string newName(name + "<" + typeForNewName + ">");

        if (expandedtemplates.find(newName) == expandedtemplates.end()) {
            expandedtemplates.insert(newName);
            TemplateSimplifier::expandTemplate(tokenlist, tok,name,typeParametersInDeclaration,newName,typesUsedInTemplateInstantiation,templateInstantiations);
            instantiated = true;
        }

        // Replace all these template usages..
        std::list< std::pair<Token *, Token *> > removeTokens;
        for (Token *tok4 = tok2; tok4; tok4 = tok4->next()) {
            if (Token::simpleMatch(tok4, templateMatchPattern.c_str())) {
                Token * tok5 = tok4->tokAt(2);
                unsigned int typeCountInInstantiation = 1U; // There is always at least one type
                const Token *typetok = (!typesUsedInTemplateInstantiation.empty()) ? typesUsedInTemplateInstantiation[0] : 0;
                unsigned int indentlevel5 = 0;  // indentlevel for tok5
                while (tok5 && (indentlevel5 > 0 || tok5->str() != ">")) {
                    if (tok5->str() == "<" && templateParameters(tok5) > 0)
                        ++indentlevel5;
                    else if (indentlevel5 > 0 && Token::Match(tok5, "> [,>]"))
                        --indentlevel5;
                    else if (indentlevel5 == 0) {
                        if (tok5->str() != ",") {
                            if (!typetok ||
                                tok5->isUnsigned() != typetok->isUnsigned() ||
                                tok5->isSigned() != typetok->isSigned() ||
                                tok5->isLong() != typetok->isLong()) {
                                break;
                            }

                            typetok = typetok ? typetok->next() : 0;
                        } else {
                            typetok = (typeCountInInstantiation < typesUsedInTemplateInstantiation.size()) ? typesUsedInTemplateInstantiation[typeCountInInstantiation] : 0;
                            ++typeCountInInstantiation;
                        }
                    }
                    tok5 = tok5->next();
                }

                // matching template usage => replace tokens..
                // Foo < int >  =>  Foo<int>
                if (tok5 && tok5->str() == ">" && typeCountInInstantiation == typesUsedInTemplateInstantiation.size()) {
                    tok4->str(newName);
                    for (Token *tok6 = tok4->next(); tok6 != tok5; tok6 = tok6->next()) {
                        if (tok6->isName())
                            templateInstantiations.remove(tok6);
                    }
                    removeTokens.push_back(std::pair<Token*,Token*>(tok4, tok5->next()));
                }

                tok4 = tok5;
                if (!tok4)
                    break;
            }
        }
        while (!removeTokens.empty()) {
            Token::eraseTokens(removeTokens.back().first, removeTokens.back().second);
            removeTokens.pop_back();
        }
    }

    // Template has been instantiated .. then remove the template declaration
    return instantiated;
}