xref: /llvm-project/clang/tools/clang-diff/ClangDiff.cpp (revision a75b2cac718500b6b0d2bc59abc7ccbb1eeb0663)
1*a75b2cacSAlex Lorenz //===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===//
2*a75b2cacSAlex Lorenz //
3*a75b2cacSAlex Lorenz //                     The LLVM Compiler Infrastructure
4*a75b2cacSAlex Lorenz //
5*a75b2cacSAlex Lorenz // This file is distributed under the University of Illinois Open Source
6*a75b2cacSAlex Lorenz // License. See LICENSE.TXT for details.
7*a75b2cacSAlex Lorenz //
8*a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===//
9*a75b2cacSAlex Lorenz //
10*a75b2cacSAlex Lorenz // This file implements a tool for syntax tree based comparison using
11*a75b2cacSAlex Lorenz // Tooling/ASTDiff.
12*a75b2cacSAlex Lorenz //
13*a75b2cacSAlex Lorenz //===----------------------------------------------------------------------===//
14*a75b2cacSAlex Lorenz 
15*a75b2cacSAlex Lorenz #include "clang/Tooling/ASTDiff/ASTDiff.h"
16*a75b2cacSAlex Lorenz #include "clang/Tooling/CommonOptionsParser.h"
17*a75b2cacSAlex Lorenz #include "clang/Tooling/Tooling.h"
18*a75b2cacSAlex Lorenz #include "llvm/Support/CommandLine.h"
19*a75b2cacSAlex Lorenz 
20*a75b2cacSAlex Lorenz using namespace llvm;
21*a75b2cacSAlex Lorenz using namespace clang;
22*a75b2cacSAlex Lorenz using namespace clang::tooling;
23*a75b2cacSAlex Lorenz 
24*a75b2cacSAlex Lorenz static cl::OptionCategory ClangDiffCategory("clang-diff options");
25*a75b2cacSAlex Lorenz 
26*a75b2cacSAlex Lorenz static cl::opt<bool>
27*a75b2cacSAlex Lorenz     DumpAST("ast-dump",
28*a75b2cacSAlex Lorenz             cl::desc("Print the internal representation of the AST as JSON."),
29*a75b2cacSAlex Lorenz             cl::init(false), cl::cat(ClangDiffCategory));
30*a75b2cacSAlex Lorenz 
31*a75b2cacSAlex Lorenz static cl::opt<bool> NoCompilationDatabase(
32*a75b2cacSAlex Lorenz     "no-compilation-database",
33*a75b2cacSAlex Lorenz     cl::desc(
34*a75b2cacSAlex Lorenz         "Do not attempt to load build settings from a compilation database"),
35*a75b2cacSAlex Lorenz     cl::init(false), cl::cat(ClangDiffCategory));
36*a75b2cacSAlex Lorenz 
37*a75b2cacSAlex Lorenz static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
38*a75b2cacSAlex Lorenz                                        cl::Required,
39*a75b2cacSAlex Lorenz                                        cl::cat(ClangDiffCategory));
40*a75b2cacSAlex Lorenz 
41*a75b2cacSAlex Lorenz static cl::opt<std::string> DestinationPath(cl::Positional,
42*a75b2cacSAlex Lorenz                                             cl::desc("<destination>"),
43*a75b2cacSAlex Lorenz                                             cl::Optional,
44*a75b2cacSAlex Lorenz                                             cl::cat(ClangDiffCategory));
45*a75b2cacSAlex Lorenz 
46*a75b2cacSAlex Lorenz static std::unique_ptr<ASTUnit> getAST(const StringRef Filename) {
47*a75b2cacSAlex Lorenz   std::string ErrorMessage;
48*a75b2cacSAlex Lorenz   std::unique_ptr<CompilationDatabase> Compilations;
49*a75b2cacSAlex Lorenz   if (!NoCompilationDatabase)
50*a75b2cacSAlex Lorenz     Compilations =
51*a75b2cacSAlex Lorenz         CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
52*a75b2cacSAlex Lorenz   if (!Compilations) {
53*a75b2cacSAlex Lorenz     if (!NoCompilationDatabase)
54*a75b2cacSAlex Lorenz       llvm::errs()
55*a75b2cacSAlex Lorenz           << "Error while trying to load a compilation database, running "
56*a75b2cacSAlex Lorenz              "without flags.\n"
57*a75b2cacSAlex Lorenz           << ErrorMessage;
58*a75b2cacSAlex Lorenz     Compilations = llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
59*a75b2cacSAlex Lorenz         ".", std::vector<std::string>());
60*a75b2cacSAlex Lorenz   }
61*a75b2cacSAlex Lorenz   std::array<std::string, 1> Files = {{Filename}};
62*a75b2cacSAlex Lorenz   ClangTool Tool(*Compilations, Files);
63*a75b2cacSAlex Lorenz   std::vector<std::unique_ptr<ASTUnit>> ASTs;
64*a75b2cacSAlex Lorenz   Tool.buildASTs(ASTs);
65*a75b2cacSAlex Lorenz   if (ASTs.size() != Files.size())
66*a75b2cacSAlex Lorenz     return nullptr;
67*a75b2cacSAlex Lorenz   return std::move(ASTs[0]);
68*a75b2cacSAlex Lorenz }
69*a75b2cacSAlex Lorenz 
70*a75b2cacSAlex Lorenz int main(int argc, const char **argv) {
71*a75b2cacSAlex Lorenz   cl::HideUnrelatedOptions(ClangDiffCategory);
72*a75b2cacSAlex Lorenz   if (!cl::ParseCommandLineOptions(argc, argv)) {
73*a75b2cacSAlex Lorenz     cl::PrintOptionValues();
74*a75b2cacSAlex Lorenz     return 1;
75*a75b2cacSAlex Lorenz   }
76*a75b2cacSAlex Lorenz 
77*a75b2cacSAlex Lorenz   if (DumpAST) {
78*a75b2cacSAlex Lorenz     if (!DestinationPath.empty()) {
79*a75b2cacSAlex Lorenz       llvm::errs() << "Error: Please specify exactly one filename.\n";
80*a75b2cacSAlex Lorenz       return 1;
81*a75b2cacSAlex Lorenz     }
82*a75b2cacSAlex Lorenz     std::unique_ptr<ASTUnit> AST = getAST(SourcePath);
83*a75b2cacSAlex Lorenz     if (!AST)
84*a75b2cacSAlex Lorenz       return 1;
85*a75b2cacSAlex Lorenz     diff::SyntaxTree Tree(AST->getASTContext());
86*a75b2cacSAlex Lorenz     Tree.printAsJson(llvm::outs());
87*a75b2cacSAlex Lorenz     return 0;
88*a75b2cacSAlex Lorenz   }
89*a75b2cacSAlex Lorenz 
90*a75b2cacSAlex Lorenz   if (DestinationPath.empty()) {
91*a75b2cacSAlex Lorenz     llvm::errs() << "Error: Exactly two paths are required.\n";
92*a75b2cacSAlex Lorenz     return 1;
93*a75b2cacSAlex Lorenz   }
94*a75b2cacSAlex Lorenz 
95*a75b2cacSAlex Lorenz   std::unique_ptr<ASTUnit> Src = getAST(SourcePath);
96*a75b2cacSAlex Lorenz   std::unique_ptr<ASTUnit> Dst = getAST(DestinationPath);
97*a75b2cacSAlex Lorenz   if (!Src || !Dst)
98*a75b2cacSAlex Lorenz     return 1;
99*a75b2cacSAlex Lorenz 
100*a75b2cacSAlex Lorenz   diff::ComparisonOptions Options;
101*a75b2cacSAlex Lorenz   diff::SyntaxTree SrcTree(Src->getASTContext());
102*a75b2cacSAlex Lorenz   diff::SyntaxTree DstTree(Dst->getASTContext());
103*a75b2cacSAlex Lorenz   diff::ASTDiff DiffTool(SrcTree, DstTree, Options);
104*a75b2cacSAlex Lorenz   for (const auto &Match : DiffTool.getMatches())
105*a75b2cacSAlex Lorenz     DiffTool.printMatch(llvm::outs(), Match);
106*a75b2cacSAlex Lorenz   for (const auto &Change : DiffTool.getChanges())
107*a75b2cacSAlex Lorenz     DiffTool.printChange(llvm::outs(), Change);
108*a75b2cacSAlex Lorenz 
109*a75b2cacSAlex Lorenz   return 0;
110*a75b2cacSAlex Lorenz }
111