2009-11-09 12:50:00 +01:00
|
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
#include <QStringList>
|
|
|
|
|
#include <QTextDocument>
|
|
|
|
|
#include <QTextCursor>
|
|
|
|
|
#include <QTextBlock>
|
2009-11-09 15:10:03 +01:00
|
|
|
#include <QDir>
|
2009-11-09 12:50:00 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
|
|
#include <Control.h>
|
|
|
|
|
#include <Parser.h>
|
|
|
|
|
#include <AST.h>
|
|
|
|
|
#include <ASTVisitor.h>
|
|
|
|
|
#include <Symbols.h>
|
|
|
|
|
#include <CoreTypes.h>
|
|
|
|
|
#include <Literals.h>
|
|
|
|
|
#include <CppDocument.h>
|
|
|
|
|
#include <Overview.h>
|
2009-11-09 16:28:01 +01:00
|
|
|
#include <Names.h>
|
|
|
|
|
#include <Scope.h>
|
2009-11-09 12:50:00 +01:00
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
|
2009-11-10 10:32:36 +01:00
|
|
|
static const char copyrightHeader[] =
|
|
|
|
|
"/**************************************************************************\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** This file is part of Qt Creator\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** Contact: Nokia Corporation (qt-info@nokia.com)\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** Commercial Usage\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** Licensees holding valid Qt Commercial licenses may use this file in\n"
|
|
|
|
|
"** accordance with the Qt Commercial License Agreement provided with the\n"
|
|
|
|
|
"** Software or, alternatively, in accordance with the terms contained in\n"
|
|
|
|
|
"** a written agreement between you and Nokia.\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** GNU Lesser General Public License Usage\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** Alternatively, this file may be used under the terms of the GNU Lesser\n"
|
|
|
|
|
"** General Public License version 2.1 as published by the Free Software\n"
|
|
|
|
|
"** Foundation and appearing in the file LICENSE.LGPL included in the\n"
|
|
|
|
|
"** packaging of this file. Please review the following information to\n"
|
|
|
|
|
"** ensure the GNU Lesser General Public License version 2.1 requirements\n"
|
|
|
|
|
"** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"** If you are unsure which license is appropriate for your use, please\n"
|
|
|
|
|
"** contact the sales department at http://qt.nokia.com/contact.\n"
|
|
|
|
|
"**\n"
|
|
|
|
|
"**************************************************************************/\n"
|
|
|
|
|
;
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
class Accept0CG: protected ASTVisitor
|
2009-11-09 16:28:01 +01:00
|
|
|
{
|
2009-11-12 17:35:48 +01:00
|
|
|
QDir _cplusplusDir;
|
|
|
|
|
QTextStream *out;
|
2009-11-09 16:28:01 +01:00
|
|
|
|
|
|
|
|
public:
|
2009-11-13 11:35:19 +01:00
|
|
|
Accept0CG(const QDir &cplusplusDir, Control *control)
|
2009-11-12 17:35:48 +01:00
|
|
|
: ASTVisitor(control), _cplusplusDir(cplusplusDir), out(0)
|
2009-11-09 16:28:01 +01:00
|
|
|
{ }
|
|
|
|
|
|
2009-11-12 17:35:48 +01:00
|
|
|
void operator()(AST *ast)
|
2009-11-09 16:28:01 +01:00
|
|
|
{
|
2009-11-12 17:35:48 +01:00
|
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTVisit.cpp"));
|
|
|
|
|
|
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
|
|
|
if (! file.open(QFile::WriteOnly))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTextStream output(&file);
|
|
|
|
|
out = &output;
|
|
|
|
|
|
|
|
|
|
*out << copyrightHeader <<
|
|
|
|
|
"\n"
|
|
|
|
|
"#include \"AST.h\"\n"
|
|
|
|
|
"#include \"ASTVisitor.h\"\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"using namespace CPlusPlus;\n" << endl;
|
|
|
|
|
|
2009-11-09 16:28:01 +01:00
|
|
|
accept(ast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2009-11-12 17:35:48 +01:00
|
|
|
using ASTVisitor::visit;
|
|
|
|
|
|
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
|
|
|
|
|
|
QByteArray id_cast(NameAST *name)
|
2009-11-09 16:28:01 +01:00
|
|
|
{
|
2009-11-12 17:35:48 +01:00
|
|
|
if (! name)
|
|
|
|
|
return QByteArray();
|
|
|
|
|
|
|
|
|
|
Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
|
|
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
|
|
|
}
|
2009-11-09 16:28:01 +01:00
|
|
|
|
2009-11-12 17:35:48 +01:00
|
|
|
void visitMembers(Class *klass)
|
|
|
|
|
{
|
|
|
|
|
const QByteArray className = klass->name()->identifier()->chars();
|
2009-11-09 16:28:01 +01:00
|
|
|
|
2009-11-12 17:35:48 +01:00
|
|
|
// *out << " // visit " << className.constData() << endl;
|
|
|
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
|
|
|
Symbol *member = klass->memberAt(i);
|
|
|
|
|
if (! member->name())
|
2009-11-09 16:28:01 +01:00
|
|
|
continue;
|
2009-11-12 17:35:48 +01:00
|
|
|
|
|
|
|
|
Identifier *id = member->name()->identifier();
|
|
|
|
|
|
|
|
|
|
if (! id)
|
2009-11-09 16:28:01 +01:00
|
|
|
continue;
|
2009-11-12 17:35:48 +01:00
|
|
|
|
|
|
|
|
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
|
|
|
|
|
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
|
|
|
|
|
// nothing to do. The member is a token.
|
|
|
|
|
|
|
|
|
|
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
|
|
|
|
|
|
|
|
|
|
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
|
|
|
QByteArray typeName = namedTy->name()->identifier()->chars();
|
|
|
|
|
|
|
|
|
|
if (typeName.endsWith("AST") && memberName != "next") {
|
|
|
|
|
*out << " accept(" << memberName.constData() << ", visitor);" << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
|
|
|
|
|
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
|
|
|
|
|
|
|
|
|
|
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
|
|
|
|
|
visitMembers(baseClassSpec->symbol);
|
2009-11-09 16:28:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-11-12 17:35:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkMethod(Symbol *accept0Method) const
|
|
|
|
|
{
|
|
|
|
|
Declaration *decl = accept0Method->asDeclaration();
|
|
|
|
|
if (! decl)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
|
|
|
if (! funTy)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
else if (funTy->isPureVirtual())
|
|
|
|
|
return false;
|
2009-11-09 16:28:01 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 17:35:48 +01:00
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
|
|
|
{
|
|
|
|
|
Class *klass = ast->symbol;
|
|
|
|
|
const QByteArray className = id_cast(ast->name);
|
|
|
|
|
|
|
|
|
|
Identifier *visit_id = control()->findOrInsertIdentifier("accept0");
|
|
|
|
|
Symbol *accept0Method = klass->members()->lookat(visit_id);
|
|
|
|
|
for (; accept0Method; accept0Method = accept0Method->next()) {
|
|
|
|
|
if (accept0Method->identifier() != visit_id)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (checkMethod(accept0Method))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! accept0Method)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
|
|
|
|
|
|
*out
|
|
|
|
|
<< "void " << className.constData() << "::accept0(ASTVisitor *visitor)" << endl
|
|
|
|
|
<< "{" << endl
|
|
|
|
|
<< " if (visitor->visit(this)) {" << endl;
|
|
|
|
|
|
|
|
|
|
visitMembers(klass);
|
|
|
|
|
|
|
|
|
|
*out
|
|
|
|
|
<< " }" << endl
|
|
|
|
|
<< " visitor->endVisit(this);" << endl
|
|
|
|
|
<< "}" << endl
|
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2009-11-09 16:28:01 +01:00
|
|
|
};
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
class Match0CG: protected ASTVisitor
|
2009-11-09 16:28:01 +01:00
|
|
|
{
|
|
|
|
|
QDir _cplusplusDir;
|
|
|
|
|
QTextStream *out;
|
|
|
|
|
|
|
|
|
|
public:
|
2009-11-13 11:35:19 +01:00
|
|
|
Match0CG(const QDir &cplusplusDir, Control *control)
|
2009-11-09 16:28:01 +01:00
|
|
|
: ASTVisitor(control), _cplusplusDir(cplusplusDir), out(0)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
void operator()(AST *ast)
|
|
|
|
|
{
|
2009-11-12 17:35:48 +01:00
|
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatch0.cpp"));
|
2009-11-09 16:28:01 +01:00
|
|
|
|
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
|
|
|
if (! file.open(QFile::WriteOnly))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTextStream output(&file);
|
|
|
|
|
out = &output;
|
|
|
|
|
|
2009-11-10 10:32:36 +01:00
|
|
|
*out << copyrightHeader <<
|
2009-11-09 16:28:01 +01:00
|
|
|
"\n"
|
|
|
|
|
"#include \"AST.h\"\n"
|
2009-11-12 17:35:48 +01:00
|
|
|
"#include \"ASTMatcher.h\"\n"
|
2009-11-09 16:28:01 +01:00
|
|
|
"\n"
|
|
|
|
|
"using namespace CPlusPlus;\n" << endl;
|
|
|
|
|
|
|
|
|
|
accept(ast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using ASTVisitor::visit;
|
|
|
|
|
|
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
|
|
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
|
|
|
{
|
|
|
|
|
if (! name)
|
|
|
|
|
return QByteArray();
|
|
|
|
|
|
|
|
|
|
Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
|
|
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visitMembers(Class *klass)
|
|
|
|
|
{
|
2009-11-12 17:35:48 +01:00
|
|
|
Overview oo;
|
|
|
|
|
const QString className = oo(klass->name());
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
*out << " if (" << className << " *_other = pattern->as" << className.left(className.length() - 3) << "())" << endl;
|
2009-11-12 17:35:48 +01:00
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
*out << " return matcher->match(this, _other);" << endl;
|
|
|
|
|
|
|
|
|
|
*out << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkMethod(Symbol *accept0Method) const
|
|
|
|
|
{
|
|
|
|
|
Declaration *decl = accept0Method->asDeclaration();
|
|
|
|
|
if (! decl)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
|
|
|
if (! funTy)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
else if (funTy->isPureVirtual())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
|
|
|
{
|
|
|
|
|
Class *klass = ast->symbol;
|
|
|
|
|
const QByteArray className = id_cast(ast->name);
|
|
|
|
|
|
|
|
|
|
Identifier *match0_id = control()->findOrInsertIdentifier("match0");
|
|
|
|
|
Symbol *accept0Method = klass->members()->lookat(match0_id);
|
|
|
|
|
for (; accept0Method; accept0Method = accept0Method->next()) {
|
|
|
|
|
if (accept0Method->identifier() != match0_id)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (checkMethod(accept0Method))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! accept0Method)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
|
|
|
|
|
|
*out
|
|
|
|
|
<< "bool " << className.constData() << "::match0(AST *pattern, ASTMatcher *matcher)" << endl
|
|
|
|
|
<< "{" << endl;
|
|
|
|
|
|
|
|
|
|
visitMembers(klass);
|
|
|
|
|
|
|
|
|
|
*out
|
|
|
|
|
<< " return false;" << endl
|
|
|
|
|
<< "}" << endl
|
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MatcherCPPCG: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
QDir _cplusplusDir;
|
|
|
|
|
QTextStream *out;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
MatcherCPPCG(const QDir &cplusplusDir, Control *control)
|
|
|
|
|
: ASTVisitor(control), _cplusplusDir(cplusplusDir), out(0)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
void operator()(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
QFileInfo fileInfo(_cplusplusDir, QLatin1String("ASTMatcher.cpp"));
|
|
|
|
|
|
|
|
|
|
QFile file(fileInfo.absoluteFilePath());
|
|
|
|
|
if (! file.open(QFile::WriteOnly))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTextStream output(&file);
|
|
|
|
|
out = &output;
|
|
|
|
|
|
2009-11-13 11:43:52 +01:00
|
|
|
*out << copyrightHeader << endl
|
|
|
|
|
<< "#include \"AST.h\"" << endl
|
|
|
|
|
<< "#include \"ASTMatcher.h\"" << endl
|
|
|
|
|
<< "#include \"Control.h\"" << endl
|
|
|
|
|
<< "#include \"TranslationUnit.h\"" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "using namespace CPlusPlus;" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "ASTMatcher::ASTMatcher(Control *control)" << endl
|
|
|
|
|
<< " : _control(control)" << endl
|
|
|
|
|
<< "{ }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "ASTMatcher::~ASTMatcher()" << endl
|
|
|
|
|
<< "{ }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "Control *ASTMatcher::control() const" << endl
|
|
|
|
|
<< "{ return _control; }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "TranslationUnit *ASTMatcher::translationUnit() const" << endl
|
|
|
|
|
<< "{ return _control->translationUnit(); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "unsigned ASTMatcher::tokenCount() const" << endl
|
|
|
|
|
<< "{ return translationUnit()->tokenCount(); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "const Token &ASTMatcher::tokenAt(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->tokenAt(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "int ASTMatcher::tokenKind(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->tokenKind(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "const char *ASTMatcher::spell(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->spell(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "Identifier *ASTMatcher::identifier(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->identifier(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "Literal *ASTMatcher::literal(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->literal(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "NumericLiteral *ASTMatcher::numericLiteral(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->numericLiteral(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "StringLiteral *ASTMatcher::stringLiteral(unsigned index) const" << endl
|
|
|
|
|
<< "{ return translationUnit()->stringLiteral(index); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "void ASTMatcher::getPosition(unsigned offset," << endl
|
|
|
|
|
<< " unsigned *line," << endl
|
|
|
|
|
<< " unsigned *column," << endl
|
|
|
|
|
<< " StringLiteral **fileName) const" << endl
|
|
|
|
|
<< "{ translationUnit()->getPosition(offset, line, column, fileName); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "void ASTMatcher::getTokenPosition(unsigned index," << endl
|
|
|
|
|
<< " unsigned *line," << endl
|
|
|
|
|
<< " unsigned *column," << endl
|
|
|
|
|
<< " StringLiteral **fileName) const" << endl
|
|
|
|
|
<< "{ translationUnit()->getTokenPosition(index, line, column, fileName); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "void ASTMatcher::getTokenStartPosition(unsigned index, unsigned *line, unsigned *column) const" << endl
|
|
|
|
|
<< "{ getPosition(tokenAt(index).begin(), line, column); }" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< "void ASTMatcher::getTokenEndPosition(unsigned index, unsigned *line, unsigned *column) const" << endl
|
|
|
|
|
<< "{ getPosition(tokenAt(index).end(), line, column); }" << endl
|
2009-11-13 11:54:22 +01:00
|
|
|
<< endl
|
|
|
|
|
<< "bool ASTMatcher::matchToken(unsigned index, unsigned otherIndex) const" << endl
|
|
|
|
|
<< "{" << endl
|
|
|
|
|
<< " const Token &token = tokenAt(index);" << endl
|
|
|
|
|
<< " const Token &otherToken = tokenAt(otherIndex);" << endl
|
|
|
|
|
<< " if (token.f.kind != otherToken.f.kind)" << endl
|
|
|
|
|
<< " return false;" << endl
|
|
|
|
|
<< " else if (token.is(T_IDENTIFIER)) {" << endl
|
|
|
|
|
<< " if (token.identifier != otherToken.identifier)" << endl
|
|
|
|
|
<< " return false;" << endl
|
|
|
|
|
<< " }" << endl
|
|
|
|
|
<< " return true;" << endl
|
|
|
|
|
<< "}" << endl
|
2009-11-13 11:43:52 +01:00
|
|
|
<< endl;
|
2009-11-13 11:35:19 +01:00
|
|
|
|
|
|
|
|
accept(ast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using ASTVisitor::visit;
|
|
|
|
|
|
|
|
|
|
QMap<QByteArray, ClassSpecifierAST *> classMap;
|
|
|
|
|
|
|
|
|
|
QByteArray id_cast(NameAST *name)
|
|
|
|
|
{
|
|
|
|
|
if (! name)
|
|
|
|
|
return QByteArray();
|
|
|
|
|
|
|
|
|
|
Identifier *id = identifier(name->asSimpleName()->identifier_token);
|
|
|
|
|
|
|
|
|
|
return QByteArray::fromRawData(id->chars(), id->size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visitMembers(Class *klass)
|
|
|
|
|
{
|
|
|
|
|
const QByteArray className = klass->name()->identifier()->chars();
|
2009-11-09 16:28:01 +01:00
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < klass->memberCount(); ++i) {
|
|
|
|
|
Symbol *member = klass->memberAt(i);
|
|
|
|
|
if (! member->name())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Identifier *id = member->name()->identifier();
|
|
|
|
|
|
|
|
|
|
if (! id)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const QByteArray memberName = QByteArray::fromRawData(id->chars(), id->size());
|
|
|
|
|
if (member->type().isUnsigned() && memberName.endsWith("_token")) {
|
2009-11-13 11:35:19 +01:00
|
|
|
// nothing to do. The member is a token.
|
|
|
|
|
|
2009-11-13 11:54:22 +01:00
|
|
|
*out << " if (! matchToken(node->" << memberName << ", pattern->" << memberName << "))" << endl
|
2009-11-13 11:35:19 +01:00
|
|
|
<< " return false;" << endl;
|
2009-11-09 16:28:01 +01:00
|
|
|
|
|
|
|
|
} else if (PointerType *ptrTy = member->type()->asPointerType()) {
|
|
|
|
|
|
|
|
|
|
if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) {
|
|
|
|
|
QByteArray typeName = namedTy->name()->identifier()->chars();
|
|
|
|
|
|
2009-11-12 17:35:48 +01:00
|
|
|
if (typeName.endsWith("AST")) {
|
2009-11-13 11:35:19 +01:00
|
|
|
*out << " if (! AST::match(node->" << memberName << ", pattern->" << memberName << ", this))" << endl
|
|
|
|
|
<< " return false;" << endl;
|
2009-11-09 16:28:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
|
|
|
|
|
const QByteArray baseClassName = klass->baseClassAt(i)->identifier()->chars();
|
|
|
|
|
|
|
|
|
|
if (ClassSpecifierAST *baseClassSpec = classMap.value(baseClassName, 0)) {
|
|
|
|
|
visitMembers(baseClassSpec->symbol);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkMethod(Symbol *accept0Method) const
|
|
|
|
|
{
|
|
|
|
|
Declaration *decl = accept0Method->asDeclaration();
|
|
|
|
|
if (! decl)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Function *funTy = decl->type()->asFunctionType();
|
|
|
|
|
if (! funTy)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
else if (funTy->isPureVirtual())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
|
|
|
{
|
|
|
|
|
Class *klass = ast->symbol;
|
|
|
|
|
const QByteArray className = id_cast(ast->name);
|
|
|
|
|
|
2009-11-12 17:35:48 +01:00
|
|
|
Identifier *match0_id = control()->findOrInsertIdentifier("match0");
|
2009-11-13 11:35:19 +01:00
|
|
|
Symbol *match0Method = klass->members()->lookat(match0_id);
|
|
|
|
|
for (; match0Method; match0Method = match0Method->next()) {
|
|
|
|
|
if (match0Method->identifier() != match0_id)
|
2009-11-09 16:28:01 +01:00
|
|
|
continue;
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
if (checkMethod(match0Method))
|
2009-11-09 16:28:01 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
if (! match0Method)
|
2009-11-09 16:28:01 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
classMap.insert(className, ast);
|
|
|
|
|
|
|
|
|
|
*out
|
2009-11-13 11:35:19 +01:00
|
|
|
<< "bool ASTMatcher::match(" << className.constData() << " *node, " << className.constData() << " *pattern)" << endl
|
2009-11-12 17:35:48 +01:00
|
|
|
<< "{" << endl;
|
2009-11-09 16:28:01 +01:00
|
|
|
|
|
|
|
|
visitMembers(klass);
|
|
|
|
|
|
|
|
|
|
*out
|
2009-11-13 11:35:19 +01:00
|
|
|
<< " return true;" << endl
|
2009-11-09 16:28:01 +01:00
|
|
|
<< "}" << endl
|
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
|
|
|
|
|
|
2009-11-09 15:42:55 +01:00
|
|
|
QTextCursor createCursor(TranslationUnit *unit, AST *ast, QTextDocument *document)
|
|
|
|
|
{
|
|
|
|
|
unsigned startLine, startColumn, endLine, endColumn;
|
|
|
|
|
unit->getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
|
|
|
|
|
unit->getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn);
|
|
|
|
|
|
|
|
|
|
QTextCursor tc(document);
|
|
|
|
|
tc.setPosition(document->findBlockByNumber(startLine - 1).position());
|
|
|
|
|
tc.setPosition(document->findBlockByNumber(endLine - 1).position() + endColumn - 1,
|
|
|
|
|
QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
|
|
int charsToSkip = 0;
|
|
|
|
|
forever {
|
|
|
|
|
QChar ch = document->characterAt(tc.position() + charsToSkip);
|
|
|
|
|
|
|
|
|
|
if (! ch.isSpace())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
++charsToSkip;
|
|
|
|
|
|
|
|
|
|
if (ch == QChar::ParagraphSeparator)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tc.setPosition(tc.position() + charsToSkip, QTextCursor::KeepAnchor);
|
|
|
|
|
return tc;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-09 12:50:00 +01:00
|
|
|
class ASTNodes
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ASTNodes(): base(0) {}
|
|
|
|
|
|
|
|
|
|
ClassSpecifierAST *base; // points to "class AST"
|
|
|
|
|
QList<ClassSpecifierAST *> deriveds; // n where n extends AST
|
|
|
|
|
QList<QTextCursor> endOfPublicClassSpecifiers;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FindASTNodes: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FindASTNodes(Document::Ptr doc, QTextDocument *document)
|
|
|
|
|
: ASTVisitor(doc->control()), document(document)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASTNodes operator()(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
accept(ast);
|
|
|
|
|
return _nodes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual bool visit(ClassSpecifierAST *ast)
|
|
|
|
|
{
|
|
|
|
|
Class *klass = ast->symbol;
|
|
|
|
|
Q_ASSERT(klass != 0);
|
|
|
|
|
|
|
|
|
|
const QString className = oo(klass->name());
|
|
|
|
|
|
|
|
|
|
if (className.endsWith("AST")) {
|
|
|
|
|
if (className == QLatin1String("AST"))
|
|
|
|
|
_nodes.base = ast;
|
|
|
|
|
else {
|
|
|
|
|
_nodes.deriveds.append(ast);
|
|
|
|
|
|
|
|
|
|
AccessDeclarationAST *accessDeclaration = 0;
|
2009-11-12 17:35:48 +01:00
|
|
|
for (DeclarationListAST *it = ast->member_specifier_list; it; it = it->next) {
|
|
|
|
|
if (AccessDeclarationAST *decl = it->value->asAccessDeclaration()) {
|
2009-11-09 12:50:00 +01:00
|
|
|
if (tokenKind(decl->access_specifier_token) == T_PUBLIC)
|
|
|
|
|
accessDeclaration = decl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! accessDeclaration)
|
|
|
|
|
qDebug() << "no access declaration for class:" << className;
|
|
|
|
|
|
|
|
|
|
Q_ASSERT(accessDeclaration != 0);
|
|
|
|
|
|
2009-11-09 15:42:55 +01:00
|
|
|
QTextCursor tc = createCursor(translationUnit(), accessDeclaration, document);
|
|
|
|
|
tc.setPosition(tc.position());
|
2009-11-09 12:50:00 +01:00
|
|
|
|
|
|
|
|
_nodes.endOfPublicClassSpecifiers.append(tc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QTextDocument *document;
|
|
|
|
|
ASTNodes _nodes;
|
|
|
|
|
Overview oo;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class RemoveCastMethods: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
RemoveCastMethods(Document::Ptr doc, QTextDocument *document)
|
|
|
|
|
: ASTVisitor(doc->control()), document(document) {}
|
|
|
|
|
|
|
|
|
|
QList<QTextCursor> operator()(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
_cursors.clear();
|
|
|
|
|
accept(ast);
|
|
|
|
|
return _cursors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual bool visit(FunctionDefinitionAST *ast)
|
|
|
|
|
{
|
|
|
|
|
Function *fun = ast->symbol;
|
|
|
|
|
const QString functionName = oo(fun->name());
|
|
|
|
|
|
|
|
|
|
if (functionName.length() > 3 && functionName.startsWith(QLatin1String("as"))
|
|
|
|
|
&& functionName.at(2).isUpper()) {
|
|
|
|
|
|
2009-11-09 15:42:55 +01:00
|
|
|
QTextCursor tc = createCursor(translationUnit(), ast, document);
|
2009-11-09 12:50:00 +01:00
|
|
|
|
|
|
|
|
//qDebug() << qPrintable(tc.selectedText());
|
|
|
|
|
_cursors.append(tc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QTextDocument *document;
|
|
|
|
|
QList<QTextCursor> _cursors;
|
|
|
|
|
Overview oo;
|
|
|
|
|
};
|
|
|
|
|
|
2009-11-09 15:42:55 +01:00
|
|
|
QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir)
|
2009-11-09 12:50:00 +01:00
|
|
|
{
|
2009-11-09 15:42:55 +01:00
|
|
|
QStringList astDerivedClasses;
|
|
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
QFileInfo fileAST_h(cplusplusDir, QLatin1String("AST.h"));
|
|
|
|
|
Q_ASSERT(fileAST_h.exists());
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
const QString fileName = fileAST_h.absoluteFilePath();
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
QFile file(fileName);
|
|
|
|
|
if (! file.open(QFile::ReadOnly))
|
2009-11-09 15:42:55 +01:00
|
|
|
return astDerivedClasses;
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
const QString source = QTextStream(&file).readAll();
|
|
|
|
|
file.close();
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
QTextDocument document;
|
|
|
|
|
document.setPlainText(source);
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
Document::Ptr doc = Document::create(fileName);
|
|
|
|
|
const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
|
|
|
|
|
doc->setSource(preprocessedCode);
|
|
|
|
|
doc->check();
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
FindASTNodes process(doc, &document);
|
|
|
|
|
ASTNodes astNodes = process(doc->translationUnit()->ast());
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
RemoveCastMethods removeCastMethods(doc, &document);
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
QList<QTextCursor> baseCastMethodCursors = removeCastMethods(astNodes.base);
|
|
|
|
|
QMap<ClassSpecifierAST *, QList<QTextCursor> > cursors;
|
|
|
|
|
QMap<ClassSpecifierAST *, QString> replacementCastMethods;
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
Overview oo;
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
QStringList castMethods;
|
|
|
|
|
foreach (ClassSpecifierAST *classAST, astNodes.deriveds) {
|
|
|
|
|
cursors[classAST] = removeCastMethods(classAST);
|
|
|
|
|
const QString className = oo(classAST->symbol->name());
|
|
|
|
|
const QString methodName = QLatin1String("as") + className.mid(0, className.length() - 3);
|
|
|
|
|
replacementCastMethods[classAST] = QString(" virtual %1 *%2() { return this; }\n").arg(className, methodName);
|
|
|
|
|
castMethods.append(QString(" virtual %1 *%2() { return 0; }\n").arg(className, methodName));
|
2009-11-09 15:42:55 +01:00
|
|
|
|
|
|
|
|
astDerivedClasses.append(className);
|
2009-11-09 15:10:03 +01:00
|
|
|
}
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
if (! baseCastMethodCursors.isEmpty()) {
|
|
|
|
|
castMethods.sort();
|
|
|
|
|
for (int i = 0; i < baseCastMethodCursors.length(); ++i) {
|
|
|
|
|
baseCastMethodCursors[i].removeSelectedText();
|
2009-11-09 12:50:00 +01:00
|
|
|
}
|
|
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
baseCastMethodCursors.first().insertText(castMethods.join(QLatin1String("")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int classIndex = 0; classIndex < astNodes.deriveds.size(); ++classIndex) {
|
|
|
|
|
ClassSpecifierAST *classAST = astNodes.deriveds.at(classIndex);
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
// remove the cast methods.
|
|
|
|
|
QList<QTextCursor> c = cursors.value(classAST);
|
|
|
|
|
for (int i = 0; i < c.length(); ++i) {
|
|
|
|
|
c[i].removeSelectedText();
|
2009-11-09 12:50:00 +01:00
|
|
|
}
|
|
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
astNodes.endOfPublicClassSpecifiers[classIndex].insertText(replacementCastMethods.value(classAST));
|
|
|
|
|
}
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
if (file.open(QFile::WriteOnly)) {
|
|
|
|
|
QTextStream out(&file);
|
|
|
|
|
out << document.toPlainText();
|
|
|
|
|
}
|
2009-11-09 15:42:55 +01:00
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
Accept0CG cg(cplusplusDir, doc->control());
|
2009-11-09 16:28:01 +01:00
|
|
|
cg(doc->translationUnit()->ast());
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
Match0CG cg2(cplusplusDir, doc->control());
|
2009-11-12 17:35:48 +01:00
|
|
|
cg2(doc->translationUnit()->ast());
|
|
|
|
|
|
2009-11-13 11:35:19 +01:00
|
|
|
MatcherCPPCG cg3(cplusplusDir, doc->control());
|
|
|
|
|
cg3(doc->translationUnit()->ast());
|
|
|
|
|
|
2009-11-09 15:42:55 +01:00
|
|
|
return astDerivedClasses;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FindASTForwards: protected ASTVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FindASTForwards(Document::Ptr doc, QTextDocument *document)
|
|
|
|
|
: ASTVisitor(doc->control()), document(document)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
QList<QTextCursor> operator()(AST *ast)
|
|
|
|
|
{
|
|
|
|
|
accept(ast);
|
|
|
|
|
return _cursors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
bool visit(SimpleDeclarationAST *ast)
|
|
|
|
|
{
|
2009-11-12 17:35:48 +01:00
|
|
|
if (! ast->decl_specifier_list)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (ElaboratedTypeSpecifierAST *e = ast->decl_specifier_list->value->asElaboratedTypeSpecifier()) {
|
|
|
|
|
if (tokenKind(e->classkey_token) == T_CLASS && !ast->declarator_list) {
|
2009-11-09 15:42:55 +01:00
|
|
|
QString className = oo(e->name->name);
|
|
|
|
|
|
|
|
|
|
if (className.length() > 3 && className.endsWith(QLatin1String("AST"))) {
|
|
|
|
|
QTextCursor tc = createCursor(translationUnit(), ast, document);
|
|
|
|
|
_cursors.append(tc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QTextDocument *document;
|
|
|
|
|
QList<QTextCursor> _cursors;
|
|
|
|
|
Overview oo;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void generateASTFwd_h(const Snapshot &snapshot, const QDir &cplusplusDir, const QStringList &astDerivedClasses)
|
|
|
|
|
{
|
|
|
|
|
QFileInfo fileASTFwd_h(cplusplusDir, QLatin1String("ASTfwd.h"));
|
|
|
|
|
Q_ASSERT(fileASTFwd_h.exists());
|
|
|
|
|
|
|
|
|
|
const QString fileName = fileASTFwd_h.absoluteFilePath();
|
|
|
|
|
|
|
|
|
|
QFile file(fileName);
|
|
|
|
|
if (! file.open(QFile::ReadOnly))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QString source = QTextStream(&file).readAll();
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
QTextDocument document;
|
|
|
|
|
document.setPlainText(source);
|
|
|
|
|
|
|
|
|
|
Document::Ptr doc = Document::create(fileName);
|
|
|
|
|
const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName);
|
|
|
|
|
doc->setSource(preprocessedCode);
|
|
|
|
|
doc->check();
|
|
|
|
|
|
|
|
|
|
FindASTForwards process(doc, &document);
|
|
|
|
|
QList<QTextCursor> cursors = process(doc->translationUnit()->ast());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < cursors.length(); ++i)
|
|
|
|
|
cursors[i].removeSelectedText();
|
|
|
|
|
|
|
|
|
|
QString replacement;
|
|
|
|
|
foreach (const QString &astDerivedClass, astDerivedClasses) {
|
|
|
|
|
replacement += QString(QLatin1String("class %1;\n")).arg(astDerivedClass);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursors.first().insertText(replacement);
|
|
|
|
|
|
|
|
|
|
if (file.open(QFile::WriteOnly)) {
|
|
|
|
|
QTextStream out(&file);
|
|
|
|
|
out << document.toPlainText();
|
|
|
|
|
}
|
2009-11-09 15:10:03 +01:00
|
|
|
}
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
QCoreApplication app(argc, argv);
|
|
|
|
|
QStringList files = app.arguments();
|
|
|
|
|
files.removeFirst();
|
2009-11-09 12:50:00 +01:00
|
|
|
|
2009-11-09 15:10:03 +01:00
|
|
|
if (files.isEmpty()) {
|
|
|
|
|
std::cerr << "Usage: cplusplus [path to C++ front-end]" << std::endl;
|
|
|
|
|
return EXIT_FAILURE;
|
2009-11-09 12:50:00 +01:00
|
|
|
}
|
2009-11-09 15:10:03 +01:00
|
|
|
|
|
|
|
|
QDir cplusplusDir(files.first());
|
|
|
|
|
Snapshot snapshot;
|
|
|
|
|
|
2009-11-09 15:42:55 +01:00
|
|
|
QStringList astDerivedClasses = generateAST_H(snapshot, cplusplusDir);
|
|
|
|
|
astDerivedClasses.sort();
|
|
|
|
|
generateASTFwd_h(snapshot, cplusplusDir, astDerivedClasses);
|
2009-11-09 12:50:00 +01:00
|
|
|
}
|