xref: /llvm-project/clang/tools/clang-diff/ClangDiff.cpp (revision deb2a2adc87fa69da694d15fe1dfd45b99218529)
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<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
32                                        cl::Required,
33                                        cl::cat(ClangDiffCategory));
34 
35 static cl::opt<std::string> DestinationPath(cl::Positional,
36                                             cl::desc("<destination>"),
37                                             cl::Optional,
38                                             cl::cat(ClangDiffCategory));
39 
40 static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
41                             cl::init(-1), cl::cat(ClangDiffCategory));
42 
43 static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
44                                       cl::Optional, cl::cat(ClangDiffCategory));
45 
46 static cl::list<std::string> ArgsAfter(
47     "extra-arg",
48     cl::desc("Additional argument to append to the compiler command line"),
49     cl::cat(ClangDiffCategory));
50 
51 static cl::list<std::string> ArgsBefore(
52     "extra-arg-before",
53     cl::desc("Additional argument to prepend to the compiler command line"),
54     cl::cat(ClangDiffCategory));
55 
56 static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
57   if (!Compilations)
58     return;
59   auto AdjustingCompilations =
60       llvm::make_unique<ArgumentsAdjustingCompilations>(
61           std::move(Compilations));
62   AdjustingCompilations->appendArgumentsAdjuster(
63       getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
64   AdjustingCompilations->appendArgumentsAdjuster(
65       getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
66   Compilations = std::move(AdjustingCompilations);
67 }
68 
69 static std::unique_ptr<ASTUnit>
70 getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
71        const StringRef Filename) {
72   std::string ErrorMessage;
73   std::unique_ptr<CompilationDatabase> Compilations;
74   if (!CommonCompilations) {
75     Compilations = CompilationDatabase::autoDetectFromSource(
76         BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
77     if (!Compilations) {
78       llvm::errs()
79           << "Error while trying to load a compilation database, running "
80              "without flags.\n"
81           << ErrorMessage;
82       Compilations =
83           llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
84               ".", std::vector<std::string>());
85     }
86   }
87   addExtraArgs(Compilations);
88   std::array<std::string, 1> Files = {{Filename}};
89   ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
90   std::vector<std::unique_ptr<ASTUnit>> ASTs;
91   Tool.buildASTs(ASTs);
92   if (ASTs.size() != Files.size())
93     return nullptr;
94   return std::move(ASTs[0]);
95 }
96 
97 int main(int argc, const char **argv) {
98   std::string ErrorMessage;
99   std::unique_ptr<CompilationDatabase> CommonCompilations =
100       FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
101   if (!CommonCompilations && !ErrorMessage.empty())
102     llvm::errs() << ErrorMessage;
103   cl::HideUnrelatedOptions(ClangDiffCategory);
104   if (!cl::ParseCommandLineOptions(argc, argv)) {
105     cl::PrintOptionValues();
106     return 1;
107   }
108 
109   addExtraArgs(CommonCompilations);
110 
111   if (ASTDump) {
112     if (!DestinationPath.empty()) {
113       llvm::errs() << "Error: Please specify exactly one filename.\n";
114       return 1;
115     }
116     std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
117     if (!AST)
118       return 1;
119     diff::SyntaxTree Tree(AST->getASTContext());
120     Tree.printAsJson(llvm::outs());
121     return 0;
122   }
123 
124   if (DestinationPath.empty()) {
125     llvm::errs() << "Error: Exactly two paths are required.\n";
126     return 1;
127   }
128 
129   std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
130   std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
131   if (!Src || !Dst)
132     return 1;
133 
134   diff::ComparisonOptions Options;
135   if (MaxSize != -1)
136     Options.MaxSize = MaxSize;
137   diff::SyntaxTree SrcTree(Src->getASTContext());
138   diff::SyntaxTree DstTree(Dst->getASTContext());
139   diff::ASTDiff DiffTool(SrcTree, DstTree, Options);
140   for (const auto &Match : DiffTool.getMatches())
141     DiffTool.printMatch(llvm::outs(), Match);
142   for (const auto &Change : DiffTool.getChanges())
143     DiffTool.printChange(llvm::outs(), Change);
144 
145   return 0;
146 }
147