From e5dd70708596ae51455a0ffa086a00c5b29f8583 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 3 Aug 2020 14:31:31 +0000 Subject: Import LLVM 10.0.0 release including clang, lld and lldb. ok hackroom tested by plenty --- gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp | 536 ++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp (limited to 'gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp') diff --git a/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp b/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp new file mode 100644 index 00000000000..502409eadf3 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp @@ -0,0 +1,536 @@ +//===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 + ASTDump("ast-dump", + cl::desc("Print the internal representation of the AST."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt ASTDumpJson( + "ast-dump-json", + cl::desc("Print the internal representation of the AST as JSON."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt PrintMatches("dump-matches", + cl::desc("Print the matched nodes."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt HtmlDiff("html", + cl::desc("Output a side-by-side diff in HTML."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt SourcePath(cl::Positional, cl::desc(""), + cl::Required, + cl::cat(ClangDiffCategory)); + +static cl::opt DestinationPath(cl::Positional, + cl::desc(""), + cl::Optional, + cl::cat(ClangDiffCategory)); + +static cl::opt StopAfter("stop-diff-after", + cl::desc(""), + cl::Optional, cl::init(""), + cl::cat(ClangDiffCategory)); + +static cl::opt MaxSize("s", cl::desc(""), cl::Optional, + cl::init(-1), cl::cat(ClangDiffCategory)); + +static cl::opt BuildPath("p", cl::desc("Build path"), cl::init(""), + cl::Optional, cl::cat(ClangDiffCategory)); + +static cl::list ArgsAfter( + "extra-arg", + cl::desc("Additional argument to append to the compiler command line"), + cl::cat(ClangDiffCategory)); + +static cl::list ArgsBefore( + "extra-arg-before", + cl::desc("Additional argument to prepend to the compiler command line"), + cl::cat(ClangDiffCategory)); + +static void addExtraArgs(std::unique_ptr &Compilations) { + if (!Compilations) + return; + auto AdjustingCompilations = + std::make_unique( + std::move(Compilations)); + AdjustingCompilations->appendArgumentsAdjuster( + getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN)); + AdjustingCompilations->appendArgumentsAdjuster( + getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END)); + Compilations = std::move(AdjustingCompilations); +} + +static std::unique_ptr +getAST(const std::unique_ptr &CommonCompilations, + const StringRef Filename) { + std::string ErrorMessage; + std::unique_ptr Compilations; + if (!CommonCompilations) { + Compilations = CompilationDatabase::autoDetectFromSource( + BuildPath.empty() ? Filename : BuildPath, ErrorMessage); + if (!Compilations) { + llvm::errs() + << "Error while trying to load a compilation database, running " + "without flags.\n" + << ErrorMessage; + Compilations = + std::make_unique( + ".", std::vector()); + } + } + addExtraArgs(Compilations); + std::array Files = {{Filename}}; + ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files); + std::vector> ASTs; + Tool.buildASTs(ASTs); + if (ASTs.size() != Files.size()) + return nullptr; + return std::move(ASTs[0]); +} + +static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); } + +static const char HtmlDiffHeader[] = R"( + + + + + + + +
+)"; + +static void printHtml(raw_ostream &OS, char C) { + switch (C) { + case '&': + OS << "&"; + break; + case '<': + OS << "<"; + break; + case '>': + OS << ">"; + break; + case '\'': + OS << "'"; + break; + case '"': + OS << """; + break; + default: + OS << C; + } +} + +static void printHtml(raw_ostream &OS, const StringRef Str) { + for (char C : Str) + printHtml(OS, C); +} + +static std::string getChangeKindAbbr(diff::ChangeKind Kind) { + switch (Kind) { + case diff::None: + return ""; + case diff::Delete: + return "d"; + case diff::Update: + return "u"; + case diff::Insert: + return "i"; + case diff::Move: + return "m"; + case diff::UpdateMove: + return "u m"; + } + llvm_unreachable("Invalid enumeration value."); +} + +static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff, + diff::SyntaxTree &Tree, bool IsLeft, + diff::NodeId Id, unsigned Offset) { + const diff::Node &Node = Tree.getNode(Id); + char MyTag, OtherTag; + diff::NodeId LeftId, RightId; + diff::NodeId TargetId = Diff.getMapped(Tree, Id); + if (IsLeft) { + MyTag = 'L'; + OtherTag = 'R'; + LeftId = Id; + RightId = TargetId; + } else { + MyTag = 'R'; + OtherTag = 'L'; + LeftId = TargetId; + RightId = Id; + } + unsigned Begin, End; + std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node); + const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager(); + auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer(); + for (; Offset < Begin; ++Offset) + printHtml(OS, Code[Offset]); + OS << ""; + + for (diff::NodeId Child : Node.Children) + Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset); + + for (; Offset < End; ++Offset) + printHtml(OS, Code[Offset]); + if (Id == Tree.getRootId()) { + End = Code.size(); + for (; Offset < End; ++Offset) + printHtml(OS, Code[Offset]); + } + OS << ""; + return Offset; +} + +static void printJsonString(raw_ostream &OS, const StringRef Str) { + for (signed char C : Str) { + switch (C) { + case '"': + OS << R"(\")"; + break; + case '\\': + OS << R"(\\)"; + break; + case '\n': + OS << R"(\n)"; + break; + case '\t': + OS << R"(\t)"; + break; + default: + if ('\x00' <= C && C <= '\x1f') { + OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C); + } else { + OS << C; + } + } + } +} + +static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + const diff::Node &N = Tree.getNode(Id); + OS << R"("id":)" << int(Id); + OS << R"(,"type":")" << N.getTypeLabel() << '"'; + auto Offsets = Tree.getSourceRangeOffsets(N); + OS << R"(,"begin":)" << Offsets.first; + OS << R"(,"end":)" << Offsets.second; + std::string Value = Tree.getNodeValue(N); + if (!Value.empty()) { + OS << R"(,"value":")"; + printJsonString(OS, Value); + OS << '"'; + } +} + +static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + const diff::Node &N = Tree.getNode(Id); + OS << "{"; + printNodeAttributes(OS, Tree, Id); + auto Identifier = N.getIdentifier(); + auto QualifiedIdentifier = N.getQualifiedIdentifier(); + if (Identifier) { + OS << R"(,"identifier":")"; + printJsonString(OS, *Identifier); + OS << R"(")"; + if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) { + OS << R"(,"qualified_identifier":")"; + printJsonString(OS, *QualifiedIdentifier); + OS << R"(")"; + } + } + OS << R"(,"children":[)"; + if (N.Children.size() > 0) { + printNodeAsJson(OS, Tree, N.Children[0]); + for (size_t I = 1, E = N.Children.size(); I < E; ++I) { + OS << ","; + printNodeAsJson(OS, Tree, N.Children[I]); + } + } + OS << "]}"; +} + +static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + if (Id.isInvalid()) { + OS << "None"; + return; + } + OS << Tree.getNode(Id).getTypeLabel(); + std::string Value = Tree.getNodeValue(Id); + if (!Value.empty()) + OS << ": " << Value; + OS << "(" << Id << ")"; +} + +static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) { + for (diff::NodeId Id : Tree) { + for (int I = 0; I < Tree.getNode(Id).Depth; ++I) + OS << " "; + printNode(OS, Tree, Id); + OS << "\n"; + } +} + +static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff, + diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree, + diff::NodeId Dst) { + const diff::Node &DstNode = DstTree.getNode(Dst); + diff::NodeId Src = Diff.getMapped(DstTree, Dst); + switch (DstNode.Change) { + case diff::None: + break; + case diff::Delete: + llvm_unreachable("The destination tree can't have deletions."); + case diff::Update: + OS << "Update "; + printNode(OS, SrcTree, Src); + OS << " to " << DstTree.getNodeValue(Dst) << "\n"; + break; + case diff::Insert: + case diff::Move: + case diff::UpdateMove: + if (DstNode.Change == diff::Insert) + OS << "Insert"; + else if (DstNode.Change == diff::Move) + OS << "Move"; + else if (DstNode.Change == diff::UpdateMove) + OS << "Update and Move"; + OS << " "; + printNode(OS, DstTree, Dst); + OS << " into "; + printNode(OS, DstTree, DstNode.Parent); + OS << " at " << DstTree.findPositionInParent(Dst) << "\n"; + break; + } +} + +int main(int argc, const char **argv) { + std::string ErrorMessage; + std::unique_ptr CommonCompilations = + FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage); + if (!CommonCompilations && !ErrorMessage.empty()) + llvm::errs() << ErrorMessage; + cl::HideUnrelatedOptions(ClangDiffCategory); + if (!cl::ParseCommandLineOptions(argc, argv)) { + cl::PrintOptionValues(); + return 1; + } + + addExtraArgs(CommonCompilations); + + if (ASTDump || ASTDumpJson) { + if (!DestinationPath.empty()) { + llvm::errs() << "Error: Please specify exactly one filename.\n"; + return 1; + } + std::unique_ptr AST = getAST(CommonCompilations, SourcePath); + if (!AST) + return 1; + diff::SyntaxTree Tree(AST->getASTContext()); + if (ASTDump) { + printTree(llvm::outs(), Tree); + return 0; + } + llvm::outs() << R"({"filename":")"; + printJsonString(llvm::outs(), SourcePath); + llvm::outs() << R"(","root":)"; + printNodeAsJson(llvm::outs(), Tree, Tree.getRootId()); + llvm::outs() << "}\n"; + return 0; + } + + if (DestinationPath.empty()) { + llvm::errs() << "Error: Exactly two paths are required.\n"; + return 1; + } + + std::unique_ptr Src = getAST(CommonCompilations, SourcePath); + std::unique_ptr Dst = getAST(CommonCompilations, DestinationPath); + if (!Src || !Dst) + return 1; + + diff::ComparisonOptions Options; + if (MaxSize != -1) + Options.MaxSize = MaxSize; + if (!StopAfter.empty()) { + if (StopAfter == "topdown") + Options.StopAfterTopDown = true; + else if (StopAfter != "bottomup") { + llvm::errs() << "Error: Invalid argument for -stop-after\n"; + return 1; + } + } + diff::SyntaxTree SrcTree(Src->getASTContext()); + diff::SyntaxTree DstTree(Dst->getASTContext()); + diff::ASTDiff Diff(SrcTree, DstTree, Options); + + if (HtmlDiff) { + llvm::outs() << HtmlDiffHeader << "
";
+    llvm::outs() << "
"; + printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0); + llvm::outs() << "
"; + llvm::outs() << "
"; + printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(), + 0); + llvm::outs() << "
"; + llvm::outs() << "
\n"; + return 0; + } + + for (diff::NodeId Dst : DstTree) { + diff::NodeId Src = Diff.getMapped(DstTree, Dst); + if (PrintMatches && Src.isValid()) { + llvm::outs() << "Match "; + printNode(llvm::outs(), SrcTree, Src); + llvm::outs() << " to "; + printNode(llvm::outs(), DstTree, Dst); + llvm::outs() << "\n"; + } + printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst); + } + for (diff::NodeId Src : SrcTree) { + if (Diff.getMapped(SrcTree, Src).isInvalid()) { + llvm::outs() << "Delete "; + printNode(llvm::outs(), SrcTree, Src); + llvm::outs() << "\n"; + } + } + + return 0; +} -- cgit v1.2.3-59-g8ed1b