CppRewriter.cpp 19.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

30
#include "CppRewriter.h"
31 32 33 34 35 36 37 38 39 40

#include "Overview.h"

#include <cplusplus/TypeVisitor.h>
#include <cplusplus/NameVisitor.h>
#include <cplusplus/CoreTypes.h>
#include <cplusplus/Symbols.h>
#include <cplusplus/Literals.h>
#include <cplusplus/Names.h>
#include <cplusplus/Scope.h>
41

42 43 44
#include <QVarLengthArray>
#include <QRegExp>
#include <QDebug>
45

hjk's avatar
hjk committed
46 47 48 49 50
#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
#define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)

51
namespace CPlusPlus {
52

53
class Rewrite
54 55
{
public:
56
    Rewrite(Control *control, SubstitutionEnvironment *env)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        : control(control), env(env), rewriteType(this), rewriteName(this) {}

    class RewriteType: public TypeVisitor
    {
        Rewrite *rewrite;
        QList<FullySpecifiedType> temps;

        Control *control() const
        { return rewrite->control; }

        void accept(const FullySpecifiedType &ty)
        {
            TypeVisitor::accept(ty.type());
            unsigned flags = ty.flags();
            flags |= temps.back().flags();
            temps.back().setFlags(flags);
        }

    public:
        RewriteType(Rewrite *r): rewrite(r) {}

        FullySpecifiedType operator()(const FullySpecifiedType &ty)
        {
            accept(ty);
            return temps.takeLast();
        }

        virtual void visit(UndefinedType *)
        {
            temps.append(FullySpecifiedType());
        }

        virtual void visit(VoidType *)
        {
            temps.append(control()->voidType());
        }

        virtual void visit(IntegerType *type)
        {
            temps.append(control()->integerType(type->kind()));
        }

        virtual void visit(FloatType *type)
        {
            temps.append(control()->floatType(type->kind()));
        }

        virtual void visit(PointerToMemberType *type)
        {
            const Name *memberName = rewrite->rewriteName(type->memberName());
            const FullySpecifiedType elementType = rewrite->rewriteType(type->elementType());
            temps.append(control()->pointerToMemberType(memberName, elementType));
        }

        virtual void visit(PointerType *type)
        {
            const FullySpecifiedType elementType = rewrite->rewriteType(type->elementType());
            temps.append(control()->pointerType(elementType));
        }

        virtual void visit(ReferenceType *type)
        {
            const FullySpecifiedType elementType = rewrite->rewriteType(type->elementType());
            temps.append(control()->referenceType(elementType));
        }

        virtual void visit(ArrayType *type)
        {
            const FullySpecifiedType elementType = rewrite->rewriteType(type->elementType());
            temps.append(control()->arrayType(elementType, type->size()));
        }

        virtual void visit(NamedType *type)
        {
131
            FullySpecifiedType ty = rewrite->env->apply(type->name(), rewrite);
132
            if (! ty->isUndefinedType()) {
133
                temps.append(ty);
134
            } else {
135 136 137 138 139 140 141 142 143
                const Name *name = rewrite->rewriteName(type->name());
                temps.append(control()->namedType(name));
            }
        }

        virtual void visit(Function *type)
        {
            Function *funTy = control()->newFunction(0, 0);
            funTy->copy(type);
144 145
            funTy->setConst(type->isConst());
            funTy->setVolatile(type->isVolatile());
146 147 148 149 150

            funTy->setName(rewrite->rewriteName(type->name()));

            funTy->setReturnType(rewrite->rewriteType(type->returnType()));

151
            for (unsigned i = 0, argc = type->argumentCount(); i < argc; ++i) {
152 153 154 155 156 157 158
                Symbol *arg = type->argumentAt(i);

                Argument *newArg = control()->newArgument(0, 0);
                newArg->copy(arg);
                newArg->setName(rewrite->rewriteName(arg->name()));
                newArg->setType(rewrite->rewriteType(arg->type()));

159 160
                // the copy() call above set the scope to 'type'
                // reset it to 0 before adding addMember to avoid assert
161
                newArg->resetEnclosingScope();
162
                funTy->addMember(newArg);
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
            }

            temps.append(funTy);
        }

        virtual void visit(Namespace *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(Class *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(Enum *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(ForwardClassDeclaration *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(ObjCClass *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(ObjCProtocol *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(ObjCMethod *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(ObjCForwardClassDeclaration *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

        virtual void visit(ObjCForwardProtocolDeclaration *type)
        {
            qWarning() << Q_FUNC_INFO;
            temps.append(type);
        }

    };

    class RewriteName: public NameVisitor
    {
        Rewrite *rewrite;
        QList<const Name *> temps;

        Control *control() const
        { return rewrite->control; }

        const Identifier *identifier(const Identifier *other) const
        {
            if (! other)
                return 0;

237
            return control()->identifier(other->chars(), other->size());
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
        }

    public:
        RewriteName(Rewrite *r): rewrite(r) {}

        const Name *operator()(const Name *name)
        {
            if (! name)
                return 0;

            accept(name);
            return temps.takeLast();
        }

        virtual void visit(const QualifiedNameId *name)
        {
            const Name *base = rewrite->rewriteName(name->base());
            const Name *n = rewrite->rewriteName(name->name());
            temps.append(control()->qualifiedNameId(base, n));
        }

259
        virtual void visit(const Identifier *name)
260
        {
261
            temps.append(control()->identifier(name->chars(), name->size()));
262 263 264 265 266 267 268
        }

        virtual void visit(const TemplateNameId *name)
        {
            QVarLengthArray<FullySpecifiedType, 8> args(name->templateArgumentCount());
            for (unsigned i = 0; i < name->templateArgumentCount(); ++i)
                args[i] = rewrite->rewriteType(name->templateArgumentAt(i));
269 270
            temps.append(control()->templateNameId(identifier(name->identifier()), name->isSpecialization(),
                                                   args.data(), args.size()));
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
        }

        virtual void visit(const DestructorNameId *name)
        {
            temps.append(control()->destructorNameId(identifier(name->identifier())));
        }

        virtual void visit(const OperatorNameId *name)
        {
            temps.append(control()->operatorNameId(name->kind()));
        }

        virtual void visit(const ConversionNameId *name)
        {
            FullySpecifiedType ty = rewrite->rewriteType(name->type());
            temps.append(control()->conversionNameId(ty));
        }

        virtual void visit(const SelectorNameId *name)
        {
            QVarLengthArray<const Name *, 8> names(name->nameCount());
            for (unsigned i = 0; i < name->nameCount(); ++i)
                names[i] = rewrite->rewriteName(name->nameAt(i));
            temps.append(control()->selectorNameId(names.constData(), names.size(), name->hasArguments()));
        }
    };

public: // attributes
    Control *control;
300
    SubstitutionEnvironment *env;
301 302 303 304
    RewriteType rewriteType;
    RewriteName rewriteName;
};

305 306
SubstitutionEnvironment::SubstitutionEnvironment()
    : _scope(0)
307 308 309
{
}

310
FullySpecifiedType SubstitutionEnvironment::apply(const Name *name, Rewrite *rewrite) const
311
{
312 313 314 315 316 317 318 319 320 321 322
    if (name) {
        for (int index = _substs.size() - 1; index != -1; --index) {
            const Substitution *subst = _substs.at(index);

            FullySpecifiedType ty = subst->apply(name, rewrite);
            if (! ty->isUndefinedType())
                return ty;
        }
    }

    return FullySpecifiedType();
323 324
}

325
void SubstitutionEnvironment::enter(Substitution *subst)
326
{
327 328
    _substs.append(subst);
}
329

330 331 332 333
void SubstitutionEnvironment::leave()
{
    _substs.removeLast();
}
334

335 336 337 338
Scope *SubstitutionEnvironment::scope() const
{
    return _scope;
}
339

340 341 342 343 344 345
Scope *SubstitutionEnvironment::switchScope(Scope *scope)
{
    Scope *previous = _scope;
    _scope = scope;
    return previous;
}
346

347 348 349
const LookupContext &SubstitutionEnvironment::context() const
{
    return _context;
350 351
}

352 353 354 355
void SubstitutionEnvironment::setContext(const LookupContext &context)
{
    _context = context;
}
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

SubstitutionMap::SubstitutionMap()
{

}

SubstitutionMap::~SubstitutionMap()
{

}

void SubstitutionMap::bind(const Name *name, const FullySpecifiedType &ty)
{
    _map.append(qMakePair(name, ty));
}

FullySpecifiedType SubstitutionMap::apply(const Name *name, Rewrite *) const
{
    for (int n = _map.size() - 1; n != -1; --n) {
        const QPair<const Name *, FullySpecifiedType> &p = _map.at(n);

        if (name->isEqualTo(p.first))
            return p.second;
    }

    return FullySpecifiedType();
}

384

385 386
UseMinimalNames::UseMinimalNames(ClassOrNamespace *target)
    : _target(target)
387 388 389 390
{

}

391
UseMinimalNames::~UseMinimalNames()
392 393 394 395
{

}

396
FullySpecifiedType UseMinimalNames::apply(const Name *name, Rewrite *rewrite) const
397 398 399 400
{
    SubstitutionEnvironment *env = rewrite->env;
    Scope *scope = env->scope();

401 402
    if (name->isTemplateNameId() ||
            (name->isQualifiedNameId() && name->asQualifiedNameId()->name()->isTemplateNameId()))
403 404
        return FullySpecifiedType();

405 406 407 408 409 410 411 412
    if (! scope)
        return FullySpecifiedType();

    const LookupContext &context = env->context();
    Control *control = rewrite->control;

    const QList<LookupItem> results = context.lookup(name, scope);
    foreach (const LookupItem &r, results) {
413
        if (Symbol *d = r.declaration())
414
            return control->namedType(LookupContext::minimalName(d, _target, control));
415 416 417 418 419 420 421 422

        return r.type();
    }

    return FullySpecifiedType();
}


423 424 425 426 427 428 429 430 431 432 433 434
UseQualifiedNames::UseQualifiedNames()
    : UseMinimalNames(0)
{

}

UseQualifiedNames::~UseQualifiedNames()
{

}


435 436 437
FullySpecifiedType rewriteType(const FullySpecifiedType &type,
                               SubstitutionEnvironment *env,
                               Control *control)
438 439 440 441 442
{
    Rewrite rewrite(control, env);
    return rewrite.rewriteType(type);
}

443 444 445
const Name *rewriteName(const Name *name,
                        SubstitutionEnvironment *env,
                        Control *control)
446 447 448 449
{
    Rewrite rewrite(control, env);
    return rewrite.rewriteName(name);
}
450 451 452 453 454 455 456

// Simplify complicated STL template types,
// such as 'std::basic_string<char,std::char_traits<char>,std::allocator<char> >'
// -> 'std::string' and helpers.

static QString chopConst(QString type)
{
Orgad Shaneh's avatar
Orgad Shaneh committed
457
    while (1) {
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
        if (type.startsWith(QLatin1String("const")))
            type = type.mid(5);
        else if (type.startsWith(QLatin1Char(' ')))
            type = type.mid(1);
        else if (type.endsWith(QLatin1String("const")))
            type.chop(5);
        else if (type.endsWith(QLatin1Char(' ')))
            type.chop(1);
        else
            break;
    }
    return type;
}

static inline QRegExp stdStringRegExp(const QString &charType)
{
    QString rc = QLatin1String("basic_string<");
    rc += charType;
    rc += QLatin1String(",[ ]?std::char_traits<");
    rc += charType;
    rc += QLatin1String(">,[ ]?std::allocator<");
    rc += charType;
    rc += QLatin1String("> >");
    const QRegExp re(rc);
hjk's avatar
hjk committed
482
    QTC_ASSERT(re.isValid(), /**/);
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
    return re;
}

// Simplify string types in a type
// 'std::set<std::basic_string<char... > >' -> std::set<std::string>'
static inline void simplifyStdString(const QString &charType, const QString &replacement,
                                     QString *type)
{
    QRegExp stringRegexp = stdStringRegExp(charType);
    const int replacementSize = replacement.size();
    for (int pos = 0; pos < type->size(); ) {
        // Check next match
        const int matchPos = stringRegexp.indexIn(*type, pos);
        if (matchPos == -1)
            break;
        const int matchedLength = stringRegexp.matchedLength();
        type->replace(matchPos, matchedLength, replacement);
        pos = matchPos + replacementSize;
        // If we were inside an 'allocator<std::basic_string..char > >'
        // kill the following blank -> 'allocator<std::string>'
        if (pos + 1 < type->size() && type->at(pos) == QLatin1Char(' ')
                && type->at(pos + 1) == QLatin1Char('>'))
            type->remove(pos, 1);
    }
}

// Fix 'std::allocator<std::string >' -> 'std::allocator<std::string>',
// which can happen when replacing/simplifying
static inline QString fixNestedTemplates(QString s)
{
    const int size = s.size();
    if (size > 3
            && s.at(size - 1) == QLatin1Char('>')
            && s.at(size - 2) == QLatin1Char(' ')
            && s.at(size - 3) != QLatin1Char('>'))
Orgad Shaneh's avatar
Orgad Shaneh committed
518
        s.remove(size - 2, 1);
519 520 521 522 523 524
    return s;
}

CPLUSPLUS_EXPORT QString simplifySTLType(const QString &typeIn)
{
    QString type = typeIn;
525
    if (type.startsWith(QLatin1String("class "))) // MSVC prepends class,struct
526
        type.remove(0, 6);
527
    if (type.startsWith(QLatin1String("struct ")))
528 529
        type.remove(0, 7);

530
    type.replace(QLatin1String("std::__1::"), QLatin1String("std::"));
531 532 533
    type.replace(QLatin1Char('*'), QLatin1Char('@'));

    for (int i = 0; i < 10; ++i) {
534 535 536 537 538 539 540 541
        // std::ifstream
        QRegExp ifstreamRE(QLatin1String("std::basic_ifstream<char,\\s*std::char_traits<char>\\s*>"));
        ifstreamRE.setMinimal(true);
        QTC_ASSERT(ifstreamRE.isValid(), return typeIn);
        if (ifstreamRE.indexIn(type) != -1)
            type.replace(ifstreamRE.cap(0), QLatin1String("std::ifstream"));

        // Anything with a std::allocator
542
        int start = type.indexOf(QLatin1String("std::allocator<"));
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
        if (start == -1)
            break;
        // search for matching '>'
        int pos;
        int level = 0;
        for (pos = start + 12; pos < type.size(); ++pos) {
            int c = type.at(pos).unicode();
            if (c == '<') {
                ++level;
            } else if (c == '>') {
                --level;
                if (level == 0)
                    break;
            }
        }
        const QString alloc = fixNestedTemplates(type.mid(start, pos + 1 - start).trimmed());
        const QString inner = fixNestedTemplates(alloc.mid(15, alloc.size() - 16).trimmed());
560 561
        const QString allocEsc = QRegExp::escape(alloc);
        const QString innerEsc = QRegExp::escape(inner);
562 563 564 565 566 567 568 569
        if (inner == QLatin1String("char")) { // std::string
            simplifyStdString(QLatin1String("char"), QLatin1String("string"), &type);
        } else if (inner == QLatin1String("wchar_t")) { // std::wstring
            simplifyStdString(QLatin1String("wchar_t"), QLatin1String("wstring"), &type);
        } else if (inner == QLatin1String("unsigned short")) { // std::wstring/MSVC
            simplifyStdString(QLatin1String("unsigned short"), QLatin1String("wstring"), &type);
        }
        // std::vector, std::deque, std::list
570
        QRegExp re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(innerEsc, allocEsc));
hjk's avatar
hjk committed
571
        QTC_ASSERT(re1.isValid(), return typeIn);
572 573 574 575
        if (re1.indexIn(type) != -1)
            type.replace(re1.cap(0), QString::fromLatin1("%1<%2>").arg(re1.cap(1), inner));

        // std::stack
576
        QRegExp stackRE(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(innerEsc, innerEsc));
577
        stackRE.setMinimal(true);
hjk's avatar
hjk committed
578
        QTC_ASSERT(stackRE.isValid(), return typeIn);
579 580 581 582
        if (stackRE.indexIn(type) != -1)
            type.replace(stackRE.cap(0), QString::fromLatin1("stack<%1>").arg(inner));

        // std::set
583
        QRegExp setRE(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*>").arg(innerEsc, innerEsc, allocEsc));
584
        setRE.setMinimal(true);
hjk's avatar
hjk committed
585
        QTC_ASSERT(setRE.isValid(), return typeIn);
586 587 588 589
        if (setRE.indexIn(type) != -1)
            type.replace(setRE.cap(0), QString::fromLatin1("set<%1>").arg(inner));

        // std::map
590
        if (inner.startsWith(QLatin1String("std::pair<"))) {
591 592 593 594 595 596 597 598 599 600 601 602 603
            // search for outermost ',', split key and value
            int pos;
            int level = 0;
            for (pos = 10; pos < inner.size(); ++pos) {
                int c = inner.at(pos).unicode();
                if (c == '<')
                    ++level;
                else if (c == '>')
                    --level;
                else if (c == ',' && level == 0)
                    break;
            }
            const QString key = chopConst(inner.mid(10, pos - 10));
604
            const QString keyEsc = QRegExp::escape(key);
605 606 607
            // Get value: MSVC: 'pair<a const ,b>', gcc: 'pair<const a, b>'
            if (inner.at(++pos) == QLatin1Char(' '))
                pos++;
608 609
            const QString value = inner.mid(pos, inner.size() - pos - 1).trimmed();
            const QString valueEsc = QRegExp::escape(value);
610
            QRegExp mapRE1(QString::fromLatin1("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*>")
Orgad Shaneh's avatar
Orgad Shaneh committed
611
                           .arg(keyEsc, valueEsc, keyEsc, allocEsc));
612
            mapRE1.setMinimal(true);
hjk's avatar
hjk committed
613
            QTC_ASSERT(mapRE1.isValid(), return typeIn);
614
            if (mapRE1.indexIn(type) != -1) {
615
                type.replace(mapRE1.cap(0), QString::fromLatin1("map<%1, %2>").arg(key, value));
616
            } else {
617
                QRegExp mapRE2(QString::fromLatin1("map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*>")
Orgad Shaneh's avatar
Orgad Shaneh committed
618
                               .arg(keyEsc, valueEsc, keyEsc, allocEsc));
619
                mapRE2.setMinimal(true);
620
                if (mapRE2.indexIn(type) != -1)
621
                    type.replace(mapRE2.cap(0), QString::fromLatin1("map<const %1, %2>").arg(key, value));
622 623 624 625 626 627 628 629 630
            }
        }
    }
    type.replace(QLatin1Char('@'), QLatin1Char('*'));
    type.replace(QLatin1String(" >"), QLatin1String(">"));
    return type;
}

} // namespace CPlusPlus