forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user