Files
qt-creator/src/tools/cplusplus/Main.cpp

252 lines
7.2 KiB
C++
Raw Normal View History

#include <QCoreApplication>
#include <QStringList>
#include <QTextDocument>
#include <QTextCursor>
#include <QTextBlock>
2009-11-09 15:10:03 +01:00
#include <QDir>
#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>
#include <iostream>
#include <cstdlib>
using namespace CPlusPlus;
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;
for (DeclarationListAST *it = ast->member_specifiers; it; it = it->next) {
if (AccessDeclarationAST *decl = it->declaration->asAccessDeclaration()) {
if (tokenKind(decl->access_specifier_token) == T_PUBLIC)
accessDeclaration = decl;
}
}
if (! accessDeclaration)
qDebug() << "no access declaration for class:" << className;
Q_ASSERT(accessDeclaration != 0);
unsigned endLine, endColumn;
getTokenEndPosition(accessDeclaration->lastToken() - 1, &endLine, &endColumn);
QTextCursor tc(document);
tc.setPosition(document->findBlockByNumber(endLine - 1).position() + endColumn - 1);
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);
_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()) {
unsigned startLine, startColumn, endLine, endColumn;
getTokenStartPosition(ast->firstToken(), &startLine, &startColumn);
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);
//qDebug() << qPrintable(tc.selectedText());
_cursors.append(tc);
}
return true;
}
private:
QTextDocument *document;
QList<QTextCursor> _cursors;
Overview oo;
};
2009-11-09 15:10:03 +01:00
void generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir)
{
2009-11-09 15:10:03 +01:00
QFileInfo fileAST_h(cplusplusDir, QLatin1String("AST.h"));
Q_ASSERT(fileAST_h.exists());
2009-11-09 15:10:03 +01:00
const QString fileName = fileAST_h.absoluteFilePath();
2009-11-09 15:10:03 +01:00
QFile file(fileName);
if (! file.open(QFile::ReadOnly))
return;
2009-11-09 15:10:03 +01:00
const QString source = QTextStream(&file).readAll();
file.close();
2009-11-09 15:10:03 +01:00
QTextDocument document;
document.setPlainText(source);
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 15:10:03 +01:00
FindASTNodes process(doc, &document);
ASTNodes astNodes = process(doc->translationUnit()->ast());
2009-11-09 15:10:03 +01:00
RemoveCastMethods removeCastMethods(doc, &document);
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 15:10:03 +01:00
Overview oo;
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:10:03 +01:00
if (! baseCastMethodCursors.isEmpty()) {
castMethods.sort();
for (int i = 0; i < baseCastMethodCursors.length(); ++i) {
baseCastMethodCursors[i].removeSelectedText();
}
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 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 15:10:03 +01:00
astNodes.endOfPublicClassSpecifiers[classIndex].insertText(replacementCastMethods.value(classAST));
}
2009-11-09 15:10:03 +01:00
if (file.open(QFile::WriteOnly)) {
QTextStream out(&file);
out << document.toPlainText();
}
}
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 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 15:10:03 +01:00
QDir cplusplusDir(files.first());
Snapshot snapshot;
generateAST_H(snapshot, cplusplusDir);
}