diff --git a/src/tools/cplusplus/cplusplus.pro b/src/tools/cplusplus/cplusplus.pro index c7de25740997fd2024df8638b5713df7accd09a6..24a8b74c4e8f9589c601de435294219247144005 100644 --- a/src/tools/cplusplus/cplusplus.pro +++ b/src/tools/cplusplus/cplusplus.pro @@ -7,9 +7,9 @@ macx:CONFIG -= app_bundle TEMPLATE = app TARGET = generate-ast DEPENDPATH += . -INCLUDEPATH += . +INCLUDEPATH += . ../../libs include(../../libs/cplusplus/cplusplus-lib.pri) # Input -SOURCES += generate-ast.cpp +SOURCES += generate-ast.cpp ../../libs/utils/changeset.cpp diff --git a/src/tools/cplusplus/generate-ast.cpp b/src/tools/cplusplus/generate-ast.cpp index 3bab1aa41b791b00aa52a6072eeee2508a231ccb..2a66b8d7f0c54e9c59083c9f94a7c4a31bcfdddf 100644 --- a/src/tools/cplusplus/generate-ast.cpp +++ b/src/tools/cplusplus/generate-ast.cpp @@ -46,6 +46,10 @@ #include <Overview.h> #include <Names.h> #include <Scope.h> +#include <BackwardsScanner.h> +#include <TokenCache.h> + +#include <utils/changeset.h> #include <iostream> #include <cstdlib> @@ -792,7 +796,7 @@ static QList<QTextCursor> removeConstructors(ClassSpecifierAST *classAST, return cursors; } -static QStringList collectFields(ClassSpecifierAST *classAST) +static QStringList collectFieldNames(ClassSpecifierAST *classAST, bool onlyTokensAndASTNodes) { QStringList fields; Overview oo; @@ -800,11 +804,20 @@ static QStringList collectFields(ClassSpecifierAST *classAST) for (unsigned i = 0; i < clazz->memberCount(); ++i) { Symbol *s = clazz->memberAt(i); if (Declaration *decl = s->asDeclaration()) { - FullySpecifiedType ty = decl->type(); - if (ty->isPointerType()) - fields.append(oo(decl->name())); - else if (ty.isUnsigned()) - fields.append(oo(decl->name())); + const QString declName = oo(decl->name()); + const FullySpecifiedType ty = decl->type(); + if (const PointerType *ptrTy = ty->asPointerType()) { + if (onlyTokensAndASTNodes) { + if (const NamedType *namedTy = ptrTy->elementType()->asNamedType()) { + if (oo(namedTy->name()).endsWith(QLatin1String("AST"))) + fields.append(declName); + } + } else { + fields.append(declName); + } + } else if (ty.isUnsigned()) { + fields.append(declName); + } } } return fields; @@ -819,7 +832,7 @@ static QString createConstructor(ClassSpecifierAST *classAST) result.append(oo(clazz->name())); result.append(QLatin1String("()\n")); - QStringList classFields = collectFields(classAST); + QStringList classFields = collectFieldNames(classAST, false); for (int i = 0; i < classFields.size(); ++i) { if (i == 0) { result.append(QLatin1String(" : ")); @@ -836,6 +849,253 @@ static QString createConstructor(ClassSpecifierAST *classAST) return result; } +bool checkGenerated(TokenCache *tokenCache, const QTextCursor &cursor, int *doxyStart) +{ + BackwardsScanner tokens(tokenCache, cursor); + SimpleToken prevToken = tokens.LA(1); + if (prevToken.kind() != T_DOXY_COMMENT && prevToken.kind() != T_CPP_DOXY_COMMENT) + return false; + + *doxyStart = tokens.startPosition() + prevToken.position(); + + return tokens.text(tokens.startToken() - 1).contains(QLatin1String("\\generated")); +} + +struct GenInfo { + GenInfo() + : classAST(0) + , start(0) + , end(0) + , firstToken(false) + , lastToken(false) + , remove(false) + {} + + ClassSpecifierAST *classAST; + int start; + int end; + bool firstToken; + bool lastToken; + bool remove; +}; + +void generateFirstToken(QTextStream &os, const QString &className, const QStringList &fields) +{ + os << "unsigned "<< className << "::firstToken() const" << endl + << "{" << endl; + + foreach (const QString &field, fields) { + os << " if (" << field << ")" << endl; + + if (field.endsWith(QLatin1String("_token"))) { + os << " return " << field << ";" << endl; + } else { + os << " if (unsigned candidate = " << field << "->firstToken())" << endl; + os << " return candidate;" << endl; + } + } + + os << " return 0;" << endl; + os << "}" << endl << endl; +} + +void generateLastToken(QTextStream &os, const QString &className, const QStringList &fields) +{ + os << "unsigned "<< className << "::lastToken() const" << endl + << "{" << endl; + + for (int i = fields.size() - 1; i >= 0; --i) { + const QString field = fields.at(i); + + os << " if (" << field << ")" << endl; + + if (field.endsWith(QLatin1String("_token"))) { + os << " return " << field << " + 1;" << endl; + } else { + os << " if (unsigned candidate = " << field << "->lastToken())" << endl; + os << " return candidate;" << endl; + } + } + + os << " return 0;" << endl; + os << "}" << endl << endl; +} + +void generateAST_cpp(const Snapshot &snapshot, const QDir &cplusplusDir) +{ + QFileInfo fileAST_cpp(cplusplusDir, QLatin1String("AST.cpp")); + Q_ASSERT(fileAST_cpp.exists()); + + const QString fileName = fileAST_cpp.absoluteFilePath(); + + QFile file(fileName); + if (! file.open(QFile::ReadOnly)) { + std::cerr << "Cannot open " << fileName.toLatin1().data() << std::endl; + return; + } + + const QString source = QTextStream(&file).readAll(); + file.close(); + + QTextDocument cpp_document; + cpp_document.setPlainText(source); + + Document::Ptr AST_cpp_document = Document::create(fileName); + const QByteArray preprocessedCode = snapshot.preprocessedCode(source, fileName); + AST_cpp_document->setSource(preprocessedCode); + AST_cpp_document->check(); + + Overview oo; + QMap<QString, ClassSpecifierAST *> classesNeedingFirstToken; + QMap<QString, ClassSpecifierAST *> classesNeedingLastToken; + + // find all classes with method declarations for firstToken/lastToken + foreach (ClassSpecifierAST *classAST, astNodes.deriveds) { + const QString className = oo(classAST->symbol->name()); + if (className.isEmpty()) + continue; + + for (DeclarationListAST *declIter = classAST->member_specifier_list; declIter; declIter = declIter->next) { + if (SimpleDeclarationAST *decl = declIter->value->asSimpleDeclaration()) { + if (decl->symbols && decl->symbols->value) { + if (decl->symbols->next) + std::cerr << "Found simple declaration with multiple symbols in " << className.toLatin1().data() << std::endl; + + Symbol *s = decl->symbols->value; + const QString funName = oo(s->name()); + if (funName == QLatin1String("firstToken")) { + // found it: + classesNeedingFirstToken.insert(className, classAST); + } else if (funName == QLatin1String("lastToken")) { + // found it: + classesNeedingLastToken.insert(className, classAST); + } + } + } + } + } + + QList<GenInfo> todo; + + TokenCache tokenCache; + tokenCache.setDocument(&cpp_document); + TranslationUnitAST *xUnit = AST_cpp_document->translationUnit()->ast()->asTranslationUnit(); + for (DeclarationListAST *iter = xUnit->declaration_list; iter; iter = iter->next) { + if (FunctionDefinitionAST *funDef = iter->value->asFunctionDefinition()) { + if (const QualifiedNameId *qName = funDef->symbol->name()->asQualifiedNameId()) { + if (qName->nameCount() != 2) + continue; + const QString className = oo(qName->nameAt(0)); + const QString methodName = oo(qName->nameAt(1)); + + QTextCursor cursor(&cpp_document); + + unsigned line = 0, column = 0; + AST_cpp_document->translationUnit()->getTokenStartPosition(funDef->firstToken(), &line, &column); + const int start = cpp_document.findBlockByNumber(line - 1).position() + column - 1; + cursor.setPosition(start); + int doxyStart = start; + const bool isGenerated = checkGenerated(&tokenCache, cursor, &doxyStart); + + AST_cpp_document->translationUnit()->getTokenEndPosition(funDef->lastToken() - 1, &line, &column); + int end = cpp_document.findBlockByNumber(line - 1).position() + column - 1; + while (cpp_document.characterAt(end).isSpace()) + ++end; + + if (methodName == QLatin1String("firstToken")) { + ClassSpecifierAST *classAST = classesNeedingFirstToken.value(className, 0); + GenInfo info; + info.end = end; + if (classAST) { + info.classAST = classAST; + info.firstToken = true; + info.start = start; + classesNeedingFirstToken.remove(className); + } else { + info.start = doxyStart; + info.remove = true; + } + if (isGenerated) + todo.append(info); + } else if (methodName == QLatin1String("lastToken")) { + ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0); + GenInfo info; + info.end = end; + if (classAST) { + info.classAST = classAST; + info.start = start; + info.lastToken = true; + classesNeedingLastToken.remove(className); + } else { + info.start = doxyStart; + info.remove = true; + } + if (isGenerated) + todo.append(info); + } + } + } + } + + const int documentEnd = cpp_document.lastBlock().position() + cpp_document.lastBlock().length() - 1; + + Utils::ChangeSet changes; + foreach (GenInfo info, todo) { + if (info.end > documentEnd) + info.end = documentEnd; + + if (info.remove) { + changes.remove(info.start, info.end - info.start); + return; + } + + Overview oo; + + const QString className = oo(info.classAST->symbol->name()); + + QString method; + QTextStream os(&method); + const QStringList fields = collectFieldNames(info.classAST, true); + + if (info.firstToken) { + generateFirstToken(os, className, fields); + } else if (info.lastToken) { + generateLastToken(os, className, fields); + } + + changes.replace(info.start, info.end - info.start, method); + } + + QTextCursor tc(&cpp_document); + changes.apply(&tc); + + QString newMethods; + QTextStream os(&newMethods); + foreach (const QString &className, classesNeedingFirstToken.keys()) { + const QStringList fields = collectFieldNames(classesNeedingFirstToken.value(className), true); + os << "/** \\generated */" << endl; + generateFirstToken(os, className, fields); + if (ClassSpecifierAST *classAST = classesNeedingLastToken.value(className, 0)) { + const QStringList fields = collectFieldNames(classAST, true); + os << "/** \\generated */" << endl; + generateLastToken(os, className, fields); + classesNeedingLastToken.remove(className); + } + } + foreach (const QString &className, classesNeedingLastToken.keys()) { + const QStringList fields = collectFieldNames(classesNeedingLastToken.value(className), true); + os << "/** \\generated */" << endl; + generateLastToken(os, className, fields); + } + tc.setPosition(documentEnd); + tc.insertText(newMethods); + + if (file.open(QFile::WriteOnly)) { + QTextStream out(&file); + out << cpp_document.toPlainText(); + } +} + QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir) { QStringList astDerivedClasses; @@ -934,6 +1194,8 @@ QStringList generateAST_H(const Snapshot &snapshot, const QDir &cplusplusDir) CloneCPPCG cg4(cplusplusDir, AST_h_document->translationUnit()); cg4(AST_h_document->translationUnit()->ast()); + generateAST_cpp(snapshot, cplusplusDir); + return astDerivedClasses; }