912 changes: 912 additions & 0 deletions clang/lib/Tooling/ASTDiff/ASTDiff.cpp

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions clang/lib/Tooling/ASTDiff/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangToolingASTDiff
ASTDiff.cpp
LINK_LIBS
clangBasic
clangAST
clangLex
)
1 change: 1 addition & 0 deletions clang/lib/Tooling/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS

add_subdirectory(Core)
add_subdirectory(Refactoring)
add_subdirectory(ASTDiff)

add_clang_library(clangTooling
ArgumentsAdjusters.cpp
Expand Down
78 changes: 78 additions & 0 deletions clang/test/Tooling/clang-diff-basic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// RUN: %clang_cc1 -E %s > %T/src.cpp
// RUN: %clang_cc1 -E %s > %T/dst.cpp -DDEST
// RUN: clang-diff -no-compilation-database %T/src.cpp %T/dst.cpp | FileCheck %s

#ifndef DEST
namespace src {

void foo() {
int x = 321;
}

void main() { foo(); };

const char *a = "foo";

typedef unsigned int nat;

int p = 1 * 2 * 3 * 4;
int squared = p * p;

class X {
const char *foo(int i) {
if (i == 0)
return "foo";
return 0;
}

public:
X(){};

int id(int i) { return i; }
};
}
#else
// CHECK: Match TranslationUnitDecl{{.*}} to TranslationUnitDecl
// CHECK: Match NamespaceDecl: src{{.*}} to NamespaceDecl: dst
namespace dst {
// CHECK-NOT: Match NamespaceDecl: src{{.*}} to NamespaceDecl: inner
namespace inner {
void foo() {
// CHECK: Match IntegerLiteral: 321{{.*}} to IntegerLiteral: 322
int x = 322;
}
}

// CHECK: Match DeclRefExpr: foo{{.*}} to DeclRefExpr: inner::foo
void main() { inner::foo(); }

// CHECK: Match StringLiteral: foo{{.*}} to StringLiteral: foo
const char *b = "f" "o" "o";

// unsigned is canonicalized to unsigned int
// CHECK: Match TypedefDecl: nat;unsigned int;{{.*}} to TypedefDecl: nat;unsigned int;
typedef unsigned nat;

// CHECK: Match VarDecl: p(int){{.*}} to VarDecl: prod(double)
// CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
// CHECK: Update VarDecl: p(int){{.*}} to prod(double)
double prod = 1 * 2 * 10;
// CHECK: Update DeclRefExpr
int squared = prod * prod;

class X {
const char *foo(int i) {
if (i == 0)
return "Bar";
// CHECK: Insert IfStmt{{.*}} into IfStmt
// CHECK: Insert BinaryOperator: =={{.*}} into IfStmt
else if (i == -1)
return "foo";
return 0;
}
// CHECK: Delete AccessSpecDecl: public
X(){};
// CHECK: Delete CXXMethodDecl
};
}
#endif
1 change: 1 addition & 0 deletions clang/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ create_subdirectory_options(CLANG TOOL)

add_clang_subdirectory(diagtool)
add_clang_subdirectory(driver)
add_clang_subdirectory(clang-diff)
add_clang_subdirectory(clang-format)
add_clang_subdirectory(clang-format-vs)
add_clang_subdirectory(clang-fuzzer)
Expand Down
13 changes: 13 additions & 0 deletions clang/tools/clang-diff/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
set(LLVM_LINK_COMPONENTS
Support
)

add_clang_executable(clang-diff
ClangDiff.cpp
)

target_link_libraries(clang-diff
clangFrontend
clangTooling
clangToolingASTDiff
)
110 changes: 110 additions & 0 deletions clang/tools/clang-diff/ClangDiff.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//===- 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;
}