Alex Lorenz a75b2cac71 [clang-diff] Add initial implementation
This is the first commit for the "Clang-based C/C++ diff tool" GSoC project.

ASTDiff is a new library that computes a structural AST diff between two ASTs
using the gumtree algorithm. Clang-diff is a new Clang tool that will show
the structural code changes between different ASTs.

Patch by Johannes Altmanninger!

Differential Revision: https://reviews.llvm.org/D34329

llvm-svn: 308731
2017-07-21 12:49:28 +00:00

111 lines
3.6 KiB
C++

//===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a tool for syntax tree based comparison using
// Tooling/ASTDiff.
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/ASTDiff/ASTDiff.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
using namespace clang;
using namespace clang::tooling;
static cl::OptionCategory ClangDiffCategory("clang-diff options");
static cl::opt<bool>
DumpAST("ast-dump",
cl::desc("Print the internal representation of the AST as JSON."),
cl::init(false), cl::cat(ClangDiffCategory));
static cl::opt<bool> NoCompilationDatabase(
"no-compilation-database",
cl::desc(
"Do not attempt to load build settings from a compilation database"),
cl::init(false), cl::cat(ClangDiffCategory));
static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
cl::Required,
cl::cat(ClangDiffCategory));
static cl::opt<std::string> DestinationPath(cl::Positional,
cl::desc("<destination>"),
cl::Optional,
cl::cat(ClangDiffCategory));
static std::unique_ptr<ASTUnit> getAST(const StringRef Filename) {
std::string ErrorMessage;
std::unique_ptr<CompilationDatabase> Compilations;
if (!NoCompilationDatabase)
Compilations =
CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
if (!Compilations) {
if (!NoCompilationDatabase)
llvm::errs()
<< "Error while trying to load a compilation database, running "
"without flags.\n"
<< ErrorMessage;
Compilations = llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
".", std::vector<std::string>());
}
std::array<std::string, 1> Files = {{Filename}};
ClangTool Tool(*Compilations, Files);
std::vector<std::unique_ptr<ASTUnit>> ASTs;
Tool.buildASTs(ASTs);
if (ASTs.size() != Files.size())
return nullptr;
return std::move(ASTs[0]);
}
int main(int argc, const char **argv) {
cl::HideUnrelatedOptions(ClangDiffCategory);
if (!cl::ParseCommandLineOptions(argc, argv)) {
cl::PrintOptionValues();
return 1;
}
if (DumpAST) {
if (!DestinationPath.empty()) {
llvm::errs() << "Error: Please specify exactly one filename.\n";
return 1;
}
std::unique_ptr<ASTUnit> AST = getAST(SourcePath);
if (!AST)
return 1;
diff::SyntaxTree Tree(AST->getASTContext());
Tree.printAsJson(llvm::outs());
return 0;
}
if (DestinationPath.empty()) {
llvm::errs() << "Error: Exactly two paths are required.\n";
return 1;
}
std::unique_ptr<ASTUnit> Src = getAST(SourcePath);
std::unique_ptr<ASTUnit> Dst = getAST(DestinationPath);
if (!Src || !Dst)
return 1;
diff::ComparisonOptions Options;
diff::SyntaxTree SrcTree(Src->getASTContext());
diff::SyntaxTree DstTree(Dst->getASTContext());
diff::ASTDiff DiffTool(SrcTree, DstTree, Options);
for (const auto &Match : DiffTool.getMatches())
DiffTool.printMatch(llvm::outs(), Match);
for (const auto &Change : DiffTool.getChanges())
DiffTool.printChange(llvm::outs(), Change);
return 0;
}