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