diff options
author | Nikolai Kosjar <nikolai.kosjar@digia.com> | 2013-01-31 16:23:39 +0100 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@digia.com> | 2013-02-01 14:30:12 +0100 |
commit | 4da344c0bf5481ebc36fabd53895c8461716f475 (patch) | |
tree | 96124c8edbc251de40b5e356d3b4f5c8d5c70c12 /tests/tools | |
parent | 1a0bed277db3238d46715c2ec8a3be1635355e7c (diff) | |
download | qt-creator-4da344c0bf5481ebc36fabd53895c8461716f475.tar.gz |
C++: ast2png: Try parsing harder
Try to parse a declarator, if that fails an expression, if that fails,
...at last a TranslationUnit is tried. It is also possible to specify
which AST should be parsed.
This simplifies the code snippets we can pass to this tool.
Change-Id: Idbc1a8a6f1c5cf7e20d899f7a2e4263c7f9d33a6
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Diffstat (limited to 'tests/tools')
-rw-r--r-- | tests/tools/cplusplus-ast2png/cplusplus-ast2png.cpp | 197 |
1 files changed, 177 insertions, 20 deletions
diff --git a/tests/tools/cplusplus-ast2png/cplusplus-ast2png.cpp b/tests/tools/cplusplus-ast2png/cplusplus-ast2png.cpp index 308090ed09..900b0169b6 100644 --- a/tests/tools/cplusplus-ast2png/cplusplus-ast2png.cpp +++ b/tests/tools/cplusplus-ast2png/cplusplus-ast2png.cpp @@ -363,8 +363,7 @@ private: Overview o; }; - -void createImageFromDot(const QString &inputFile, const QString &outputFile, bool verbose) +static void createImageFromDot(const QString &inputFile, const QString &outputFile, bool verbose) { const QString command = CplusplusToolsUtils::portableExecutableName(QLatin1String("dot")); const QStringList arguments = QStringList() @@ -372,9 +371,9 @@ void createImageFromDot(const QString &inputFile, const QString &outputFile, boo CplusplusToolsUtils::executeCommand(command, arguments, QString(), verbose); } -const char PATH_STDIN_FILE[] = "_stdincontents.cpp"; +static const char PATH_STDIN_FILE[] = "_stdincontents.cpp"; -QString example() +static QString example() { return #if defined(Q_OS_WIN) @@ -387,16 +386,120 @@ QString example() .arg(QFileInfo(qApp->arguments().at(0)).fileName(), QLatin1String(PATH_STDIN_FILE)); } -void printUsage() +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] <file1> <file2> ...\n\n"; + << " [-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( - "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" - "Standard input is also read. The resulting files starts with \"%1\"\n" + "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" @@ -415,9 +518,12 @@ int main(int argc, char *argv[]) args.removeFirst(); bool optionVerbose = false; + int optionParseMode = -1; - // Data from stdin? - if (!tty_for_stdin()) { + // 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()) @@ -430,15 +536,46 @@ int main(int argc, char *argv[]) } // 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")); } - const bool helpRequested = args.contains(QLatin1String("-h")) - || args.contains(QLatin1String("-help")); - if (args.isEmpty() || helpRequested) { + 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 helpRequested ? EXIT_SUCCESS : EXIT_FAILURE; + return EXIT_SUCCESS; } // Process files @@ -466,12 +603,32 @@ int main(int argc, char *argv[]) const QByteArray source = file.readAll(); file.close(); - Document::Ptr doc = Document::create(fileName); - doc->control()->setDiagnosticClient(0); - doc->setUtf8Source(source); - doc->parse(); + // 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()); |