aboutsummaryrefslogtreecommitdiffstats
path: root/src/tools/cplusplus-ast2png
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@theqtcompany.com>2015-01-29 10:08:39 +0100
committerNikolai Kosjar <nikolai.kosjar@theqtcompany.com>2015-01-29 13:07:05 +0000
commit739cc9f053e08b8b873680385ac4c8638d51946a (patch)
tree74283f053248979f9a253f5991c930ac3dd1f2ad /src/tools/cplusplus-ast2png
parentc001e98da8c11a5aa3dfe3ee85701e788411aec4 (diff)
C++: Clean up dev tools
* Move cplusplus-frontend to src/tools * Make them depend on libs/cplusplus instead of building that lib again * Put the binaries into bin of the qtcreator build directory * Unify *.pro files * Build them all if BUILD_CPLUSPLUS_TOOLS is set Change-Id: I8f9cd731625cbf9f41d5f6464c6cd946ffd6e141 Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Diffstat (limited to 'src/tools/cplusplus-ast2png')
-rw-r--r--src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp653
-rw-r--r--src/tools/cplusplus-ast2png/cplusplus-ast2png.pro4
-rw-r--r--src/tools/cplusplus-ast2png/dumpers.inc1743
-rw-r--r--src/tools/cplusplus-ast2png/tests/templ01.cpp46
4 files changed, 2446 insertions, 0 deletions
diff --git a/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp b/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp
new file mode 100644
index 0000000000..fa44245c8f
--- /dev/null
+++ b/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp
@@ -0,0 +1,653 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include <cplusplus/AST.h>
+#include <cplusplus/ASTMatcher.h>
+#include <cplusplus/ASTPatternBuilder.h>
+#include <cplusplus/ASTVisitor.h>
+#include <cplusplus/Control.h>
+#include <cplusplus/CoreTypes.h>
+#include <cplusplus/CppDocument.h>
+#include <cplusplus/Literals.h>
+#include <cplusplus/Names.h>
+#include <cplusplus/Overview.h>
+#include <cplusplus/Scope.h>
+#include <cplusplus/SymbolVisitor.h>
+#include <cplusplus/Symbols.h>
+#include <cplusplus/TranslationUnit.h>
+
+#include "utils.h"
+
+#include <QDir>
+#include <QFile>
+#include <QList>
+#include <QCoreApplication>
+#include <QStringList>
+#include <QFileInfo>
+#include <QTime>
+#include <QDebug>
+
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <string>
+#ifdef __GNUC__
+# include <cxxabi.h>
+#endif
+
+// For isatty(), _isatty()
+#if defined(Q_OS_WIN)
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+bool tty_for_stdin()
+{
+#if defined(Q_OS_WIN)
+ return _isatty(_fileno(stdin));
+#else
+ return isatty(fileno(stdin));
+#endif
+}
+
+using namespace CPlusPlus;
+
+class ASTDump: protected ASTVisitor
+{
+public:
+ ASTDump(TranslationUnit *unit)
+ : ASTVisitor(unit) {}
+
+ void operator()(AST *ast) {
+ QByteArray basename = translationUnit()->fileName();
+ basename.append(".ast.dot");
+ out.open(basename.constData());
+
+ out << "digraph AST { ordering=out;" << std::endl;
+ // std::cout << "rankdir = \"LR\";" << std::endl;
+
+ generateTokens();
+ accept(ast);
+
+ typedef QPair<QByteArray, QByteArray> Pair;
+
+ foreach (const Pair &conn, _connections)
+ out << conn.first.constData() << " -> " << conn.second.constData() << std::endl;
+
+ alignTerminals();
+
+ out << "}" << std::endl;
+ out.close();
+ }
+
+ // the following file can be generated by using:
+ // cplusplus-update-frontend <frontend-dir> <dumpers-file>
+#include "dumpers.inc"
+
+protected:
+ void alignTerminals() {
+ out<<"{ rank=same;" << std::endl;
+ foreach (const QByteArray &terminalShape, _terminalShapes) {
+ out << " " << std::string(terminalShape.constData(), terminalShape.size()).c_str() << ";" << std::endl;
+ }
+ out<<"}"<<std::endl;
+ }
+
+ static QByteArray name(AST *ast) {
+#ifdef __GNUC__
+ QByteArray name = abi::__cxa_demangle(typeid(*ast).name(), 0, 0, 0) + 11;
+ name.truncate(name.length() - 3);
+#else
+ QByteArray name = typeid(*ast).name();
+#endif
+ return name;
+ }
+
+ QByteArray terminalId(unsigned token)
+ { return 't' + QByteArray::number(token); }
+
+ void terminal(unsigned token, AST *node) {
+ _connections.append(qMakePair(_id[node], terminalId(token)));
+ }
+
+ void generateTokens() {
+ for (unsigned token = 1; token < translationUnit()->tokenCount(); ++token) {
+ if (translationUnit()->tokenKind(token) == T_EOF_SYMBOL)
+ break;
+
+ QByteArray t;
+
+ t.append(terminalId(token));
+ t.append(" [shape=rect label = \"");
+ t.append(spell(token));
+ t.append("\"]");
+
+ if (token > 1) {
+ t.append("; ");
+ t.append(terminalId(token - 1));
+ t.append(" -> ");
+ t.append(terminalId(token));
+ t.append(" [arrowhead=\"vee\" color=\"transparent\"]");
+ }
+
+ _terminalShapes.append(t);
+ }
+ }
+
+ virtual void nonterminal(AST *ast) {
+ accept(ast);
+ }
+
+ virtual void node(AST *ast) {
+ out << _id[ast].constData() << " [label=\"" << name(ast).constData() << "\"];" << std::endl;
+ }
+
+ virtual bool preVisit(AST *ast) {
+ static int count = 1;
+ const QByteArray id = 'n' + QByteArray::number(count++);
+ _id[ast] = id;
+
+
+ if (! _stack.isEmpty())
+ _connections.append(qMakePair(_id[_stack.last()], id));
+
+ _stack.append(ast);
+
+ node(ast);
+
+ return true;
+ }
+
+ virtual void postVisit(AST *) {
+ _stack.removeLast();
+ }
+
+private:
+ QHash<AST *, QByteArray> _id;
+ QList<QPair<QByteArray, QByteArray> > _connections;
+ QList<AST *> _stack;
+ QList<QByteArray> _terminalShapes;
+ std::ofstream out;
+};
+
+class SymbolDump: protected SymbolVisitor
+{
+public:
+ SymbolDump(TranslationUnit *unit)
+ : translationUnit(unit)
+ {
+ o.showArgumentNames = true;
+ o.showFunctionSignatures = true;
+ o.showReturnTypes = true;
+ }
+
+ void operator()(Symbol *s) {
+ QByteArray basename = translationUnit->fileName();
+ basename.append(".symbols.dot");
+ out.open(basename.constData());
+
+ out << "digraph Symbols { ordering=out;" << std::endl;
+ // std::cout << "rankdir = \"LR\";" << std::endl;
+ accept(s);
+
+ for (int i = 0; i < _connections.size(); ++i) {
+ QPair<Symbol*,Symbol*> connection = _connections.at(i);
+ QByteArray from = _id.value(connection.first);
+ if (from.isEmpty())
+ from = name(connection.first);
+ QByteArray to = _id.value(connection.second);
+ if (to.isEmpty())
+ to = name(connection.second);
+ out << from.constData() << " -> " << to.constData() << ";" << std::endl;
+ }
+
+ out << "}" << std::endl;
+ out.close();
+ }
+
+protected:
+ QByteArray name(Symbol *s) {
+#ifdef __GNUC__
+ QByteArray result = abi::__cxa_demangle(typeid(*s).name(), 0, 0, 0) + 11;
+#else
+ QByteArray result = typeid(*s).name();
+#endif
+ if (s->identifier()) {
+ result.append("\\nid: ");
+ result.append(s->identifier()->chars());
+ }
+ if (s->isDeprecated())
+ result.append("\\n(deprecated)");
+
+ return result;
+ }
+
+ virtual bool preVisit(Symbol *s) {
+ static int count = 0;
+ QByteArray nodeId("s");
+ nodeId.append(QByteArray::number(++count));
+ _id[s] = nodeId;
+
+ if (!_stack.isEmpty())
+ _connections.append(qMakePair(_stack.last(), s));
+
+ _stack.append(s);
+
+ return true;
+ }
+
+ virtual void postVisit(Symbol *) {
+ _stack.removeLast();
+ }
+
+ virtual void simpleNode(Symbol *symbol) {
+ out << _id[symbol].constData() << " [label=\"" << name(symbol).constData() << "\"];" << std::endl;
+ }
+
+ virtual bool visit(Class *symbol) {
+ const char *id = _id.value(symbol).constData();
+ out << id << " [label=\"";
+ if (symbol->isClass()) {
+ out << "class";
+ } else if (symbol->isStruct()) {
+ out << "struct";
+ } else if (symbol->isUnion()) {
+ out << "union";
+ } else {
+ out << "UNKNOWN";
+ }
+
+ out << "\\nid: ";
+ if (symbol->identifier()) {
+ out << symbol->identifier()->chars();
+ } else {
+ out << "NO ID";
+ }
+ if (symbol->isDeprecated())
+ out << "\\n(deprecated)";
+ out << "\"];" << std::endl;
+
+ return true;
+ }
+
+ virtual bool visit(UsingNamespaceDirective *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(UsingDeclaration *symbol) { simpleNode(symbol); return true; }
+
+ virtual bool visit(Declaration *symbol) {
+ out << _id[symbol].constData() << " [label=\"";
+ out << "Declaration\\n";
+ out << qPrintable(o(symbol->name()));
+ out << ": ";
+ out << qPrintable(o(symbol->type()));
+ if (symbol->isDeprecated())
+ out << "\\n(deprecated)";
+ if (Function *funTy = symbol->type()->asFunctionType()) {
+ if (funTy->isPureVirtual())
+ out << "\\n(pure virtual)";
+ else if (funTy->isVirtual())
+ out << "\\n(virtual)";
+
+ if (funTy->isSignal())
+ out << "\\n(signal)";
+ if (funTy->isSlot())
+ out << "\\n(slot)";
+ if (funTy->isInvokable())
+ out << "\\n(invokable)";
+ }
+ out << "\"];" << std::endl;
+
+ return true;
+ }
+
+ virtual bool visit(Argument *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(TypenameArgument *symbol) { simpleNode(symbol); return true; }
+
+ virtual bool visit(BaseClass *symbol) {
+ out << _id[symbol].constData() << " [label=\"BaseClass\\n";
+ out << qPrintable(o(symbol->name()));
+ if (symbol->isDeprecated())
+ out << "\\n(deprecated)";
+ out << "\"];" << std::endl;
+
+ return true;
+ }
+
+ virtual bool visit(Enum *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(Function *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(Namespace *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(Block *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ForwardClassDeclaration *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCBaseClass *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCBaseProtocol *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCClass *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCForwardClassDeclaration *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCProtocol *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCForwardProtocolDeclaration *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCMethod *symbol) { simpleNode(symbol); return true; }
+ virtual bool visit(ObjCPropertyDeclaration *symbol) { simpleNode(symbol); return true; }
+
+private:
+ TranslationUnit *translationUnit;
+ QHash<Symbol *, QByteArray> _id;
+ QList<QPair<Symbol *,Symbol*> >_connections;
+ QList<Symbol *> _stack;
+ std::ofstream out;
+ Overview o;
+};
+
+static void createImageFromDot(const QString &inputFile, const QString &outputFile, bool verbose)
+{
+ const QString command = CplusplusToolsUtils::portableExecutableName(QLatin1String("dot"));
+ const QStringList arguments = QStringList()
+ << QLatin1String("-Tpng") << QLatin1String("-o") << outputFile << inputFile;
+ CplusplusToolsUtils::executeCommand(command, arguments, QString(), verbose);
+}
+
+static const char PATH_STDIN_FILE[] = "_stdincontents.cpp";
+
+static QString example()
+{
+ return
+#if defined(Q_OS_WIN)
+ QString::fromLatin1("> echo int foo() {} | %1 && %2.ast.png")
+#elif defined(Q_OS_MAC)
+ QString::fromLatin1("$ echo \"int foo() {}\" | ./%1 && open %2.ast.png")
+#else
+ QString::fromLatin1("$ echo \"int foo() {}\" | ./%1 && xdg-open %2.ast.png")
+#endif
+ .arg(QFileInfo(qApp->arguments().at(0)).fileName(), QLatin1String(PATH_STDIN_FILE));
+}
+
+static QString parseModeToString(Document::ParseMode parseMode)
+{
+ switch (parseMode) {
+ case Document::ParseTranlationUnit:
+ return QLatin1String("TranlationUnit");
+ case Document::ParseDeclaration:
+ return QLatin1String("Declaration");
+ case Document::ParseExpression:
+ return QLatin1String("Expression");
+ case Document::ParseDeclarator:
+ return QLatin1String("Declarator");
+ case Document::ParseStatement:
+ return QLatin1String("Statement");
+ default:
+ return QLatin1String("UnknownParseMode");
+ }
+}
+
+/// Counts errors and appends error messages containing the parse mode to an error string
+class ErrorHandler: public DiagnosticClient {
+public:
+ int m_errorCount;
+ QByteArray *m_errorString;
+ Document::ParseMode m_parseMode;
+
+ ErrorHandler(Document::ParseMode parseMode, QByteArray *errorStringOutput)
+ : m_errorCount(0)
+ , m_errorString(errorStringOutput)
+ , m_parseMode(parseMode) {}
+
+ void report(int level,
+ const StringLiteral *fileName,
+ unsigned line, unsigned column,
+ const char *format, va_list ap)
+ {
+ ++m_errorCount;
+
+ if (! m_errorString)
+ return;
+
+ static const char *const pretty[] = { "warning", "error", "fatal" };
+
+ QString str;
+ str.sprintf("%s:%d:%d: When parsing as %s: %s: ", fileName->chars(), line, column,
+ parseModeToString(m_parseMode).toUtf8().constData(), pretty[level]);
+ m_errorString->append(str.toUtf8());
+
+ str.vsprintf(format, ap);
+ m_errorString->append(str.toUtf8());
+ m_errorString->append('\n');
+ }
+};
+
+/// Try to parse with given parseModes. Returns a document pointer if it was possible to
+/// successfully parse with one of the given parseModes (one parse mode after the other
+/// is tried), otherwise a null pointer.
+static Document::Ptr parse(const QString &fileName, const QByteArray &source,
+ QList<Document::ParseMode> parseModes, QByteArray *errors,
+ bool verbose = false)
+{
+ foreach (const Document::ParseMode parseMode, parseModes) {
+ ErrorHandler *errorHandler = new ErrorHandler(parseMode, errors); // Deleted by ~Document.
+ if (verbose)
+ std::cout << "Parsing as " << qPrintable(parseModeToString(parseMode)) << "...";
+
+ Document::Ptr doc = Document::create(fileName);
+ doc->control()->setDiagnosticClient(errorHandler);
+ doc->setUtf8Source(source);
+ const bool parsed = doc->parse(parseMode);
+ if (parsed && errorHandler->m_errorCount == 0) {
+ if (verbose)
+ std::cout << "succeeded." << std::endl;
+ return doc;
+ }
+
+ if (verbose)
+ std::cout << "failed." << std::endl;
+ }
+
+ return Document::Ptr();
+}
+
+/// Convenience function
+static Document::Ptr parse(const QString &fileName, const QByteArray &source,
+ Document::ParseMode parseMode, QByteArray *errors,
+ bool verbose = false)
+{
+ QList<Document::ParseMode> parseModes = QList<Document::ParseMode>() << parseMode;
+ return parse(fileName, source, parseModes, errors, verbose);
+}
+
+static void printUsage()
+{
+ std::cout << "Usage: " << qPrintable(QFileInfo(qApp->arguments().at(0)).fileName())
+ << " [-v] [-p ast] <file1> <file2> ...\n\n";
+
+ std::cout
+ << "Visualize AST and symbol hierarchy of given C++ files by generating png image files\n"
+ << "in the same directory as the input files. Print paths to generated image files.\n"
+ << "\n"
+ << "Options:\n"
+ << " -v Run with increased verbosity.\n"
+ << " -p <ast> Parse each file as <ast>. <ast> is one of:\n"
+ << " - 'declarator' or 'dr'\n"
+ << " - 'expression' or 'ex'\n"
+ << " - 'declaration' or 'dn'\n"
+ << " - 'statement' or 'st'\n"
+ << " - 'translationunit' or 'tr'\n"
+ << " If this option is not provided, each file is tried to be parsed as\n"
+ << " declarator, expression, etc. using the stated order.\n"
+ << "\n";
+
+ std::cout << QString::fromLatin1(
+ "Standard input is also read. The resulting files start with \"%1\"\n"
+ "and are created in the current working directory. To show the AST for simple snippets\n"
+ "you might want to execute:\n"
+ "\n"
+ " %2\n"
+ "\n"
+ "Prerequisites:\n"
+ " 1) Make sure to have 'dot' from graphviz locatable by PATH.\n"
+ " 2) Make sure to have an up to date dumpers file by using 'cplusplus-update-frontend'.\n"
+ ).arg(QLatin1String(PATH_STDIN_FILE), example()).toLocal8Bit().constData();
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ QStringList args = app.arguments();
+ args.removeFirst();
+
+ bool optionVerbose = false;
+ int optionParseMode = -1;
+
+ // Test only for stdin if not input files are specified.
+ const bool doTestForStdIn = args.isEmpty()
+ || (args.count() == 1 && args.contains(QLatin1String("-v")));
+ if (doTestForStdIn && !tty_for_stdin()) {
+ QFile file((QLatin1String(PATH_STDIN_FILE)));
+ if (! file.open(QFile::WriteOnly)) {
+ std::cerr << "Error: Cannot open file for writing\"" << qPrintable(file.fileName())
+ << "\"" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ file.write(QTextStream(stdin).readAll().toLocal8Bit());
+ file.close();
+ args.append(file.fileName());
+ }
+
+ // Process options & arguments
+ const bool helpRequested = args.contains(QLatin1String("-h"))
+ || args.contains(QLatin1String("-help"));
+ if (helpRequested) {
+ printUsage();
+ return helpRequested ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
+
+ if (args.contains(QLatin1String("-v"))) {
+ optionVerbose = true;
+ args.removeOne(QLatin1String("-v"));
+ }
+ if (args.contains(QLatin1String("-p"))) {
+ args.removeOne(QLatin1String("-p"));
+ if (args.isEmpty()) {
+ std::cerr << "Error: Expected ast after option \"-p\"." << std::endl;
+ printUsage();
+ exit(EXIT_FAILURE);
+ }
+ const QString parseAs = args.first();
+ if (parseAs == QLatin1String("declarator") || parseAs == QLatin1String("dr")) {
+ optionParseMode = Document::ParseDeclarator;
+ } else if (parseAs == QLatin1String("expression") || parseAs == QLatin1String("ex")) {
+ optionParseMode = Document::ParseExpression;
+ } else if (parseAs == QLatin1String("declaration") || parseAs == QLatin1String("dn")) {
+ optionParseMode = Document::ParseDeclaration;
+ } else if (parseAs == QLatin1String("statement") || parseAs == QLatin1String("st")) {
+ optionParseMode = Document::ParseStatement;
+ } else if (parseAs == QLatin1String("translationunit") || parseAs == QLatin1String("tr")) {
+ optionParseMode = Document::ParseTranlationUnit;
+ } else {
+ std::cerr << "Error: Invalid ast for option \"-p\"." << std::endl;
+ printUsage();
+ exit(EXIT_FAILURE);
+ }
+ args.removeOne(parseAs);
+ }
+
+ if (args.isEmpty()) {
+ printUsage();
+ return EXIT_SUCCESS;
+ }
+
+ // Process files
+ const QStringList files = args;
+ foreach (const QString &fileName, files) {
+ if (! QFile::exists(fileName)) {
+ std::cerr << "Error: File \"" << qPrintable(fileName) << "\" does not exist."
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ // Run the preprocessor
+ const QString fileNamePreprocessed = fileName + QLatin1String(".preprocessed");
+ CplusplusToolsUtils::SystemPreprocessor preprocessor(optionVerbose);
+ preprocessor.preprocessFile(fileName, fileNamePreprocessed);
+
+ // Convert to dot
+ QFile file(fileNamePreprocessed);
+ if (! file.open(QFile::ReadOnly)) {
+ std::cerr << "Error: Could not open file \"" << qPrintable(fileNamePreprocessed)
+ << "\"" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ const QByteArray source = file.readAll();
+ file.close();
+
+ // Parse Document
+ QByteArray errors;
+ Document::Ptr doc;
+ if (optionParseMode == -1) {
+ QList<Document::ParseMode> parseModes;
+ parseModes
+ << Document::ParseDeclarator
+ << Document::ParseExpression
+ << Document::ParseDeclaration
+ << Document::ParseStatement
+ << Document::ParseTranlationUnit;
+ doc = parse(fileName, source, parseModes, &errors, optionVerbose);
+ } else {
+ doc = parse(fileName, source, static_cast<Document::ParseMode>(optionParseMode),
+ &errors, optionVerbose);
+ }
+
+ if (!doc) {
+ std::cerr << "Error: Could not parse file \"" << qPrintable(fileName) << "\".\n";
+ std::cerr << errors.constData();
+ exit(EXIT_FAILURE);
+ }
+
+ doc->check();
+
+ // Run AST dumper
+ ASTDump dump(doc->translationUnit());
+ dump(doc->translationUnit()->ast());
+
+ SymbolDump dump2(doc->translationUnit());
+ dump2(doc->globalNamespace());
+
+ // Create images
+ typedef QPair<QString, QString> Pair;
+ QList<Pair> inputOutputFiles;
+ inputOutputFiles.append(qMakePair(QString(fileName + QLatin1String(".ast.dot")),
+ QString(fileName + QLatin1String(".ast.png"))));
+ inputOutputFiles.append(qMakePair(QString(fileName + QLatin1String(".symbols.dot")),
+ QString(fileName + QLatin1String(".symbols.png"))));
+ foreach (const Pair &pair, inputOutputFiles) {
+ createImageFromDot(pair.first, pair.second, optionVerbose);
+ std::cout << qPrintable(QDir::toNativeSeparators(pair.second)) << std::endl;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/tools/cplusplus-ast2png/cplusplus-ast2png.pro b/src/tools/cplusplus-ast2png/cplusplus-ast2png.pro
new file mode 100644
index 0000000000..2178100684
--- /dev/null
+++ b/src/tools/cplusplus-ast2png/cplusplus-ast2png.pro
@@ -0,0 +1,4 @@
+include(../cplusplus-shared/tool.pri)
+include(../cplusplus-shared/utils.pri)
+
+SOURCES += cplusplus-ast2png.cpp
diff --git a/src/tools/cplusplus-ast2png/dumpers.inc b/src/tools/cplusplus-ast2png/dumpers.inc
new file mode 100644
index 0000000000..5e5bb1a10b
--- /dev/null
+++ b/src/tools/cplusplus-ast2png/dumpers.inc
@@ -0,0 +1,1743 @@
+// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is automatically generated by "cplusplus-update-frontend".
+// Changes will be lost.
+//
+
+
+virtual bool visit(ObjCSelectorArgumentAST *ast)
+{
+ if (ast->name_token)
+ terminal(ast->name_token, ast);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCSelectorAST *ast)
+{
+ for (ObjCSelectorArgumentListAST *iter = ast->selector_argument_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(SimpleSpecifierAST *ast)
+{
+ if (ast->specifier_token)
+ terminal(ast->specifier_token, ast);
+ return false;
+}
+
+virtual bool visit(AlignmentSpecifierAST *ast)
+{
+ if (ast->align_token)
+ terminal(ast->align_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->typeIdExprOrAlignmentExpr);
+ if (ast->ellipses_token)
+ terminal(ast->ellipses_token, ast);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(GnuAttributeSpecifierAST *ast)
+{
+ if (ast->attribute_token)
+ terminal(ast->attribute_token, ast);
+ if (ast->first_lparen_token)
+ terminal(ast->first_lparen_token, ast);
+ if (ast->second_lparen_token)
+ terminal(ast->second_lparen_token, ast);
+ for (GnuAttributeListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->first_rparen_token)
+ terminal(ast->first_rparen_token, ast);
+ if (ast->second_rparen_token)
+ terminal(ast->second_rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(GnuAttributeAST *ast)
+{
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ if (ast->tag_token)
+ terminal(ast->tag_token, ast);
+ for (ExpressionListAST *iter = ast->expression_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(TypeofSpecifierAST *ast)
+{
+ if (ast->typeof_token)
+ terminal(ast->typeof_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(DecltypeSpecifierAST *ast)
+{
+ if (ast->decltype_token)
+ terminal(ast->decltype_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(DeclaratorAST *ast)
+{
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (PtrOperatorListAST *iter = ast->ptr_operator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->core_declarator);
+ for (PostfixDeclaratorListAST *iter = ast->postfix_declarator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (SpecifierListAST *iter = ast->post_attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->initializer);
+ return false;
+}
+
+virtual bool visit(SimpleDeclarationAST *ast)
+{
+ if (ast->qt_invokable_token)
+ terminal(ast->qt_invokable_token, ast);
+ for (SpecifierListAST *iter = ast->decl_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (DeclaratorListAST *iter = ast->declarator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(EmptyDeclarationAST *ast)
+{
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(AccessDeclarationAST *ast)
+{
+ if (ast->access_specifier_token)
+ terminal(ast->access_specifier_token, ast);
+ if (ast->slots_token)
+ terminal(ast->slots_token, ast);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ return false;
+}
+
+virtual bool visit(QtObjectTagAST *ast)
+{
+ if (ast->q_object_token)
+ terminal(ast->q_object_token, ast);
+ return false;
+}
+
+virtual bool visit(QtPrivateSlotAST *ast)
+{
+ if (ast->q_private_slot_token)
+ terminal(ast->q_private_slot_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ if (ast->dptr_token)
+ terminal(ast->dptr_token, ast);
+ if (ast->dptr_lparen_token)
+ terminal(ast->dptr_lparen_token, ast);
+ if (ast->dptr_rparen_token)
+ terminal(ast->dptr_rparen_token, ast);
+ if (ast->comma_token)
+ terminal(ast->comma_token, ast);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(QtPropertyDeclarationItemAST *ast)
+{
+ if (ast->item_name_token)
+ terminal(ast->item_name_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(QtPropertyDeclarationAST *ast)
+{
+ if (ast->property_specifier_token)
+ terminal(ast->property_specifier_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->comma_token)
+ terminal(ast->comma_token, ast);
+ nonterminal(ast->type_id);
+ nonterminal(ast->property_name);
+ for (QtPropertyDeclarationItemListAST *iter = ast->property_declaration_item_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(QtEnumDeclarationAST *ast)
+{
+ if (ast->enum_specifier_token)
+ terminal(ast->enum_specifier_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (NameListAST *iter = ast->enumerator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(QtFlagsDeclarationAST *ast)
+{
+ if (ast->flags_specifier_token)
+ terminal(ast->flags_specifier_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (NameListAST *iter = ast->flag_enums_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(QtInterfaceNameAST *ast)
+{
+ nonterminal(ast->interface_name);
+ for (NameListAST *iter = ast->constraint_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(QtInterfacesDeclarationAST *ast)
+{
+ if (ast->interfaces_token)
+ terminal(ast->interfaces_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (QtInterfaceNameListAST *iter = ast->interface_name_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(AsmDefinitionAST *ast)
+{
+ if (ast->asm_token)
+ terminal(ast->asm_token, ast);
+ if (ast->volatile_token)
+ terminal(ast->volatile_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(BaseSpecifierAST *ast)
+{
+ if (ast->virtual_token)
+ terminal(ast->virtual_token, ast);
+ if (ast->access_specifier_token)
+ terminal(ast->access_specifier_token, ast);
+ nonterminal(ast->name);
+ return false;
+}
+
+virtual bool visit(IdExpressionAST *ast)
+{
+ nonterminal(ast->name);
+ return false;
+}
+
+virtual bool visit(CompoundExpressionAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->statement);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(CompoundLiteralAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->type_id);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->initializer);
+ return false;
+}
+
+virtual bool visit(QtMethodAST *ast)
+{
+ if (ast->method_token)
+ terminal(ast->method_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->declarator);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(QtMemberDeclarationAST *ast)
+{
+ if (ast->q_token)
+ terminal(ast->q_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->type_id);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(BinaryExpressionAST *ast)
+{
+ nonterminal(ast->left_expression);
+ if (ast->binary_op_token)
+ terminal(ast->binary_op_token, ast);
+ nonterminal(ast->right_expression);
+ return false;
+}
+
+virtual bool visit(CastExpressionAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->type_id);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(ClassSpecifierAST *ast)
+{
+ if (ast->classkey_token)
+ terminal(ast->classkey_token, ast);
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->name);
+ if (ast->final_token)
+ terminal(ast->final_token, ast);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ for (BaseSpecifierListAST *iter = ast->base_clause_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (DeclarationListAST *iter = ast->member_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(CaseStatementAST *ast)
+{
+ if (ast->case_token)
+ terminal(ast->case_token, ast);
+ nonterminal(ast->expression);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(CompoundStatementAST *ast)
+{
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (StatementListAST *iter = ast->statement_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(ConditionAST *ast)
+{
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ return false;
+}
+
+virtual bool visit(ConditionalExpressionAST *ast)
+{
+ nonterminal(ast->condition);
+ if (ast->question_token)
+ terminal(ast->question_token, ast);
+ nonterminal(ast->left_expression);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ nonterminal(ast->right_expression);
+ return false;
+}
+
+virtual bool visit(CppCastExpressionAST *ast)
+{
+ if (ast->cast_token)
+ terminal(ast->cast_token, ast);
+ if (ast->less_token)
+ terminal(ast->less_token, ast);
+ nonterminal(ast->type_id);
+ if (ast->greater_token)
+ terminal(ast->greater_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(CtorInitializerAST *ast)
+{
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ for (MemInitializerListAST *iter = ast->member_initializer_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ return false;
+}
+
+virtual bool visit(DeclarationStatementAST *ast)
+{
+ nonterminal(ast->declaration);
+ return false;
+}
+
+virtual bool visit(DeclaratorIdAST *ast)
+{
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ nonterminal(ast->name);
+ return false;
+}
+
+virtual bool visit(NestedDeclaratorAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->declarator);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(FunctionDeclaratorAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->parameter_declaration_clause);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ for (SpecifierListAST *iter = ast->cv_qualifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->ref_qualifier_token)
+ terminal(ast->ref_qualifier_token, ast);
+ nonterminal(ast->exception_specification);
+ nonterminal(ast->trailing_return_type);
+ nonterminal(ast->as_cpp_initializer);
+ return false;
+}
+
+virtual bool visit(ArrayDeclaratorAST *ast)
+{
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ return false;
+}
+
+virtual bool visit(DeleteExpressionAST *ast)
+{
+ if (ast->scope_token)
+ terminal(ast->scope_token, ast);
+ if (ast->delete_token)
+ terminal(ast->delete_token, ast);
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(DoStatementAST *ast)
+{
+ if (ast->do_token)
+ terminal(ast->do_token, ast);
+ nonterminal(ast->statement);
+ if (ast->while_token)
+ terminal(ast->while_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(NamedTypeSpecifierAST *ast)
+{
+ nonterminal(ast->name);
+ return false;
+}
+
+virtual bool visit(ElaboratedTypeSpecifierAST *ast)
+{
+ if (ast->classkey_token)
+ terminal(ast->classkey_token, ast);
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->name);
+ return false;
+}
+
+virtual bool visit(EnumSpecifierAST *ast)
+{
+ if (ast->enum_token)
+ terminal(ast->enum_token, ast);
+ if (ast->key_token)
+ terminal(ast->key_token, ast);
+ nonterminal(ast->name);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (EnumeratorListAST *iter = ast->enumerator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->stray_comma_token)
+ terminal(ast->stray_comma_token, ast);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(EnumeratorAST *ast)
+{
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(ExceptionDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ return false;
+}
+
+virtual bool visit(DynamicExceptionSpecificationAST *ast)
+{
+ if (ast->throw_token)
+ terminal(ast->throw_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ for (ExpressionListAST *iter = ast->type_id_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(NoExceptSpecificationAST *ast)
+{
+ if (ast->noexcept_token)
+ terminal(ast->noexcept_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(ExpressionOrDeclarationStatementAST *ast)
+{
+ nonterminal(ast->expression);
+ nonterminal(ast->declaration);
+ return false;
+}
+
+virtual bool visit(ExpressionStatementAST *ast)
+{
+ nonterminal(ast->expression);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(FunctionDefinitionAST *ast)
+{
+ if (ast->qt_invokable_token)
+ terminal(ast->qt_invokable_token, ast);
+ for (SpecifierListAST *iter = ast->decl_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ nonterminal(ast->ctor_initializer);
+ nonterminal(ast->function_body);
+ return false;
+}
+
+virtual bool visit(ForeachStatementAST *ast)
+{
+ if (ast->foreach_token)
+ terminal(ast->foreach_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ nonterminal(ast->initializer);
+ if (ast->comma_token)
+ terminal(ast->comma_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(RangeBasedForStatementAST *ast)
+{
+ if (ast->for_token)
+ terminal(ast->for_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(ForStatementAST *ast)
+{
+ if (ast->for_token)
+ terminal(ast->for_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->initializer);
+ nonterminal(ast->condition);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(IfStatementAST *ast)
+{
+ if (ast->if_token)
+ terminal(ast->if_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->condition);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ if (ast->else_token)
+ terminal(ast->else_token, ast);
+ nonterminal(ast->else_statement);
+ return false;
+}
+
+virtual bool visit(ArrayInitializerAST *ast)
+{
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (ExpressionListAST *iter = ast->expression_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(LabeledStatementAST *ast)
+{
+ if (ast->label_token)
+ terminal(ast->label_token, ast);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(LinkageBodyAST *ast)
+{
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (DeclarationListAST *iter = ast->declaration_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(LinkageSpecificationAST *ast)
+{
+ if (ast->extern_token)
+ terminal(ast->extern_token, ast);
+ if (ast->extern_type_token)
+ terminal(ast->extern_type_token, ast);
+ nonterminal(ast->declaration);
+ return false;
+}
+
+virtual bool visit(MemInitializerAST *ast)
+{
+ nonterminal(ast->name);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(NestedNameSpecifierAST *ast)
+{
+ nonterminal(ast->class_or_namespace_name);
+ if (ast->scope_token)
+ terminal(ast->scope_token, ast);
+ return false;
+}
+
+virtual bool visit(QualifiedNameAST *ast)
+{
+ if (ast->global_scope_token)
+ terminal(ast->global_scope_token, ast);
+ for (NestedNameSpecifierListAST *iter = ast->nested_name_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->unqualified_name);
+ return false;
+}
+
+virtual bool visit(OperatorFunctionIdAST *ast)
+{
+ if (ast->operator_token)
+ terminal(ast->operator_token, ast);
+ nonterminal(ast->op);
+ return false;
+}
+
+virtual bool visit(ConversionFunctionIdAST *ast)
+{
+ if (ast->operator_token)
+ terminal(ast->operator_token, ast);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (PtrOperatorListAST *iter = ast->ptr_operator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(AnonymousNameAST *ast)
+{
+ if (ast->class_token)
+ terminal(ast->class_token, ast);
+ return false;
+}
+
+virtual bool visit(SimpleNameAST *ast)
+{
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ return false;
+}
+
+virtual bool visit(DestructorNameAST *ast)
+{
+ if (ast->tilde_token)
+ terminal(ast->tilde_token, ast);
+ nonterminal(ast->unqualified_name);
+ return false;
+}
+
+virtual bool visit(TemplateIdAST *ast)
+{
+ if (ast->template_token)
+ terminal(ast->template_token, ast);
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ if (ast->less_token)
+ terminal(ast->less_token, ast);
+ for (ExpressionListAST *iter = ast->template_argument_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->greater_token)
+ terminal(ast->greater_token, ast);
+ return false;
+}
+
+virtual bool visit(NamespaceAST *ast)
+{
+ if (ast->inline_token)
+ terminal(ast->inline_token, ast);
+ if (ast->namespace_token)
+ terminal(ast->namespace_token, ast);
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->linkage_body);
+ return false;
+}
+
+virtual bool visit(NamespaceAliasDefinitionAST *ast)
+{
+ if (ast->namespace_token)
+ terminal(ast->namespace_token, ast);
+ if (ast->namespace_name_token)
+ terminal(ast->namespace_name_token, ast);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->name);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(AliasDeclarationAST *ast)
+{
+ if (ast->using_token)
+ terminal(ast->using_token, ast);
+ nonterminal(ast->name);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->typeId);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ExpressionListParenAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (ExpressionListAST *iter = ast->expression_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(NewArrayDeclaratorAST *ast)
+{
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ return false;
+}
+
+virtual bool visit(NewExpressionAST *ast)
+{
+ if (ast->scope_token)
+ terminal(ast->scope_token, ast);
+ if (ast->new_token)
+ terminal(ast->new_token, ast);
+ nonterminal(ast->new_placement);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->type_id);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->new_type_id);
+ nonterminal(ast->new_initializer);
+ return false;
+}
+
+virtual bool visit(NewTypeIdAST *ast)
+{
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (PtrOperatorListAST *iter = ast->ptr_operator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (NewArrayDeclaratorListAST *iter = ast->new_array_declarator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(OperatorAST *ast)
+{
+ if (ast->op_token)
+ terminal(ast->op_token, ast);
+ if (ast->open_token)
+ terminal(ast->open_token, ast);
+ if (ast->close_token)
+ terminal(ast->close_token, ast);
+ return false;
+}
+
+virtual bool visit(ParameterDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(ParameterDeclarationClauseAST *ast)
+{
+ for (ParameterDeclarationListAST *iter = ast->parameter_declaration_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ return false;
+}
+
+virtual bool visit(CallAST *ast)
+{
+ nonterminal(ast->base_expression);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (ExpressionListAST *iter = ast->expression_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(ArrayAccessAST *ast)
+{
+ nonterminal(ast->base_expression);
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ return false;
+}
+
+virtual bool visit(PostIncrDecrAST *ast)
+{
+ nonterminal(ast->base_expression);
+ if (ast->incr_decr_token)
+ terminal(ast->incr_decr_token, ast);
+ return false;
+}
+
+virtual bool visit(MemberAccessAST *ast)
+{
+ nonterminal(ast->base_expression);
+ if (ast->access_token)
+ terminal(ast->access_token, ast);
+ if (ast->template_token)
+ terminal(ast->template_token, ast);
+ nonterminal(ast->member_name);
+ return false;
+}
+
+virtual bool visit(TypeidExpressionAST *ast)
+{
+ if (ast->typeid_token)
+ terminal(ast->typeid_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(TypenameCallExpressionAST *ast)
+{
+ if (ast->typename_token)
+ terminal(ast->typename_token, ast);
+ nonterminal(ast->name);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(TypeConstructorCallAST *ast)
+{
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(PointerToMemberAST *ast)
+{
+ if (ast->global_scope_token)
+ terminal(ast->global_scope_token, ast);
+ for (NestedNameSpecifierListAST *iter = ast->nested_name_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->star_token)
+ terminal(ast->star_token, ast);
+ for (SpecifierListAST *iter = ast->cv_qualifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->ref_qualifier_token)
+ terminal(ast->ref_qualifier_token, ast);
+ return false;
+}
+
+virtual bool visit(PointerAST *ast)
+{
+ if (ast->star_token)
+ terminal(ast->star_token, ast);
+ for (SpecifierListAST *iter = ast->cv_qualifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(ReferenceAST *ast)
+{
+ if (ast->reference_token)
+ terminal(ast->reference_token, ast);
+ return false;
+}
+
+virtual bool visit(BreakStatementAST *ast)
+{
+ if (ast->break_token)
+ terminal(ast->break_token, ast);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ContinueStatementAST *ast)
+{
+ if (ast->continue_token)
+ terminal(ast->continue_token, ast);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(GotoStatementAST *ast)
+{
+ if (ast->goto_token)
+ terminal(ast->goto_token, ast);
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ReturnStatementAST *ast)
+{
+ if (ast->return_token)
+ terminal(ast->return_token, ast);
+ nonterminal(ast->expression);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(SizeofExpressionAST *ast)
+{
+ if (ast->sizeof_token)
+ terminal(ast->sizeof_token, ast);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(AlignofExpressionAST *ast)
+{
+ if (ast->alignof_token)
+ terminal(ast->alignof_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->typeId);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(PointerLiteralAST *ast)
+{
+ if (ast->literal_token)
+ terminal(ast->literal_token, ast);
+ return false;
+}
+
+virtual bool visit(NumericLiteralAST *ast)
+{
+ if (ast->literal_token)
+ terminal(ast->literal_token, ast);
+ return false;
+}
+
+virtual bool visit(BoolLiteralAST *ast)
+{
+ if (ast->literal_token)
+ terminal(ast->literal_token, ast);
+ return false;
+}
+
+virtual bool visit(ThisExpressionAST *ast)
+{
+ if (ast->this_token)
+ terminal(ast->this_token, ast);
+ return false;
+}
+
+virtual bool visit(NestedExpressionAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(StaticAssertDeclarationAST *ast)
+{
+ if (ast->static_assert_token)
+ terminal(ast->static_assert_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->expression);
+ if (ast->comma_token)
+ terminal(ast->comma_token, ast);
+ nonterminal(ast->string_literal);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(StringLiteralAST *ast)
+{
+ if (ast->literal_token)
+ terminal(ast->literal_token, ast);
+ nonterminal(ast->next);
+ return false;
+}
+
+virtual bool visit(SwitchStatementAST *ast)
+{
+ if (ast->switch_token)
+ terminal(ast->switch_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->condition);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(TemplateDeclarationAST *ast)
+{
+ if (ast->export_token)
+ terminal(ast->export_token, ast);
+ if (ast->template_token)
+ terminal(ast->template_token, ast);
+ if (ast->less_token)
+ terminal(ast->less_token, ast);
+ for (DeclarationListAST *iter = ast->template_parameter_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->greater_token)
+ terminal(ast->greater_token, ast);
+ nonterminal(ast->declaration);
+ return false;
+}
+
+virtual bool visit(ThrowExpressionAST *ast)
+{
+ if (ast->throw_token)
+ terminal(ast->throw_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(TranslationUnitAST *ast)
+{
+ for (DeclarationListAST *iter = ast->declaration_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(TryBlockStatementAST *ast)
+{
+ if (ast->try_token)
+ terminal(ast->try_token, ast);
+ nonterminal(ast->statement);
+ for (CatchClauseListAST *iter = ast->catch_clause_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(CatchClauseAST *ast)
+{
+ if (ast->catch_token)
+ terminal(ast->catch_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->exception_declaration);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(TypeIdAST *ast)
+{
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ return false;
+}
+
+virtual bool visit(TypenameTypeParameterAST *ast)
+{
+ if (ast->classkey_token)
+ terminal(ast->classkey_token, ast);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ nonterminal(ast->name);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->type_id);
+ return false;
+}
+
+virtual bool visit(TemplateTypeParameterAST *ast)
+{
+ if (ast->template_token)
+ terminal(ast->template_token, ast);
+ if (ast->less_token)
+ terminal(ast->less_token, ast);
+ for (DeclarationListAST *iter = ast->template_parameter_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->greater_token)
+ terminal(ast->greater_token, ast);
+ if (ast->class_token)
+ terminal(ast->class_token, ast);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ nonterminal(ast->name);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->type_id);
+ return false;
+}
+
+virtual bool visit(UnaryExpressionAST *ast)
+{
+ if (ast->unary_op_token)
+ terminal(ast->unary_op_token, ast);
+ nonterminal(ast->expression);
+ return false;
+}
+
+virtual bool visit(UsingAST *ast)
+{
+ if (ast->using_token)
+ terminal(ast->using_token, ast);
+ if (ast->typename_token)
+ terminal(ast->typename_token, ast);
+ nonterminal(ast->name);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(UsingDirectiveAST *ast)
+{
+ if (ast->using_token)
+ terminal(ast->using_token, ast);
+ if (ast->namespace_token)
+ terminal(ast->namespace_token, ast);
+ nonterminal(ast->name);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(WhileStatementAST *ast)
+{
+ if (ast->while_token)
+ terminal(ast->while_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->condition);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(ObjCClassForwardDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->class_token)
+ terminal(ast->class_token, ast);
+ for (NameListAST *iter = ast->identifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCClassDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->interface_token)
+ terminal(ast->interface_token, ast);
+ if (ast->implementation_token)
+ terminal(ast->implementation_token, ast);
+ nonterminal(ast->class_name);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->category_name);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ if (ast->colon_token)
+ terminal(ast->colon_token, ast);
+ nonterminal(ast->superclass);
+ nonterminal(ast->protocol_refs);
+ nonterminal(ast->inst_vars_decl);
+ for (DeclarationListAST *iter = ast->member_declaration_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->end_token)
+ terminal(ast->end_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCProtocolForwardDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->protocol_token)
+ terminal(ast->protocol_token, ast);
+ for (NameListAST *iter = ast->identifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCProtocolDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->protocol_token)
+ terminal(ast->protocol_token, ast);
+ nonterminal(ast->name);
+ nonterminal(ast->protocol_refs);
+ for (DeclarationListAST *iter = ast->member_declaration_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->end_token)
+ terminal(ast->end_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCProtocolRefsAST *ast)
+{
+ if (ast->less_token)
+ terminal(ast->less_token, ast);
+ for (NameListAST *iter = ast->identifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->greater_token)
+ terminal(ast->greater_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCMessageArgumentAST *ast)
+{
+ nonterminal(ast->parameter_value_expression);
+ return false;
+}
+
+virtual bool visit(ObjCMessageExpressionAST *ast)
+{
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ nonterminal(ast->receiver_expression);
+ nonterminal(ast->selector);
+ for (ObjCMessageArgumentListAST *iter = ast->argument_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCProtocolExpressionAST *ast)
+{
+ if (ast->protocol_token)
+ terminal(ast->protocol_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCTypeNameAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ if (ast->type_qualifier_token)
+ terminal(ast->type_qualifier_token, ast);
+ nonterminal(ast->type_id);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCEncodeExpressionAST *ast)
+{
+ if (ast->encode_token)
+ terminal(ast->encode_token, ast);
+ nonterminal(ast->type_name);
+ return false;
+}
+
+virtual bool visit(ObjCSelectorExpressionAST *ast)
+{
+ if (ast->selector_token)
+ terminal(ast->selector_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->selector);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCInstanceVariablesDeclarationAST *ast)
+{
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (DeclarationListAST *iter = ast->instance_variable_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCVisibilityDeclarationAST *ast)
+{
+ if (ast->visibility_token)
+ terminal(ast->visibility_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCPropertyAttributeAST *ast)
+{
+ if (ast->attribute_identifier_token)
+ terminal(ast->attribute_identifier_token, ast);
+ if (ast->equals_token)
+ terminal(ast->equals_token, ast);
+ nonterminal(ast->method_selector);
+ return false;
+}
+
+virtual bool visit(ObjCPropertyDeclarationAST *ast)
+{
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->property_token)
+ terminal(ast->property_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (ObjCPropertyAttributeListAST *iter = ast->property_attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->simple_declaration);
+ return false;
+}
+
+virtual bool visit(ObjCMessageArgumentDeclarationAST *ast)
+{
+ nonterminal(ast->type_name);
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->param_name);
+ return false;
+}
+
+virtual bool visit(ObjCMethodPrototypeAST *ast)
+{
+ if (ast->method_type_token)
+ terminal(ast->method_type_token, ast);
+ nonterminal(ast->type_name);
+ nonterminal(ast->selector);
+ for (ObjCMessageArgumentDeclarationListAST *iter = ast->argument_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->dot_dot_dot_token)
+ terminal(ast->dot_dot_dot_token, ast);
+ for (SpecifierListAST *iter = ast->attribute_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(ObjCMethodDeclarationAST *ast)
+{
+ nonterminal(ast->method_prototype);
+ nonterminal(ast->function_body);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCSynthesizedPropertyAST *ast)
+{
+ if (ast->property_identifier_token)
+ terminal(ast->property_identifier_token, ast);
+ if (ast->equals_token)
+ terminal(ast->equals_token, ast);
+ if (ast->alias_identifier_token)
+ terminal(ast->alias_identifier_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCSynthesizedPropertiesDeclarationAST *ast)
+{
+ if (ast->synthesized_token)
+ terminal(ast->synthesized_token, ast);
+ for (ObjCSynthesizedPropertyListAST *iter = ast->property_identifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCDynamicPropertiesDeclarationAST *ast)
+{
+ if (ast->dynamic_token)
+ terminal(ast->dynamic_token, ast);
+ for (NameListAST *iter = ast->property_identifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->semicolon_token)
+ terminal(ast->semicolon_token, ast);
+ return false;
+}
+
+virtual bool visit(ObjCFastEnumerationAST *ast)
+{
+ if (ast->for_token)
+ terminal(ast->for_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ nonterminal(ast->initializer);
+ if (ast->in_token)
+ terminal(ast->in_token, ast);
+ nonterminal(ast->fast_enumeratable_expression);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(ObjCSynchronizedStatementAST *ast)
+{
+ if (ast->synchronized_token)
+ terminal(ast->synchronized_token, ast);
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->synchronized_object);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(LambdaExpressionAST *ast)
+{
+ nonterminal(ast->lambda_introducer);
+ nonterminal(ast->lambda_declarator);
+ nonterminal(ast->statement);
+ return false;
+}
+
+virtual bool visit(LambdaIntroducerAST *ast)
+{
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ nonterminal(ast->lambda_capture);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ return false;
+}
+
+virtual bool visit(LambdaCaptureAST *ast)
+{
+ if (ast->default_capture_token)
+ terminal(ast->default_capture_token, ast);
+ for (CaptureListAST *iter = ast->capture_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ return false;
+}
+
+virtual bool visit(CaptureAST *ast)
+{
+ if (ast->amper_token)
+ terminal(ast->amper_token, ast);
+ nonterminal(ast->identifier);
+ return false;
+}
+
+virtual bool visit(LambdaDeclaratorAST *ast)
+{
+ if (ast->lparen_token)
+ terminal(ast->lparen_token, ast);
+ nonterminal(ast->parameter_declaration_clause);
+ if (ast->rparen_token)
+ terminal(ast->rparen_token, ast);
+ for (SpecifierListAST *iter = ast->attributes; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->mutable_token)
+ terminal(ast->mutable_token, ast);
+ nonterminal(ast->exception_specification);
+ nonterminal(ast->trailing_return_type);
+ return false;
+}
+
+virtual bool visit(TrailingReturnTypeAST *ast)
+{
+ if (ast->arrow_token)
+ terminal(ast->arrow_token, ast);
+ for (SpecifierListAST *iter = ast->attributes; iter; iter = iter->next)
+ nonterminal(iter->value);
+ for (SpecifierListAST *iter = ast->type_specifier_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ nonterminal(ast->declarator);
+ return false;
+}
+
+virtual bool visit(BracedInitializerAST *ast)
+{
+ if (ast->lbrace_token)
+ terminal(ast->lbrace_token, ast);
+ for (ExpressionListAST *iter = ast->expression_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->comma_token)
+ terminal(ast->comma_token, ast);
+ if (ast->rbrace_token)
+ terminal(ast->rbrace_token, ast);
+ return false;
+}
+
+virtual bool visit(DotDesignatorAST *ast)
+{
+ if (ast->dot_token)
+ terminal(ast->dot_token, ast);
+ if (ast->identifier_token)
+ terminal(ast->identifier_token, ast);
+ return false;
+}
+
+virtual bool visit(BracketDesignatorAST *ast)
+{
+ if (ast->lbracket_token)
+ terminal(ast->lbracket_token, ast);
+ nonterminal(ast->expression);
+ if (ast->rbracket_token)
+ terminal(ast->rbracket_token, ast);
+ return false;
+}
+
+virtual bool visit(DesignatedInitializerAST *ast)
+{
+ for (DesignatorListAST *iter = ast->designator_list; iter; iter = iter->next)
+ nonterminal(iter->value);
+ if (ast->equal_token)
+ terminal(ast->equal_token, ast);
+ nonterminal(ast->initializer);
+ return false;
+}
+
diff --git a/src/tools/cplusplus-ast2png/tests/templ01.cpp b/src/tools/cplusplus-ast2png/tests/templ01.cpp
new file mode 100644
index 0000000000..646794618f
--- /dev/null
+++ b/src/tools/cplusplus-ast2png/tests/templ01.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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://www.qt.io/licensing. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** 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
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+struct QString
+{
+ void append(char ch);
+};
+
+template <typename _Tp> struct QList {
+ const _Tp &at(int index);
+};
+
+struct QStringList: public QList<QString> {};
+
+int main()
+{
+ QStringList l;
+ l.at(0).append('a');
+}