[PATCH] D34329: [clang-diff] Initial implementation.

2018-08-27 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D34329#1213667, @sylvestre.ledru wrote:

> @arphaman @johannes Is that normal that clang-diff isn't installed by cmake? 
> (like clang-format?)


Yes, we did not add that. I don't know if anyone would use it.


Repository:
  rL LLVM

https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-18 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added subscribers: mgorny, klimek.

https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,58 @@
+//===- 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.
+//
+//===--===//
+///
+/// \file
+/// \brief
+///
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+cl::init(false), cl::cat(ClangDiffCategory));
+
+int main(int argc, const char **argv) {
+  tooling::CommonOptionsParser Options(argc, argv, ClangDiffCategory);
+  ArrayRef Files = Options.getSourcePathList();
+
+  tooling::ClangTool Tool(Options.getCompilations(), Files);
+
+  if (DumpAST) {
+if (Files.size() != 1) {
+  errs() << "Error: please specify exactly one filename.\n";
+  return 1;
+}
+std::vector> ASTs;
+Tool.buildASTs(ASTs);
+if (ASTs.size() != 1) {
+  return 1;
+}
+clang::diff::TreeRoot Tree(*ASTs[0]);
+Tree.dumpAsJson();
+return 0;
+  }
+  if (Files.size() != 2) {
+errs() << "Error: exactly two filenames are required.\n";
+return 1;
+  }
+  std::vector> ASTs;
+  Tool.buildASTs(ASTs);
+  if (ASTs.size() != 2) {
+return 1;
+  }
+  clang::diff::runDiff(*ASTs[0], *ASTs[1]);
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: lib/Tooling/CMakeLists.txt
===
--- lib/Tooling/CMakeLists.txt
+++ lib/Tooling/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_subdirectory(Core)
 add_subdirectory(Refactoring)
+add_subdirectory(ASTDiff)
 
 add_clang_library(clangTooling
   ArgumentsAdjusters.cpp
Index: lib/Tooling/ASTDiff/CMakeLists.txt
===
--- /dev/null
+++ lib/Tooling/ASTDiff/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+  # Option
+  Support
+  )
+
+add_clang_library(clangToolingASTDiff
+  ASTDiff.cpp
+  LINK_LIBS
+  clangBasic
+  clangAST
+  # clangToolingCore
+  )
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- /dev/null
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -0,0 +1,865 @@
+//===- ASTDiff.cpp - AST differencing implementation---*- C++ -*- -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+///
+/// \file
+/// \brief
+///
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "llvm/ADT/PriorityQueue.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#include 
+#include 
+#include 
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace diff {
+
+static bool isRelevantNode(Decl *D) { return D != nullptr; }
+static bool isRelevantNode(Stmt *S) { return S != nullptr; }
+
+namespace {
+// Count the number of non-null nodes
+struct NodeCountVisitor : public RecursiveASTVisitor {
+  int Count = 0;
+  bool TraverseDecl(Decl *D) {
+if (isRelevantNode(D)) {
+  ++Count;
+  RecursiveASTVisitor::Trave

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-18 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 102986.

https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,94 @@
+//===- 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.
+//
+//===--===//
+///
+/// \file
+/// \brief
+///
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr Compilations =
+  CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
+  if (!Compilations) {
+errs() << "Error while trying to load a compilation database, running "
+  "without flags.\n"
+   << ErrorMessage;
+Compilations.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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()) {
+  errs() << "Error: Please specify exactly one filename.\n";
+  return 1;
+}
+std::unique_ptr AST = getAST(SourcePath);
+if (!AST) {
+  return 1;
+}
+clang::diff::TreeRoot Tree(*AST);
+Tree.dumpAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst) {
+return 1;
+  }
+
+  clang::diff::runDiff(*Src, *Dst);
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: lib/Tooling/CMakeLists.txt
===
--- lib/Tooling/CMakeLists.txt
+++ lib/Tooling/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_subdirectory(Core)
 add_subdirectory(Refactoring)
+add_subdirectory(ASTDiff)
 
 add_clang_library(clangTooling
   ArgumentsAdjusters.cpp
Index: lib/Tooling/ASTDiff/CMakeLists.txt
===
--- /dev/null
+++ lib/Tooling/ASTDiff/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+  # Option
+  Support
+  )
+
+add_clang_library(clangToolingASTDiff
+  ASTDiff.cpp
+  LINK_LIBS
+  clangBasic
+  clangAST
+  # clangToolingCore
+  )
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- /dev/null
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -0,0 +1,836 @@
+//===- ASTDiff.cpp - AST di

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-19 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103147.
johannes added a comment.

- style fixes
- do not compare nodes from system headers


https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,94 @@
+//===- 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.
+//
+//===--===//
+///
+/// \file
+/// \brief
+///
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr Compilations =
+  CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
+  if (!Compilations) {
+errs() << "Error while trying to load a compilation database, running "
+  "without flags.\n"
+   << ErrorMessage;
+Compilations.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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()) {
+  errs() << "Error: Please specify exactly one filename.\n";
+  return 1;
+}
+std::unique_ptr AST = getAST(SourcePath);
+if (!AST) {
+  return 1;
+}
+clang::diff::TreeRoot Tree(*AST);
+Tree.dumpAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst) {
+return 1;
+  }
+
+  clang::diff::runDiff(*Src, *Dst);
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: lib/Tooling/CMakeLists.txt
===
--- lib/Tooling/CMakeLists.txt
+++ lib/Tooling/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_subdirectory(Core)
 add_subdirectory(Refactoring)
+add_subdirectory(ASTDiff)
 
 add_clang_library(clangTooling
   ArgumentsAdjusters.cpp
Index: lib/Tooling/ASTDiff/CMakeLists.txt
===
--- /dev/null
+++ lib/Tooling/ASTDiff/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+  # Option
+  Support
+  )
+
+add_clang_library(clangToolingASTDiff
+  ASTDiff.cpp
+  LINK_LIBS
+  clangBasic
+  clangAST
+  # clangToolingCore
+  )
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- /dev/nu

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-19 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D34329#784090, @arphaman wrote:

> Generally we shouldn't have untested code in trunk, so I think that we need 
> to find a way to test this before committing. We can start off by testing the 
> output of the diff tool. Since there will be a lot of changes in the future, 
> you don't have to have everything covered now, so I think that even a couple 
> of tests be enough.


Thanks for your feedback! I think I have adressed all issues, except for the 
tests.

For the tests to run properly, I tried to create a local compile_commands.json, 
because ClangTool refuses to build an AST when the command for a source not 
found in the compilation database. However, it seems like relative paths do not 
work for the "directory" property.
So maybe this can be added? Other options are: 1. patching the compilation 
database before running the test, so that it has the absolute path or 2. adding 
an option to my tool to not load a compilation database. WDYT?




Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:31
+
+static bool isRelevantNode(Decl *D) { return D != nullptr; }
+static bool isRelevantNode(Stmt *S) { return S != nullptr; }

arphaman wrote:
> I don't see the point in these functions. Are you going to add some more 
> logic to them?
> 
> I'd suggest removing them and using early return in the traversal functions 
> instead, e.g:
> 
> ```
>   bool TraverseDecl(Decl *D) {
> if (!D)
>   return true;
> ++Count;
> return RecursiveASTVisitor::TraverseDecl(D);
>   }
> ```
This is now done in `discardNode`, which also filters nodes from system headers.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-19 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103151.

https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,92 @@
+//===- 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 tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr Compilations =
+  CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
+  if (!Compilations) {
+llvm::errs()
+<< "Error while trying to load a compilation database, running "
+   "without flags.\n"
+<< ErrorMessage;
+Compilations.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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 AST = getAST(SourcePath);
+if (!AST)
+  return 1;
+clang::diff::TreeRoot Tree(AST->getASTContext());
+Tree.printAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+llvm::errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst)
+return 1;
+
+  clang::diff::runDiff(Src->getASTContext(), Dst->getASTContext());
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: lib/Tooling/CMakeLists.txt
===
--- lib/Tooling/CMakeLists.txt
+++ lib/Tooling/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_subdirectory(Core)
 add_subdirectory(Refactoring)
+add_subdirectory(ASTDiff)
 
 add_clang_library(clangTooling
   ArgumentsAdjusters.cpp
Index: lib/Tooling/ASTDiff/CMakeLists.txt
===
--- /dev/null
+++ lib/Tooling/ASTDiff/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+  # Option
+  Support
+  )
+
+add_clang_library(clangToolingASTDiff
+  ASTDiff.cpp
+  LINK_LIBS
+  clangBasic
+  clangAST
+  # clangToolingCore
+  )
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
==

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103163.
johannes added a comment.

- Add the option to not use compilation databases.
- Add a basic test.


https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  test/Tooling/clang-diff-basic.cpp
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,101 @@
+//===- 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 tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt NoCompilationDatabase(
+"no-compilation-database",
+cl::desc(
+"Do not attempt to load build settigns from a compilation database"),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr 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.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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 AST = getAST(SourcePath);
+if (!AST)
+  return 1;
+clang::diff::TreeRoot Tree(AST->getASTContext());
+Tree.printAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+llvm::errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst)
+return 1;
+
+  clang::diff::runDiff(Src->getASTContext(), Dst->getASTContext());
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: test/Tooling/clang-diff-basic.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-basic.cpp
@@ -0,0 +1,65 @@
+// RUN: awk '/^.. src/{f=1;next}/^.. end src/{f=0}f' %s > %T/src.cpp
+// RUN: awk '/^.. dst/{f=1;next}/^.. end dst/{f=0}f' %s > %T/dst.cpp
+// 

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes marked 10 inline comments as done.
johannes added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:123
+
+void runDiff(ASTContext &AST1, ASTContext &AST2);
+

klimek wrote:
> This is the main exposed interface?
> 
> Generally, if all we want to do is printing, I wouldn't put that into a 
> library in Tooling, but just implement a tools/ASTDiffer or somesuch.
> 
> If you want to make this a library, it should return the diff in some form 
> that's nice to use (or print).
> 
I started out by creating a self contained tool, that's why the interface does 
not really make sense.

I will change it to provide the mappings and the edit script in a nice way, 
this might be quite useful.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103208.
johannes marked 7 inline comments as done.

https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  test/Tooling/clang-diff-basic.cpp
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -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 tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt NoCompilationDatabase(
+"no-compilation-database",
+cl::desc(
+"Do not attempt to load build settigns from a compilation database"),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr 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.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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 AST = getAST(SourcePath);
+if (!AST)
+  return 1;
+clang::diff::TreeRoot Tree(AST->getASTContext());
+Tree.printAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+llvm::errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst)
+return 1;
+
+  diff::TreeRoot T1(Src->getASTContext());
+  diff::TreeRoot T2(Dst->getASTContext());
+  diff::ASTDiff DiffTool(T1, T2);
+  diff::Mapping M = DiffTool.computeMapping();
+  M.printMapping();
+  auto Changes = DiffTool.computeChanges(M);
+  for (const auto &C : Changes)
+DiffTool.printChange(C);
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangFrontend
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: test/Tooling/clang-diff-basic.cpp
===
--- /dev/null
+++ test/Tooli

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes marked an inline comment as done.
johannes added a comment.

In https://reviews.llvm.org/D34329#785190, @arphaman wrote:

> Looking at the output of the tool, I have the following suggestion:
>
> - We should avoid implicit expressions (We don't need to see things like 
> `Insert ImplicitCastExpr(21) into BinaryOperator: *(22) at 0`). This can be 
> done in a follow-up patch though.


Ok, I will include that and other features in future patches.




Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:474
+for (SNodeId D1 = LMD1 + 1; D1 <= Id1; ++D1) {
+  double DeletionCost = 1.0;
+  ForestDist[D1][LMD2] = ForestDist[D1 - 1][LMD2] + DeletionCost;

arphaman wrote:
> Are the `DeletionCost` and `InsertionCost` constants or are you planning to 
> modify them in the future?
I think they could be modified for minor improvements of the matching, but I am 
probably not going to do so anytime soon. Maybe it is better to store them at 
class scope as static const / constexpr?



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:693
+  H2 = L2.pop();
+  for (NodeId Id1 : H1)
+for (NodeId Id2 : H2)

arphaman wrote:
> Please wrap these loops in braces.
Not sure if I got this right.



Comment at: test/Tooling/clang-diff-basic.cpp:3
+// RUN: awk '/^.. dst/{f=1;next}/^.. end dst/{f=0}f' %s > %T/dst.cpp
+// RUN: clang-diff -no-compilation-database %T/src.cpp %T/dst.cpp
+// RUN: clang-diff -no-compilation-database %T/src.cpp %T/dst.cpp | FileCheck 
%s

arphaman wrote:
> I'd prefer it if we used something like `clang -E` and preprocessor to 
> generate the two files.
> 
> E.g.:
> 
> ```
> RUN: %clang_cc1 -E %s > %T/src.cpp
> RUN: %clang_cc1 -E %s -D DEST > %T/dst.cpp
> #ifndef DEST
> namespace src { };
> #else
> namespace dst { };
> #endif
> ```
Yes, much better!



Comment at: test/Tooling/clang-diff-basic.cpp:4
+// RUN: clang-diff -no-compilation-database %T/src.cpp %T/dst.cpp
+// RUN: clang-diff -no-compilation-database %T/src.cpp %T/dst.cpp | FileCheck 
%s
+

arphaman wrote:
> Why do you need two invocations of `clang-diff` with the same arguments?
That was unintentional, I removed it.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:303
+/// Identifies a node in this subtree by its postorder offset.
+using SNodeId = int;
+

arphaman wrote:
> What's the difference between `SNodeId` and `NodeId`?
NodeId is the preorder offset inside the root tree, starting at 0.
SNodeId is the postorder offset within a subtree, starting at 1.
Not sure if this irregularity can be improved upon easily, but I guess I can 
mention it in the comments. 


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:171
+
+std::string TreeRoot::label(NodeId Id) const {
+  const Node &N = getNode(Id);

arphaman wrote:
> I believe that this method that you call `label` actually represents the 
> `value` attribute that's described in the paper for the gumtree algorithm. Is 
> that right? If so, then this method name should be updated to reflect that.
Yes, good catch. Strangely, the gumtree implementation uses `label`. I think we 
should use `type` for node kinds and `value` for their actual value, in order 
to avoid confusion about what a label is.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-20 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103320.
johannes added a comment.

- Fix a bug in getSimilarity()
- Change terminology: `label` -> `value`
- Define SNodeId: Now it cannot be implicitly constructed from an int, however 
it can be converted to int. Still feels a bit weird
- Fix some issues with SNodeId
- Rewrite the computation of leftmost descendants in subtrees.


https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  test/Tooling/clang-diff-basic.cpp
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -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 tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt NoCompilationDatabase(
+"no-compilation-database",
+cl::desc(
+"Do not attempt to load build settigns from a compilation database"),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr 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.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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 AST = getAST(SourcePath);
+if (!AST)
+  return 1;
+clang::diff::TreeRoot Tree(AST->getASTContext());
+Tree.printAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+llvm::errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst)
+return 1;
+
+  diff::TreeRoot T1(Src->getASTContext());
+  diff::TreeRoot T2(Dst->getASTContext());
+  diff::ASTDiff DiffTool(T1, T2);
+  diff::Mapping M = DiffTool.computeMapping();
+  M.printMapping();
+  auto Changes = DiffTool.computeChanges(M);
+  for (const auto &C : Changes)
+DiffTool.printChange(C);
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangFrontend
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_subdirectory(diagtool)
 add_clang_subdirectory(driver)
+add_cl

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-21 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103409.
johannes marked 4 inline comments as done.
johannes added a comment.

- move some unnecessary things out of the public header

Is this a proper way to declutter the header file? Using inheritance would also 
be possible.

I have to define a destructor for ASTDiff because TreeComparator is 
forward-declared


https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  test/Tooling/clang-diff-basic.cpp
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,107 @@
+//===- 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 tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt NoCompilationDatabase(
+"no-compilation-database",
+cl::desc(
+"Do not attempt to load build settigns from a compilation database"),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr 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.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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 AST = getAST(SourcePath);
+if (!AST)
+  return 1;
+clang::diff::TreeRoot Tree(AST->getASTContext());
+Tree.printAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+llvm::errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst)
+return 1;
+
+  diff::ASTDiff DiffTool(Src->getASTContext(), Dst->getASTContext());
+  for (const auto &Match : DiffTool.getMatches())
+DiffTool.printMatch(Match);
+  for (const auto &Change : DiffTool.getChanges())
+DiffTool.printChange(Change);
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangFrontend
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_subdirectory(diagtool)
 add_clang_subdirectory(driver)
+add_clang_subdirectory(clang-diff)
 add_clang_subdirectory(clang-format)
 add_clan

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-21 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:157
+  int Leaves = 0;
+  std::function Traverse = [&](NodeId Id) {
+const Node &N = getNode(Id);

arphaman wrote:
> you should be able to use `auto` instead of `std::function` here I think.
It does not work because the function is recursive. I'm not sure whether it is 
good practise to do it like this.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:209
+if (X->hasQualifier() && X->getQualifier()->getAsIdentifier())
+  Value += std::string(X->getQualifier()->getAsIdentifier()->getName());
+Value += X->getDecl()->getNameAsString();

arphaman wrote:
> Qualifiers (i.e. `NestedNameSpecifier`s) can contain multiple identifiers 
> (e.g. `foo::bar::`). You should use the `print` method from 
> `NestedNameSpecifier` instead.
Ok nice, this works properly!
This function needs a lot of work. I try to extract relevant information from 
base classes first.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-21 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 103432.

https://reviews.llvm.org/D34329

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  lib/Tooling/CMakeLists.txt
  test/Tooling/clang-diff-basic.cpp
  tools/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- /dev/null
+++ tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,107 @@
+//===- 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 tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt
+DumpAST("ast-dump",
+cl::desc("Print the internal representation of the AST as JSON."),
+cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt NoCompilationDatabase(
+"no-compilation-database",
+cl::desc(
+"Do not attempt to load build settigns from a compilation database"),
+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 std::unique_ptr getAST(const StringRef Filename) {
+  std::string ErrorMessage;
+  std::unique_ptr 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.reset(
+new FixedCompilationDatabase(".", std::vector()));
+  }
+  std::array Files = {{Filename}};
+  ClangTool Tool(*Compilations, Files);
+  std::vector> 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 AST = getAST(SourcePath);
+if (!AST)
+  return 1;
+clang::diff::TreeRoot Tree(AST->getASTContext());
+Tree.printAsJson();
+return 0;
+  }
+
+  if (DestinationPath.empty()) {
+llvm::errs() << "Error: Exactly two paths are required.\n";
+return 1;
+  }
+
+  std::unique_ptr Src = getAST(SourcePath);
+  std::unique_ptr Dst = getAST(DestinationPath);
+  if (!Src || !Dst)
+return 1;
+
+  diff::ASTDiff DiffTool(Src->getASTContext(), Dst->getASTContext());
+  for (const auto &Match : DiffTool.getMatches())
+DiffTool.printMatch(Match);
+  for (const auto &Change : DiffTool.getChanges())
+DiffTool.printChange(Change);
+
+  return 0;
+}
Index: tools/clang-diff/CMakeLists.txt
===
--- /dev/null
+++ tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-diff
+  ClangDiff.cpp
+  )
+
+target_link_libraries(clang-diff
+  clangFrontend
+  clangTooling
+  clangToolingASTDiff
+  )
Index: tools/CMakeLists.txt
===
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 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)
Index: test/Tooling/clang-diff-basic.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-basic.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -E %s > %T/src.cpp
+// RUN: %clang_cc1 -E

[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-21 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:57
+/// Within a tree, this identifies a node by its preorder offset.
+using NodeId = int;
+

arphaman wrote:
> I think that it's better to make make `NodeId` a structure as well and make 
> `InvalidNodeId` a private member. I suggest the following interface for 
> `NodeId`:
> 
> ```
> struct NodeId {
> private:
>   static const int InvalidNodeId; 
> public:
>   int Id;
> 
>   NodeId() : Id(InvalidNodeId) { }
>   NodeId(int Id) : Id(Id) { }
>   
>   bool isValid() const { return Id != InvalidNodeId; }
>   bool isInvalid() const { return Id == InvalidNodeId; }
> };
> ```
> 
> 
> This way you'll get rid of a couple of variable initializations that use 
> `InvalidNodeId`. You also won't need to call the `memset` when creating the 
> unique pointer array of `NodeId`s.
Ok, I did it like this. 

Can I create a header file inside lib/Tooling/ASTDiff and include it from the 
public interface? This would help reduce the clutter.

Instead of NodeId we could also just use references / pointer types. I don't 
see any particularly good reason for choosing either one above the other.
I guess indices make it more obvious how to compute the number of descendants 
and such. On the other hand, when using reference types, there is less 
boilerplate to write for loops.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-23 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:730
+
+Mapping TreeComparator::matchTopDown() const {
+  PriorityList L1(T1);

arphaman wrote:
> Johannes, it seems to me that your implementation of the top-down portion of 
> the GumTree algorithm doesn't use the `an empty list A of candidate mappings` 
> that's described in the paper (and that you have in the Python prototype). Is 
> that correct or am I missing something?
Yes, initially I implemented it as it is described in the paper, but then I 
realized that the list of candidate mappings will always stay empty, because 
the condition in the top-down algorithm in the paper on line 14 will never be 
true. Maybe I am mistaken here, but if `t1` and `t2` are isomorphic, then none 
of the descendants of `t1` will be isomorphic to `t2`. I mean, the height of 
isomorphic trees must be equal, and the descendant does not have the same 
height. So to me this looks like an error in the paper, I probably should have 
communicated this.
What I did instead is, I had a look at the reference implementation, and what 
they do instead of using a list of candidate mappings is to just use a data 
structure for the mapping that allows multiple matches for each node.
After matching collecting all candidates this way, they extract the unambiguous 
matches and then sort the ambiguous matches by their parents' similarity.
[[ 
https://github.com/GumTreeDiff/gumtree/blob/develop/core/src/main/java/com/github/gumtreediff/matchers/heuristic/gt/AbstractSubtreeMatcher.java
 | AbstractSubtreeMatcher.java ]]
[[ 
https://github.com/GumTreeDiff/gumtree/blob/develop/core/src/main/java/com/github/gumtreediff/matchers/heuristic/gt/GreedySubtreeMatcher.java
 | GreedySubtreeMatcher.java ]]
This seems to be a good solution,  I plan to implement that in the future.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34329: [GSoC] Clang AST diffing

2017-06-26 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:730
+
+Mapping TreeComparator::matchTopDown() const {
+  PriorityList L1(T1);

johannes wrote:
> arphaman wrote:
> > Johannes, it seems to me that your implementation of the top-down portion 
> > of the GumTree algorithm doesn't use the `an empty list A of candidate 
> > mappings` that's described in the paper (and that you have in the Python 
> > prototype). Is that correct or am I missing something?
> Yes, initially I implemented it as it is described in the paper, but then I 
> realized that the list of candidate mappings will always stay empty, because 
> the condition in the top-down algorithm in the paper on line 14 will never be 
> true. Maybe I am mistaken here, but if `t1` and `t2` are isomorphic, then 
> none of the descendants of `t1` will be isomorphic to `t2`. I mean, the 
> height of isomorphic trees must be equal, and the descendant does not have 
> the same height. So to me this looks like an error in the paper, I probably 
> should have communicated this.
> What I did instead is, I had a look at the reference implementation, and what 
> they do instead of using a list of candidate mappings is to just use a data 
> structure for the mapping that allows multiple matches for each node.
> After matching collecting all candidates this way, they extract the 
> unambiguous matches and then sort the ambiguous matches by their parents' 
> similarity.
> [[ 
> https://github.com/GumTreeDiff/gumtree/blob/develop/core/src/main/java/com/github/gumtreediff/matchers/heuristic/gt/AbstractSubtreeMatcher.java
>  | AbstractSubtreeMatcher.java ]]
> [[ 
> https://github.com/GumTreeDiff/gumtree/blob/develop/core/src/main/java/com/github/gumtreediff/matchers/heuristic/gt/GreedySubtreeMatcher.java
>  | GreedySubtreeMatcher.java ]]
> This seems to be a good solution,  I plan to implement that in the future.
My bad, I misread the algorithm in the paper, of course the entire tree is 
searched for other isomorphic subtrees.
I will still stick to the way it is implemented in gumtree, it should be more 
efficient.


https://reviews.llvm.org/D34329



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37001: [clang-diff] Use data collectors for node comparison

2017-08-25 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D37001#852442, @arphaman wrote:

> Can you remove `getNodeValue` now or do you still need it?


It is only used in the client, I think it makes sense to move it there, or to 
remove it altogether.
I think I'd keep it in the client for now, to disambiguate things with 
different names in the tests.


https://reviews.llvm.org/D37001



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37004: [clang-diff] Fix the html output for CXXOperatorCallExpr

2017-08-25 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: tools/clang-diff/ClangDiff.cpp:319
+   "A Binary operator is supposed to have two arguments.");
+for (int I : {1, 0, 2})
+  Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Children[I], Offset);

arphaman wrote:
> Please add a short comment that describes why this out-of-order traversal is 
> required
Should we do this in LexicallyOrderedRecursiveASTVisitor?

There are some other cases with CXXOperatorCallExpr where the order needs to be 
changed, e.g. postfix operators, operator->, operator() and operator[].
It can be done by sorting by SourceLocation of the first two elements, as the 
operator is always the first one.


https://reviews.llvm.org/D37004



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36998: [AST] Traverse templates in LexicallyOrderedRecursiveASTVisitor

2017-08-25 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112707.
johannes added a comment.

test ordering, class template


https://reviews.llvm.org/D36998

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,8 +21,9 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM)
-  : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher) {}
+  const SourceManager &SM, bool EmitIndices)
+  : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
+EmitIndices(EmitIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
@@ -35,15 +36,20 @@
 
 private:
   DummyMatchVisitor &Matcher;
+  bool EmitIndices;
+  unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
+  bool EmitIndices;
+
 public:
+  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
@@ -64,9 +70,11 @@
   OS << ND->getNameAsString();
 else
   OS << "???";
-if (isa(D))
+if (isa(D) or isa(D))
   OS << "/";
   }
+  if (EmitIndices)
+OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
 }
@@ -138,4 +146,18 @@
   EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitTemplateDecl) {
+  StringRef Source = R"(
+template  T f();
+template  class Class {};
+)";
+  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  Visitor.ExpectMatch("/f/T@0", 2, 11);
+  Visitor.ExpectMatch("/f/f/@1", 2, 20);
+  Visitor.ExpectMatch("/Class/U@2", 3, 11);
+  Visitor.ExpectMatch("/Class/@3", 3, 20);
+  Visitor.ExpectMatch("/Class/Class/@4", 3, 34);
+  EXPECT_TRUE(Visitor.runOver(Source));
+}
+
 } // end anonymous namespace
Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -499,10 +499,10 @@
 
   bool canIgnoreChildDeclWhileTraversingDeclContext(const Decl *Child);
 
-private:
   // These are helper methods used by more than one Traverse* method.
   bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
 
+private:
   // Traverses template parameter lists of either a DeclaratorDecl or TagDecl.
   template 
   bool TraverseDeclTemplateParameterLists(T *D);
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,6 +111,21 @@
 return true;
   }
 
+#define DEF_TRAVERSE_TEMPLATE_DECL(TMPLDECLKIND)   \
+  bool Traverse##TMPLDECLKIND##TemplateDecl(TMPLDECLKIND##TemplateDecl *TD) {  \
+if (!BaseType::TraverseTemplateParameterListHelper(\
+TD->getTemplateParameters()))  \
+  return false;\
+assert(!BaseType::getDerived().shouldVisitTemplateInstantiations() &&  \
+   "It does not make sense for most clients to visit template "\
+   "instantiations here.");\
+return BaseType::getDerived().TraverseDecl(TD->getTemplatedDecl());\
+  }
+  DEF_TRAVERSE_TEMPLATE_DECL(Class)
+  DEF_TRAVERSE_TEMPLATE_DECL(Var)
+  DEF_TRAVERSE_TEMPLATE_DECL(Function)
+#undef DEF_TRAVERSE_TEMPLATE_DECL
+
 private:
   bool TraverseAdditionalLexicallyNestedDeclarations() {
 // FIXME: Ideally the gathered declarations and the declarations in the
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-27 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added a subscriber: klimek.

This affects overloaded operators, which are represented by a
CXXOperatorCallExpr whose first child is always a DeclRefExpr referring to the
operator. For infix, postfix and call operators we want the first argument
to be traversed before the operator.


https://reviews.llvm.org/D37200

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,9 +21,10 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM, bool EmitIndices)
+  const SourceManager &SM, bool EmitDeclIndices,
+  bool EmitStmtIndices)
   : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
-EmitIndices(EmitIndices) {}
+EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
@@ -33,28 +34,33 @@
   }
 
   bool VisitNamedDecl(const NamedDecl *D);
+  bool VisitDeclRefExpr(const DeclRefExpr *D);
 
 private:
   DummyMatchVisitor &Matcher;
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
   unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
 
 public:
-  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
+  DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false)
+  : EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices,
+   EmitStmtIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
 
-  void match(StringRef Path, const Decl *D) { Match(Path, D->getLocStart()); }
+  template  void match(StringRef Path, const T *D) {
+Match(Path, D->getLocStart());
+  }
 };
 
 bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) {
@@ -73,7 +79,16 @@
 if (isa(D) or isa(D))
   OS << "/";
   }
-  if (EmitIndices)
+  if (EmitDeclIndices)
+OS << "@" << Index++;
+  Matcher.match(OS.str(), D);
+  return true;
+}
+
+bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) {
+  std::string Name = D->getFoundDecl()->getNameAsString();
+  llvm::raw_string_ostream OS(Name);
+  if (EmitStmtIndices)
 OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
@@ -151,13 +166,50 @@
 template  T f();
 template  class Class {};
 )";
-  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/true);
   Visitor.ExpectMatch("/f/T@0", 2, 11);
   Visitor.ExpectMatch("/f/f/@1", 2, 20);
   Visitor.ExpectMatch("/Class/U@2", 3, 11);
   Visitor.ExpectMatch("/Class/@3", 3, 20);
   Visitor.ExpectMatch("/Class/Class/@4", 3, 34);
   EXPECT_TRUE(Visitor.runOver(Source));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) {
+  StringRef Source = R"(
+struct S {
+  S &operator+(S&);
+  S *operator->();
+  S &operator++();
+  S operator++(int);
+  void operator()(int, int);
+  void operator[](int);
+  void f();
+};
+S a, b, c;
+
+void test() {
+  a = b + c;
+  a->f();
+  a(1, 2);
+  b[0];
+  ++a;
+  b++;
+}
+)";
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false,
+/*EmitStmtIndices=*/true);
+  Visitor.ExpectMatch("a@0", 14, 3);
+  Visitor.ExpectMatch("operator=@1", 14, 5);
+  Visitor.ExpectMatch("b@2", 14, 7);
+  Visitor.ExpectMatch("operator+@3", 14, 9);
+  Visitor.ExpectMatch("c@4", 14, 11);
+  Visitor.ExpectMatch("operator->@6", 15, 4);
+  Visitor.ExpectMatch("operator()@8", 16, 4);
+  Visitor.ExpectMatch("operator[]@10", 17, 4);
+  Visitor.ExpectMatch("operator++@11", 18, 3);
+  Visitor.ExpectMatch("operator++@14", 19, 4);
+  EXPECT_TRUE(Visitor.runOver(Source));
+}
+
 } // end anonymous namespace
Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -3209,7 +3209,6 @@
 
 #undef DEF_TRAVERSE_STMT
 #undef TRAVERSE_STMT
-#undef TRAVERSE_STMT_BASE
 
 #undef TRY_TO
 
Index: include/clang/AST/L

[PATCH] D37201: [clang-diff] Use correct SourceRange for CXXConstructExpr

2017-08-27 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added a subscriber: klimek.

This way the variable name of a declaration is not included


https://reviews.llvm.org/D37201

Files:
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-html.test


Index: test/Tooling/clang-diff-html.test
===
--- test/Tooling/clang-diff-html.test
+++ test/Tooling/clang-diff-html.test
@@ -26,6 +26,20 @@
 update + move
 CHECK: 2' class='u m'>2
 
+VarDecl of same type but different variable name
+ensure that only the latter is marked changed
+CHECK: VarDecl: s
+CHECK: TypeLoc: class T
+CHECK-NEXT: T{{.*}}CXXConstructExpr
+CHECK-NEXT: CXXConstructExpr
+CHECK:  s
+CHECK: CXXTemporaryObjectExpr
+CHECK-NEXT: CXXTemporaryObjectExpr
+CHECK-NEXT: 
+CHECK-NEXT: TypeLoc: class S
+CHECK-NEXT: S
+
+
 insertion
 CHECK: 2
 
+VarDecl of same type but different variable name
+ensure that only the latter is marked changed
+CHECK: VarDecl: s
+CHECK: TypeLoc: class T
+CHECK-NEXT: T{{.*}}CXXConstructExpr
+CHECK-NEXT: CXXConstructExpr
+CHECK:  s
+CHECK: CXXTemporaryObjectExpr
+CHECK-NEXT: CXXTemporaryObjectExpr
+CHECK-NEXT: 
+CHECK-NEXT: TypeLoc: class S
+CHECK-NEXT: S
+
+
 insertion
 CHECK: 

[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-27 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112845.
johannes retitled this revision from "Add 
include/clang/Tooling/ASTDiff/ASTPatch.h" to "[clang-diff] Initial 
implementation of patching".
johannes edited the summary of this revision.
johannes added a comment.

use rewriter to patch a third AST


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTDiffTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTDiffTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -43,4 +44,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTDiffTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTDiffTest.cpp
@@ -0,0 +1,85 @@
+//===- unittest/Tooling/ASTDiffTest.cpp ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+
+static std::string patchResult(std::array Codes) {
+  diff::SyntaxTree Trees[3];
+  std::unique_ptr ASTs[3];
+  std::vector Args = {};
+  for (int I = 0; I < 3; I++) {
+ASTs[I] = buildASTFromCode(Codes[I]);
+if (!ASTs[I]) {
+  llvm::errs() << "Failed to build AST from code:\n" << Codes[I] << "\n";
+  return "";
+}
+Trees[I] = diff::SyntaxTree(*ASTs[I]);
+  }
+
+  diff::ComparisonOptions Options;
+  std::string TargetDstCode;
+  llvm::raw_string_ostream OS(TargetDstCode);
+  if (!diff::patch(/*ModelSrc=*/Trees[0], /*ModelDst=*/Trees[1],
+   /*TargetSrc=*/Trees[2], Options, OS))
+return "";
+  return OS.str();
+}
+
+// abstract the EXPECT_EQ call so that the code snippets align properly
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Preamble, ModelSrc, ModelDst, Target, Expected)  \
+  EXPECT_EQ(patchResult({{std::string(Preamble) + ModelSrc,\
+  std::string(Preamble) + ModelDst,\
+  std::string(Preamble) + Target}}),   \
+std::string(Preamble) + Expected)
+
+TEST(ASTDiff, TestDeleteArguments) {
+  PATCH(R"(void printf(const char *, ...);)",
+R"(void foo(int x) { printf("%d", x, x); })",
+R"(void foo(int x) { printf("%d", x); })",
+R"(void foo(int x) { printf("different string %d", x, x); })",
+R"(void foo(int x) { printf("different string %d", x); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo ( 1 + 1); })",
+R"(void test1() { foo ( ); })",
+R"(void test2() { foo ( 1 + 1 ); })",
+R"(void test2() { foo (  ); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2 + 2); })",
+R"(void test1() { foo (2 + 2); })",
+R"(void test2() { foo (/*L*/ 0 /*R*/ , 2 + 2); })",
+R"(void test2() { foo (/*L*/  2 + 2); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2); })",
+R"(void test1() { foo (1); })",
+R"(void test2() { foo (0, /*L*/ 0 /*R*/); })",
+R"(void test2() { foo (0 /*R*/); })");
+}
+
+TEST(ASTDiff, TestDeleteDecls) {
+  PATCH(R"()",
+R"()",
+R"()",
+R"()",
+R"()");
+
+  PATCH(R"()",
+R"(void foo(){})",
+R"()",
+R"(int x; void foo() {;;} int y;)",
+R"(int x;  int y;)");
+}
Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -42,6 +42,12 @@
   cl::desc("Output a side-by-side diff in HTML."),
   cl::init(false), cl::cat(ClangDiffCategory));
 
+static cl::opt
+Patch("patch",
+  cl::desc("Try to apply the edit actions between the two input "
+   "files to the specified target."),
+  cl::desc(""), cl::cat(ClangDiffCategory));
+
 static cl::opt SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -563,6 +569,16 @@
   }
   diff::SyntaxTree SrcTree(*Src);
   diff::Syntax

[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h:75
 
+  Derived &getDerived() { return *static_cast(this); }
+

arphaman wrote:
> I don't think you need this since `getDerived` in RecursiveASTVisitor is 
> already public.
`has_same_member_pointer` and `getDerived` are used by the TRAVERSE_STMT macro, 
which should be the right one to use here.
Also the `RecursiveASTVisitor` typedef just above.



Comment at: include/clang/AST/RecursiveASTVisitor.h:3212
 #undef TRAVERSE_STMT
-#undef TRAVERSE_STMT_BASE
 

arphaman wrote:
> Getting rid of `undef` is not ideal. You might want to extract these macros 
> into one .def file that's included by both RecursiveASTVisitor.h and the 
> LexicallyOrdered one.
Ok, I will do that


https://reviews.llvm.org/D37200



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112885.
johannes added a comment.

use RecursiveASTVisitor::TraverseStmt


https://reviews.llvm.org/D37200

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,9 +21,10 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM, bool EmitIndices)
+  const SourceManager &SM, bool EmitDeclIndices,
+  bool EmitStmtIndices)
   : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
-EmitIndices(EmitIndices) {}
+EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
@@ -33,28 +34,33 @@
   }
 
   bool VisitNamedDecl(const NamedDecl *D);
+  bool VisitDeclRefExpr(const DeclRefExpr *D);
 
 private:
   DummyMatchVisitor &Matcher;
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
   unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
 
 public:
-  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
+  DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false)
+  : EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices,
+   EmitStmtIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
 
-  void match(StringRef Path, const Decl *D) { Match(Path, D->getLocStart()); }
+  template  void match(StringRef Path, const T *D) {
+Match(Path, D->getLocStart());
+  }
 };
 
 bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) {
@@ -73,7 +79,16 @@
 if (isa(D) or isa(D))
   OS << "/";
   }
-  if (EmitIndices)
+  if (EmitDeclIndices)
+OS << "@" << Index++;
+  Matcher.match(OS.str(), D);
+  return true;
+}
+
+bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) {
+  std::string Name = D->getFoundDecl()->getNameAsString();
+  llvm::raw_string_ostream OS(Name);
+  if (EmitStmtIndices)
 OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
@@ -151,13 +166,50 @@
 template  T f();
 template  class Class {};
 )";
-  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/true);
   Visitor.ExpectMatch("/f/T@0", 2, 11);
   Visitor.ExpectMatch("/f/f/@1", 2, 20);
   Visitor.ExpectMatch("/Class/U@2", 3, 11);
   Visitor.ExpectMatch("/Class/@3", 3, 20);
   Visitor.ExpectMatch("/Class/Class/@4", 3, 34);
   EXPECT_TRUE(Visitor.runOver(Source));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) {
+  StringRef Source = R"(
+struct S {
+  S &operator+(S&);
+  S *operator->();
+  S &operator++();
+  S operator++(int);
+  void operator()(int, int);
+  void operator[](int);
+  void f();
+};
+S a, b, c;
+
+void test() {
+  a = b + c;
+  a->f();
+  a(1, 2);
+  b[0];
+  ++a;
+  b++;
+}
+)";
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false,
+/*EmitStmtIndices=*/true);
+  Visitor.ExpectMatch("a@0", 14, 3);
+  Visitor.ExpectMatch("operator=@1", 14, 5);
+  Visitor.ExpectMatch("b@2", 14, 7);
+  Visitor.ExpectMatch("operator+@3", 14, 9);
+  Visitor.ExpectMatch("c@4", 14, 11);
+  Visitor.ExpectMatch("operator->@6", 15, 4);
+  Visitor.ExpectMatch("operator()@8", 16, 4);
+  Visitor.ExpectMatch("operator[]@10", 17, 4);
+  Visitor.ExpectMatch("operator++@11", 18, 3);
+  Visitor.ExpectMatch("operator++@14", 19, 4);
+  EXPECT_TRUE(Visitor.runOver(Source));
+}
+
 } // end anonymous namespace
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -60,6 +60,7 @@
 class LexicallyOrderedRecursiveASTVisitor
 : public RecursiveASTVisitor {
   using BaseType = RecursiveASTVisitor;
+  using typename BaseType::DataRecursionQueue;
 
 public:
   LexicallyOrderedRecursiveASTVisitor(const SourceManager &SM) : SM(SM) {}
@@ -126,6 +127,36 @@
   DEF_TRAVERSE_TEMPLATE_DECL(Function)
 #undef DEF_T

[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112889.
johannes added a comment.

do call derived TraverseStmt for children of CXXOperatorCallExpr


https://reviews.llvm.org/D37200

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,42 +21,55 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM, bool EmitIndices)
+  const SourceManager &SM, bool EmitDeclIndices,
+  bool EmitStmtIndices)
   : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
-EmitIndices(EmitIndices) {}
+EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
 LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
 TraversalStack.pop_back();
 return true;
   }
 
+  bool TraverseStmt(Stmt *S);
+
   bool VisitNamedDecl(const NamedDecl *D);
+  bool VisitDeclRefExpr(const DeclRefExpr *D);
 
 private:
   DummyMatchVisitor &Matcher;
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
   unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
 
 public:
-  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
+  DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false)
+  : EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices,
+   EmitStmtIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
 
-  void match(StringRef Path, const Decl *D) { Match(Path, D->getLocStart()); }
+  template  void match(StringRef Path, const T *D) {
+Match(Path, D->getLocStart());
+  }
 };
 
+bool LexicallyOrderedDeclVisitor::TraverseStmt(Stmt *S) {
+  Matcher.match("overridden TraverseStmt", S);
+  return LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S);
+}
+
 bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) {
   std::string Path;
   llvm::raw_string_ostream OS(Path);
@@ -73,7 +86,16 @@
 if (isa(D) or isa(D))
   OS << "/";
   }
-  if (EmitIndices)
+  if (EmitDeclIndices)
+OS << "@" << Index++;
+  Matcher.match(OS.str(), D);
+  return true;
+}
+
+bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) {
+  std::string Name = D->getFoundDecl()->getNameAsString();
+  llvm::raw_string_ostream OS(Name);
+  if (EmitStmtIndices)
 OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
@@ -151,13 +173,55 @@
 template  T f();
 template  class Class {};
 )";
-  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/true);
   Visitor.ExpectMatch("/f/T@0", 2, 11);
   Visitor.ExpectMatch("/f/f/@1", 2, 20);
   Visitor.ExpectMatch("/Class/U@2", 3, 11);
   Visitor.ExpectMatch("/Class/@3", 3, 20);
   Visitor.ExpectMatch("/Class/Class/@4", 3, 34);
   EXPECT_TRUE(Visitor.runOver(Source));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) {
+  StringRef Source = R"(
+struct S {
+  S &operator+(S&);
+  S *operator->();
+  S &operator++();
+  S operator++(int);
+  void operator()(int, int);
+  void operator[](int);
+  void f();
+};
+S a, b, c;
+
+void test() {
+  a = b + c;
+  a->f();
+  a(1, 2);
+  b[0];
+  ++a;
+  b++;
+}
+)";
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false,
+/*EmitStmtIndices=*/true);
+  // There are two overloaded operators that start at this point
+  // This makes sure they are both traversed using the overridden
+  // TraverseStmt, as the traversal is implemented by us for
+  // CXXOperatorCallExpr.
+  Visitor.ExpectMatch("overridden TraverseStmt", 14, 3, 2);
+  Visitor.ExpectMatch("a@0", 14, 3);
+  Visitor.ExpectMatch("operator=@1", 14, 5);
+  Visitor.ExpectMatch("b@2", 14, 7);
+  Visitor.ExpectMatch("operator+@3", 14, 9);
+  Visitor.ExpectMatch("c@4", 14, 11);
+  Visitor.ExpectMatch("operator->@6", 15, 4);
+  Visitor.ExpectMatch("operator()@8", 16, 4);
+  Visitor.ExpectMatch("operator[]@10", 17, 4);
+  Visitor.ExpectMatch("operator++@11", 18, 3);
+  Visitor.E

[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

The previous version didn't call TraverseDecl of the derived class, this is 
fixed now.
The public getDerived.TraverseStmt() does not accept a DataRecursionQueue, so 
this also could not be used (I think)
 I used the wrapper TraverseStmtBase, which should behave exactly as the method 
that originally traverses CXXOperatorCallExpr




Comment at: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h:63
   using BaseType = RecursiveASTVisitor;
+  using typename BaseType::DataRecursionQueue;
 

arphaman wrote:
> Do you still need the using here?
removed it now as it's only used once


https://reviews.llvm.org/D37200



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112894.
johannes added a comment.

detect prefix/postfix from number of arguments


https://reviews.llvm.org/D37200

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,42 +21,55 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM, bool EmitIndices)
+  const SourceManager &SM, bool EmitDeclIndices,
+  bool EmitStmtIndices)
   : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
-EmitIndices(EmitIndices) {}
+EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
 LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
 TraversalStack.pop_back();
 return true;
   }
 
+  bool TraverseStmt(Stmt *S);
+
   bool VisitNamedDecl(const NamedDecl *D);
+  bool VisitDeclRefExpr(const DeclRefExpr *D);
 
 private:
   DummyMatchVisitor &Matcher;
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
   unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
 
 public:
-  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
+  DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false)
+  : EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices,
+   EmitStmtIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
 
-  void match(StringRef Path, const Decl *D) { Match(Path, D->getLocStart()); }
+  template  void match(StringRef Path, const T *D) {
+Match(Path, D->getLocStart());
+  }
 };
 
+bool LexicallyOrderedDeclVisitor::TraverseStmt(Stmt *S) {
+  Matcher.match("overridden TraverseStmt", S);
+  return LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S);
+}
+
 bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) {
   std::string Path;
   llvm::raw_string_ostream OS(Path);
@@ -73,7 +86,16 @@
 if (isa(D) or isa(D))
   OS << "/";
   }
-  if (EmitIndices)
+  if (EmitDeclIndices)
+OS << "@" << Index++;
+  Matcher.match(OS.str(), D);
+  return true;
+}
+
+bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) {
+  std::string Name = D->getFoundDecl()->getNameAsString();
+  llvm::raw_string_ostream OS(Name);
+  if (EmitStmtIndices)
 OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
@@ -151,13 +173,55 @@
 template  T f();
 template  class Class {};
 )";
-  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/true);
   Visitor.ExpectMatch("/f/T@0", 2, 11);
   Visitor.ExpectMatch("/f/f/@1", 2, 20);
   Visitor.ExpectMatch("/Class/U@2", 3, 11);
   Visitor.ExpectMatch("/Class/@3", 3, 20);
   Visitor.ExpectMatch("/Class/Class/@4", 3, 34);
   EXPECT_TRUE(Visitor.runOver(Source));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) {
+  StringRef Source = R"(
+struct S {
+  S &operator+(S&);
+  S *operator->();
+  S &operator++();
+  S operator++(int);
+  void operator()(int, int);
+  void operator[](int);
+  void f();
+};
+S a, b, c;
+
+void test() {
+  a = b + c;
+  a->f();
+  a(1, 2);
+  b[0];
+  ++a;
+  b++;
+}
+)";
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false,
+/*EmitStmtIndices=*/true);
+  // There are two overloaded operators that start at this point
+  // This makes sure they are both traversed using the overridden
+  // TraverseStmt, as the traversal is implemented by us for
+  // CXXOperatorCallExpr.
+  Visitor.ExpectMatch("overridden TraverseStmt", 14, 3, 2);
+  Visitor.ExpectMatch("a@0", 14, 3);
+  Visitor.ExpectMatch("operator=@1", 14, 5);
+  Visitor.ExpectMatch("b@2", 14, 7);
+  Visitor.ExpectMatch("operator+@3", 14, 9);
+  Visitor.ExpectMatch("c@4", 14, 11);
+  Visitor.ExpectMatch("operator->@6", 15, 4);
+  Visitor.ExpectMatch("operator()@8", 16, 4);
+  Visitor.ExpectMatch("operator[]@10", 17, 4);
+  Visitor.ExpectMatch("operator++@11", 18, 3);
+  Visitor.ExpectMatch("operat

[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112926.
johannes edited the summary of this revision.
johannes added a comment.

split to ASTDiff/ASTPatch


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTPatch.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/ASTPatch.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  test/Tooling/clang-diff-patch.test
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTDiffTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTDiffTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -43,4 +44,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTDiffTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTDiffTest.cpp
@@ -0,0 +1,86 @@
+//===- unittest/Tooling/ASTDiffTest.cpp ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+
+static std::string patchResult(std::array Codes) {
+  diff::SyntaxTree Trees[3];
+  std::unique_ptr ASTs[3];
+  std::vector Args = {};
+  for (int I = 0; I < 3; I++) {
+ASTs[I] = buildASTFromCode(Codes[I]);
+if (!ASTs[I]) {
+  llvm::errs() << "Failed to build AST from code:\n" << Codes[I] << "\n";
+  return "";
+}
+Trees[I] = diff::SyntaxTree(*ASTs[I]);
+  }
+
+  diff::ComparisonOptions Options;
+  std::string TargetDstCode;
+  llvm::raw_string_ostream OS(TargetDstCode);
+  if (!diff::patch(/*ModelSrc=*/Trees[0], /*ModelDst=*/Trees[1],
+   /*TargetSrc=*/Trees[2], Options, OS))
+return "";
+  return OS.str();
+}
+
+// abstract the EXPECT_EQ call so that the code snippets align properly
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Preamble, ModelSrc, ModelDst, Target, Expected)  \
+  EXPECT_EQ(patchResult({{std::string(Preamble) + ModelSrc,\
+  std::string(Preamble) + ModelDst,\
+  std::string(Preamble) + Target}}),   \
+std::string(Preamble) + Expected)
+
+TEST(ASTDiff, TestDeleteArguments) {
+  PATCH(R"(void printf(const char *, ...);)",
+R"(void foo(int x) { printf("%d", x, x); })",
+R"(void foo(int x) { printf("%d", x); })",
+R"(void foo(int x) { printf("different string %d", x, x); })",
+R"(void foo(int x) { printf("different string %d", x); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo ( 1 + 1); })",
+R"(void test1() { foo ( ); })",
+R"(void test2() { foo ( 1 + 1 ); })",
+R"(void test2() { foo (  ); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2 + 2); })",
+R"(void test1() { foo (2 + 2); })",
+R"(void test2() { foo (/*L*/ 0 /*R*/ , 2 + 2); })",
+R"(void test2() { foo (/*L*/  2 + 2); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2); })",
+R"(void test1() { foo (1); })",
+R"(void test2() { foo (0, /*L*/ 0 /*R*/); })",
+R"(void test2() { foo (0 /*R*/); })");
+}
+
+TEST(ASTDiff, TestDeleteDecls) {
+  PATCH(R"()",
+R"()",
+R"()",
+R"()",
+R"()");
+
+  PATCH(R"()",
+R"(void foo(){})",
+R"()",
+R"(int x; void foo() {;;} int y;)",
+R"(int x;  int y;)");
+}
Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -13,6 +13,7 @@
 //===--===//
 
 #include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/CommandLine.h"
@@ -42,6 +43,12 @@
   cl::desc("Output a side-by-side diff in HTML."),
   cl::init(false), cl::cat(ClangDiffCategory));
 
+static cl::opt
+Patch("patch",
+  cl::desc("Try to apply the edit actions between the two input "
+   "files to the specifi

[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 112951.
johannes added a comment.

use a specialized getStmtChildren to fix the order for CXXOperatorCallExpr


https://reviews.llvm.org/D37200

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,42 +21,55 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM, bool EmitIndices)
+  const SourceManager &SM, bool EmitDeclIndices,
+  bool EmitStmtIndices)
   : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
-EmitIndices(EmitIndices) {}
+EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
 LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
 TraversalStack.pop_back();
 return true;
   }
 
+  bool TraverseStmt(Stmt *S);
+
   bool VisitNamedDecl(const NamedDecl *D);
+  bool VisitDeclRefExpr(const DeclRefExpr *D);
 
 private:
   DummyMatchVisitor &Matcher;
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
   unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
-  bool EmitIndices;
+  bool EmitDeclIndices, EmitStmtIndices;
 
 public:
-  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
+  DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false)
+  : EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices,
+   EmitStmtIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
 
-  void match(StringRef Path, const Decl *D) { Match(Path, D->getLocStart()); }
+  template  void match(StringRef Path, const T *D) {
+Match(Path, D->getLocStart());
+  }
 };
 
+bool LexicallyOrderedDeclVisitor::TraverseStmt(Stmt *S) {
+  Matcher.match("overridden TraverseStmt", S);
+  return LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S);
+}
+
 bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) {
   std::string Path;
   llvm::raw_string_ostream OS(Path);
@@ -73,7 +86,16 @@
 if (isa(D) or isa(D))
   OS << "/";
   }
-  if (EmitIndices)
+  if (EmitDeclIndices)
+OS << "@" << Index++;
+  Matcher.match(OS.str(), D);
+  return true;
+}
+
+bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) {
+  std::string Name = D->getFoundDecl()->getNameAsString();
+  llvm::raw_string_ostream OS(Name);
+  if (EmitStmtIndices)
 OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
@@ -151,13 +173,55 @@
 template  T f();
 template  class Class {};
 )";
-  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/true);
   Visitor.ExpectMatch("/f/T@0", 2, 11);
   Visitor.ExpectMatch("/f/f/@1", 2, 20);
   Visitor.ExpectMatch("/Class/U@2", 3, 11);
   Visitor.ExpectMatch("/Class/@3", 3, 20);
   Visitor.ExpectMatch("/Class/Class/@4", 3, 34);
   EXPECT_TRUE(Visitor.runOver(Source));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) {
+  StringRef Source = R"(
+struct S {
+  S &operator+(S&);
+  S *operator->();
+  S &operator++();
+  S operator++(int);
+  void operator()(int, int);
+  void operator[](int);
+  void f();
+};
+S a, b, c;
+
+void test() {
+  a = b + c;
+  a->f();
+  a(1, 2);
+  b[0];
+  ++a;
+  b++;
+}
+)";
+  DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false,
+/*EmitStmtIndices=*/true);
+  // There are two overloaded operators that start at this point
+  // This makes sure they are both traversed using the overridden
+  // TraverseStmt, as the traversal is implemented by us for
+  // CXXOperatorCallExpr.
+  Visitor.ExpectMatch("overridden TraverseStmt", 14, 3, 2);
+  Visitor.ExpectMatch("a@0", 14, 3);
+  Visitor.ExpectMatch("operator=@1", 14, 5);
+  Visitor.ExpectMatch("b@2", 14, 7);
+  Visitor.ExpectMatch("operator+@3", 14, 9);
+  Visitor.ExpectMatch("c@4", 14, 11);
+  Visitor.ExpectMatch("operator->@6", 15, 4);
+  Visitor.ExpectMatch("operator()@8", 16, 4);
+  Visitor.ExpectMatch("operator[]@10", 17, 4);
+  Visitor.ExpectMatch("operator++@11", 18, 3);
+ 

[PATCH] D37200: [AST] Traverse CXXOperatorCallExpr in LexicallyOrderedRecursiveASTVisitor

2017-08-28 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

Yeah, this seems to be the best solution for this. I think I ran into the same 
issue when working on the StmtDataCollector - basically there can only be two 
Traverse*, one in the Derived class and the other one needs to do all the magic 
with walking the specialisation hierarchy.


https://reviews.llvm.org/D37200



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36998: [AST] Traverse templates in LexicallyOrderedRecursiveASTVisitor

2017-08-29 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

This is currently broken, if a user provides a TraverseClassTemplateDecl, then 
the same method in this class will not be called, I think I will add a flag 
(probably not user visible) in RecursiveASTVisitor.h to switch the order for 
templates


https://reviews.llvm.org/D36998



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-29 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 113078.
johannes added a comment.

fixes


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTPatch.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  lib/Tooling/ASTDiff/ASTPatch.cpp
  lib/Tooling/ASTDiff/CMakeLists.txt
  test/Tooling/clang-diff-patch.test
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp
  unittests/Tooling/ASTDiffTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTDiffTest.cpp
   ASTSelectionTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
@@ -43,4 +44,5 @@
   clangTooling
   clangToolingCore
   clangToolingRefactor
+  clangToolingASTDiff
   )
Index: unittests/Tooling/ASTDiffTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ASTDiffTest.cpp
@@ -0,0 +1,85 @@
+//===- unittest/Tooling/ASTDiffTest.cpp ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+
+static std::string patchResult(std::array Codes) {
+  llvm::Optional Trees[3];
+  std::unique_ptr ASTs[3];
+  for (int I = 0; I < 3; I++) {
+ASTs[I] = buildASTFromCode(Codes[I]);
+if (!ASTs[I]) {
+  llvm::errs() << "Failed to build AST from code:\n" << Codes[I] << "\n";
+  return "";
+}
+Trees[I].emplace(*ASTs[I]);
+  }
+
+  diff::ComparisonOptions Options;
+  std::string TargetDstCode;
+  llvm::raw_string_ostream OS(TargetDstCode);
+  if (!diff::patch(/*ModelSrc=*/*Trees[0], /*ModelDst=*/*Trees[1],
+   /*TargetSrc=*/*Trees[2], Options, OS))
+return "";
+  return OS.str();
+}
+
+// abstract the EXPECT_EQ call so that the code snippets align properly
+// use macros for this to make test failures have proper line numbers
+#define PATCH(Preamble, ModelSrc, ModelDst, Target, Expected)  \
+  EXPECT_EQ(patchResult({{std::string(Preamble) + ModelSrc,\
+  std::string(Preamble) + ModelDst,\
+  std::string(Preamble) + Target}}),   \
+std::string(Preamble) + Expected)
+
+TEST(ASTDiff, TestDeleteArguments) {
+  PATCH(R"(void printf(const char *, ...);)",
+R"(void foo(int x) { printf("%d", x, x); })",
+R"(void foo(int x) { printf("%d", x); })",
+R"(void foo(int x) { printf("different string %d", x, x); })",
+R"(void foo(int x) { printf("different string %d", x); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo ( 1 + 1); })",
+R"(void test1() { foo ( ); })",
+R"(void test2() { foo ( 1 + 1 ); })",
+R"(void test2() { foo (  ); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2 + 2); })",
+R"(void test1() { foo (2 + 2); })",
+R"(void test2() { foo (/*L*/ 0 /*R*/ , 2 + 2); })",
+R"(void test2() { foo (/*L*/  2 + 2); })");
+
+  PATCH(R"(void foo(...);)",
+R"(void test1() { foo (1, 2); })",
+R"(void test1() { foo (1); })",
+R"(void test2() { foo (0, /*L*/ 0 /*R*/); })",
+R"(void test2() { foo (0 /*R*/); })");
+}
+
+TEST(ASTDiff, TestDeleteDecls) {
+  PATCH(R"()",
+R"()",
+R"()",
+R"()",
+R"()");
+
+  PATCH(R"()",
+R"(void foo(){})",
+R"()",
+R"(int x; void foo() {;;} int y;)",
+R"(int x;  int y;)");
+}
Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -13,6 +13,7 @@
 //===--===//
 
 #include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/CommandLine.h"
@@ -42,6 +43,12 @@
   cl::desc("Output a side-by-side diff in HTML."),
   cl::init(false), cl::cat(ClangDiffCategory));
 
+static cl::opt
+Patch("patch",
+  cl::desc("Try to apply the edit actions between the two input "
+   "files to the specified target."),
+  cl::desc(""), cl::cat(ClangDiffCategory));
+
 static cl::opt SourcePath(cl::P

[PATCH] D37005: [clang-diff] Initial implementation of patching

2017-08-29 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:73
 public:
+  /// Empty (invalid) SyntaxTree.
+  SyntaxTree();

arphaman wrote:
> Why do you need to create an empty tree? What about using llvm::Optional 
> instead?
ok, i use optional now instead in the unittest


https://reviews.llvm.org/D37005



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37001: [clang-diff] Use data collectors for node comparison

2017-09-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:537
+
+#include "../../AST/DeclDataCollectors.inc"
+

arphaman wrote:
> I didn't realize that you're including files from within `lib`. That's not 
> ideal. You should add a pre-commit that moves the *.inc files over to 
> include/AST (and marks them as textual in Clang's `module.modulemap`). Then 
> you should include them using the clang/AST path.
I tried moving StmtDataCollectors.inc to and adding
`+  textual header "AST/StmtDataCollectors.inc" `
in the modulemap, then I get some CMake errors, not sure why.

It might make sense to tablegen it like the other .inc files.


https://reviews.llvm.org/D37001



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36998: [AST] Traverse templates in LexicallyOrderedRecursiveASTVisitor

2017-09-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 113546.
johannes added a comment.

fix by adding an option in RecursiveASTVisitor


https://reviews.llvm.org/D36998

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,8 +21,9 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM)
-  : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher) {}
+  const SourceManager &SM, bool EmitIndices)
+  : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
+EmitIndices(EmitIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
@@ -35,15 +36,20 @@
 
 private:
   DummyMatchVisitor &Matcher;
+  bool EmitIndices;
+  unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
+  bool EmitIndices;
+
 public:
+  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
@@ -64,9 +70,11 @@
   OS << ND->getNameAsString();
 else
   OS << "???";
-if (isa(D))
+if (isa(D) or isa(D))
   OS << "/";
   }
+  if (EmitIndices)
+OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
 }
@@ -138,4 +146,18 @@
   EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitTemplateDecl) {
+  StringRef Source = R"(
+template  T f();
+template  class Class {};
+)";
+  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  Visitor.ExpectMatch("/f/T@1", 2, 11);
+  Visitor.ExpectMatch("/f/f/@2", 2, 20);
+  Visitor.ExpectMatch("/Class/U@4", 3, 11);
+  Visitor.ExpectMatch("/Class/@5", 3, 20);
+  Visitor.ExpectMatch("/Class/Class/@6", 3, 34);
+  EXPECT_TRUE(Visitor.runOver(Source));
+}
+
 } // end anonymous namespace
Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -499,10 +499,10 @@
 
   bool canIgnoreChildDeclWhileTraversingDeclContext(const Decl *Child);
 
-private:
   // These are helper methods used by more than one Traverse* method.
   bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
 
+private:
   // Traverses template parameter lists of either a DeclaratorDecl or TagDecl.
   template 
   bool TraverseDeclTemplateParameterLists(T *D);
@@ -535,6 +535,7 @@
 
   bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
   bool PostVisitStmt(Stmt *S);
+  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return false; }
 };
 
 template 
@@ -1688,8 +1689,13 @@
 // template declarations.
 #define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND)   \
   DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, {  \
-TRY_TO(TraverseDecl(D->getTemplatedDecl()));   \
-TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));   \
+if (getDerived().shouldTraverseTemplateArgumentsBeforeDecl()) {\
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \
+  TRY_TO(TraverseDecl(D->getTemplatedDecl())); \
+} else {   \
+  TRY_TO(TraverseDecl(D->getTemplatedDecl())); \
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \
+}  \
\
 /* By default, we do not traverse the instantiations of\
class templates since they do not appear in the user code. The  \
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,6 +111,8 @@
 return true;
   }
 
+  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return

[PATCH] D37383: [AST] Add TableGen for StmtDataCollectors

2017-09-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added a subscriber: mgorny.
Herald added 1 blocking reviewer(s): teemperor.

This adds an option "-gen-clang-data-collectors" to the Clang TableGen
that is used to generate StmtDataCollectors.inc.


https://reviews.llvm.org/D37383

Files:
  include/clang/AST/CMakeLists.txt
  include/clang/AST/StmtDataCollectors.td
  lib/AST/StmtDataCollectors.inc
  lib/Analysis/CloneDetection.cpp
  unittests/AST/DataCollectionTest.cpp
  utils/TableGen/CMakeLists.txt
  utils/TableGen/ClangDataCollectorsEmitter.cpp
  utils/TableGen/TableGen.cpp
  utils/TableGen/TableGenBackends.h

Index: utils/TableGen/TableGenBackends.h
===
--- utils/TableGen/TableGenBackends.h
+++ utils/TableGen/TableGenBackends.h
@@ -75,6 +75,8 @@
 void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS);
 
+void EmitClangDataCollectors(RecordKeeper &Records, raw_ostream &OS);
+
 void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records,
 raw_ostream &OS);
 
Index: utils/TableGen/TableGen.cpp
===
--- utils/TableGen/TableGen.cpp
+++ utils/TableGen/TableGen.cpp
@@ -57,6 +57,7 @@
   GenAttrDocs,
   GenDiagDocs,
   GenOptDocs,
+  GenDataCollectors,
   GenTestPragmaAttributeSupportedAttributes
 };
 
@@ -147,6 +148,7 @@
 clEnumValN(GenDiagDocs, "gen-diag-docs",
"Generate diagnostic documentation"),
 clEnumValN(GenOptDocs, "gen-opt-docs", "Generate option documentation"),
+clEnumValN(GenDataCollectors, "gen-clang-data-collectors", "Generate data collectors for AST nodes"),
 clEnumValN(GenTestPragmaAttributeSupportedAttributes,
"gen-clang-test-pragma-attribute-supported-attributes",
"Generate a list of attributes supported by #pragma clang "
@@ -262,6 +264,9 @@
   case GenOptDocs:
 EmitClangOptDocs(Records, OS);
 break;
+  case GenDataCollectors:
+EmitClangDataCollectors(Records, OS);
+break;
   case GenTestPragmaAttributeSupportedAttributes:
 EmitTestPragmaAttributeSupportedAttributes(Records, OS);
 break;
Index: utils/TableGen/ClangDataCollectorsEmitter.cpp
===
--- /dev/null
+++ utils/TableGen/ClangDataCollectorsEmitter.cpp
@@ -0,0 +1,18 @@
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+using namespace llvm;
+
+namespace clang {
+void EmitClangDataCollectors(RecordKeeper &RK, raw_ostream &OS) {
+  const auto &Defs = RK.getClasses();
+  for (const auto &Entry : Defs) {
+Record &R = *Entry.second;
+OS << "DEF_ADD_DATA(" << R.getName() << ", {";
+auto Code = R.getValue("Code")->getValue();
+OS << Code->getAsUnquotedString() << "}\n)";
+OS << "\n";
+  }
+  OS << "#undef DEF_ADD_DATA\n";
+}
+} // end namespace clang
Index: utils/TableGen/CMakeLists.txt
===
--- utils/TableGen/CMakeLists.txt
+++ utils/TableGen/CMakeLists.txt
@@ -6,6 +6,7 @@
   ClangCommentCommandInfoEmitter.cpp
   ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
   ClangCommentHTMLTagsEmitter.cpp
+  ClangDataCollectorsEmitter.cpp
   ClangDiagnosticsEmitter.cpp
   ClangOptionDocEmitter.cpp
   ClangSACheckersEmitter.cpp
Index: unittests/AST/DataCollectionTest.cpp
===
--- unittests/AST/DataCollectionTest.cpp
+++ unittests/AST/DataCollectionTest.cpp
@@ -46,7 +46,7 @@
 ConstStmtVisitor::Visit##CLASS(S);  \
   }
 
-#include "../../lib/AST/StmtDataCollectors.inc"
+#include "clang/AST/StmtDataCollectors.inc"
 };
 } // end anonymous namespace
 
Index: lib/Analysis/CloneDetection.cpp
===
--- lib/Analysis/CloneDetection.cpp
+++ lib/Analysis/CloneDetection.cpp
@@ -205,7 +205,7 @@
 ConstStmtVisitor>::Visit##CLASS(S);\
   }
 
-#include "../AST/StmtDataCollectors.inc"
+#include "clang/AST/StmtDataCollectors.inc"
 
 // Type II clones ignore variable names and literals, so let's skip them.
 #define SKIP(CLASS)\
Index: lib/AST/StmtDataCollectors.inc
===
--- lib/AST/StmtDataCollectors.inc
+++ /dev/null
@@ -1,141 +0,0 @@
-// The functions below collect the class specific data of each Stmt subclass.
-
-DEF_ADD_DATA(Stmt, {
-  addData(S->getStmtClass());
-  // This ensures that non-macro-generated code isn't identical to
-  // macro-generated code.
-  addData(data_collection::getMacroStack(S->getLocStart(), Context));
-  addData(data_collection::getMacroStack(S->getLocEnd(), Context));
-})
-DEF_ADD_DATA(Expr, { addData(S->getType()); })
-
-//--- Builtin 

[PATCH] D37383: [AST] Add TableGen for StmtDataCollectors

2017-09-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D37383#858905, @teemperor wrote:

> @arphaman I suggested tablegen in https://reviews.llvm.org/D36664 because I 
> remembered we had some CMake sanity check about not having an *.inc in our 
> include dir: 
> https://github.com/llvm-mirror/clang/blob/master/CMakeLists.txt#L266 Not sure 
> if it actually fires for our case, but it looks like it does.


Yes, that was the issue why it didn't work under include/. I think the sanity 
check could be removed, since in-source builds are forbidden anyway.
Or maybe it is still useful by telling us that .inc is probably an generated 
file (most, but not all of them are generated, it seems), so another solution 
would be to just change the file extension. Actually, `.def` might be the right 
extension.

Shall we move to using TableGen only when we need it, and rename it to `.def` 
for now?
A preprocessor-only solution for the two modes could look like this:

user:

  #define DERIVED MyStmtVisitor
  #define SINGLE_TU
  #include "clang/AST/StmtDataCollectors.def"

StmtDataCollectors.def:

  #define DEF_VISIT(CLASS, CODE)\
  template  void Visit##CLASS(const CLASS *S) {\
CODE;\
ConstStmtVisitor<##DERIVED>::Visit##CLASS(S)\
  }
  
  #ifdef SINGLE_TU
  #define DEF_ADD_DATA(CLASS, COMMON, SINGLE, CROSS) DEF_VISIT(CLASS, COMMON; 
SINGLE)
  #else
  #define DEF_ADD_DATA(CLASS, COMMON, SINGLE, CROSS) DEF_VISIT(CLASS, COMMON, 
CROSS)
  #endif
  
  DEF_ADD_DATA(Stmt, .., .., ..)
  ...

However, I think TableGen has an advantage when we can extract some structure 
instead of just storing the code. Basically, a list of primitive and one of 
pointer members for each class could simplify things.


https://reviews.llvm.org/D37383



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36998: [AST] Traverse templates in LexicallyOrderedRecursiveASTVisitor

2017-09-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 113581.
johannes added a comment.

undo visibility change


https://reviews.llvm.org/D36998

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp

Index: unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
===
--- unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
+++ unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp
@@ -21,8 +21,9 @@
 : public LexicallyOrderedRecursiveASTVisitor {
 public:
   LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher,
-  const SourceManager &SM)
-  : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher) {}
+  const SourceManager &SM, bool EmitIndices)
+  : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher),
+EmitIndices(EmitIndices) {}
 
   bool TraverseDecl(Decl *D) {
 TraversalStack.push_back(D);
@@ -35,15 +36,20 @@
 
 private:
   DummyMatchVisitor &Matcher;
+  bool EmitIndices;
+  unsigned Index = 0;
   llvm::SmallVector TraversalStack;
 };
 
 class DummyMatchVisitor : public ExpectedLocationVisitor {
+  bool EmitIndices;
+
 public:
+  DummyMatchVisitor(bool EmitIndices = false) : EmitIndices(EmitIndices) {}
   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) {
 const ASTContext &Context = TU->getASTContext();
 const SourceManager &SM = Context.getSourceManager();
-LexicallyOrderedDeclVisitor SubVisitor(*this, SM);
+LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitIndices);
 SubVisitor.TraverseDecl(TU);
 return false;
   }
@@ -64,9 +70,11 @@
   OS << ND->getNameAsString();
 else
   OS << "???";
-if (isa(D))
+if (isa(D) or isa(D))
   OS << "/";
   }
+  if (EmitIndices)
+OS << "@" << Index++;
   Matcher.match(OS.str(), D);
   return true;
 }
@@ -138,4 +146,18 @@
   EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC));
 }
 
+TEST(LexicallyOrderedRecursiveASTVisitor, VisitTemplateDecl) {
+  StringRef Source = R"(
+template  T f();
+template  class Class {};
+)";
+  DummyMatchVisitor Visitor(/*EmitIndices=*/true);
+  Visitor.ExpectMatch("/f/T@1", 2, 11);
+  Visitor.ExpectMatch("/f/f/@2", 2, 20);
+  Visitor.ExpectMatch("/Class/U@4", 3, 11);
+  Visitor.ExpectMatch("/Class/@5", 3, 20);
+  Visitor.ExpectMatch("/Class/Class/@6", 3, 34);
+  EXPECT_TRUE(Visitor.runOver(Source));
+}
+
 } // end anonymous namespace
Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -535,6 +535,7 @@
 
   bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
   bool PostVisitStmt(Stmt *S);
+  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return false; }
 };
 
 template 
@@ -1688,8 +1689,13 @@
 // template declarations.
 #define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND)   \
   DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, {  \
-TRY_TO(TraverseDecl(D->getTemplatedDecl()));   \
-TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));   \
+if (getDerived().shouldTraverseTemplateArgumentsBeforeDecl()) {\
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \
+  TRY_TO(TraverseDecl(D->getTemplatedDecl())); \
+} else {   \
+  TRY_TO(TraverseDecl(D->getTemplatedDecl())); \
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \
+}  \
\
 /* By default, we do not traverse the instantiations of\
class templates since they do not appear in the user code. The  \
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,6 +111,8 @@
 return true;
   }
 
+  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return true; }
+
 private:
   bool TraverseAdditionalLexicallyNestedDeclarations() {
 // FIXME: Ideally the gathered declarations and the declarations in the
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37383: [AST] Add TableGen for StmtDataCollectors

2017-09-06 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

@teemperor ok for you? did phabricator make you a blocking reviewer because of 
the affected code, or did I do that somehow?


https://reviews.llvm.org/D37383



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37662: [AST] Make RecursiveASTVisitor visit TemplateDecls in source order

2017-09-09 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.

This causes template arguments to be traversed before the templated
declaration, which is useful for clients that expect the nodes in
the same order as they are in the source code. Additionally, there
seems to be no good reason not to do so.

This was moved here from LexicallyOrderedRecursiveASTVisitor. The tests
still reside in LexicallyOrderedRecursiveASTVisitorTest.cpp under
VisitTemplateDecls.


https://reviews.llvm.org/D37662

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h


Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -537,7 +537,6 @@
 
   bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
   bool PostVisitStmt(Stmt *S);
-  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return false; }
 };
 
 template 
@@ -1691,13 +1690,8 @@
 // template declarations.
 #define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND)   
\
   DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, {  
\
-if (getDerived().shouldTraverseTemplateArgumentsBeforeDecl()) {
\
-  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); 
\
-  TRY_TO(TraverseDecl(D->getTemplatedDecl())); 
\
-} else {   
\
-  TRY_TO(TraverseDecl(D->getTemplatedDecl())); 
\
-  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); 
\
-}  
\
+TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));   
\
+TRY_TO(TraverseDecl(D->getTemplatedDecl()));   
\

\
 /* By default, we do not traverse the instantiations of
\
class templates since they do not appear in the user code. The  
\
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,8 +111,6 @@
 return true;
   }
 
-  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return true; }
-
   Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
 
   SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {


Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -537,7 +537,6 @@
 
   bool dataTraverseNode(Stmt *S, DataRecursionQueue *Queue);
   bool PostVisitStmt(Stmt *S);
-  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return false; }
 };
 
 template 
@@ -1691,13 +1690,8 @@
 // template declarations.
 #define DEF_TRAVERSE_TMPL_DECL(TMPLDECLKIND)   \
   DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateDecl, {  \
-if (getDerived().shouldTraverseTemplateArgumentsBeforeDecl()) {\
-  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \
-  TRY_TO(TraverseDecl(D->getTemplatedDecl())); \
-} else {   \
-  TRY_TO(TraverseDecl(D->getTemplatedDecl())); \
-  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); \
-}  \
+TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));   \
+TRY_TO(TraverseDecl(D->getTemplatedDecl()));   \
\
 /* By default, we do not traverse the instantiations of\
class templates since they do not appear in the user code. The  \
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,8 +111,6 @@
 return true;
   }
 
-  bool shouldTraverseTemplateArgumentsBeforeDecl() const { return true; }
-
   Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
 
   SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37663: [AST] Make RecursiveASTVisitor visit CXXOperatorCallExpr in source order

2017-09-09 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.

This adds a special case for traversing CXXOperatorCallExpr. Its
children are traversed in the order in which they appear in the source
code, so infix and postfix operators are visited after their first
argument.

This behavior was previously only in
LexicallyOrderedRecursiveASTVisitor, moving it here could avoid bugs in
the future. It is still tested in
LexicallyOrderedRecursiveASTVisitorTest.cpp in VisitTemplateDecl.
Clients that somehow rely on the original traversal order will have to
be updated though.


https://reviews.llvm.org/D37663

Files:
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h


Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -315,15 +315,40 @@
 
 //  Methods on Stmts 
 
-  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
-
 private:
   template
   struct has_same_member_pointer_type : std::false_type {};
   template
   struct has_same_member_pointer_type
   : std::true_type {};
 
+  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
+
+  SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {
+SmallVector Children(CE->children());
+bool Swap;
+// Swap the operator and the first operand for all infix and postfix
+// operations.
+switch (CE->getOperator()) {
+case OO_Arrow:
+case OO_Call:
+case OO_Subscript:
+  Swap = true;
+  break;
+case OO_PlusPlus:
+case OO_MinusMinus:
+  // These are postfix unless there is exactly one argument.
+  Swap = Children.size() != 2;
+  break;
+default:
+  Swap = CE->isInfixBinaryOp();
+  break;
+}
+if (Swap && Children.size() > 1)
+  std::swap(Children[0], Children[1]);
+return Children;
+  }
+
   // Traverse the given statement. If the most-derived traverse function takes 
a
   // data recursion queue, pass it on; otherwise, discard it. Note that the
   // first branch of this conditional must compile whether or not the derived
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,33 +111,6 @@
 return true;
   }
 
-  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
-
-  SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {
-SmallVector Children(CE->children());
-bool Swap;
-// Switch the operator and the first operand for all infix and postfix
-// operations.
-switch (CE->getOperator()) {
-case OO_Arrow:
-case OO_Call:
-case OO_Subscript:
-  Swap = true;
-  break;
-case OO_PlusPlus:
-case OO_MinusMinus:
-  // These are postfix unless there is exactly one argument.
-  Swap = Children.size() != 2;
-  break;
-default:
-  Swap = CE->isInfixBinaryOp();
-  break;
-}
-if (Swap && Children.size() > 1)
-  std::swap(Children[0], Children[1]);
-return Children;
-  }
-
 private:
   bool TraverseAdditionalLexicallyNestedDeclarations() {
 // FIXME: Ideally the gathered declarations and the declarations in the


Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -315,15 +315,40 @@
 
 //  Methods on Stmts 
 
-  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
-
 private:
   template
   struct has_same_member_pointer_type : std::false_type {};
   template
   struct has_same_member_pointer_type
   : std::true_type {};
 
+  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
+
+  SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {
+SmallVector Children(CE->children());
+bool Swap;
+// Swap the operator and the first operand for all infix and postfix
+// operations.
+switch (CE->getOperator()) {
+case OO_Arrow:
+case OO_Call:
+case OO_Subscript:
+  Swap = true;
+  break;
+case OO_PlusPlus:
+case OO_MinusMinus:
+  // These are postfix unless there is exactly one argument.
+  Swap = Children.size() != 2;
+  break;
+default:
+  Swap = CE->isInfixBinaryOp();
+  break;
+}
+if (Swap && Children.size() > 1)
+  std::swap(Children[0], Children[1]);
+return Children;
+  }
+
   // Traverse the given statement. If the most-derived traverse function takes a
   // data recursion queue, pass it on; otherwise, discard it. Note that the
   // first branch of this conditional must compile whether or not the derived
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
==

[PATCH] D37663: [AST] Make RecursiveASTVisitor visit CXXOperatorCallExpr in source order

2017-09-11 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/AST/RecursiveASTVisitor.h:334
+case OO_Arrow:
+case OO_Call:
+case OO_Subscript:

klimek wrote:
> Why do we need to swap for calls?
The idea is that the opening parenthesis/bracket  comes after the caller, for 
example in this call with two arguments.

```
`-CXXOperatorCallExpr
  |-ImplicitCastExpr
  | `-DeclRefExpr operator()
  |-DeclRefExpr caller
  |-IntegerLiteral
  `-IntegerLiteral
```

 Of course we fail to capture the fact that there is also a closing parenthesis 
or bracket. So this is no perfect solution.


https://reviews.llvm.org/D37663



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D37663: [AST] Make RecursiveASTVisitor visit CXXOperatorCallExpr in source order

2017-09-13 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 115018.
johannes added a comment.

use CXXOperatorCallExpr::isPrefixOp() to determine whether it's infix or postfix
directly traverse statement children instead of copying


https://reviews.llvm.org/D37663

Files:
  include/clang/AST/ExprCXX.h
  include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
  include/clang/AST/RecursiveASTVisitor.h
  lib/AST/ExprCXX.cpp

Index: lib/AST/ExprCXX.cpp
===
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -41,6 +41,12 @@
   }
 }
 
+bool CXXOperatorCallExpr::isPrefixOp() const {
+  return getNumArgs() == 1 && getOperator() != OO_Call &&
+ getOperator() != OO_Arrow;
+}
+
+
 bool CXXTypeidExpr::isPotentiallyEvaluated() const {
   if (isTypeOperand())
 return false;
Index: include/clang/AST/RecursiveASTVisitor.h
===
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -315,8 +315,6 @@
 
 //  Methods on Stmts 
 
-  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
-
 private:
   template
   struct has_same_member_pointer_type : std::false_type {};
@@ -2080,15 +2078,26 @@
   TRY_TO(WalkUpFrom##STMT(S)); \
 { CODE; }  \
 if (ShouldVisitChildren) { \
-  for (Stmt * SubStmt : getDerived().getStmtChildren(S)) { \
-TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(SubStmt);  \
+  auto ChildIterator = S->child_begin();   \
+  /* Make the operator come second for infix and postfix overloaded\
+   * operators. */ \
+  if (auto *CE = dyn_cast(S)) {   \
+if (CE->isInfixBinaryOp() || !CE->isPrefixOp()) {  \
+  Stmt *Operator = *ChildIterator; \
+  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(*++ChildIterator);   \
+  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(Operator);   \
+  ++ChildIterator; \
+}  \
+  }\
+  for (auto ChildEnd = S->child_end(); ChildIterator != ChildEnd;  \
+   ++ChildIterator) {  \
+TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(*ChildIterator);   \
   }\
 }  \
 if (!Queue && ReturnValue && getDerived().shouldTraversePostOrder())   \
   TRY_TO(WalkUpFrom##STMT(S)); \
 return ReturnValue;\
   }
-
 DEF_TRAVERSE_STMT(GCCAsmStmt, {
   TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmString());
   for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) {
Index: include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
===
--- include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
+++ include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
@@ -111,33 +111,6 @@
 return true;
   }
 
-  Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
-
-  SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {
-SmallVector Children(CE->children());
-bool Swap;
-// Switch the operator and the first operand for all infix and postfix
-// operations.
-switch (CE->getOperator()) {
-case OO_Arrow:
-case OO_Call:
-case OO_Subscript:
-  Swap = true;
-  break;
-case OO_PlusPlus:
-case OO_MinusMinus:
-  // These are postfix unless there is exactly one argument.
-  Swap = Children.size() != 2;
-  break;
-default:
-  Swap = CE->isInfixBinaryOp();
-  break;
-}
-if (Swap && Children.size() > 1)
-  std::swap(Children[0], Children[1]);
-return Children;
-  }
-
 private:
   bool TraverseAdditionalLexicallyNestedDeclarations() {
 // FIXME: Ideally the gathered declarations and the declarations in the
Index: include/clang/AST/ExprCXX.h
===
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -87,6 +87,8 @@
   /// \brief Is this written as an infix binary operator?
   bool isInfixBinaryOp() const;
 
+  bool isPrefixOp() const;
+
   /// \brief Returns the location of the operator symbol in the expression.
   ///
   /// When \c getOperator()==O

[PATCH] D37663: [AST] Make RecursiveASTVisitor visit CXXOperatorCallExpr in source order

2017-09-13 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/AST/RecursiveASTVisitor.h:327
+
+  SmallVector getStmtChildren(CXXOperatorCallExpr *CE) {
+SmallVector Children(CE->children());

rsmith wrote:
> The copy here is more expensive than I'd like. Instead of returning a list of 
> children, please make this function *traverse* the children (which is what 
> its one and only caller is going to do anyway) to avoid the copy.
I wasn't sure whether there is a way to do this in a function without writing a 
custom iterator that checks whether it should be swapped; I used a different 
approach.



Comment at: include/clang/AST/RecursiveASTVisitor.h:334
+case OO_Arrow:
+case OO_Call:
+case OO_Subscript:

rsmith wrote:
> johannes wrote:
> > klimek wrote:
> > > Why do we need to swap for calls?
> > The idea is that the opening parenthesis/bracket  comes after the caller, 
> > for example in this call with two arguments.
> > 
> > ```
> > `-CXXOperatorCallExpr
> >   |-ImplicitCastExpr
> >   | `-DeclRefExpr operator()
> >   |-DeclRefExpr caller
> >   |-IntegerLiteral
> >   `-IntegerLiteral
> > ```
> > 
> >  Of course we fail to capture the fact that there is also a closing 
> > parenthesis or bracket. So this is no perfect solution.
> > Of course we fail to capture the fact that there is also a closing 
> > parenthesis or bracket.
> 
> One way we could handle this would be to effectively order by the start 
> location for preorder traversal, and by the end location for postorder 
> traversal. So for a case like your `caller(1,2)`, we would visit 
> `CXXOperatorCallExpr`, `caller`, `operator()`, `1`, then `2` when doing 
> preorder visitation, and `caller`, `1`, `2`, `operator()`, then 
> `CXXOperatorCallExpr` when doing postorder visitation (because the notional 
> source location for the `operator()` invocation extends across both `1` and 
> `2` subexpressions).
> 
> But I think it's probably not worthwhile to go to this level of detail, and 
> treating the `(` as the location of the `operator()` call and ignoring the 
> `)` seems like a reasonable approach to me.
Yeah, we can do that if someone needs it.

Another way would be to model the operator as parent of the operands, although 
I assume that this would not compose well.
I don't expect it to be feasible but I imagine CXXOperatorCallExpr could also 
inherit from DeclRefExpr.


https://reviews.llvm.org/D37663



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40781: [DataCollection] Allow choosing between collection categories

2017-12-04 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added 1 blocking reviewer(s): teemperor.
Herald added a subscriber: ilya-biryukov.

This adds a list of collection categories to
ClangDataCollectorsEmitter.cpp, in an aim to eventually unify the
various AST hashing mechanisms (each one will get its own category).

In the various *DataCollectors.td files we can specify for each AST node
a way to collect data specific to a collection category. Clients can
then define COLLECT_FROM_* to collect data as defined in this category.


https://reviews.llvm.org/D40781

Files:
  include/clang/AST/StmtDataCollectors.td
  lib/Analysis/CloneDetection.cpp
  unittests/AST/DataCollectionTest.cpp
  utils/TableGen/ClangDataCollectorsEmitter.cpp

Index: utils/TableGen/ClangDataCollectorsEmitter.cpp
===
--- utils/TableGen/ClangDataCollectorsEmitter.cpp
+++ utils/TableGen/ClangDataCollectorsEmitter.cpp
@@ -4,15 +4,31 @@
 using namespace llvm;
 
 namespace clang {
+
+static struct {
+  const char *Name;
+  const char *MacroName;
+} CollectionCategories[] = {
+{"TypeIIClone", "COLLECT_FOR_TYPEIICLONE"},
+};
+
 void EmitClangDataCollectors(RecordKeeper &RK, raw_ostream &OS) {
-  const auto &Defs = RK.getClasses();
+  for (const auto &Category : CollectionCategories) {
+OS << "#ifndef " << Category.MacroName << "\n";
+OS << "#define " << Category.MacroName << "(A, B)\n";
+OS << "#endif\n";
+  }
+  const auto &Defs = RK.getDefs();
   for (const auto &Entry : Defs) {
 Record &R = *Entry.second;
-OS << "DEF_ADD_DATA(" << R.getName() << ", {\n";
-auto Code = R.getValue("Code")->getValue();
-OS << Code->getAsUnquotedString() << "}\n)";
-OS << "\n";
+for (auto Category : CollectionCategories) {
+  OS << Category.MacroName << "(" << R.getName() << ", {\n";
+  auto Code = R.getValue(Category.Name)->getValue();
+  OS << Code->getAsUnquotedString() << "}\n)";
+  OS << "\n";
+}
   }
-  OS << "#undef DEF_ADD_DATA\n";
+  for (const auto &Category : CollectionCategories)
+OS << "#undef " << Category.MacroName << "\n";
 }
 } // end namespace clang
Index: unittests/AST/DataCollectionTest.cpp
===
--- unittests/AST/DataCollectionTest.cpp
+++ unittests/AST/DataCollectionTest.cpp
@@ -40,7 +40,7 @@
 this->Visit(S);
   }
 
-#define DEF_ADD_DATA(CLASS, CODE)  \
+#define COLLECT_FOR_TYPEIICLONE(CLASS, CODE)   \
   template  Dummy Visit##CLASS(const CLASS *S) {   \
 CODE;  \
 ConstStmtVisitor::Visit##CLASS(S);  \
Index: lib/Analysis/CloneDetection.cpp
===
--- lib/Analysis/CloneDetection.cpp
+++ lib/Analysis/CloneDetection.cpp
@@ -199,7 +199,7 @@
 // Define a visit method for each class to collect data and subsequently visit
 // all parent classes. This uses a template so that custom visit methods by us
 // take precedence.
-#define DEF_ADD_DATA(CLASS, CODE)  \
+#define COLLECT_FOR_TYPEIICLONE(CLASS, CODE)   \
   template  void Visit##CLASS(const CLASS *S) {  \
 CODE;  \
 ConstStmtVisitor>::Visit##CLASS(S);\
Index: include/clang/AST/StmtDataCollectors.td
===
--- include/clang/AST/StmtDataCollectors.td
+++ include/clang/AST/StmtDataCollectors.td
@@ -1,46 +1,50 @@
-class Stmt {
-  code Code = [{
+class Collector {
+  code TypeIIClone = [{}];
+}
+
+def Stmt : Collector {
+  let TypeIIClone = [{
 addData(S->getStmtClass());
 // This ensures that non-macro-generated code isn't identical to
 // macro-generated code.
 addData(data_collection::getMacroStack(S->getLocStart(), Context));
 addData(data_collection::getMacroStack(S->getLocEnd(), Context));
   }];
 }
 
-class Expr {
-  code Code = [{
+def Expr : Collector {
+  let TypeIIClone = [{
 addData(S->getType());
   }];
 }
 
 //--- Builtin functionality --//
-class ArrayTypeTraitExpr {
-  code Code = [{
+def ArrayTypeTraitExpr : Collector {
+  let TypeIIClone = [{
 addData(S->getTrait());
   }];
 }
-class ExpressionTraitExpr {
-  code Code = [{
+def ExpressionTraitExpr : Collector {
+  let TypeIIClone = [{
 addData(S->getTrait());
   }];
 }
-class PredefinedExpr {
-  code Code = [{
+def PredefinedExpr : Collector {
+  let TypeIIClone = [{
 addData(S->getIdentType());
   }];
 }
-class TypeTraitExpr {
-  code Code = [{
+def TypeTraitExpr : Collector {
+  let TypeIIClone = [{
 addData(S->getTrait());
 for (unsigned i = 0; i < S->getNumArgs(); ++i)
   addData(S->getArg(i)-

[PATCH] D40731: Integrate CHash into CLang

2017-12-04 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D40731#943469, @stettberger wrote:

> @Eugene.Zelenko Thank you for pointing me out on these issues. I ran 
> clang-tidy and clang-format on CHashVisitor.h
>
> @rsmith You're right, there is already more than one implemenation of 
> {partial,unstable} AST hashes within LLVM, as we already discussed on cfe-dev 
> in August[1]. Therefore, I rewrote our original CHash implementation to 
> extend the already existing StmtDataCollectors approach. However, you are 
> right, efforts should be coordinated to get a AST hashing implementation that 
> can be adapted to various use-case scenarios.
>
> [1] http://lists.llvm.org/pipermail/cfe-dev/2017-August/054911.html


I think it would be quite nice if we manage to consolidate the implementation 
of hashing mechanisms.

Modifications to StmtDataCollectors.td will alter the behaviour of clone 
detection. Currently, this file assumes that we want to find type 2 clones. So 
we should maintain that, and add things only where needed.
I submitted this patch so we can have different categories of data collection
https://reviews.llvm.org/D40781


Repository:
  rC Clang

https://reviews.llvm.org/D40731



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40731: Integrate CHash into CLang

2017-12-04 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

So you can define a category for cHash and add `let cHash = [{ .. }]` or `let 
cHash = TypeIIClone;` if you just want to reuse it.
With this patch you can also use !codeconcat to append some code: 
https://reviews.llvm.org/D40782


Repository:
  rC Clang

https://reviews.llvm.org/D40731



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D40731: Integrate CHash into CLang

2017-12-04 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D40731#943564, @stettberger wrote:

> For my changes to StmtDataCollectors: I tried to leave the already existing 
> entries in StmtDataCollector.td untouched and the test-cases still work OK. 
> If there is something somebody could break by changing these files it would 
> be good to have a test case for it.


Yeah, the tests are not comprehensive unfortunately

> However, some of the collectors are not node local, which seems a little bit 
> inconsequent to me. For example, DeclStmt includes information about the its 
> Decl Children. In my opinion every entry in the *DataCollector.td should 
> include only information that can be directly retrieved from this particular 
> AST node. Cross-referencing between nodes should be done on the user side of 
> the collectors (as it is done in CHashVisitor.h, which follows pointers in a 
> cross-tree manner). With this separation of concerns it was quite easy for me 
> to base CHash of the TableGen'ed  headers. Furthermore, a purely syntactical 
> hash (only the children, no cross-tree dependencies) could be derived easily.

Good point, I think we can move the code for DeclStmt to CloneDetection.cpp

> @johannes For the different types of information that should be included for 
> one AST node (e.g., Type II clones): We could use a hierachy of "What should 
> be considered here". But we should clearly define what is the definition of 
> equivalency for that level. For example, if you wake me up at night  I could 
> not define you the difference between a type I and a type II clone.

Yeah, it's difficult to clearly define that though. The thing I noticed is that 
the function name is included for PredefinedExpr, but Type II clones do not 
care about values, but only structure. I think most of your additions should be 
fine , @teemperor could confirm them.


Repository:
  rC Clang

https://reviews.llvm.org/D40731



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D35921: [clang-diff] HTML side-by-side diff

2017-08-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes abandoned this revision.
johannes added a comment.

moved to https://reviews.llvm.org/D36182, sorry for the noise


https://reviews.llvm.org/D35921



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D34748: [clang-diff] improve mapping accuracy

2017-08-01 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes abandoned this revision.
johannes added a comment.

split up in several commits starting from https://reviews.llvm.org/D36176




Comment at: lib/Tooling/ASTDiff/ASTDiff.cpp:170
+  // Ignore everything from other files.
+  if (!SrcMgr.isInMainFile(SLoc))
+return true;

arphaman wrote:
> How will you deal with changes in headers if you limit analysis to main files 
> only? 
I will change this to support headers in another patch


https://reviews.llvm.org/D34748



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36176: [clang-diff] Fix some errors and inconsistencies

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:96
   : TreeImpl(llvm::make_unique(this, Node, AST)) {}
+  SyntaxTree(const SyntaxTree &Tree) = delete;
   ~SyntaxTree();

arphaman wrote:
> It might be better to add a move constructor which would disable copy 
> constructors.
ASTDiff::getMapped internally uses the address of the SyntaxTree that is passed 
as parameter.
So the tree must be exactly the same as the one that is passed to the 
constructor of ASTDiff. 

This is quite bad. I will change ASTDiff::getMapped to accept a boolean or 
split it into two methods
Should I still add a move constructor?


https://reviews.llvm.org/D36176



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36176: [clang-diff] Fix some errors and inconsistencies

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109414.
johannes added a comment.

move most functional changes to other commits
move constructor for Syntaxtree


https://reviews.llvm.org/D36176

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTDiffInternal.h
  lib/Tooling/ASTDiff/ASTDiff.cpp

Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -27,6 +27,7 @@
 namespace clang {
 namespace diff {
 
+namespace {
 /// Maps nodes of the left tree to ones on the right, and vice versa.
 class Mapping {
 public:
@@ -76,6 +77,7 @@
 private:
   std::unique_ptr[]> SrcToDst, DstToSrc;
 };
+} // end anonymous namespace
 
 class ASTDiff::Impl {
 public:
@@ -110,10 +112,6 @@
   // Returns false if the nodes must not be mached.
   bool isMatchingPossible(NodeId Id1, NodeId Id2) const;
 
-  // Adds all corresponding subtrees of the two nodes to the mapping.
-  // The two nodes must be identical.
-  void addIsomorphicSubTrees(Mapping &M, NodeId Id1, NodeId Id2) const;
-
   // Uses an optimal albeit slow algorithm to compute a mapping between two
   // subtrees, but only if both have fewer nodes than MaxSize.
   void addOptimalMapping(Mapping &M, NodeId Id1, NodeId Id2) const;
@@ -254,7 +252,10 @@
 Parent = PreviousParent;
 --Depth;
 Node &N = Tree.getMutableNode(MyId);
-N.RightMostDescendant = Id;
+N.RightMostDescendant = Id - 1;
+assert(N.RightMostDescendant >= 0 &&
+   N.RightMostDescendant < Tree.getSize() &&
+   "Rightmost descendant must be a valid tree node.");
 if (N.isLeaf())
   Tree.Leaves.push_back(MyId);
 N.Height = 1;
@@ -358,9 +359,7 @@
 }
 
 bool SyntaxTree::Impl::isInSubtree(NodeId Id, NodeId SubtreeRoot) const {
-  NodeId Lower = SubtreeRoot;
-  NodeId Upper = getNode(SubtreeRoot).RightMostDescendant;
-  return Id >= Lower && Id <= Upper;
+  return Id >= SubtreeRoot && Id <= getNode(SubtreeRoot).RightMostDescendant;
 }
 
 std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const {
@@ -625,12 +624,11 @@
   }
 
 private:
-  /// Simple cost model for edit actions.
+  /// We use a simple cost model for edit actions, which seems good enough.
+  /// Simple cost model for edit actions. This seems to make the matching
+  /// algorithm perform reasonably well.
   /// The values range between 0 and 1, or infinity if this edit action should
   /// always be avoided.
-
-  /// These costs could be modified to better model the estimated cost of /
-  /// inserting / deleting the current node.
   static constexpr double DeletionCost = 1;
   static constexpr double InsertionCost = 1;
 
@@ -687,6 +685,7 @@
 };
 } // end anonymous namespace
 
+namespace {
 // Priority queue for nodes, sorted descendingly by their height.
 class PriorityList {
   const SyntaxTree::Impl &Tree;
@@ -723,6 +722,7 @@
   push(Child);
   }
 };
+} // end anonymous namespace
 
 bool ASTDiff::Impl::identical(NodeId Id1, NodeId Id2) const {
   const Node &N1 = T1.getNode(Id1);
@@ -759,16 +759,6 @@
T2.getNode(Id2).ASTNode);
 }
 
-void ASTDiff::Impl::addIsomorphicSubTrees(Mapping &M, NodeId Id1,
-  NodeId Id2) const {
-  assert(identical(Id1, Id2) && "Can only be called on identical subtrees.");
-  M.link(Id1, Id2);
-  const Node &N1 = T1.getNode(Id1);
-  const Node &N2 = T2.getNode(Id2);
-  for (size_t Id = 0, E = N1.Children.size(); Id < E; ++Id)
-addIsomorphicSubTrees(M, N1.Children[Id], N2.Children[Id]);
-}
-
 void ASTDiff::Impl::addOptimalMapping(Mapping &M, NodeId Id1,
   NodeId Id2) const {
   if (std::max(T1.getNumberOfDescendants(Id1),
@@ -805,7 +795,7 @@
 if (M.hasDst(Id2))
   continue;
 double Similarity = getSimilarity(M, Id1, Id2);
-if (Similarity > HighestSimilarity) {
+if (Similarity >= Options.MinSimilarity && Similarity > HighestSimilarity) {
   HighestSimilarity = Similarity;
   Candidate = Id2;
 }
@@ -816,7 +806,8 @@
 void ASTDiff::Impl::matchBottomUp(Mapping &M) const {
   std::vector Postorder = getSubtreePostorder(T1, T1.getRootId());
   for (NodeId Id1 : Postorder) {
-if (Id1 == T1.getRootId()) {
+if (Id1 == T1.getRootId() && !M.hasSrc(T1.getRootId()) &&
+!M.hasDst(T2.getRootId())) {
   if (isMatchingPossible(T1.getRootId(), T2.getRootId())) {
 M.link(T1.getRootId(), T2.getRootId());
 addOptimalMapping(M, T1.getRootId(), T2.getRootId());
@@ -831,11 +822,10 @@
 if (Matched || !MatchedChildren)
   continue;
 NodeId Id2 = findCandidate(M, Id1);
-if (Id2.isInvalid() || !canBeAddedToMapping(M, Id1, Id2) ||
-getSimilarity(M, Id1, Id2) < Options.MinSimilarity)
-  continue;
-M.link(Id1, Id2);
-addOptimalMapping(M, Id1, Id2);
+if (Id2.isValid() && canBeAddedToMapping(M, Id1, Id2)) {
+  M.link(Id1, Id2);
+

[PATCH] D36177: [clang-diff] Add commandline arguments.

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109415.
johannes added a comment.

add some test, replace -no-compilation-database with --


https://reviews.llvm.org/D36177

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-args.sh
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -28,12 +28,6 @@
 cl::desc("Print the internal representation of the AST as JSON."),
 cl::init(false), cl::cat(ClangDiffCategory));
 
-static cl::opt 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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -43,43 +37,88 @@
 cl::Optional,
 cl::cat(ClangDiffCategory));
 
-static std::unique_ptr getAST(const StringRef Filename) {
+static cl::opt StopAfter("stop-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 =
+  llvm::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 (!NoCompilationDatabase)
-Compilations =
-CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
-  if (!Compilations) {
-if (!NoCompilationDatabase)
+  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 = llvm::make_unique(
-".", std::vector());
+  Compilations =
+  llvm::make_unique(
+  ".", std::vector());
+}
   }
+  addExtraArgs(Compilations);
   std::array Files = {{Filename}};
-  ClangTool Tool(*Compilations, Files);
+  ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
   std::vector> ASTs;
   Tool.buildASTs(ASTs);
   if (ASTs.size() != Files.size())
 return nullptr;
   return std::move(ASTs[0]);
 }
 
 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) {
 if (!DestinationPath.empty()) {
   llvm::errs() << "Error: Please specify exactly one filename.\n";
   return 1;
 }
-std::unique_ptr AST = getAST(SourcePath);
+std::unique_ptr AST = getAST(CommonCompilations, SourcePath);
 if (!AST)
   return 1;
 diff::SyntaxTree Tree(AST->getASTContext());
@@ -92,12 +131,22 @@
 return 1;
   }
 
-  std::unique_ptr Src = getAST(SourcePath);
-  std::unique_ptr Dst = getAST(DestinationPath);
+  std::unique_ptr Src = getAST(CommonCompilations, SourcePath);
+  std::unique_ptr Dst = getAST(CommonCompilations, DestinationPath);
   if (!Src || !Dst)
 return 1;
 
   diff::ComparisonOptions Options;
+ 

[PATCH] D36178: [clang-diff] Move the JSON export function to clang-diff

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109416.
johannes added a comment.

getSourceRangeOffsets, test


https://reviews.llvm.org/D36178

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-json.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -99,6 +99,65 @@
   return std::move(ASTs[0]);
 }
 
+static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
+
+static void printJsonString(raw_ostream &OS, const StringRef Str) {
+  for (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.ASTNode);
+  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);
+  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 << "]}";
+}
+
 int main(int argc, const char **argv) {
   std::string ErrorMessage;
   std::unique_ptr CommonCompilations =
@@ -122,7 +181,11 @@
 if (!AST)
   return 1;
 diff::SyntaxTree Tree(AST->getASTContext());
-Tree.printAsJson(llvm::outs());
+llvm::outs() << R"({"filename":")";
+printJsonString(llvm::outs(), SourcePath);
+llvm::outs() << R"(","root":)";
+printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
+llvm::outs() << "}\n";
 return 0;
   }
 
Index: test/Tooling/clang-diff-json.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-json.cpp
@@ -0,0 +1,21 @@
+// RUN: clang-diff -ast-dump %s -- | python -m json.tool | FileCheck %s
+
+// CHECK: "type": "CXXRecordDecl",
+// CHECK: "begin": 185,
+// CHECK: "end": 205,
+// CHECK: "type": "FieldDecl",
+class A {
+  int x;
+};
+
+// CHECK: "type": "VarDecl",
+// CHECK: "children": [
+// CHECK: "type": "CharacterLiteral",
+// CHECK: "children": []
+// CHECK-NOT: "children": [
+// CHECK: ]
+char nl = '\n';
+
+// CHECK: "value": "abc \n\t\u\u001f\udcc4\udca3 \udceb\udc8d\udcb0\udceb\udcb0\udc95"
+char s[] = "abc \n\t\0\x1f\u0123 데박";
+
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -176,9 +176,6 @@
   void printTree(NodeId Root) const;
   void printTree(raw_ostream &OS, NodeId Root) const;
 
-  void printAsJsonImpl(raw_ostream &OS) const;
-  void printNodeAsJson(raw_ostream &OS, NodeId Id) const;
-
 private:
   /// Nodes in preorder.
   std::vector Nodes;
@@ -438,28 +435,6 @@
   OS << "(" << PostorderIds[Id] << ")";
 }
 
-void SyntaxTree::Impl::printNodeAsJson(raw_ostream &OS, NodeId Id) const {
-  auto N = getNode(Id);
-  OS << R"({"type":")" << N.getTypeLabel() << R"(")";
-  if (getNodeValue(Id) != "")
-OS << R"(,"value":")" << getNodeValue(Id) << R"(")";
-  OS << R"(,"children":[)";
-  if (N.Children.size() > 0) {
-printNodeAsJson(OS, N.Children[0]);
-for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
-  OS << ",";
-  printNodeAsJson(OS, N.Children[I]);
-}
-  }
-  OS << "]}";
-}
-
-void SyntaxTree::Impl::printAsJsonImpl(raw_ostream &OS) const {
-  OS << R"({"root":)";
-  printNodeAsJson(OS, getRootId());
-  OS << "}\n";
-}
-
 /// Identifies a node in a subtree by its postorder offset, starting at 1.
 struct SNodeId {
   int Id = 0;
@@ -674,6 +649,12 @@
   }
 };
 
+ast_type_traits::ASTNodeKind Node::getType() const {
+  return ASTNode.getNodeKind();
+}
+
+StringRef Node::getTypeLabel() const { return getType().asStringRef(); }
+
 namespace {
 // Compares nodes by their depth.
 struct HeightLess {
@@ -1001,7 +982,28 @@
 
 SyntaxTree::~SyntaxTree() = default;
 
-void SyntaxTree::printAsJson(raw_ostream &OS) { TreeImpl->p

[PATCH] D36179: [clang-diff] Move printing of matches and changes to clang-diff

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109417.
johannes added a comment.

remove unused SubtreeIterator


https://reviews.llvm.org/D36179

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTDiffInternal.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -134,7 +134,7 @@
   auto Offsets = Tree.getSourceRangeOffsets(N);
   OS << R"(,"begin":)" << Offsets.first;
   OS << R"(,"end":)" << Offsets.second;
-  std::string Value = Tree.getNodeValue(N.ASTNode);
+  std::string Value = Tree.getNodeValue(N);
   if (!Value.empty()) {
 OS << R"(,"value":")";
 printJsonString(OS, Value);
@@ -158,6 +158,52 @@
   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 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.ChangeKind) {
+  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.ChangeKind == diff::Insert)
+  OS << "Insert";
+else if (DstNode.ChangeKind == diff::Move)
+  OS << "Move";
+else if (DstNode.ChangeKind == 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 =
@@ -212,11 +258,26 @@
   }
   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);
+  diff::ASTDiff Diff(SrcTree, DstTree, Options);
+
+  for (diff::NodeId Dst : DstTree) {
+diff::NodeId Src = Diff.getMapped(DstTree, Dst);
+if (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;
 }
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -54,8 +54,8 @@
 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)
+// CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
 double prod = 1 * 2 * 10;
 // CHECK: Update DeclRefExpr
 int squared = prod * prod;
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -82,26 +82,23 @@
 class ASTDiff::Impl {
 public:
   SyntaxTree::Impl &T1, &T2;
-  bool IsMappingDone = false;
   Mapping TheMapping;
 
   Impl(SyntaxTree::Impl &T1, SyntaxTree::Impl &T2,
-   const ComparisonOptions &Options)
-  : T1(T1), T2(T2), Options(Options) {}
+   const ComparisonOptions &Options);
 
   /// Matches nodes one-by-one based on their similarity.
   void computeMapping();
 
-  std::vector getMatches(Mapping &M);
+  // Compute ChangeKind for each node based on similarity.
+  void computeChangeKinds(Mapping &M);
 
-  /// Finds an edit script that converts T1 to T2.
-  std::vector computeChanges(Mapping &M);
-
-  void printChangeImpl(raw_ostream &OS, const Change &Chg) const;
-  void printMatchImpl(raw_ostream &OS, const Match &M) const;
-
-  

[PATCH] D36181: [clang-diff] Make printing of matches optional

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109420.
johannes added a comment.

rename to -dump-matches, add a test


https://reviews.llvm.org/D36181

Files:
  test/Tooling/clang-diff-args.sh
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp


Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -33,6 +33,10 @@
 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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -280,7 +284,7 @@
 
   for (diff::NodeId Dst : DstTree) {
 diff::NodeId Src = Diff.getMapped(DstTree, Dst);
-if (Src.isValid()) {
+if (PrintMatches && Src.isValid()) {
   llvm::outs() << "Match ";
   printNode(llvm::outs(), SrcTree, Src);
   llvm::outs() << " to ";
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %T/src.cpp
 // RUN: %clang_cc1 -E %s > %T/dst.cpp -DDEST
-// RUN: clang-diff %T/src.cpp %T/dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches %T/src.cpp %T/dst.cpp -- | FileCheck %s
 
 #ifndef DEST
 namespace src {
Index: test/Tooling/clang-diff-args.sh
===
--- test/Tooling/clang-diff-args.sh
+++ test/Tooling/clang-diff-args.sh
@@ -6,3 +6,7 @@
 RUN: clang-diff -ast-dump -extra-arg=-Da=X%t.cpp -- 2>&1 | FileCheck 
%t1
 RUN: clang-diff -ast-dump -extra-arg-before=-Da=X %t.cpp -- 2>&1 | FileCheck 
%t1
 RUN: clang-diff -ast-dump %t.cpp -- 2>&1 -Da=X | FileCheck %t1
+
+RUN: echo "// CHECK-NOT: {{.}}" > %t-no-output
+RUN: clang-diff %S/clang-diff-ast.cpp %S/clang-diff-ast.cpp -- 2>&1 -std=c++11 
\
+RUN: | FileCheck -allow-empty %t-no-output


Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -33,6 +33,10 @@
 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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -280,7 +284,7 @@
 
   for (diff::NodeId Dst : DstTree) {
 diff::NodeId Src = Diff.getMapped(DstTree, Dst);
-if (Src.isValid()) {
+if (PrintMatches && Src.isValid()) {
   llvm::outs() << "Match ";
   printNode(llvm::outs(), SrcTree, Src);
   llvm::outs() << " to ";
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %T/src.cpp
 // RUN: %clang_cc1 -E %s > %T/dst.cpp -DDEST
-// RUN: clang-diff %T/src.cpp %T/dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches %T/src.cpp %T/dst.cpp -- | FileCheck %s
 
 #ifndef DEST
 namespace src {
Index: test/Tooling/clang-diff-args.sh
===
--- test/Tooling/clang-diff-args.sh
+++ test/Tooling/clang-diff-args.sh
@@ -6,3 +6,7 @@
 RUN: clang-diff -ast-dump -extra-arg=-Da=X%t.cpp -- 2>&1 | FileCheck %t1
 RUN: clang-diff -ast-dump -extra-arg-before=-Da=X %t.cpp -- 2>&1 | FileCheck %t1
 RUN: clang-diff -ast-dump %t.cpp -- 2>&1 -Da=X | FileCheck %t1
+
+RUN: echo "// CHECK-NOT: {{.}}" > %t-no-output
+RUN: clang-diff %S/clang-diff-ast.cpp %S/clang-diff-ast.cpp -- 2>&1 -std=c++11 \
+RUN: | FileCheck -allow-empty %t-no-output
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36180: [clang-diff] Add option to dump the AST, one node per line

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109419.
johannes added a comment.
Herald added a subscriber: klimek.

add a test


https://reviews.llvm.org/D36180

Files:
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-json.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -25,9 +25,14 @@
 
 static cl::opt
 ASTDump("ast-dump",
-cl::desc("Print the internal representation of the AST as JSON."),
+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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -171,6 +176,15 @@
   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) {
@@ -218,15 +232,19 @@
 
   addExtraArgs(CommonCompilations);
 
-  if (ASTDump) {
+  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":)";
Index: test/Tooling/clang-diff-json.cpp
===
--- test/Tooling/clang-diff-json.cpp
+++ test/Tooling/clang-diff-json.cpp
@@ -1,8 +1,8 @@
-// RUN: clang-diff -ast-dump %s -- | python -m json.tool | FileCheck %s
+// RUN: clang-diff -ast-dump-json %s -- | python -m json.tool | FileCheck %s
 
 // CHECK: "type": "CXXRecordDecl",
-// CHECK: "begin": 185,
-// CHECK: "end": 205,
+// CHECK: "begin": 190,
+// CHECK: "end": 210,
 // CHECK: "type": "FieldDecl",
 class A {
   int x;
Index: test/Tooling/clang-diff-ast.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-ast.cpp
@@ -0,0 +1,50 @@
+// RUN: clang-diff -ast-dump %s -- -std=c++11 | FileCheck %s
+
+
+// CHECK: {{^}}TranslationUnitDecl(0)
+// CHECK: {{^}} NamespaceDecl: test;(
+namespace test {
+
+// CHECK: {{^}}  FunctionDecl: f(
+// CHECK: CompoundStmt(
+void f() {
+  // CHECK: VarDecl: i(int)(
+  // CHECK: IntegerLiteral: 1
+  auto i = 1;
+  // CHECK: CallExpr(
+  // CHECK: DeclRefExpr: f(
+  f();
+  // CHECK: BinaryOperator: =(
+  i = i;
+}
+
+} // end namespace test
+
+// CHECK: TypedefDecl: nat;unsigned int;(
+typedef unsigned nat;
+// CHECK: TypeAliasDecl: real;double;(
+using real = double;
+
+class Base {
+};
+
+// CHECK: CXXRecordDecl: X;class X;(
+class X : Base {
+  int m;
+  // CHECK: CXXMethodDecl: foo(const char *(int))(
+  // CHECK: ParmVarDecl: i(int)(
+  const char *foo(int i) {
+if (i == 0)
+  // CHECK: StringLiteral: foo(
+  return "foo";
+return 0;
+  }
+
+  // CHECK: AccessSpecDecl: public(
+public:
+  // CHECK: CXXConstructorDecl: X(void (char, int))(
+  X(char, int) : Base(), m(0) {
+// CHECK: MemberExpr(
+int x = m;
+  }
+};
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36182: [clang-diff] Add HTML side-by-side diff output

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109421.
johannes added a comment.

-


https://reviews.llvm.org/D36182

Files:
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-html.py
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -37,6 +37,10 @@
 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));
@@ -110,6 +114,161 @@
 
 static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
 
+static const char HtmlDiffHeader[] = R"(
+
+
+
+
+span.d { color: red; }
+span.u { color: #cc00cc; }
+span.i { color: green; }
+span.m { font-weight: bold; }
+span   { font-weight: normal; color: black; }
+div.code {
+  width: 48%;
+  height: 98%;
+  overflow: scroll;
+  float: left;
+  padding: 0 0 0.5% 0.5%;
+  border: solid 2px LightGrey;
+  border-radius: 5px;
+}
+
+
+
+highlightStack = []
+function clearHighlight() {
+  while (highlightStack.length) {
+let [l, r] = highlightStack.pop()
+document.getElementById(l).style.backgroundColor = 'white'
+document.getElementById(r).style.backgroundColor = 'white'
+  }
+}
+function highlight(event) {
+  id = event.target['id']
+  doHighlight(id)
+}
+function doHighlight(id) {
+  clearHighlight()
+  source = document.getElementById(id)
+  if (!source.attributes['tid'])
+return
+  tid = source.attributes['tid'].value
+  target = document.getElementById(tid)
+  if (!target || source.parentElement && source.parentElement.classList.contains('code'))
+return
+  source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
+  highlightStack.push([id, tid])
+  source.scrollIntoView()
+  target.scrollIntoView()
+  location.hash = '#' + id
+}
+function scrollToBoth() {
+  doHighlight(location.hash.substr(1))
+}
+window.onload = scrollToBoth
+
+
+
+)";
+
+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";
+  }
+}
+
+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 (char C : Str) {
 switch (C) {
@@ -282,6 +441,19 @@
   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(),
+  

[PATCH] D36183: [clang-diff] Simplify mapping

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109422.
johannes edited the summary of this revision.
johannes added a comment.

-


https://reviews.llvm.org/D36183

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp

Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -38,8 +38,15 @@
   return "foo";
 return 0;
   }
-  // CHECK: Delete AccessSpecDecl: public
   X(){};
-  // CHECK: Delete CXXMethodDecl
 };
 }
+
+namespace {
+// match with parents of different type
+// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: f1
+void f1() {{ (void) __func__;;; }}
+}
+
+// CHECK: Delete AccessSpecDecl: public
+// CHECK: Delete CXXMethodDecl
Index: test/Tooling/Inputs/clang-diff-basic-src.cpp
===
--- test/Tooling/Inputs/clang-diff-basic-src.cpp
+++ test/Tooling/Inputs/clang-diff-basic-src.cpp
@@ -26,3 +26,5 @@
   int id(int i) { return i; }
 };
 }
+
+void f1() {{ (void) __func__;;; }}
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -34,48 +34,23 @@
   Mapping() = default;
   Mapping(Mapping &&Other) = default;
   Mapping &operator=(Mapping &&Other) = default;
-  Mapping(int Size1, int Size2) {
-// Maximum possible size after patching one tree.
-int Size = Size1 + Size2;
-SrcToDst = llvm::make_unique[]>(Size);
-DstToSrc = llvm::make_unique[]>(Size);
+
+  Mapping(size_t Size) {
+SrcToDst = llvm::make_unique(Size);
+DstToSrc = llvm::make_unique(Size);
   }
 
   void link(NodeId Src, NodeId Dst) {
-SrcToDst[Src].push_back(Dst);
-DstToSrc[Dst].push_back(Src);
-  }
-
-  NodeId getDst(NodeId Src) const {
-if (hasSrc(Src))
-  return SrcToDst[Src][0];
-return NodeId();
-  }
-  NodeId getSrc(NodeId Dst) const {
-if (hasDst(Dst))
-  return DstToSrc[Dst][0];
-return NodeId();
-  }
-  const SmallVector &getAllDsts(NodeId Src) const {
-return SrcToDst[Src];
-  }
-  const SmallVector &getAllSrcs(NodeId Dst) const {
-return DstToSrc[Dst];
-  }
-  bool hasSrc(NodeId Src) const { return !SrcToDst[Src].empty(); }
-  bool hasDst(NodeId Dst) const { return !DstToSrc[Dst].empty(); }
-  bool hasSrcDst(NodeId Src, NodeId Dst) const {
-for (NodeId DstId : SrcToDst[Src])
-  if (DstId == Dst)
-return true;
-for (NodeId SrcId : DstToSrc[Dst])
-  if (SrcId == Src)
-return true;
-return false;
+SrcToDst[Src] = Dst, DstToSrc[Dst] = Src;
   }
 
+  NodeId getDst(NodeId Src) const { return SrcToDst[Src]; }
+  NodeId getSrc(NodeId Dst) const { return DstToSrc[Dst]; }
+  bool hasSrc(NodeId Src) const { return getDst(Src).isValid(); }
+  bool hasDst(NodeId Dst) const { return getSrc(Dst).isValid(); }
+
 private:
-  std::unique_ptr[]> SrcToDst, DstToSrc;
+  std::unique_ptr SrcToDst, DstToSrc;
 };
 } // end anonymous namespace
 
@@ -104,8 +79,6 @@
   // Returns true if the two subtrees are identical.
   bool identical(NodeId Id1, NodeId Id2) const;
 
-  bool canBeAddedToMapping(const Mapping &M, NodeId Id1, NodeId Id2) const;
-
   // Returns false if the nodes must not be mached.
   bool isMatchingPossible(NodeId Id1, NodeId Id2) const;
 
@@ -711,23 +684,6 @@
   return true;
 }
 
-bool ASTDiff::Impl::canBeAddedToMapping(const Mapping &M, NodeId Id1,
-NodeId Id2) const {
-  assert(isMatchingPossible(Id1, Id2) &&
- "Matching must be possible in the first place.");
-  if (M.hasSrcDst(Id1, Id2))
-return false;
-  if (Options.EnableMatchingWithUnmatchableParents)
-return true;
-  const Node &N1 = T1.getNode(Id1);
-  const Node &N2 = T2.getNode(Id2);
-  NodeId P1 = N1.Parent;
-  NodeId P2 = N2.Parent;
-  // Only allow matching if parents can be matched.
-  return (P1.isInvalid() && P2.isInvalid()) ||
- (P1.isValid() && P2.isValid() && isMatchingPossible(P1, P2));
-}
-
 bool ASTDiff::Impl::isMatchingPossible(NodeId Id1, NodeId Id2) const {
   return Options.isMatchingAllowed(T1.getNode(Id1), T2.getNode(Id2));
 }
@@ -750,7 +706,7 @@
   for (const auto Tuple : R) {
 NodeId Src = Tuple.first;
 NodeId Dst = Tuple.second;
-if (canBeAddedToMapping(M, Src, Dst))
+if (!M.hasSrc(Src) && !M.hasDst(Dst))
   M.link(Src, Dst);
   }
 }
@@ -803,7 +759,7 @@
 if (Matched || !MatchedChildren)
   continue;
 NodeId Id2 = findCandidate(M, Id1);
-if (Id2.isValid() && canBeAddedToMapping(M, Id1, Id2)) {
+if (Id2.isValid()) {
   M.link(Id1, Id2);
   addOptimalMapping(M, Id1, Id2);
 }
@@ -814,7 +770,7 @@
   PriorityList L1(T1);
   PriorityList L2(T2);
 
-  Mapping M(T1.getSize(), T2.getSize());
+  Mapping M(T1.getSize() + T2.ge

[PATCH] D36184: [clang-diff] Filter AST nodes

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109423.
johannes added a comment.

tests


https://reviews.llvm.org/D36184

Files:
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp

Index: test/Tooling/clang-diff-ast.cpp
===
--- test/Tooling/clang-diff-ast.cpp
+++ test/Tooling/clang-diff-ast.cpp
@@ -12,7 +12,8 @@
   // CHECK: IntegerLiteral: 1
   auto i = 1;
   // CHECK: CallExpr(
-  // CHECK: DeclRefExpr: f(
+  // CHECK-NOT: ImplicitCastExpr
+  // CHECK-NEXT: DeclRefExpr: f(
   f();
   // CHECK: BinaryOperator: =(
   i = i;
@@ -37,6 +38,7 @@
 if (i == 0)
   // CHECK: StringLiteral: foo(
   return "foo";
+// CHECK-NOT: ImplicitCastExpr
 return 0;
   }
 
@@ -48,3 +50,22 @@
 int x = m;
   }
 };
+
+#define M (void)1
+#define MA(a, b) (void)a, b
+// CHECK: FunctionDecl
+// CHECK-NEXT: CompoundStmt
+void macros() {
+  M;
+  MA(1, 2);
+}
+// CHECK-NEXT: NamespaceDecl
+
+#ifndef GUARD
+#define GUARD
+namespace world {
+// nodes from other files are excluded
+// CHECK-NOT {{.}}
+#include "clang-diff-ast.cpp"
+}
+#endif
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -162,24 +162,37 @@
   if (!N)
 return true;
   SourceLocation SLoc = N->getLocStart();
-  return SLoc.isValid() && SrcMgr.isInSystemHeader(SLoc);
+  if (!SLoc.isValid())
+return false;
+  // Ignore everything from other files.
+  if (!SrcMgr.isInMainFile(SLoc))
+return true;
+  // Ignore macros.
+  if (N->getLocStart() != SrcMgr.getSpellingLoc(N->getLocStart()))
+return true;
+  return false;
 }
 
+static bool isDeclExcluded(const Decl *D) { return D->isImplicit(); }
+static bool isStmtExcluded(const Stmt *S) { return false; }
+
 namespace {
 /// Counts the number of nodes that will be compared.
 struct NodeCountVisitor : public RecursiveASTVisitor {
   int Count = 0;
   const SyntaxTree::Impl &Tree;
   NodeCountVisitor(const SyntaxTree::Impl &Tree) : Tree(Tree) {}
   bool TraverseDecl(Decl *D) {
-if (isNodeExcluded(Tree.AST.getSourceManager(), D))
+if (isNodeExcluded(Tree.AST.getSourceManager(), D) || isDeclExcluded(D))
   return true;
 ++Count;
 RecursiveASTVisitor::TraverseDecl(D);
 return true;
   }
   bool TraverseStmt(Stmt *S) {
-if (isNodeExcluded(Tree.AST.getSourceManager(), S))
+if (S)
+  S = S->IgnoreImplicit();
+if (isNodeExcluded(Tree.AST.getSourceManager(), S) || isStmtExcluded(S))
   return true;
 ++Count;
 RecursiveASTVisitor::TraverseStmt(S);
@@ -233,15 +246,17 @@
   N.Height = std::max(N.Height, 1 + Tree.getNode(Child).Height);
   }
   bool TraverseDecl(Decl *D) {
-if (isNodeExcluded(Tree.AST.getSourceManager(), D))
+if (isNodeExcluded(Tree.AST.getSourceManager(), D) || isDeclExcluded(D))
   return true;
 auto SavedState = PreTraverse(D);
 RecursiveASTVisitor::TraverseDecl(D);
 PostTraverse(SavedState);
 return true;
   }
   bool TraverseStmt(Stmt *S) {
-if (isNodeExcluded(Tree.AST.getSourceManager(), S))
+if (S)
+  S = S->IgnoreImplicit();
+if (isNodeExcluded(Tree.AST.getSourceManager(), S) || isStmtExcluded(S))
   return true;
 auto SavedState = PreTraverse(S);
 RecursiveASTVisitor::TraverseStmt(S);
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36185: [clang-diff] Fix similarity computation

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109424.
johannes added a comment.

add test for Options.MaxSize


https://reviews.llvm.org/D36185

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-opt.cpp
  test/Tooling/clang-diff-topdown.cpp

Index: test/Tooling/clang-diff-topdown.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-topdown.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the top-down matching of identical subtrees only.
+
+#ifndef DEST
+
+void f1()
+{
+  // Match some subtree of height greater than 2.
+  // CHECK: Match CompoundStmt(3) to CompoundStmt(3)
+  // CHECK: Match CompoundStmt(4) to CompoundStmt(4)
+  // CHECK: Match NullStmt(5) to NullStmt(5)
+  {{;}}
+
+  // Don't match subtrees that are smaller.
+  // CHECK-NOT: Match CompoundStmt(6)
+  // CHECK-NOT: Match NullStmt(7)
+  {;}
+
+  // Greedy approach - use the first matching subtree when there are multiple
+  // identical subtrees.
+  // CHECK: Match CompoundStmt(8) to CompoundStmt(8)
+  // CHECK: Match CompoundStmt(9) to CompoundStmt(9)
+  // CHECK: Match NullStmt(10) to NullStmt(10)
+  {{;;}}
+}
+
+#else
+
+void f1() {
+
+  {{;}}
+
+  {;}
+
+  {{;;}}
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(11)
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(12)
+  // CHECK-NOT: Match {{.*}} to NullStmt(13)
+  {{;;}}
+
+  // CHECK-NOT: Match {{.*}} to NullStmt(14)
+  ;
+}
+
+#endif
Index: test/Tooling/clang-diff-opt.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-opt.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=10 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the behaviour of the matching according to the optimal tree edit
+// distance, implemented with Zhang and Shasha's algorithm.
+// Just for testing we use a tiny value of 10 for maxsize. Subtrees bigger than
+// this size will not be processed by the optimal algorithm.
+
+#ifndef DEST
+
+void f1() { {;} {{;}} }
+
+void f2() { {;} {{;}} }
+
+void f3() { {;} {{;;}} }
+
+#else
+
+void f1() {
+// Jaccard similarity = 3 / (5 + 4 - 3) = 3 / 6 >= 0.5
+// The optimal matching algorithm should move the ; into the outer block
+// CHECK: Match CompoundStmt(2) to CompoundStmt(2)
+// CHECK-NOT: Match CompoundStmt(3)
+// CHECK-NEXT: Match NullStmt(4) to NullStmt(3)
+  ; {{;}}
+}
+ 
+void f2() {
+  // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5
+  // As none of the subtrees is bigger than 10 nodes, the optimal algorithm
+  // will be run.
+  // CHECK: Match NullStmt(11) to NullStmt(9)
+  ;; {{;}}
+}
+
+void f3() {
+  // Jaccard similarity = 8 / (11 + 11 - 8) >= 0.5
+  // As the subtrees are bigger than 10 nodes, the optimal algorithm will not
+  // be run.
+  // CHECK: Delete NullStmt(22)
+  ;; {{;;}}
+}
+#endif
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=0 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the bottom-up matching, with maxsize set to 0, so that the optimal matching will never be applied.
+
+#ifndef DEST
+
+void f1() { ; {{;}} }
+void f2() { ;; {{;}} }
+
+#else
+
+// Jaccard similarity threshold is 0.5.
+
+void f1() {
+// CompoundStmt: 3 matched descendants, subtree sizes 4 and 5
+// Jaccard similarity = 3 / (4 + 5 - 3) = 3 / 6 >= 0.5
+// CHECK: Match FunctionDecl: f1(void (void))(1) to FunctionDecl: f1(void (void))(1)
+// CHECK: Match CompoundStmt(2) to CompoundStmt(2)
+// CHECK: Match CompoundStmt(4) to CompoundStmt(3)
+// CHECK: Match CompoundStmt(5) to CompoundStmt(4)
+// CHECK: Match NullStmt(6) to NullStmt(5)
+  {{;}} ;;
+}
+
+void f2() {
+// CompoundStmt: 3 matched descendants, subtree sizes 4 and 5
+// Jaccard similarity = 3 / (5 + 6 - 3) = 3 / 8 < 0.5
+// CHECK-NOT: Match FunctionDecl(9)
+// CHECK-NOT: Match CompoundStmt(10)
+// CHECK: Match CompoundStmt(11) to CompoundStmt(10)
+// CHECK: Match CompoundStmt(12) to CompoundStmt(11)
+// CHECK: Match NullStmt(13) to NullStmt(12)
+// CHECK-NOT: Match NullStmt(13)
+  {{;}} ;;;
+}
+ 
+#endif
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -68,7 +68,8 @@
   // Compute ChangeKind for each node based on similarity.
   void computeChangeKinds(Mapping &M);
 
-  NodeId getMapped(const std::unique_ptr &Tree, NodeId Id) const {
+  NodeId getMapped(const st

[PATCH] D36186: [clang-diff] Improve and test getNodeValue

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109425.
johannes added a comment.

NFC renames


https://reviews.llvm.org/D36186

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-bottomup.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -315,6 +315,18 @@
   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]);
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- test/Tooling/clang-diff-bottomup.cpp
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -16,7 +16,7 @@
 void f1() {
 // CompoundStmt: 3 matched descendants, subtree sizes 4 and 5
 // Jaccard similarity = 3 / (4 + 5 - 3) = 3 / 6 >= 0.5
-// CHECK: Match FunctionDecl: f1(void (void))(1) to FunctionDecl: f1(void (void))(1)
+// CHECK: Match FunctionDecl: f1(void ())(1) to FunctionDecl: f1(void ())(1)
 // CHECK: Match CompoundStmt(2) to CompoundStmt(2)
 // CHECK: Match CompoundStmt(4) to CompoundStmt(3)
 // CHECK: Match CompoundStmt(5) to CompoundStmt(4)
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -11,18 +11,18 @@
 }
 }
 
-// CHECK: Match DeclRefExpr: foo{{.*}} to DeclRefExpr: inner::foo
+// CHECK: Match DeclRefExpr: src::foo{{.*}} to DeclRefExpr: dst::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;
+// CHECK: Match TypedefDecl: src::nat;unsigned int;{{.*}} to TypedefDecl: dst::nat;unsigned int;
 typedef unsigned nat;
 
-// CHECK: Match VarDecl: p(int){{.*}} to VarDecl: prod(double)
-// CHECK: Update VarDecl: p(int){{.*}} to prod(double)
+// CHECK: Match VarDecl: src::p(int){{.*}} to VarDecl: dst::prod(double)
+// CHECK: Update VarDecl: src::p(int){{.*}} to dst::prod(double)
 // CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
 double prod = 1 * 2 * 10;
 // CHECK: Update DeclRefExpr
@@ -44,7 +44,7 @@
 
 namespace {
 // match with parents of different type
-// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: f1
+// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: (anonymous namespace)::f1
 void f1() {{ (void) __func__;;; }}
 }
 
Index: test/Tooling/clang-diff-ast.cpp
===
--- test/Tooling/clang-diff-ast.cpp
+++ test/Tooling/clang-diff-ast.cpp
@@ -5,34 +5,43 @@
 // CHECK: {{^}} NamespaceDecl: test;(
 namespace test {
 
-// CHECK: {{^}}  FunctionDecl: f(
+// CHECK: {{^}} FunctionDecl: test::f(
 // CHECK: CompoundStmt(
 void f() {
   // CHECK: VarDecl: i(int)(
   // CHECK: IntegerLiteral: 1
   auto i = 1;
+  // CHECK: FloatingLiteral: 1.5(
+  auto r = 1.5;
+  // CHECK: CXXBoolLiteralExpr: true(
+  auto b = true;
   // CHECK: CallExpr(
   // CHECK-NOT: ImplicitCastExpr
-  // CHECK-NEXT: DeclRefExpr: f(
+  // CHECK: DeclRefExpr: test::f(
   f();
+  // CHECK: UnaryOperator: ++(
+  ++i;
   // CHECK: BinaryOperator: =(
   i = i;
 }
 
 } // end namespace test
 
+// CHECK: UsingDirectiveDecl: test(
+using namespace test;
+
 // CHECK: TypedefDecl: nat;unsigned int;(
 typedef unsigned nat;
 // CHECK: TypeAliasDecl: real;double;(
 using real = double;
 
 class Base {
 };
 
-// CHECK: CXXRecordDecl: X;class X;(
+// CHECK: CXXRecordDecl: X;X;(
 class X : Base {
   int m;
-  // CHECK: CXXMethodDecl: foo(const char *(int))(
+  // CHECK: CXXMethodDecl: X::foo(const char *(int))(
   // CHECK: ParmVarDecl: i(int)(
   const char *foo(int i) {
 if (i == 0)
@@ -44,9 +53,9 @@
 
   // CHECK: AccessSpecDecl: public(
 public:
-  // CHECK: CXXConstructorDecl: X(void (char, int))(
+  // CHECK: CXXConstructorDecl: X::X(void (char, int))Base,X::m,(
   X(char, int) : Base(), m(0) {
-// CHECK: MemberExpr(
+// CHECK: MemberExpr: X::m(
 int x = m;
   }
 };
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -149,6 +149,8 @

[PATCH] D36186: [clang-diff] Improve and test getNodeValue

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109426.
johannes added a comment.

renamse, NFC


https://reviews.llvm.org/D36186

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-opt.cpp
  test/Tooling/clang-diff-topdown.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -315,6 +315,18 @@
   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]);
Index: test/Tooling/clang-diff-topdown.cpp
===
--- test/Tooling/clang-diff-topdown.cpp
+++ test/Tooling/clang-diff-topdown.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %t.src.cpp
 // RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
-// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- -std=c++11 | FileCheck %s
 //
 // Test the top-down matching of identical subtrees only.
 
@@ -27,8 +27,19 @@
   {{;;}}
 }
 
+int x;
+
+namespace src {
+  int x;
+  int x1 = x + 1;
+  int x2 = ::x + 1;
+}
+
+class A { int x = 1 + 1; void f() { int x1 = x; } };
+
 #else
 
+
 void f1() {
 
   {{;}}
@@ -45,4 +56,28 @@
   ;
 }
 
+int x;
+
+namespace dst {
+  int x;
+  // CHECK: Match DeclRefExpr: :x(17) to DeclRefExpr: :x(22)
+  int x1 = x + 1;
+  // CHECK: Match DeclRefExpr: x(21) to DeclRefExpr: x(26)
+  int x2 = ::x + 1;
+}
+
+class B {
+  // Only the class name changed; it is not included in the field value,
+  // therefore there is no update.
+  // CHECK: Match FieldDecl: :x(int)(24) to FieldDecl: :x(int)(29)
+  // CHECK-NOT: Update FieldDecl: :x(int)(24)
+  int x = 1+1;
+  void f() {
+// CHECK: Match MemberExpr: :x(32) to MemberExpr: :x(37)
+// CHECK-NOT: Update MemberExpr: :x(32)
+int x1 = B::x;
+  }
+
+};
+
 #endif
Index: test/Tooling/clang-diff-opt.cpp
===
--- test/Tooling/clang-diff-opt.cpp
+++ test/Tooling/clang-diff-opt.cpp
@@ -25,7 +25,7 @@
 // CHECK-NEXT: Match NullStmt(4) to NullStmt(3)
   ; {{;}}
 }
- 
+
 void f2() {
   // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5
   // As none of the subtrees is bigger than 10 nodes, the optimal algorithm
@@ -41,4 +41,5 @@
   // CHECK: Delete NullStmt(22)
   ;; {{;;}}
 }
+ 
 #endif
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- test/Tooling/clang-diff-bottomup.cpp
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -16,7 +16,7 @@
 void f1() {
 // CompoundStmt: 3 matched descendants, subtree sizes 4 and 5
 // Jaccard similarity = 3 / (4 + 5 - 3) = 3 / 6 >= 0.5
-// CHECK: Match FunctionDecl: f1(void (void))(1) to FunctionDecl: f1(void (void))(1)
+// CHECK: Match FunctionDecl: f1(void ())(1) to FunctionDecl: f1(void ())(1)
 // CHECK: Match CompoundStmt(2) to CompoundStmt(2)
 // CHECK: Match CompoundStmt(4) to CompoundStmt(3)
 // CHECK: Match CompoundStmt(5) to CompoundStmt(4)
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -11,18 +11,18 @@
 }
 }
 
-// CHECK: Match DeclRefExpr: foo{{.*}} to DeclRefExpr: inner::foo
+// 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;
+// CHECK: Match TypedefDecl: :nat;unsigned int;{{.*}} to TypedefDecl: :nat;unsigned int;
 typedef unsigned nat;
 
-// CHECK: Match VarDecl: p(int){{.*}} to VarDecl: prod(double)
-// CHECK: Update VarDecl: p(int){{.*}} to prod(double)
+// CHECK: Match VarDecl: :p(int){{.*}} to VarDecl: :prod(double)
+// CHECK: Update VarDecl: :p(int){{.*}} to :prod(double)
 // CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
 double prod = 1 * 2 * 10;
 // CHECK: Update DeclRefExpr
@@ -44,7 +44,7 @@
 
 namespace {
 // match with parents of different type
-// CHECK: Match FunctionDecl: f1{{.*}} to 

[PATCH] D36176: [clang-diff] Fix some errors and inconsistencies

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: include/clang/Tooling/ASTDiff/ASTDiff.h:96
   : TreeImpl(llvm::make_unique(this, Node, AST)) {}
+  SyntaxTree(const SyntaxTree &Tree) = delete;
   ~SyntaxTree();

arphaman wrote:
> johannes wrote:
> > arphaman wrote:
> > > It might be better to add a move constructor which would disable copy 
> > > constructors.
> > ASTDiff::getMapped internally uses the address of the SyntaxTree that is 
> > passed as parameter.
> > So the tree must be exactly the same as the one that is passed to the 
> > constructor of ASTDiff. 
> > 
> > This is quite bad. I will change ASTDiff::getMapped to accept a boolean or 
> > split it into two methods
> > Should I still add a move constructor?
> Can't you just use the `TreeImpl` pointer value? That should be the same even 
> after you've moved `SyntaxTree` with the move constructor.
ok perfect


https://reviews.llvm.org/D36176



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36177: [clang-diff] Add commandline arguments.

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D36177#828909, @arphaman wrote:

> There should be a test that ensures that `stop-after` works and `extra-arg`s 
> are correctly passed to the compiler.


`stop-after` is not tested yet. It is also broken in this patch, I will move it 
to a later patch, then it is easier to test.


https://reviews.llvm.org/D36177



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36184: [clang-diff] Filter AST nodes

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D36184#828866, @klimek wrote:

> Why? Also, missing tests.


implicit nodes are noisy / they generally don't add information; I guess one 
could also keep them.

I excluded nodes outside of the main file are because the visualisation only 
works with single files for now.
That will change, same as with macros.


https://reviews.llvm.org/D36184



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36185: [clang-diff] Fix similarity computation

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: test/Tooling/clang-diff-bottomup.cpp:3
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -m -no-compilation-database -s=0 %t.src.cpp %t.dst.cpp | 
FileCheck %s
+//

klimek wrote:
> Instead of using -no-compilation-database most tools support adding -- (args) 
> for using a fixed compilation database. Any specific reason to deviate from 
> that for clang-diff?
ah I actually wasn't aware of that, done


https://reviews.llvm.org/D36185



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36186: [clang-diff] Improve and test getNodeValue

2017-08-02 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: test/Tooling/clang-diff-ast.cpp:32
+
+// CHECK: TypedefDecl: nat;unsigned int;(
+typedef unsigned nat;

klimek wrote:
> For my curiosity: why are there semicolons here? Is the format documented 
> somewhere?
To avoid collisions (the value should be unique per TypedefDecl in this 
example), the second one is unnecessary I guess. It is not documented, If it 
works out I want to move to StmtDataCollector for node comparison and keep 
these just for visualisation / debugging.


https://reviews.llvm.org/D36186



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36178: [clang-diff] Move the JSON export function to clang-diff

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: test/Tooling/clang-diff-json.cpp:1
+// RUN: clang-diff -ast-dump %s -- | python -m json.tool | FileCheck %s
+

arphaman wrote:
> I think you have to use `%python` instead of `python`, like LLVM tests do.
ok
So I have to use python 2.7? (for the test in https://reviews.llvm.org/D36182)


https://reviews.llvm.org/D36178



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36177: [clang-diff] Add commandline arguments.

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109502.
johannes added a comment.

fix tests


https://reviews.llvm.org/D36177

Files:
  test/Tooling/clang-diff-args.test
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -28,12 +28,6 @@
 cl::desc("Print the internal representation of the AST as JSON."),
 cl::init(false), cl::cat(ClangDiffCategory));
 
-static cl::opt 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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -43,43 +37,83 @@
 cl::Optional,
 cl::cat(ClangDiffCategory));
 
-static std::unique_ptr getAST(const StringRef Filename) {
+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 =
+  llvm::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 (!NoCompilationDatabase)
-Compilations =
-CompilationDatabase::autoDetectFromSource(Filename, ErrorMessage);
-  if (!Compilations) {
-if (!NoCompilationDatabase)
+  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 = llvm::make_unique(
-".", std::vector());
+  Compilations =
+  llvm::make_unique(
+  ".", std::vector());
+}
   }
+  addExtraArgs(Compilations);
   std::array Files = {{Filename}};
-  ClangTool Tool(*Compilations, Files);
+  ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
   std::vector> ASTs;
   Tool.buildASTs(ASTs);
   if (ASTs.size() != Files.size())
 return nullptr;
   return std::move(ASTs[0]);
 }
 
 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) {
 if (!DestinationPath.empty()) {
   llvm::errs() << "Error: Please specify exactly one filename.\n";
   return 1;
 }
-std::unique_ptr AST = getAST(SourcePath);
+std::unique_ptr AST = getAST(CommonCompilations, SourcePath);
 if (!AST)
   return 1;
 diff::SyntaxTree Tree(AST->getASTContext());
@@ -92,12 +126,14 @@
 return 1;
   }
 
-  std::unique_ptr Src = getAST(SourcePath);
-  std::unique_ptr Dst = getAST(DestinationPath);
+  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;
   diff::SyntaxTree SrcTree(Src->getASTContext());
   diff::SyntaxTree DstTree(Dst->getASTContext());
   diff::ASTDiff DiffTool(SrcTree, DstTree, Options);
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic

[PATCH] D36181: [clang-diff] Make printing of matches optional

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109503.
johannes added a comment.

fix tests


https://reviews.llvm.org/D36181

Files:
  test/Tooling/clang-diff-args.test
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp


Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -33,6 +33,10 @@
 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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -267,7 +271,7 @@
 
   for (diff::NodeId Dst : DstTree) {
 diff::NodeId Src = Diff.getMapped(DstTree, Dst);
-if (Src.isValid()) {
+if (PrintMatches && Src.isValid()) {
   llvm::outs() << "Match ";
   printNode(llvm::outs(), SrcTree, Src);
   llvm::outs() << " to ";
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %T/src.cpp
 // RUN: %clang_cc1 -E %s > %T/dst.cpp -DDEST
-// RUN: clang-diff %T/src.cpp %T/dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches %T/src.cpp %T/dst.cpp -- | FileCheck %s
 
 #ifndef DEST
 namespace src {
Index: test/Tooling/clang-diff-args.test
===
--- test/Tooling/clang-diff-args.test
+++ test/Tooling/clang-diff-args.test
@@ -6,3 +6,7 @@
 RUN: clang-diff -ast-dump -extra-arg=-Da=X%t.cpp -- 2>&1 | FileCheck %s
 RUN: clang-diff -ast-dump -extra-arg-before=-Da=X %t.cpp -- 2>&1 | FileCheck %s
 RUN: clang-diff -ast-dump %t.cpp -- 2>&1 -Da=X | FileCheck %s
+
+NOMATCH-CHECK-NOT: {{.}}
+RUN: clang-diff %S/clang-diff-ast.cpp %S/clang-diff-ast.cpp -- 2>&1 -std=c++11 
\
+RUN: | FileCheck -check-prefix=NOMATCH-CHECK -allow-empty %s


Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -33,6 +33,10 @@
 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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -267,7 +271,7 @@
 
   for (diff::NodeId Dst : DstTree) {
 diff::NodeId Src = Diff.getMapped(DstTree, Dst);
-if (Src.isValid()) {
+if (PrintMatches && Src.isValid()) {
   llvm::outs() << "Match ";
   printNode(llvm::outs(), SrcTree, Src);
   llvm::outs() << " to ";
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %T/src.cpp
 // RUN: %clang_cc1 -E %s > %T/dst.cpp -DDEST
-// RUN: clang-diff %T/src.cpp %T/dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches %T/src.cpp %T/dst.cpp -- | FileCheck %s
 
 #ifndef DEST
 namespace src {
Index: test/Tooling/clang-diff-args.test
===
--- test/Tooling/clang-diff-args.test
+++ test/Tooling/clang-diff-args.test
@@ -6,3 +6,7 @@
 RUN: clang-diff -ast-dump -extra-arg=-Da=X%t.cpp -- 2>&1 | FileCheck %s
 RUN: clang-diff -ast-dump -extra-arg-before=-Da=X %t.cpp -- 2>&1 | FileCheck %s
 RUN: clang-diff -ast-dump %t.cpp -- 2>&1 -Da=X | FileCheck %s
+
+NOMATCH-CHECK-NOT: {{.}}
+RUN: clang-diff %S/clang-diff-ast.cpp %S/clang-diff-ast.cpp -- 2>&1 -std=c++11 \
+RUN: | FileCheck -check-prefix=NOMATCH-CHECK -allow-empty %s
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36183: [clang-diff] Simplify mapping

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109504.
johannes added a comment.

merge parent changes


https://reviews.llvm.org/D36183

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp

Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -38,8 +38,15 @@
   return "foo";
 return 0;
   }
-  // CHECK: Delete AccessSpecDecl: public
   X(){};
-  // CHECK: Delete CXXMethodDecl
 };
 }
+
+namespace {
+// match with parents of different type
+// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: f1
+void f1() {{ (void) __func__;;; }}
+}
+
+// CHECK: Delete AccessSpecDecl: public
+// CHECK: Delete CXXMethodDecl
Index: test/Tooling/Inputs/clang-diff-basic-src.cpp
===
--- test/Tooling/Inputs/clang-diff-basic-src.cpp
+++ test/Tooling/Inputs/clang-diff-basic-src.cpp
@@ -26,3 +26,5 @@
   int id(int i) { return i; }
 };
 }
+
+void f1() {{ (void) __func__;;; }}
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -34,48 +34,23 @@
   Mapping() = default;
   Mapping(Mapping &&Other) = default;
   Mapping &operator=(Mapping &&Other) = default;
-  Mapping(int Size1, int Size2) {
-// Maximum possible size after patching one tree.
-int Size = Size1 + Size2;
-SrcToDst = llvm::make_unique[]>(Size);
-DstToSrc = llvm::make_unique[]>(Size);
+
+  Mapping(size_t Size) {
+SrcToDst = llvm::make_unique(Size);
+DstToSrc = llvm::make_unique(Size);
   }
 
   void link(NodeId Src, NodeId Dst) {
-SrcToDst[Src].push_back(Dst);
-DstToSrc[Dst].push_back(Src);
-  }
-
-  NodeId getDst(NodeId Src) const {
-if (hasSrc(Src))
-  return SrcToDst[Src][0];
-return NodeId();
-  }
-  NodeId getSrc(NodeId Dst) const {
-if (hasDst(Dst))
-  return DstToSrc[Dst][0];
-return NodeId();
-  }
-  const SmallVector &getAllDsts(NodeId Src) const {
-return SrcToDst[Src];
-  }
-  const SmallVector &getAllSrcs(NodeId Dst) const {
-return DstToSrc[Dst];
-  }
-  bool hasSrc(NodeId Src) const { return !SrcToDst[Src].empty(); }
-  bool hasDst(NodeId Dst) const { return !DstToSrc[Dst].empty(); }
-  bool hasSrcDst(NodeId Src, NodeId Dst) const {
-for (NodeId DstId : SrcToDst[Src])
-  if (DstId == Dst)
-return true;
-for (NodeId SrcId : DstToSrc[Dst])
-  if (SrcId == Src)
-return true;
-return false;
+SrcToDst[Src] = Dst, DstToSrc[Dst] = Src;
   }
 
+  NodeId getDst(NodeId Src) const { return SrcToDst[Src]; }
+  NodeId getSrc(NodeId Dst) const { return DstToSrc[Dst]; }
+  bool hasSrc(NodeId Src) const { return getDst(Src).isValid(); }
+  bool hasDst(NodeId Dst) const { return getSrc(Dst).isValid(); }
+
 private:
-  std::unique_ptr[]> SrcToDst, DstToSrc;
+  std::unique_ptr SrcToDst, DstToSrc;
 };
 } // end anonymous namespace
 
@@ -104,8 +79,6 @@
   // Returns true if the two subtrees are identical.
   bool identical(NodeId Id1, NodeId Id2) const;
 
-  bool canBeAddedToMapping(const Mapping &M, NodeId Id1, NodeId Id2) const;
-
   // Returns false if the nodes must not be mached.
   bool isMatchingPossible(NodeId Id1, NodeId Id2) const;
 
@@ -711,23 +684,6 @@
   return true;
 }
 
-bool ASTDiff::Impl::canBeAddedToMapping(const Mapping &M, NodeId Id1,
-NodeId Id2) const {
-  assert(isMatchingPossible(Id1, Id2) &&
- "Matching must be possible in the first place.");
-  if (M.hasSrcDst(Id1, Id2))
-return false;
-  if (Options.EnableMatchingWithUnmatchableParents)
-return true;
-  const Node &N1 = T1.getNode(Id1);
-  const Node &N2 = T2.getNode(Id2);
-  NodeId P1 = N1.Parent;
-  NodeId P2 = N2.Parent;
-  // Only allow matching if parents can be matched.
-  return (P1.isInvalid() && P2.isInvalid()) ||
- (P1.isValid() && P2.isValid() && isMatchingPossible(P1, P2));
-}
-
 bool ASTDiff::Impl::isMatchingPossible(NodeId Id1, NodeId Id2) const {
   return Options.isMatchingAllowed(T1.getNode(Id1), T2.getNode(Id2));
 }
@@ -750,7 +706,7 @@
   for (const auto Tuple : R) {
 NodeId Src = Tuple.first;
 NodeId Dst = Tuple.second;
-if (canBeAddedToMapping(M, Src, Dst))
+if (!M.hasSrc(Src) && !M.hasDst(Dst))
   M.link(Src, Dst);
   }
 }
@@ -803,7 +759,7 @@
 if (Matched || !MatchedChildren)
   continue;
 NodeId Id2 = findCandidate(M, Id1);
-if (Id2.isValid() && canBeAddedToMapping(M, Id1, Id2)) {
+if (Id2.isValid()) {
   M.link(Id1, Id2);
   addOptimalMapping(M, Id1, Id2);
 }
@@ -814,7 +770,7 @@
   PriorityList L1(T1);
   PriorityList L2(T2);
 
-  Mapping M(T1.getSize(), T2.getSize());
+  Mapping M(T1.getSize() + T2.getSize());
 
   L1.push(T1.g

[PATCH] D36185: [clang-diff] Fix similarity computation

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109505.
johannes added a comment.

merge parent changes


https://reviews.llvm.org/D36185

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-opt.cpp
  test/Tooling/clang-diff-topdown.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -50,6 +50,11 @@
 cl::Optional,
 cl::cat(ClangDiffCategory));
 
+static cl::opt StopAfter("stop-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));
 
@@ -424,6 +429,14 @@
   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";
+  return 1;
+}
+  }
   diff::SyntaxTree SrcTree(Src->getASTContext());
   diff::SyntaxTree DstTree(Dst->getASTContext());
   diff::ASTDiff Diff(SrcTree, DstTree, Options);
Index: test/Tooling/clang-diff-topdown.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-topdown.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the top-down matching of identical subtrees only.
+
+#ifndef DEST
+
+void f1()
+{
+  // Match some subtree of height greater than 2.
+  // CHECK: Match CompoundStmt(3) to CompoundStmt(3)
+  // CHECK: Match CompoundStmt(4) to CompoundStmt(4)
+  // CHECK: Match NullStmt(5) to NullStmt(5)
+  {{;}}
+
+  // Don't match subtrees that are smaller.
+  // CHECK-NOT: Match CompoundStmt(6)
+  // CHECK-NOT: Match NullStmt(7)
+  {;}
+
+  // Greedy approach - use the first matching subtree when there are multiple
+  // identical subtrees.
+  // CHECK: Match CompoundStmt(8) to CompoundStmt(8)
+  // CHECK: Match CompoundStmt(9) to CompoundStmt(9)
+  // CHECK: Match NullStmt(10) to NullStmt(10)
+  {{;;}}
+}
+
+#else
+
+void f1() {
+
+  {{;}}
+
+  {;}
+
+  {{;;}}
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(11)
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(12)
+  // CHECK-NOT: Match {{.*}} to NullStmt(13)
+  {{;;}}
+
+  // CHECK-NOT: Match {{.*}} to NullStmt(14)
+  ;
+}
+
+#endif
Index: test/Tooling/clang-diff-opt.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-opt.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=10 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the behaviour of the matching according to the optimal tree edit
+// distance, implemented with Zhang and Shasha's algorithm.
+// Just for testing we use a tiny value of 10 for maxsize. Subtrees bigger than
+// this size will not be processed by the optimal algorithm.
+
+#ifndef DEST
+
+void f1() { {;} {{;}} }
+
+void f2() { {;} {{;}} }
+
+void f3() { {;} {{;;}} }
+
+#else
+
+void f1() {
+// Jaccard similarity = 3 / (5 + 4 - 3) = 3 / 6 >= 0.5
+// The optimal matching algorithm should move the ; into the outer block
+// CHECK: Match CompoundStmt(2) to CompoundStmt(2)
+// CHECK-NOT: Match CompoundStmt(3)
+// CHECK-NEXT: Match NullStmt(4) to NullStmt(3)
+  ; {{;}}
+}
+ 
+void f2() {
+  // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5
+  // As none of the subtrees is bigger than 10 nodes, the optimal algorithm
+  // will be run.
+  // CHECK: Match NullStmt(11) to NullStmt(9)
+  ;; {{;}}
+}
+
+void f3() {
+  // Jaccard similarity = 8 / (11 + 11 - 8) >= 0.5
+  // As the subtrees are bigger than 10 nodes, the optimal algorithm will not
+  // be run.
+  // CHECK: Delete NullStmt(22)
+  ;; {{;;}}
+}
+#endif
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=0 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the bottom-up matching, with maxsize set to 0, so that the optimal matching will never be applied.
+
+#ifndef DEST
+
+void f1() { ; {{;}} }
+void f2() { ;; {{;}} }
+
+#else
+
+// Jaccard similarity threshold is 0.5.
+
+void f1() {
+// CompoundStmt: 3 m

[PATCH] D36178: [clang-diff] Move the JSON export function to clang-diff

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: test/Tooling/clang-diff-json.cpp:1
+// RUN: clang-diff -ast-dump %s -- | python -m json.tool | FileCheck %s
+

johannes wrote:
> arphaman wrote:
> > I think you have to use `%python` instead of `python`, like LLVM tests do.
> ok
> So I have to use python 2.7? (for the test in https://reviews.llvm.org/D36182)
It looks like %python is expanded in the wrong way.
I get the same as when using %Sython, the directory of the test case is 
substituted


https://reviews.llvm.org/D36178



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36176: [clang-diff] Fix some errors and inconsistencies

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added a comment.

In https://reviews.llvm.org/D36176#830341, @arphaman wrote:

> LGTM. Although I'm not 100% sure it's fully NFC, so if you can't come up with 
> a test please justify why.


There are changes that affect the output.
Firstly the computation of the rightmost descendant; the added assertion fails 
every time with the old version.
Secondly, the change around line 811 prevents root nodes from being mapped if 
they are already mapped. When comparing translation units this happens only if 
the two trees are identical (in this case the output would not change). 
Otherwise, it would require some change in the client to demonstrate how the 
behaviour changed. One needs to compare AST Nodes other than entire translation 
units. At some point there will be something that uses this feature so we cover 
it too (or remove the feature). So I'd keep it like this


https://reviews.llvm.org/D36176



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36178: [clang-diff] Move the JSON export function to clang-diff

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109537.
johannes added a comment.

use %python in tests


https://reviews.llvm.org/D36178

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-json.cpp
  test/lit.cfg
  test/lit.site.cfg.in
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -94,6 +94,65 @@
   return std::move(ASTs[0]);
 }
 
+static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
+
+static void printJsonString(raw_ostream &OS, const StringRef Str) {
+  for (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.ASTNode);
+  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);
+  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 << "]}";
+}
+
 int main(int argc, const char **argv) {
   std::string ErrorMessage;
   std::unique_ptr CommonCompilations =
@@ -117,7 +176,11 @@
 if (!AST)
   return 1;
 diff::SyntaxTree Tree(AST->getASTContext());
-Tree.printAsJson(llvm::outs());
+llvm::outs() << R"({"filename":")";
+printJsonString(llvm::outs(), SourcePath);
+llvm::outs() << R"(","root":)";
+printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
+llvm::outs() << "}\n";
 return 0;
   }
 
Index: test/lit.site.cfg.in
===
--- test/lit.site.cfg.in
+++ test/lit.site.cfg.in
@@ -25,6 +25,7 @@
 config.enable_backtrace = @ENABLE_BACKTRACES@
 config.host_arch = "@HOST_ARCH@"
 config.enable_abi_breaking_checks = "@LLVM_ENABLE_ABI_BREAKING_CHECKS@"
+config.python_executable = "@PYTHON_EXECUTABLE@"
 
 # Support substitution of the tools and libs dirs with user parameters. This is
 # used when we can't determine the tool dir at configuration time.
Index: test/lit.cfg
===
--- test/lit.cfg
+++ test/lit.cfg
@@ -276,6 +276,7 @@
 config.substitutions.append( ('%itanium_abi_triple', makeItaniumABITriple(config.target_triple)) )
 config.substitutions.append( ('%ms_abi_triple', makeMSABITriple(config.target_triple)) )
 config.substitutions.append( ('%resource_dir', getClangBuiltinIncludeDir(config.clang)) )
+config.substitutions.append( ('%python', config.python_executable) )
 
 # The host triple might not be set, at least if we're compiling clang from
 # an already installed llvm.
Index: test/Tooling/clang-diff-json.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-json.cpp
@@ -0,0 +1,27 @@
+// RUN: clang-diff -ast-dump %s -- \
+// RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \
+// RUN: | FileCheck %s
+
+// CHECK: "begin": 294,
+// CHECK: "type": "CXXRecordDecl",
+// CHECK: "type": "FieldDecl",
+// CHECK: "end": 314,
+class A {
+  int x;
+};
+
+// CHECK: "children": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "begin":
+// CHECK-NEXT: "children": []
+// CHECK-NEXT: "end":
+// CHECK-NEXT: "id":
+// CHECK-NEXT: "type": "CharacterLiteral"
+// CHECK-NEXT: }
+// CHECK: ]
+// CHECK: "type": "VarDecl",
+char nl = '\n';
+
+// CHECK: "value": "abc \n\t\u\u001f\u0123 \ub370\ubc15"
+char s[] = "abc \n\t\0\x1f\u0123 데박";
+
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -176,9 +176,6 @@
   void printTree(NodeId Root) const;
   void printTree(raw_ostream &OS, NodeId Root) const;
 
-  void printAsJsonImpl(raw_ostream &OS) const;
-  void printNodeA

[PATCH] D36180: [clang-diff] Add option to dump the AST, one node per line

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109538.
johannes added a comment.

merge update


https://reviews.llvm.org/D36180

Files:
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-json.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -25,9 +25,14 @@
 
 static cl::opt
 ASTDump("ast-dump",
-cl::desc("Print the internal representation of the AST as JSON."),
+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 SourcePath(cl::Positional, cl::desc(""),
cl::Required,
cl::cat(ClangDiffCategory));
@@ -166,6 +171,15 @@
   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) {
@@ -213,15 +227,19 @@
 
   addExtraArgs(CommonCompilations);
 
-  if (ASTDump) {
+  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":)";
Index: test/Tooling/clang-diff-json.cpp
===
--- test/Tooling/clang-diff-json.cpp
+++ test/Tooling/clang-diff-json.cpp
@@ -1,11 +1,11 @@
-// RUN: clang-diff -ast-dump %s -- \
+// RUN: clang-diff -ast-dump-json %s -- \
 // RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \
 // RUN: | FileCheck %s
 
-// CHECK: "begin": 294,
+// CHECK: "begin": 299,
 // CHECK: "type": "CXXRecordDecl",
 // CHECK: "type": "FieldDecl",
-// CHECK: "end": 314,
+// CHECK: "end": 319,
 class A {
   int x;
 };
Index: test/Tooling/clang-diff-ast.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-ast.cpp
@@ -0,0 +1,50 @@
+// RUN: clang-diff -ast-dump %s -- -std=c++11 | FileCheck %s
+
+
+// CHECK: {{^}}TranslationUnitDecl(0)
+// CHECK: {{^}} NamespaceDecl: test;(
+namespace test {
+
+// CHECK: {{^}}  FunctionDecl: f(
+// CHECK: CompoundStmt(
+void f() {
+  // CHECK: VarDecl: i(int)(
+  // CHECK: IntegerLiteral: 1
+  auto i = 1;
+  // CHECK: CallExpr(
+  // CHECK: DeclRefExpr: f(
+  f();
+  // CHECK: BinaryOperator: =(
+  i = i;
+}
+
+} // end namespace test
+
+// CHECK: TypedefDecl: nat;unsigned int;(
+typedef unsigned nat;
+// CHECK: TypeAliasDecl: real;double;(
+using real = double;
+
+class Base {
+};
+
+// CHECK: CXXRecordDecl: X;class X;(
+class X : Base {
+  int m;
+  // CHECK: CXXMethodDecl: foo(const char *(int))(
+  // CHECK: ParmVarDecl: i(int)(
+  const char *foo(int i) {
+if (i == 0)
+  // CHECK: StringLiteral: foo(
+  return "foo";
+return 0;
+  }
+
+  // CHECK: AccessSpecDecl: public(
+public:
+  // CHECK: CXXConstructorDecl: X(void (char, int))(
+  X(char, int) : Base(), m(0) {
+// CHECK: MemberExpr(
+int x = m;
+  }
+};
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36182: [clang-diff] Add HTML side-by-side diff output

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109539.
johannes added a comment.

make test work with python 2


https://reviews.llvm.org/D36182

Files:
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-html.py
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -37,6 +37,10 @@
 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));
@@ -105,6 +109,161 @@
 
 static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
 
+static const char HtmlDiffHeader[] = R"(
+
+
+
+
+span.d { color: red; }
+span.u { color: #cc00cc; }
+span.i { color: green; }
+span.m { font-weight: bold; }
+span   { font-weight: normal; color: black; }
+div.code {
+  width: 48%;
+  height: 98%;
+  overflow: scroll;
+  float: left;
+  padding: 0 0 0.5% 0.5%;
+  border: solid 2px LightGrey;
+  border-radius: 5px;
+}
+
+
+
+highlightStack = []
+function clearHighlight() {
+  while (highlightStack.length) {
+let [l, r] = highlightStack.pop()
+document.getElementById(l).style.backgroundColor = 'white'
+document.getElementById(r).style.backgroundColor = 'white'
+  }
+}
+function highlight(event) {
+  id = event.target['id']
+  doHighlight(id)
+}
+function doHighlight(id) {
+  clearHighlight()
+  source = document.getElementById(id)
+  if (!source.attributes['tid'])
+return
+  tid = source.attributes['tid'].value
+  target = document.getElementById(tid)
+  if (!target || source.parentElement && source.parentElement.classList.contains('code'))
+return
+  source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
+  highlightStack.push([id, tid])
+  source.scrollIntoView()
+  target.scrollIntoView()
+  location.hash = '#' + id
+}
+function scrollToBoth() {
+  doHighlight(location.hash.substr(1))
+}
+window.onload = scrollToBoth
+
+
+
+)";
+
+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";
+  }
+}
+
+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 (char C : Str) {
 switch (C) {
@@ -269,6 +428,19 @@
   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.getRo

[PATCH] D36176: [clang-diff] Fix some errors and inconsistencies

2017-08-03 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 109536.
johannes edited the summary of this revision.
johannes added a comment.

update commit message


https://reviews.llvm.org/D36176

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTDiffInternal.h
  lib/Tooling/ASTDiff/ASTDiff.cpp

Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -27,6 +27,7 @@
 namespace clang {
 namespace diff {
 
+namespace {
 /// Maps nodes of the left tree to ones on the right, and vice versa.
 class Mapping {
 public:
@@ -76,6 +77,7 @@
 private:
   std::unique_ptr[]> SrcToDst, DstToSrc;
 };
+} // end anonymous namespace
 
 class ASTDiff::Impl {
 public:
@@ -110,10 +112,6 @@
   // Returns false if the nodes must not be mached.
   bool isMatchingPossible(NodeId Id1, NodeId Id2) const;
 
-  // Adds all corresponding subtrees of the two nodes to the mapping.
-  // The two nodes must be identical.
-  void addIsomorphicSubTrees(Mapping &M, NodeId Id1, NodeId Id2) const;
-
   // Uses an optimal albeit slow algorithm to compute a mapping between two
   // subtrees, but only if both have fewer nodes than MaxSize.
   void addOptimalMapping(Mapping &M, NodeId Id1, NodeId Id2) const;
@@ -254,7 +252,10 @@
 Parent = PreviousParent;
 --Depth;
 Node &N = Tree.getMutableNode(MyId);
-N.RightMostDescendant = Id;
+N.RightMostDescendant = Id - 1;
+assert(N.RightMostDescendant >= 0 &&
+   N.RightMostDescendant < Tree.getSize() &&
+   "Rightmost descendant must be a valid tree node.");
 if (N.isLeaf())
   Tree.Leaves.push_back(MyId);
 N.Height = 1;
@@ -358,9 +359,7 @@
 }
 
 bool SyntaxTree::Impl::isInSubtree(NodeId Id, NodeId SubtreeRoot) const {
-  NodeId Lower = SubtreeRoot;
-  NodeId Upper = getNode(SubtreeRoot).RightMostDescendant;
-  return Id >= Lower && Id <= Upper;
+  return Id >= SubtreeRoot && Id <= getNode(SubtreeRoot).RightMostDescendant;
 }
 
 std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const {
@@ -625,12 +624,11 @@
   }
 
 private:
-  /// Simple cost model for edit actions.
+  /// We use a simple cost model for edit actions, which seems good enough.
+  /// Simple cost model for edit actions. This seems to make the matching
+  /// algorithm perform reasonably well.
   /// The values range between 0 and 1, or infinity if this edit action should
   /// always be avoided.
-
-  /// These costs could be modified to better model the estimated cost of /
-  /// inserting / deleting the current node.
   static constexpr double DeletionCost = 1;
   static constexpr double InsertionCost = 1;
 
@@ -687,6 +685,7 @@
 };
 } // end anonymous namespace
 
+namespace {
 // Priority queue for nodes, sorted descendingly by their height.
 class PriorityList {
   const SyntaxTree::Impl &Tree;
@@ -723,6 +722,7 @@
   push(Child);
   }
 };
+} // end anonymous namespace
 
 bool ASTDiff::Impl::identical(NodeId Id1, NodeId Id2) const {
   const Node &N1 = T1.getNode(Id1);
@@ -759,16 +759,6 @@
T2.getNode(Id2).ASTNode);
 }
 
-void ASTDiff::Impl::addIsomorphicSubTrees(Mapping &M, NodeId Id1,
-  NodeId Id2) const {
-  assert(identical(Id1, Id2) && "Can only be called on identical subtrees.");
-  M.link(Id1, Id2);
-  const Node &N1 = T1.getNode(Id1);
-  const Node &N2 = T2.getNode(Id2);
-  for (size_t Id = 0, E = N1.Children.size(); Id < E; ++Id)
-addIsomorphicSubTrees(M, N1.Children[Id], N2.Children[Id]);
-}
-
 void ASTDiff::Impl::addOptimalMapping(Mapping &M, NodeId Id1,
   NodeId Id2) const {
   if (std::max(T1.getNumberOfDescendants(Id1),
@@ -805,7 +795,7 @@
 if (M.hasDst(Id2))
   continue;
 double Similarity = getSimilarity(M, Id1, Id2);
-if (Similarity > HighestSimilarity) {
+if (Similarity >= Options.MinSimilarity && Similarity > HighestSimilarity) {
   HighestSimilarity = Similarity;
   Candidate = Id2;
 }
@@ -816,7 +806,8 @@
 void ASTDiff::Impl::matchBottomUp(Mapping &M) const {
   std::vector Postorder = getSubtreePostorder(T1, T1.getRootId());
   for (NodeId Id1 : Postorder) {
-if (Id1 == T1.getRootId()) {
+if (Id1 == T1.getRootId() && !M.hasSrc(T1.getRootId()) &&
+!M.hasDst(T2.getRootId())) {
   if (isMatchingPossible(T1.getRootId(), T2.getRootId())) {
 M.link(T1.getRootId(), T2.getRootId());
 addOptimalMapping(M, T1.getRootId(), T2.getRootId());
@@ -831,11 +822,10 @@
 if (Matched || !MatchedChildren)
   continue;
 NodeId Id2 = findCandidate(M, Id1);
-if (Id2.isInvalid() || !canBeAddedToMapping(M, Id1, Id2) ||
-getSimilarity(M, Id1, Id2) < Options.MinSimilarity)
-  continue;
-M.link(Id1, Id2);
-addOptimalMapping(M, Id1, Id2);
+if (Id2.isValid() && canBeAddedToMapping(M, Id1, Id2)) {
+  M.link(Id1, Id2);
+  addOptim

[PATCH] D36179: [clang-diff] Move printing of matches and changes to clang-diff

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110552.
johannes added a comment.

add test for 'Move' and 'Update and Move' in output


https://reviews.llvm.org/D36179

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTDiffInternal.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -129,7 +129,7 @@
   auto Offsets = Tree.getSourceRangeOffsets(N);
   OS << R"(,"begin":)" << Offsets.first;
   OS << R"(,"end":)" << Offsets.second;
-  std::string Value = Tree.getNodeValue(N.ASTNode);
+  std::string Value = Tree.getNodeValue(N);
   if (!Value.empty()) {
 OS << R"(,"value":")";
 printJsonString(OS, Value);
@@ -153,6 +153,52 @@
   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 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.ChangeKind) {
+  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.ChangeKind == diff::Insert)
+  OS << "Insert";
+else if (DstNode.ChangeKind == diff::Move)
+  OS << "Move";
+else if (DstNode.ChangeKind == 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 =
@@ -199,11 +245,26 @@
 Options.MaxSize = MaxSize;
   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);
+  diff::ASTDiff Diff(SrcTree, DstTree, Options);
+
+  for (diff::NodeId Dst : DstTree) {
+diff::NodeId Src = Diff.getMapped(DstTree, Dst);
+if (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;
 }
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -31,6 +31,10 @@
   int id(int i) { return i; }
 };
 }
+
+void m() { int x = 0 + 0 + 0; }
+int um = 1 + 2 + 3;
+
 #else
 // CHECK: Match TranslationUnitDecl{{.*}} to TranslationUnitDecl
 // CHECK: Match NamespaceDecl: src{{.*}} to NamespaceDecl: dst
@@ -54,8 +58,8 @@
 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)
+// CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
 double prod = 1 * 2 * 10;
 // CHECK: Update DeclRefExpr
 int squared = prod * prod;
@@ -70,9 +74,15 @@
   return "foo";
 return 0;
   }
-  // CHECK: Delete AccessSpecDecl: public
-  X(){};
-  // CHECK: Delete CXXMethodDecl
+  X(){}
 };
 }
+
+// CHECK: Move DeclStmt{{.*}} into CompoundStmt
+void m() { { int x = 0 + 0 + 0; } }
+// CHECK: Update and Move IntegerLiteral: 7{{.*}} into BinaryOperator: +({{.*}}) at 1
+int um = 1 + 7;
 #endif
+
+// CHECK: Delete AccessSpecDecl: public
+// CHECK: Delete CXXMethodDecl
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -82,26 +82,23 @@
 class ASTDiff::Impl {

[PATCH] D36182: [clang-diff] Add HTML side-by-side diff output

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110553.
johannes edited the summary of this revision.
johannes added a comment.

change tests


https://reviews.llvm.org/D36182

Files:
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-html.test
  tools/clang-diff/CMakeLists.txt
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -37,6 +37,10 @@
 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));
@@ -105,6 +109,161 @@
 
 static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
 
+static const char HtmlDiffHeader[] = R"(
+
+
+
+
+span.d { color: red; }
+span.u { color: #cc00cc; }
+span.i { color: green; }
+span.m { font-weight: bold; }
+span   { font-weight: normal; color: black; }
+div.code {
+  width: 48%;
+  height: 98%;
+  overflow: scroll;
+  float: left;
+  padding: 0 0 0.5% 0.5%;
+  border: solid 2px LightGrey;
+  border-radius: 5px;
+}
+
+
+
+highlightStack = []
+function clearHighlight() {
+  while (highlightStack.length) {
+let [l, r] = highlightStack.pop()
+document.getElementById(l).style.backgroundColor = 'white'
+document.getElementById(r).style.backgroundColor = 'white'
+  }
+}
+function highlight(event) {
+  id = event.target['id']
+  doHighlight(id)
+}
+function doHighlight(id) {
+  clearHighlight()
+  source = document.getElementById(id)
+  if (!source.attributes['tid'])
+return
+  tid = source.attributes['tid'].value
+  target = document.getElementById(tid)
+  if (!target || source.parentElement && source.parentElement.classList.contains('code'))
+return
+  source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
+  highlightStack.push([id, tid])
+  source.scrollIntoView()
+  target.scrollIntoView()
+  location.hash = '#' + id
+}
+function scrollToBoth() {
+  doHighlight(location.hash.substr(1))
+}
+window.onload = scrollToBoth
+
+
+
+)";
+
+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";
+  }
+}
+
+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 (char C : Str) {
 switch (C) {
@@ -269,6 +428,19 @@
   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(), Dif

[PATCH] D36183: [clang-diff] Simplify mapping

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110554.
johannes added a comment.

remove unused


https://reviews.llvm.org/D36183

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/Inputs/clang-diff-basic-src.cpp
  test/Tooling/clang-diff-basic.cpp

Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -42,10 +42,16 @@
 };
 }
 
-// CHECK: Move DeclStmt{{.*}} into CompoundStmt
+// CHECK: Move CompoundStmt{{.*}} into CompoundStmt
 void m() { { int x = 0 + 0 + 0; } }
 // CHECK: Update and Move IntegerLiteral: 7{{.*}} into BinaryOperator: +({{.*}}) at 1
 int um = 1 + 7;
 
+namespace {
+// match with parents of different type
+// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: f1
+void f1() {{ (void) __func__;;; }}
+}
+
 // CHECK: Delete AccessSpecDecl: public
 // CHECK: Delete CXXMethodDecl
Index: test/Tooling/Inputs/clang-diff-basic-src.cpp
===
--- test/Tooling/Inputs/clang-diff-basic-src.cpp
+++ test/Tooling/Inputs/clang-diff-basic-src.cpp
@@ -28,4 +28,6 @@
 }
 
 void m() { int x = 0 + 0 + 0; }
-int um = 1 + 2 + 3;
+int um = 1 * 2 + 3;
+
+void f1() {{ (void) __func__;;; }}
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -34,48 +34,23 @@
   Mapping() = default;
   Mapping(Mapping &&Other) = default;
   Mapping &operator=(Mapping &&Other) = default;
-  Mapping(int Size1, int Size2) {
-// Maximum possible size after patching one tree.
-int Size = Size1 + Size2;
-SrcToDst = llvm::make_unique[]>(Size);
-DstToSrc = llvm::make_unique[]>(Size);
+
+  Mapping(size_t Size) {
+SrcToDst = llvm::make_unique(Size);
+DstToSrc = llvm::make_unique(Size);
   }
 
   void link(NodeId Src, NodeId Dst) {
-SrcToDst[Src].push_back(Dst);
-DstToSrc[Dst].push_back(Src);
-  }
-
-  NodeId getDst(NodeId Src) const {
-if (hasSrc(Src))
-  return SrcToDst[Src][0];
-return NodeId();
-  }
-  NodeId getSrc(NodeId Dst) const {
-if (hasDst(Dst))
-  return DstToSrc[Dst][0];
-return NodeId();
-  }
-  const SmallVector &getAllDsts(NodeId Src) const {
-return SrcToDst[Src];
-  }
-  const SmallVector &getAllSrcs(NodeId Dst) const {
-return DstToSrc[Dst];
-  }
-  bool hasSrc(NodeId Src) const { return !SrcToDst[Src].empty(); }
-  bool hasDst(NodeId Dst) const { return !DstToSrc[Dst].empty(); }
-  bool hasSrcDst(NodeId Src, NodeId Dst) const {
-for (NodeId DstId : SrcToDst[Src])
-  if (DstId == Dst)
-return true;
-for (NodeId SrcId : DstToSrc[Dst])
-  if (SrcId == Src)
-return true;
-return false;
+SrcToDst[Src] = Dst, DstToSrc[Dst] = Src;
   }
 
+  NodeId getDst(NodeId Src) const { return SrcToDst[Src]; }
+  NodeId getSrc(NodeId Dst) const { return DstToSrc[Dst]; }
+  bool hasSrc(NodeId Src) const { return getDst(Src).isValid(); }
+  bool hasDst(NodeId Dst) const { return getSrc(Dst).isValid(); }
+
 private:
-  std::unique_ptr[]> SrcToDst, DstToSrc;
+  std::unique_ptr SrcToDst, DstToSrc;
 };
 } // end anonymous namespace
 
@@ -104,8 +79,6 @@
   // Returns true if the two subtrees are identical.
   bool identical(NodeId Id1, NodeId Id2) const;
 
-  bool canBeAddedToMapping(const Mapping &M, NodeId Id1, NodeId Id2) const;
-
   // Returns false if the nodes must not be mached.
   bool isMatchingPossible(NodeId Id1, NodeId Id2) const;
 
@@ -711,23 +684,6 @@
   return true;
 }
 
-bool ASTDiff::Impl::canBeAddedToMapping(const Mapping &M, NodeId Id1,
-NodeId Id2) const {
-  assert(isMatchingPossible(Id1, Id2) &&
- "Matching must be possible in the first place.");
-  if (M.hasSrcDst(Id1, Id2))
-return false;
-  if (Options.EnableMatchingWithUnmatchableParents)
-return true;
-  const Node &N1 = T1.getNode(Id1);
-  const Node &N2 = T2.getNode(Id2);
-  NodeId P1 = N1.Parent;
-  NodeId P2 = N2.Parent;
-  // Only allow matching if parents can be matched.
-  return (P1.isInvalid() && P2.isInvalid()) ||
- (P1.isValid() && P2.isValid() && isMatchingPossible(P1, P2));
-}
-
 bool ASTDiff::Impl::isMatchingPossible(NodeId Id1, NodeId Id2) const {
   return Options.isMatchingAllowed(T1.getNode(Id1), T2.getNode(Id2));
 }
@@ -750,7 +706,7 @@
   for (const auto Tuple : R) {
 NodeId Src = Tuple.first;
 NodeId Dst = Tuple.second;
-if (canBeAddedToMapping(M, Src, Dst))
+if (!M.hasSrc(Src) && !M.hasDst(Dst))
   M.link(Src, Dst);
   }
 }
@@ -803,7 +759,7 @@
 if (Matched || !MatchedChildren)
   continue;
 NodeId Id2 = findCandidate(M, Id1);
-if (Id2.isValid() && canBeAddedToMapping(M, Id1, Id2)) {
+if (Id2.isValid()) {
   M.link(Id1, Id2);
   addOptimalMapping(M, Id1, Id2);
 }
@@ -814,7 +770,7 @@

[PATCH] D36184: [clang-diff] Filter AST nodes

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110555.
johannes added a comment.

refactor isNodeExcluded


https://reviews.llvm.org/D36184

Files:
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-json.cpp

Index: test/Tooling/clang-diff-json.cpp
===
--- test/Tooling/clang-diff-json.cpp
+++ test/Tooling/clang-diff-json.cpp
@@ -3,9 +3,9 @@
 // RUN: | FileCheck %s
 
 // CHECK: "begin": 299,
-// CHECK: "type": "CXXRecordDecl",
 // CHECK: "type": "FieldDecl",
 // CHECK: "end": 319,
+// CHECK: "type": "CXXRecordDecl",
 class A {
   int x;
 };
Index: test/Tooling/clang-diff-ast.cpp
===
--- test/Tooling/clang-diff-ast.cpp
+++ test/Tooling/clang-diff-ast.cpp
@@ -12,7 +12,8 @@
   // CHECK: IntegerLiteral: 1
   auto i = 1;
   // CHECK: CallExpr(
-  // CHECK: DeclRefExpr: f(
+  // CHECK-NOT: ImplicitCastExpr
+  // CHECK-NEXT: DeclRefExpr: f(
   f();
   // CHECK: BinaryOperator: =(
   i = i;
@@ -37,6 +38,7 @@
 if (i == 0)
   // CHECK: StringLiteral: foo(
   return "foo";
+// CHECK-NOT: ImplicitCastExpr
 return 0;
   }
 
@@ -48,3 +50,22 @@
 int x = m;
   }
 };
+
+#define M (void)1
+#define MA(a, b) (void)a, b
+// CHECK: FunctionDecl
+// CHECK-NEXT: CompoundStmt
+void macros() {
+  M;
+  MA(1, 2);
+}
+// CHECK-NEXT: NamespaceDecl
+
+#ifndef GUARD
+#define GUARD
+namespace world {
+// nodes from other files are excluded
+// CHECK-NOT {{.}}
+#include "clang-diff-ast.cpp"
+}
+#endif
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -68,7 +68,8 @@
   // Compute ChangeKind for each node based on similarity.
   void computeChangeKinds(Mapping &M);
 
-  NodeId getMapped(const std::unique_ptr &Tree, NodeId Id) const {
+  NodeId getMapped(const std::unique_ptr &Tree,
+   NodeId Id) const {
 if (&*Tree == &T1)
   return TheMapping.getDst(Id);
 assert(&*Tree == &T2 && "Invalid tree.");
@@ -157,12 +158,23 @@
   void setLeftMostDescendants();
 };
 
+static bool isSpecializedNodeExcluded(const Decl *D) { return D->isImplicit(); }
+static bool isSpecializedNodeExcluded(const Stmt *S) { return false; }
+
 template 
 static bool isNodeExcluded(const SourceManager &SrcMgr, T *N) {
   if (!N)
 return true;
   SourceLocation SLoc = N->getLocStart();
-  return SLoc.isValid() && SrcMgr.isInSystemHeader(SLoc);
+  if (SLoc.isValid()) {
+// Ignore everything from other files.
+if (!SrcMgr.isInMainFile(SLoc))
+  return true;
+// Ignore macros.
+if (SLoc != SrcMgr.getSpellingLoc(SLoc))
+  return true;
+  }
+  return isSpecializedNodeExcluded(N);
 }
 
 namespace {
@@ -179,6 +191,8 @@
 return true;
   }
   bool TraverseStmt(Stmt *S) {
+if (S)
+  S = S->IgnoreImplicit();
 if (isNodeExcluded(Tree.AST.getSourceManager(), S))
   return true;
 ++Count;
@@ -241,6 +255,8 @@
 return true;
   }
   bool TraverseStmt(Stmt *S) {
+if (S)
+  S = S->IgnoreImplicit();
 if (isNodeExcluded(Tree.AST.getSourceManager(), S))
   return true;
 auto SavedState = PreTraverse(S);
@@ -900,7 +916,8 @@
   return TreeImpl->findPositionInParent(Id);
 }
 
-std::pair SyntaxTree::getSourceRangeOffsets(const Node &N) const {
+std::pair
+SyntaxTree::getSourceRangeOffsets(const Node &N) const {
   const SourceManager &SrcMgr = TreeImpl->AST.getSourceManager();
   SourceRange Range = N.ASTNode.getSourceRange();
   SourceLocation BeginLoc = Range.getBegin();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36187: [clang-diff] Use the relative name for NamedDecls

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110557.
johannes added a comment.

substr


https://reviews.llvm.org/D36187

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-html.test
  test/Tooling/clang-diff-topdown.cpp

Index: test/Tooling/clang-diff-topdown.cpp
===
--- test/Tooling/clang-diff-topdown.cpp
+++ test/Tooling/clang-diff-topdown.cpp
@@ -27,8 +27,19 @@
   {{;;}}
 }
 
+int x;
+
+namespace src {
+  int x;
+  int x1 = x + 1;
+  int x2 = ::x + 1;
+}
+
+class A { int x = 1 + 1; void f() { int x1 = x; } };
+
 #else
 
+
 void f1() {
 
   {{;}}
@@ -45,4 +56,28 @@
   ;
 }
 
+int x;
+
+namespace dst {
+  int x;
+  // CHECK: Match DeclRefExpr: :x(17) to DeclRefExpr: :x(22)
+  int x1 = x + 1;
+  // CHECK: Match DeclRefExpr: x(21) to DeclRefExpr: x(26)
+  int x2 = ::x + 1;
+}
+
+class B {
+  // Only the class name changed; it is not included in the field value,
+  // therefore there is no update.
+  // CHECK: Match FieldDecl: :x(int)(24) to FieldDecl: :x(int)(29)
+  // CHECK-NOT: Update FieldDecl: :x(int)(24)
+  int x = 1+1;
+  void f() {
+// CHECK: Match MemberExpr: :x(32) to MemberExpr: :x(37)
+// CHECK-NOT: Update MemberExpr: :x(32)
+int x1 = B::x;
+  }
+
+};
+
 #endif
Index: test/Tooling/clang-diff-html.test
===
--- test/Tooling/clang-diff-html.test
+++ test/Tooling/clang-diff-html.test
@@ -11,12 +11,12 @@
 // match, move
 // CHECK: void foo()
+// CHECK-NEXT: :foo(void ())' class='m'>void foo()
 
 // match
 // CHECK: void main()
+// CHECK-NEXT: :main(void ())'>void main()
 
 // deletion
 // CHECK: (Context))
+ContextPrefix = Namespace->getQualifiedNameAsString();
+  else if (auto *Tag = dyn_cast(Context))
+ContextPrefix = Tag->getQualifiedNameAsString();
+  std::string Val = ND->getQualifiedNameAsString();
+  // Strip the qualifier, if Val refers to somthing in the current scope.
+  // But leave one leading ':' in place, so that we know that this is a
+  // relative path.
+  if (!ContextPrefix.empty() &&
+  StringRef(Val).startswith(ContextPrefix))
+Val = Val.substr(ContextPrefix.size() + 1);
+  return Val;
+}
+
+static std::string getRelativeName(const NamedDecl *ND) {
+  return getRelativeName(ND, ND->getDeclContext());
+}
+
+static const DeclContext *getEnclosingDeclContext(ASTContext &AST,
+  const Stmt *S) {
+  while (S) {
+const auto &Parents = AST.getParents(*S);
+if (Parents.empty())
+  return nullptr;
+const auto &P = Parents[0];
+if (const auto *D = P.get())
+  return D->getDeclContext();
+S = P.get();
+  }
+  llvm_unreachable("Could not find Decl ancestor.");
+}
+
 std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const {
   return getNodeValue(getNode(Id));
 }
@@ -384,8 +419,7 @@
   TypePP.AnonymousTagLocations = false;
 
   if (auto *V = dyn_cast(D)) {
-Value += V->getQualifiedNameAsString() + "(" +
- V->getType().getAsString(TypePP) + ")";
+Value += getRelativeName(V) + "(" + V->getType().getAsString(TypePP) + ")";
 if (auto *C = dyn_cast(D)) {
   for (auto *Init : C->inits()) {
 if (!Init->isWritten())
@@ -398,14 +432,14 @@
   Value += C->getNameAsString() + ",";
 } else {
   assert(Init->isAnyMemberInitializer());
-  Value += Init->getMember()->getQualifiedNameAsString() + ",";
+  Value += getRelativeName(Init->getMember()) + ",";
 }
   }
 }
 return Value;
   }
   if (auto *N = dyn_cast(D))
-Value += N->getQualifiedNameAsString() + ";";
+Value += getRelativeName(N) + ";";
   if (auto *T = dyn_cast(D))
 return Value + T->getUnderlyingType().getAsString(TypePP) + ";";
   if (auto *T = dyn_cast(D))
@@ -429,7 +463,7 @@
   if (auto *B = dyn_cast(S))
 return B->getOpcodeStr();
   if (auto *M = dyn_cast(S))
-return M->getMemberDecl()->getQualifiedNameAsString();
+return getRelativeName(M->getMemberDecl());
   if (auto *I = dyn_cast(S)) {
 SmallString<256> Str;
 I->getValue().toString(Str, /*Radix=*/10, /*Signed=*/false);
@@ -441,7 +475,7 @@
 return Str.str();
   }
   if (auto *D = dyn_cast(S))
-return D->getDecl()->getQualifiedNameAsString();
+return getRelativeName(D->getDecl(), getEnclosingDeclContext(AST, S));
   if (auto *String = dyn_cast(S))
 return String->getString();
   if (auto *B = dyn_cast(S))
@@ -946,7 +980,7 @@
   return DiffImpl->getMapped(SourceTree.TreeImpl, Id);
 }
 
-SyntaxTree::SyntaxTree(const ASTContext &AST)
+SyntaxTree::SyntaxTree(ASTContext &AST)
 : TreeImpl(llvm::make_unique(
   this, AST.getTranslationUnitDecl(), AST)) {}
 
Index: include/clang/Tooling/ASTDiff/ASTDiff.h
===
--- include/clang/Tooling/

[PATCH] D36179: [clang-diff] Move printing of matches and changes to clang-diff

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110608.
johannes added a comment.

initialize Node::Shift


https://reviews.llvm.org/D36179

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTDiffInternal.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -129,7 +129,7 @@
   auto Offsets = Tree.getSourceRangeOffsets(N);
   OS << R"(,"begin":)" << Offsets.first;
   OS << R"(,"end":)" << Offsets.second;
-  std::string Value = Tree.getNodeValue(N.ASTNode);
+  std::string Value = Tree.getNodeValue(N);
   if (!Value.empty()) {
 OS << R"(,"value":")";
 printJsonString(OS, Value);
@@ -153,6 +153,52 @@
   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 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.ChangeKind) {
+  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.ChangeKind == diff::Insert)
+  OS << "Insert";
+else if (DstNode.ChangeKind == diff::Move)
+  OS << "Move";
+else if (DstNode.ChangeKind == 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 =
@@ -199,11 +245,26 @@
 Options.MaxSize = MaxSize;
   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);
+  diff::ASTDiff Diff(SrcTree, DstTree, Options);
+
+  for (diff::NodeId Dst : DstTree) {
+diff::NodeId Src = Diff.getMapped(DstTree, Dst);
+if (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;
 }
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -31,6 +31,10 @@
   int id(int i) { return i; }
 };
 }
+
+void m() { int x = 0 + 0 + 0; }
+int um = 1 + 2 + 3;
+
 #else
 // CHECK: Match TranslationUnitDecl{{.*}} to TranslationUnitDecl
 // CHECK: Match NamespaceDecl: src{{.*}} to NamespaceDecl: dst
@@ -54,8 +58,8 @@
 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)
+// CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
 double prod = 1 * 2 * 10;
 // CHECK: Update DeclRefExpr
 int squared = prod * prod;
@@ -70,9 +74,15 @@
   return "foo";
 return 0;
   }
-  // CHECK: Delete AccessSpecDecl: public
-  X(){};
-  // CHECK: Delete CXXMethodDecl
+  X(){}
 };
 }
+
+// CHECK: Move DeclStmt{{.*}} into CompoundStmt
+void m() { { int x = 0 + 0 + 0; } }
+// CHECK: Update and Move IntegerLiteral: 7{{.*}} into BinaryOperator: +({{.*}}) at 1
+int um = 1 + 7;
 #endif
+
+// CHECK: Delete AccessSpecDecl: public
+// CHECK: Delete CXXMethodDecl
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -82,26 +82,23 @@
 class ASTDiff::Impl {
 public:
   SyntaxTree::Impl 

[PATCH] D36184: [clang-diff] Filter AST nodes

2017-08-10 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes added inline comments.



Comment at: test/Tooling/clang-diff-ast.cpp:68
+// nodes from other files are excluded
+// CHECK-NOT {{.}}
+#include "clang-diff-ast.cpp"

arphaman wrote:
> 1) Missing ':'
> 2) What exactly does this regex accomplish? Right now it will match any 
> character which doesn't look correct
I want to assert that there is no output here, because other files are 
excluded, there may be a better way..


https://reviews.llvm.org/D36184



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36184: [clang-diff] Filter AST nodes

2017-08-11 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110687.
johannes added a comment.

clarify test/Tooling/clang-diff-ast.cpp


https://reviews.llvm.org/D36184

Files:
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-json.cpp

Index: test/Tooling/clang-diff-json.cpp
===
--- test/Tooling/clang-diff-json.cpp
+++ test/Tooling/clang-diff-json.cpp
@@ -3,9 +3,9 @@
 // RUN: | FileCheck %s
 
 // CHECK: "begin": 299,
-// CHECK: "type": "CXXRecordDecl",
 // CHECK: "type": "FieldDecl",
 // CHECK: "end": 319,
+// CHECK: "type": "CXXRecordDecl",
 class A {
   int x;
 };
Index: test/Tooling/clang-diff-ast.cpp
===
--- test/Tooling/clang-diff-ast.cpp
+++ test/Tooling/clang-diff-ast.cpp
@@ -12,7 +12,8 @@
   // CHECK: IntegerLiteral: 1
   auto i = 1;
   // CHECK: CallExpr(
-  // CHECK: DeclRefExpr: f(
+  // CHECK-NOT: ImplicitCastExpr
+  // CHECK-NEXT: DeclRefExpr: f(
   f();
   // CHECK: BinaryOperator: =(
   i = i;
@@ -37,6 +38,7 @@
 if (i == 0)
   // CHECK: StringLiteral: foo(
   return "foo";
+// CHECK-NOT: ImplicitCastExpr
 return 0;
   }
 
@@ -48,3 +50,23 @@
 int x = m;
   }
 };
+
+#define M (void)1
+#define MA(a, b) (void)a, b
+// CHECK: FunctionDecl
+// CHECK-NEXT: CompoundStmt
+void macros() {
+  M;
+  MA(1, 2);
+}
+// CHECK-NEXT: NamespaceDecl
+
+#ifndef GUARD
+#define GUARD
+namespace world {
+// nodes from other files are excluded, there should be no output from this
+// point on
+// CHECK-NOT: {{.}}
+#include "clang-diff-ast.cpp"
+}
+#endif
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -68,7 +68,8 @@
   // Compute ChangeKind for each node based on similarity.
   void computeChangeKinds(Mapping &M);
 
-  NodeId getMapped(const std::unique_ptr &Tree, NodeId Id) const {
+  NodeId getMapped(const std::unique_ptr &Tree,
+   NodeId Id) const {
 if (&*Tree == &T1)
   return TheMapping.getDst(Id);
 assert(&*Tree == &T2 && "Invalid tree.");
@@ -157,12 +158,23 @@
   void setLeftMostDescendants();
 };
 
+static bool isSpecializedNodeExcluded(const Decl *D) { return D->isImplicit(); }
+static bool isSpecializedNodeExcluded(const Stmt *S) { return false; }
+
 template 
 static bool isNodeExcluded(const SourceManager &SrcMgr, T *N) {
   if (!N)
 return true;
   SourceLocation SLoc = N->getLocStart();
-  return SLoc.isValid() && SrcMgr.isInSystemHeader(SLoc);
+  if (SLoc.isValid()) {
+// Ignore everything from other files.
+if (!SrcMgr.isInMainFile(SLoc))
+  return true;
+// Ignore macros.
+if (SLoc != SrcMgr.getSpellingLoc(SLoc))
+  return true;
+  }
+  return isSpecializedNodeExcluded(N);
 }
 
 namespace {
@@ -179,6 +191,8 @@
 return true;
   }
   bool TraverseStmt(Stmt *S) {
+if (S)
+  S = S->IgnoreImplicit();
 if (isNodeExcluded(Tree.AST.getSourceManager(), S))
   return true;
 ++Count;
@@ -241,6 +255,8 @@
 return true;
   }
   bool TraverseStmt(Stmt *S) {
+if (S)
+  S = S->IgnoreImplicit();
 if (isNodeExcluded(Tree.AST.getSourceManager(), S))
   return true;
 auto SavedState = PreTraverse(S);
@@ -900,7 +916,8 @@
   return TreeImpl->findPositionInParent(Id);
 }
 
-std::pair SyntaxTree::getSourceRangeOffsets(const Node &N) const {
+std::pair
+SyntaxTree::getSourceRangeOffsets(const Node &N) const {
   const SourceManager &SrcMgr = TreeImpl->AST.getSourceManager();
   SourceRange Range = N.ASTNode.getSourceRange();
   SourceLocation BeginLoc = Range.getBegin();
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36185: [clang-diff] Fix similarity computation

2017-08-11 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110688.
johannes added a comment.

newline in error message


https://reviews.llvm.org/D36185

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-opt.cpp
  test/Tooling/clang-diff-topdown.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -50,6 +50,11 @@
 cl::Optional,
 cl::cat(ClangDiffCategory));
 
+static cl::opt StopAfter("stop-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));
 
@@ -424,6 +429,14 @@
   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);
Index: test/Tooling/clang-diff-topdown.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-topdown.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the top-down matching of identical subtrees only.
+
+#ifndef DEST
+
+void f1()
+{
+  // Match some subtree of height greater than 2.
+  // CHECK: Match CompoundStmt(3) to CompoundStmt(3)
+  // CHECK: Match CompoundStmt(4) to CompoundStmt(4)
+  // CHECK: Match NullStmt(5) to NullStmt(5)
+  {{;}}
+
+  // Don't match subtrees that are smaller.
+  // CHECK-NOT: Match CompoundStmt(6)
+  // CHECK-NOT: Match NullStmt(7)
+  {;}
+
+  // Greedy approach - use the first matching subtree when there are multiple
+  // identical subtrees.
+  // CHECK: Match CompoundStmt(8) to CompoundStmt(8)
+  // CHECK: Match CompoundStmt(9) to CompoundStmt(9)
+  // CHECK: Match NullStmt(10) to NullStmt(10)
+  {{;;}}
+}
+
+#else
+
+void f1() {
+
+  {{;}}
+
+  {;}
+
+  {{;;}}
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(11)
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(12)
+  // CHECK-NOT: Match {{.*}} to NullStmt(13)
+  {{;;}}
+
+  // CHECK-NOT: Match {{.*}} to NullStmt(14)
+  ;
+}
+
+#endif
Index: test/Tooling/clang-diff-opt.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-opt.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=10 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the behaviour of the matching according to the optimal tree edit
+// distance, implemented with Zhang and Shasha's algorithm.
+// Just for testing we use a tiny value of 10 for maxsize. Subtrees bigger than
+// this size will not be processed by the optimal algorithm.
+
+#ifndef DEST
+
+void f1() { {;} {{;}} }
+
+void f2() { {;} {{;}} }
+
+void f3() { {;} {{;;}} }
+
+#else
+
+void f1() {
+// Jaccard similarity = 3 / (5 + 4 - 3) = 3 / 6 >= 0.5
+// The optimal matching algorithm should move the ; into the outer block
+// CHECK: Match CompoundStmt(2) to CompoundStmt(2)
+// CHECK-NOT: Match CompoundStmt(3)
+// CHECK-NEXT: Match NullStmt(4) to NullStmt(3)
+  ; {{;}}
+}
+ 
+void f2() {
+  // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5
+  // As none of the subtrees is bigger than 10 nodes, the optimal algorithm
+  // will be run.
+  // CHECK: Match NullStmt(11) to NullStmt(9)
+  ;; {{;}}
+}
+
+void f3() {
+  // Jaccard similarity = 8 / (11 + 11 - 8) >= 0.5
+  // As the subtrees are bigger than 10 nodes, the optimal algorithm will not
+  // be run.
+  // CHECK: Delete NullStmt(22)
+  ;; {{;;}}
+}
+#endif
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=0 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the bottom-up matching, with maxsize set to 0, so that the optimal matching will never be applied.
+
+#ifndef DEST
+
+void f1() { ; {{;}} }
+void f2() { ;; {{;}} }
+
+#else
+
+// Jaccard similarity threshold is 0.5.
+
+void f1() {
+// CompoundStm

[PATCH] D36664: [analyzer] Make StmtDataCollector customizable

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added subscribers: xazax.hun, mgorny.
Herald added 1 blocking reviewer(s): teemperor.

This moves the data collection macro calls for Stmt nodes
to lib/AST/StmtDataCollectors.inc

Users can subclass ConstStmtVisitor and include StmtDataCollectors.inc
to define visitor methods for each Stmt subclass. This makes it also
possible to customize the visit methods as exemplified in
CloneDetection.cpp.

Move helper methods for data collection to a new module,

  AST/DataCollection.

Add data collection for DeclRefExpr, MemberExpr and some literals


https://reviews.llvm.org/D36664

Files:
  include/clang/AST/DataCollection.h
  include/clang/Analysis/CloneDetection.h
  lib/AST/CMakeLists.txt
  lib/AST/DataCollection.cpp
  lib/AST/StmtDataCollectors.inc
  lib/Analysis/CloneDetection.cpp

Index: lib/Analysis/CloneDetection.cpp
===
--- lib/Analysis/CloneDetection.cpp
+++ lib/Analysis/CloneDetection.cpp
@@ -13,16 +13,12 @@
 
 #include "clang/Analysis/CloneDetection.h"
 
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/AST/Stmt.h"
-#include "clang/Lex/Lexer.h"
+#include "clang/AST/DataCollection.h"
+#include "clang/AST/DeclTemplate.h"
 #include "llvm/Support/MD5.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/Path.h"
 
 using namespace clang;
-using namespace clang::clone_detection;
 
 StmtSequence::StmtSequence(const CompoundStmt *Stmt, const Decl *D,
unsigned StartIndex, unsigned EndIndex)
@@ -91,34 +87,6 @@
   return SourceRange(getStartLoc(), getEndLoc());
 }
 
-/// Prints the macro name that contains the given SourceLocation into the given
-/// raw_string_ostream.
-static void printMacroName(llvm::raw_string_ostream &MacroStack,
-   ASTContext &Context, SourceLocation Loc) {
-  MacroStack << Lexer::getImmediateMacroName(Loc, Context.getSourceManager(),
- Context.getLangOpts());
-
-  // Add an empty space at the end as a padding to prevent
-  // that macro names concatenate to the names of other macros.
-  MacroStack << " ";
-}
-
-std::string clone_detection::getMacroStack(SourceLocation Loc,
-   ASTContext &Context) {
-  std::string MacroStack;
-  llvm::raw_string_ostream MacroStackStream(MacroStack);
-  SourceManager &SM = Context.getSourceManager();
-
-  // Iterate over all macros that expanded into the given SourceLocation.
-  while (Loc.isMacroID()) {
-// Add the macro name to the stream.
-printMacroName(MacroStackStream, Context, Loc);
-Loc = SM.getImmediateMacroCallerLoc(Loc);
-  }
-  MacroStackStream.flush();
-  return MacroStack;
-}
-
 void CloneDetector::analyzeCodeBody(const Decl *D) {
   assert(D);
   assert(D->hasBody());
@@ -201,6 +169,59 @@
   return false;
 }
 
+/// Collects the data of a single Stmt.
+///
+/// This class defines what a code clone is: If it collects for two statements
+/// the same data, then those two statements are considered to be clones of each
+/// other.
+///
+/// All collected data is forwarded to the given data consumer of the type T.
+/// The data consumer class needs to provide a member method with the signature:
+///   update(StringRef Str)
+template 
+class CloneTypeIIStmtDataCollector
+: public ConstStmtVisitor> {
+  ASTContext &Context;
+  /// The data sink to which all data is forwarded.
+  T &DataConsumer;
+
+  template  void addData(Ty Data) {
+addDataToConsumer(DataConsumer, Data);
+  }
+
+public:
+  CloneTypeIIStmtDataCollector(const Stmt *S, ASTContext &Context,
+   T &DataConsumer)
+  : Context(Context), DataConsumer(DataConsumer) {
+this->Visit(S);
+  }
+
+// Define a visit method for each class to collect data and subsequently visit
+// all parent classes. This uses a template so that custom visit methods by us
+// take precedence.
+#define DEF_ADD_DATA(CLASS, CODE)  \
+  template  Dummy Visit##CLASS(const CLASS *S) {   \
+CODE;  \
+ConstStmtVisitor>::Visit##CLASS(S);\
+  }
+
+#include "../AST/StmtDataCollectors.inc"
+
+// Override the definition for some visitors.
+#define SKIP(CLASS)\
+  void Visit##CLASS(const CLASS *S) {  \
+ConstStmtVisitor>::Visit##CLASS(S);\
+  }
+  SKIP(DeclRefExpr)
+  SKIP(MemberExpr)
+  SKIP(IntegerLiteral)
+  SKIP(FloatingLiteral)
+  SKIP(StringLiteral)
+  SKIP(CXXBoolLiteralExpr)
+  SKIP(CharacterLiteral)
+#undef SKIP
+};
+
 static size_t createHash(llvm::MD5 &Hash) {
   size_t HashCode;
 
@@ -222,7 +243,7 @@
   llvm::MD5 Hash;
   ASTContext &Context = D->getASTContext();
 
-  StmtDataCollector(S, Context, Hash);
+  CloneTypeIIStmtDataC

[PATCH] D36179: [clang-diff] Move printing of matches and changes to clang-diff

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110945.
johannes added a comment.

format


https://reviews.llvm.org/D36179

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  include/clang/Tooling/ASTDiff/ASTDiffInternal.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-basic.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -129,7 +129,7 @@
   auto Offsets = Tree.getSourceRangeOffsets(N);
   OS << R"(,"begin":)" << Offsets.first;
   OS << R"(,"end":)" << Offsets.second;
-  std::string Value = Tree.getNodeValue(N.ASTNode);
+  std::string Value = Tree.getNodeValue(N);
   if (!Value.empty()) {
 OS << R"(,"value":")";
 printJsonString(OS, Value);
@@ -153,6 +153,52 @@
   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 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.ChangeKind) {
+  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.ChangeKind == diff::Insert)
+  OS << "Insert";
+else if (DstNode.ChangeKind == diff::Move)
+  OS << "Move";
+else if (DstNode.ChangeKind == 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 =
@@ -199,11 +245,26 @@
 Options.MaxSize = MaxSize;
   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);
+  diff::ASTDiff Diff(SrcTree, DstTree, Options);
+
+  for (diff::NodeId Dst : DstTree) {
+diff::NodeId Src = Diff.getMapped(DstTree, Dst);
+if (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;
 }
Index: test/Tooling/clang-diff-basic.cpp
===
--- test/Tooling/clang-diff-basic.cpp
+++ test/Tooling/clang-diff-basic.cpp
@@ -31,6 +31,10 @@
   int id(int i) { return i; }
 };
 }
+
+void m() { int x = 0 + 0 + 0; }
+int um = 1 + 2 + 3;
+
 #else
 // CHECK: Match TranslationUnitDecl{{.*}} to TranslationUnitDecl
 // CHECK: Match NamespaceDecl: src{{.*}} to NamespaceDecl: dst
@@ -54,8 +58,8 @@
 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)
+// CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
 double prod = 1 * 2 * 10;
 // CHECK: Update DeclRefExpr
 int squared = prod * prod;
@@ -70,9 +74,15 @@
   return "foo";
 return 0;
   }
-  // CHECK: Delete AccessSpecDecl: public
-  X(){};
-  // CHECK: Delete CXXMethodDecl
+  X(){}
 };
 }
+
+// CHECK: Move DeclStmt{{.*}} into CompoundStmt
+void m() { { int x = 0 + 0 + 0; } }
+// CHECK: Update and Move IntegerLiteral: 7{{.*}} into BinaryOperator: +({{.*}}) at 1
+int um = 1 + 7;
 #endif
+
+// CHECK: Delete AccessSpecDecl: public
+// CHECK: Delete CXXMethodDecl
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -82,26 +82,24 @@
 class ASTDiff::Impl {
 public:
   SyntaxTree::Impl &T1, &T2;
-  boo

[PATCH] D36185: [clang-diff] Fix similarity computation

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110951.
johannes added a comment.

comment getJaccardSimilarity


https://reviews.llvm.org/D36185

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-opt.cpp
  test/Tooling/clang-diff-topdown.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -50,6 +50,11 @@
 cl::Optional,
 cl::cat(ClangDiffCategory));
 
+static cl::opt StopAfter("stop-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));
 
@@ -424,6 +429,14 @@
   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);
Index: test/Tooling/clang-diff-topdown.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-topdown.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the top-down matching of identical subtrees only.
+
+#ifndef DEST
+
+void f1()
+{
+  // Match some subtree of height greater than 2.
+  // CHECK: Match CompoundStmt(3) to CompoundStmt(3)
+  // CHECK: Match CompoundStmt(4) to CompoundStmt(4)
+  // CHECK: Match NullStmt(5) to NullStmt(5)
+  {{;}}
+
+  // Don't match subtrees that are smaller.
+  // CHECK-NOT: Match CompoundStmt(6)
+  // CHECK-NOT: Match NullStmt(7)
+  {;}
+
+  // Greedy approach - use the first matching subtree when there are multiple
+  // identical subtrees.
+  // CHECK: Match CompoundStmt(8) to CompoundStmt(8)
+  // CHECK: Match CompoundStmt(9) to CompoundStmt(9)
+  // CHECK: Match NullStmt(10) to NullStmt(10)
+  {{;;}}
+}
+
+#else
+
+void f1() {
+
+  {{;}}
+
+  {;}
+
+  {{;;}}
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(11)
+  // CHECK-NOT: Match {{.*}} to CompoundStmt(12)
+  // CHECK-NOT: Match {{.*}} to NullStmt(13)
+  {{;;}}
+
+  // CHECK-NOT: Match {{.*}} to NullStmt(14)
+  ;
+}
+
+#endif
Index: test/Tooling/clang-diff-opt.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-opt.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=10 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the behaviour of the matching according to the optimal tree edit
+// distance, implemented with Zhang and Shasha's algorithm.
+// Just for testing we use a tiny value of 10 for maxsize. Subtrees bigger than
+// this size will not be processed by the optimal algorithm.
+
+#ifndef DEST
+
+void f1() { {;} {{;}} }
+
+void f2() { {;} {{;}} }
+
+void f3() { {;} {{;;}} }
+
+#else
+
+void f1() {
+// Jaccard similarity = 3 / (5 + 4 - 3) = 3 / 6 >= 0.5
+// The optimal matching algorithm should move the ; into the outer block
+// CHECK: Match CompoundStmt(2) to CompoundStmt(2)
+// CHECK-NOT: Match CompoundStmt(3)
+// CHECK-NEXT: Match NullStmt(4) to NullStmt(3)
+  ; {{;}}
+}
+ 
+void f2() {
+  // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5
+  // As none of the subtrees is bigger than 10 nodes, the optimal algorithm
+  // will be run.
+  // CHECK: Match NullStmt(11) to NullStmt(9)
+  ;; {{;}}
+}
+
+void f3() {
+  // Jaccard similarity = 8 / (11 + 11 - 8) >= 0.5
+  // As the subtrees are bigger than 10 nodes, the optimal algorithm will not
+  // be run.
+  // CHECK: Delete NullStmt(22)
+  ;; {{;;}}
+}
+#endif
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=0 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the bottom-up matching, with maxsize set to 0, so that the optimal matching will never be applied.
+
+#ifndef DEST
+
+void f1() { ; {{;}} }
+void f2() { ;; {{;}} }
+
+#else
+
+// Jaccard similarity threshold is 0.5.
+
+void f1() {
+// Compoun

[PATCH] D36186: [clang-diff] Improve and test getNodeValue

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110952.
johannes added a comment.

add test for delegating initializer and unwritten initializer


https://reviews.llvm.org/D36186

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-html.test
  test/Tooling/clang-diff-opt.cpp
  test/Tooling/clang-diff-topdown.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -315,6 +315,18 @@
   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]);
Index: test/Tooling/clang-diff-topdown.cpp
===
--- test/Tooling/clang-diff-topdown.cpp
+++ test/Tooling/clang-diff-topdown.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %t.src.cpp
 // RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
-// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- -std=c++11 | FileCheck %s
 //
 // Test the top-down matching of identical subtrees only.
 
Index: test/Tooling/clang-diff-opt.cpp
===
--- test/Tooling/clang-diff-opt.cpp
+++ test/Tooling/clang-diff-opt.cpp
@@ -25,7 +25,7 @@
 // CHECK-NEXT: Match NullStmt(4) to NullStmt(3)
   ; {{;}}
 }
- 
+
 void f2() {
   // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5
   // As none of the subtrees is bigger than 10 nodes, the optimal algorithm
@@ -41,4 +41,5 @@
   // CHECK: Delete NullStmt(22)
   ;; {{;;}}
 }
+ 
 #endif
Index: test/Tooling/clang-diff-html.test
===
--- test/Tooling/clang-diff-html.test
+++ test/Tooling/clang-diff-html.test
@@ -11,12 +11,12 @@
 // match, move
 // CHECK: void foo()
+// CHECK-NEXT: src::foo(void ())' class='u m'>void foo()
 
 // match
 // CHECK: void main()
+// CHECK-NEXT: src::main(void ())' class='u'>void main()
 
 // deletion
 // CHECK: 

[PATCH] D36187: [clang-diff] Use the relative name for NamedDecls

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes updated this revision to Diff 110962.
johannes added a comment.

rebase


https://reviews.llvm.org/D36187

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-ast.cpp
  test/Tooling/clang-diff-basic.cpp
  test/Tooling/clang-diff-html.test
  test/Tooling/clang-diff-topdown.cpp

Index: test/Tooling/clang-diff-topdown.cpp
===
--- test/Tooling/clang-diff-topdown.cpp
+++ test/Tooling/clang-diff-topdown.cpp
@@ -27,8 +27,19 @@
   {{;;}}
 }
 
+int x;
+
+namespace src {
+  int x;
+  int x1 = x + 1;
+  int x2 = ::x + 1;
+}
+
+class A { int x = 1 + 1; void f() { int x1 = x; } };
+
 #else
 
+
 void f1() {
 
   {{;}}
@@ -45,4 +56,28 @@
   ;
 }
 
+int x;
+
+namespace dst {
+  int x;
+  // CHECK: Match DeclRefExpr: :x(17) to DeclRefExpr: :x(22)
+  int x1 = x + 1;
+  // CHECK: Match DeclRefExpr: x(21) to DeclRefExpr: x(26)
+  int x2 = ::x + 1;
+}
+
+class B {
+  // Only the class name changed; it is not included in the field value,
+  // therefore there is no update.
+  // CHECK: Match FieldDecl: :x(int)(24) to FieldDecl: :x(int)(29)
+  // CHECK-NOT: Update FieldDecl: :x(int)(24)
+  int x = 1+1;
+  void f() {
+// CHECK: Match MemberExpr: :x(32) to MemberExpr: :x(37)
+// CHECK-NOT: Update MemberExpr: :x(32)
+int x1 = B::x;
+  }
+
+};
+
 #endif
Index: test/Tooling/clang-diff-html.test
===
--- test/Tooling/clang-diff-html.test
+++ test/Tooling/clang-diff-html.test
@@ -11,12 +11,12 @@
 // match, move
 // CHECK: void foo()
+// CHECK-NEXT: :foo(void ())' class='m'>void foo()
 
 // match
 // CHECK: void main()
+// CHECK-NEXT: :main(void ())'>void main()
 
 // deletion
 // CHECK: (Context))
+ContextPrefix = Namespace->getQualifiedNameAsString();
+  else if (auto *Tag = dyn_cast(Context))
+ContextPrefix = Tag->getQualifiedNameAsString();
+  std::string Val = ND->getQualifiedNameAsString();
+  // Strip the qualifier, if Val refers to somthing in the current scope.
+  // But leave one leading ':' in place, so that we know that this is a
+  // relative path.
+  if (!ContextPrefix.empty() &&
+  StringRef(Val).startswith(ContextPrefix))
+Val = Val.substr(ContextPrefix.size() + 1);
+  return Val;
+}
+
+static std::string getRelativeName(const NamedDecl *ND) {
+  return getRelativeName(ND, ND->getDeclContext());
+}
+
+static const DeclContext *getEnclosingDeclContext(ASTContext &AST,
+  const Stmt *S) {
+  while (S) {
+const auto &Parents = AST.getParents(*S);
+if (Parents.empty())
+  return nullptr;
+const auto &P = Parents[0];
+if (const auto *D = P.get())
+  return D->getDeclContext();
+S = P.get();
+  }
+  llvm_unreachable("Could not find Decl ancestor.");
+}
+
 std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const {
   return getNodeValue(getNode(Id));
 }
@@ -384,8 +419,7 @@
   TypePP.AnonymousTagLocations = false;
 
   if (auto *V = dyn_cast(D)) {
-Value += V->getQualifiedNameAsString() + "(" +
- V->getType().getAsString(TypePP) + ")";
+Value += getRelativeName(V) + "(" + V->getType().getAsString(TypePP) + ")";
 if (auto *C = dyn_cast(D)) {
   for (auto *Init : C->inits()) {
 if (!Init->isWritten())
@@ -397,15 +431,15 @@
   Value += C->getNameAsString();
 } else {
   assert(Init->isAnyMemberInitializer());
-  Value += Init->getMember()->getQualifiedNameAsString();
+  Value += getRelativeName(Init->getMember());
 }
 Value += ",";
   }
 }
 return Value;
   }
   if (auto *N = dyn_cast(D))
-Value += N->getQualifiedNameAsString() + ";";
+Value += getRelativeName(N) + ";";
   if (auto *T = dyn_cast(D))
 return Value + T->getUnderlyingType().getAsString(TypePP) + ";";
   if (auto *T = dyn_cast(D))
@@ -429,7 +463,7 @@
   if (auto *B = dyn_cast(S))
 return B->getOpcodeStr();
   if (auto *M = dyn_cast(S))
-return M->getMemberDecl()->getQualifiedNameAsString();
+return getRelativeName(M->getMemberDecl());
   if (auto *I = dyn_cast(S)) {
 SmallString<256> Str;
 I->getValue().toString(Str, /*Radix=*/10, /*Signed=*/false);
@@ -441,7 +475,7 @@
 return Str.str();
   }
   if (auto *D = dyn_cast(S))
-return D->getDecl()->getQualifiedNameAsString();
+return getRelativeName(D->getDecl(), getEnclosingDeclContext(AST, S));
   if (auto *String = dyn_cast(S))
 return String->getString();
   if (auto *B = dyn_cast(S))
@@ -950,7 +984,7 @@
   return DiffImpl->getMapped(SourceTree.TreeImpl, Id);
 }
 
-SyntaxTree::SyntaxTree(const ASTContext &AST)
+SyntaxTree::SyntaxTree(ASTContext &AST)
 : TreeImpl(llvm::make_unique(
   this, AST.getTranslationUnitDecl(), AST)) {}
 
Index: include/clang/Tooling/ASTDiff/ASTDiff.h
===
--- include/clang/Too

[PATCH] D36685: [clang-diff] HTML diff navigation

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.

This adds shortcuts j and k to jump between changes.
It is especially useful in diffs with few changes.


https://reviews.llvm.org/D36685

Files:
  tools/clang-diff/ClangDiff.cpp


Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -145,34 +145,79 @@
 highlightStack = []
 function clearHighlight() {
   while (highlightStack.length) {
-let [l, r] = highlightStack.pop()
+var [l, r] = highlightStack.pop()
 document.getElementById(l).style.backgroundColor = 'white'
-document.getElementById(r).style.backgroundColor = 'white'
+if (r[1] != '-')
+  document.getElementById(r).style.backgroundColor = 'white'
   }
 }
 function highlight(event) {
-  id = event.target['id']
+  var id = event.target['id']
   doHighlight(id)
 }
 function doHighlight(id) {
   clearHighlight()
   source = document.getElementById(id)
   if (!source.attributes['tid'])
 return
-  tid = source.attributes['tid'].value
-  target = document.getElementById(tid)
-  if (!target || source.parentElement && 
source.parentElement.classList.contains('code'))
+  var mapped = source
+  while (mapped && mapped.parentElement && 
mapped.attributes['tid'].value.substr(1) === '-1')
+mapped = mapped.parentElement
+  var tid = null, target = null
+  if (mapped) {
+tid = mapped.attributes['tid'].value
+target = document.getElementById(tid)
+  }
+  if (source.parentElement && source.parentElement.classList.contains('code'))
 return
-  source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
-  highlightStack.push([id, tid])
+  source.style.backgroundColor = 'lightgrey'
   source.scrollIntoView()
-  target.scrollIntoView()
+  if (target) {
+if (mapped === source)
+  target.style.backgroundColor = 'lightgrey'
+target.scrollIntoView()
+  }
+  highlightStack.push([id, tid])
   location.hash = '#' + id
 }
 function scrollToBoth() {
   doHighlight(location.hash.substr(1))
 }
+function nextWithoutClass(prefix, increment, number) {
+  do {
+number += increment
+var elem = document.getElementById(prefix + number)
+  } while(elem && elem.classList.length == 0)
+  return elem ? number : null
+}
+function handleKey(e) {
+  var down = e.code === "KeyJ"
+  var up = e.code === "KeyK"
+  if (!down && !up)
+return
+  var id = highlightStack[0] ? highlightStack[0][0] : 'R0'
+  var oldelem = document.getElementById(id)
+  var number = parseInt(id.substr(1))
+  var increment = down ? 1 : -1
+  var lastnumber = number
+  var prefix = id[0]
+  do {
+number = nextWithoutClass(prefix, increment, number)
+var elem = document.getElementById(prefix + number)
+if (up && elem) {
+  while (elem.parentElement && elem.parentElement.classList.length != 0) {
+elem = elem.parentElement
+number = elem.id.substr(1)
+  }
+}
+  } while ((down && id !== 'R0' && oldelem.contains(elem)))
+  if (!number)
+number = lastnumber
+  elem = document.getElementById(prefix + number)
+  doHighlight(prefix + number)
+}
 window.onload = scrollToBoth
+window.onkeydown = handleKey
 
 
 


Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -145,34 +145,79 @@
 highlightStack = []
 function clearHighlight() {
   while (highlightStack.length) {
-let [l, r] = highlightStack.pop()
+var [l, r] = highlightStack.pop()
 document.getElementById(l).style.backgroundColor = 'white'
-document.getElementById(r).style.backgroundColor = 'white'
+if (r[1] != '-')
+  document.getElementById(r).style.backgroundColor = 'white'
   }
 }
 function highlight(event) {
-  id = event.target['id']
+  var id = event.target['id']
   doHighlight(id)
 }
 function doHighlight(id) {
   clearHighlight()
   source = document.getElementById(id)
   if (!source.attributes['tid'])
 return
-  tid = source.attributes['tid'].value
-  target = document.getElementById(tid)
-  if (!target || source.parentElement && source.parentElement.classList.contains('code'))
+  var mapped = source
+  while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1')
+mapped = mapped.parentElement
+  var tid = null, target = null
+  if (mapped) {
+tid = mapped.attributes['tid'].value
+target = document.getElementById(tid)
+  }
+  if (source.parentElement && source.parentElement.classList.contains('code'))
 return
-  source.style.backgroundColor = target.style.backgroundColor = 'lightgrey'
-  highlightStack.push([id, tid])
+  source.style.backgroundColor = 'lightgrey'
   source.scrollIntoView()
-  target.scrollIntoView()
+  if (target) {
+if (mapped === source)
+  target.style.backgroundColor = 'lightgrey'
+target.scrollIntoView()
+  }
+  highlightStack.push([id, tid])
   location.hash = '#'

[PATCH] D36686: [clang-diff] Add option to compare files across git revisions

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.

This adds a command line option "-git-rev=".
When it is used, only one filename is accepted.  The file in its version
in the specified revision is compared against the current version. Note
that this calls `git checkout` in the current directory.


https://reviews.llvm.org/D36686

Files:
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -16,6 +16,7 @@
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Program.h"
 
 using namespace llvm;
 using namespace clang;
@@ -33,9 +34,9 @@
 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 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."),
@@ -55,6 +56,11 @@
   cl::Optional, cl::init(""),
   cl::cat(ClangDiffCategory));
 
+static cl::opt
+GitRevision("git-rev", cl::desc("Compare the file from a checkout of this "
+"revision with the current version."),
+cl::Optional, cl::init(""), cl::cat(ClangDiffCategory));
+
 static cl::opt MaxSize("s", cl::desc(""), cl::Optional,
 cl::init(-1), cl::cat(ClangDiffCategory));
 
@@ -438,6 +444,19 @@
   }
 }
 
+std::string Exec(const char *Command) {
+  char Buffer[128];
+  std::string Result;
+  std::shared_ptr Pipe(popen(Command, "r"), pclose);
+  if (!Pipe)
+return Result;
+  while (!feof(Pipe.get())) {
+if (fgets(Buffer, 128, Pipe.get()) != nullptr)
+  Result += Buffer;
+  }
+  return Result;
+}
+
 int main(int argc, const char **argv) {
   std::string ErrorMessage;
   std::unique_ptr CommonCompilations =
@@ -473,13 +492,40 @@
 return 0;
   }
 
-  if (DestinationPath.empty()) {
+  if (DestinationPath.empty() && GitRevision.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);
+  std::unique_ptr Src, Dst;
+
+  if (!GitRevision.empty()) {
+std::string CurrentRevision, Git;
+auto ErrorOrGit = llvm::sys::findProgramByName("git");
+if (!ErrorOrGit) {
+  llvm::errs() << "Error: Could not find git executable.\n";
+  return 1;
+}
+Git = ErrorOrGit.get();
+CurrentRevision = Exec("git rev-parse HEAD");
+*std::find(CurrentRevision.begin(), CurrentRevision.end(), '\n') = '\0';
+const char *Checkout[] = {"git", "checkout", GitRevision.data(), nullptr};
+if (llvm::sys::ExecuteAndWait(Git, Checkout)) {
+  llvm::errs() << "Error: Failed to checkout " << GitRevision << "\n";
+  return 1;
+}
+Src = getAST(CommonCompilations, SourcePath);
+Checkout[2] = CurrentRevision.data();
+if (llvm::sys::ExecuteAndWait(Git, Checkout)) {
+  llvm::errs() << "Error: Failed to checkout " << CurrentRevision << "\n";
+  return 1;
+}
+Dst = getAST(CommonCompilations, SourcePath);
+  } else {
+Src = getAST(CommonCompilations, SourcePath);
+Dst = getAST(CommonCompilations, DestinationPath);
+  }
+
   if (!Src || !Dst)
 return 1;
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D36687: [clang-diff] Match nodes with the same parent and same value

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.

This adds another pass to the matching algorithm that tries to match nodes with
common parents, if they have similar values.


https://reviews.llvm.org/D36687

Files:
  include/clang/Tooling/ASTDiff/ASTDiff.h
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-bottomup.cpp
  test/Tooling/clang-diff-heuristics.cpp
  test/Tooling/clang-diff-opt.cpp
  tools/clang-diff/ClangDiff.cpp

Index: tools/clang-diff/ClangDiff.cpp
===
--- tools/clang-diff/ClangDiff.cpp
+++ tools/clang-diff/ClangDiff.cpp
@@ -535,7 +535,9 @@
   if (!StopAfter.empty()) {
 if (StopAfter == "topdown")
   Options.StopAfterTopDown = true;
-else if (StopAfter != "bottomup") {
+else if (StopAfter == "bottomup")
+  Options.StopAfterBottomUp = true;
+else {
   llvm::errs() << "Error: Invalid argument for -stop-after\n";
   return 1;
 }
Index: test/Tooling/clang-diff-opt.cpp
===
--- test/Tooling/clang-diff-opt.cpp
+++ test/Tooling/clang-diff-opt.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %t.src.cpp
 // RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
-// RUN: clang-diff -dump-matches -s=10 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches -s=10 -stop-after=bottomup %t.src.cpp %t.dst.cpp -- | FileCheck %s
 //
 // Test the behaviour of the matching according to the optimal tree edit
 // distance, implemented with Zhang and Shasha's algorithm.
@@ -41,5 +41,5 @@
   // CHECK: Delete NullStmt(22)
   ;; {{;;}}
 }
- 
+
 #endif
Index: test/Tooling/clang-diff-heuristics.cpp
===
--- /dev/null
+++ test/Tooling/clang-diff-heuristics.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -E %s > %t.src.cpp
+// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
+// RUN: clang-diff -dump-matches -s=0 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+//
+// Test the heuristics, with maxsize set to 0, so that the optimal matching will never be applied.
+
+#ifndef DEST
+
+void f1() {;}
+
+void f2(int) {;}
+
+#else
+
+// same parents, same value
+// CHECK: Match FunctionDecl: f1(void ())(1) to FunctionDecl: f1(void ())(1)
+// CHECK: Match CompoundStmt
+void f1() {}
+
+// same parents, same identifier
+// CHECK: Match FunctionDecl: f2(void (int))(4) to FunctionDecl: f2(void ())(3)
+// CHECK: Match CompoundStmt
+void f2() {}
+
+#endif
Index: test/Tooling/clang-diff-bottomup.cpp
===
--- test/Tooling/clang-diff-bottomup.cpp
+++ test/Tooling/clang-diff-bottomup.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -E %s > %t.src.cpp
 // RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
-// RUN: clang-diff -dump-matches -s=0 %t.src.cpp %t.dst.cpp -- | FileCheck %s
+// RUN: clang-diff -dump-matches -s=0 -stop-after=bottomup %t.src.cpp %t.dst.cpp -- | FileCheck %s
 //
 // Test the bottom-up matching, with maxsize set to 0, so that the optimal matching will never be applied.
 
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -94,15 +94,23 @@
   // Descendants are only considered to be equal when they are mapped in M.
   double getJaccardSimilarity(const Mapping &M, NodeId Id1, NodeId Id2) const;
 
+  double getNodeSimilarity(const Mapping &M, NodeId Id1, NodeId Id2) const;
+
   // Returns the node that has the highest degree of similarity.
   NodeId findCandidate(const Mapping &M, NodeId Id1) const;
 
+  NodeId findCandidateFromChildren(const Mapping &M, NodeId Id1,
+   NodeId P2) const;
+
   // Returns a mapping of identical subtrees.
   Mapping matchTopDown() const;
 
   // Tries to match any yet unmapped nodes, in a bottom-up fashion.
   void matchBottomUp(Mapping &M) const;
 
+  // Matches nodes, whose parents are matched.
+  void matchChildren(Mapping &M);
+
   const ComparisonOptions &Options;
 
   friend class ZhangShashaMatcher;
@@ -828,6 +836,23 @@
   return CommonDescendants / Denominator;
 }
 
+double ASTDiff::Impl::getNodeSimilarity(const Mapping &M, NodeId Id1,
+NodeId Id2) const {
+  const Node &N1 = T1.getNode(Id1);
+  const Node &N2 = T2.getNode(Id2);
+  auto Ident1 = N1.getIdentifier(), Ident2 = N2.getIdentifier();
+
+  bool SameValue = T1.getNodeValue(Id1) == T2.getNodeValue(Id2);
+  auto SameIdent = Ident1 && Ident2 && *Ident1 == *Ident2;
+
+  double NodeSimilarity = 0;
+  NodeSimilarity += SameValue;
+  NodeSimilarity += SameIdent;
+
+  assert(haveSameParents(M, Id1, Id2));
+  return NodeSimilarity * Options.MinSimilarity;
+}
+
 NodeId ASTDiff::Impl::findCandidate(const Mapping &M, NodeId Id1) const {
   NodeId Candidate;
   double HighestSimilarity = 0.0;
@@ -845,6 +870,25 @@
   return Candidate;
 }
 
+NodeId ASTDiff::Impl::findCandidateFromChildren(const 

[PATCH] D36688: [clang-diff] Fix matching for unnamed NamedDecs

2017-08-14 Thread Johannes Altmanninger via Phabricator via cfe-commits
johannes created this revision.
Herald added a subscriber: klimek.

This makes Node::getIdentifier return a valid value for special
NamedDecl nodes that do not have a name, such as ctors, dtors
and unnamed classes / namespaces.


https://reviews.llvm.org/D36688

Files:
  lib/Tooling/ASTDiff/ASTDiff.cpp
  test/Tooling/clang-diff-heuristics.cpp


Index: test/Tooling/clang-diff-heuristics.cpp
===
--- test/Tooling/clang-diff-heuristics.cpp
+++ test/Tooling/clang-diff-heuristics.cpp
@@ -10,6 +10,8 @@
 
 void f2(int) {;}
 
+class C3 { C3(); };
+
 #else
 
 // same parents, same value
@@ -22,4 +24,8 @@
 // CHECK: Match CompoundStmt
 void f2() {}
 
+// same parents, same identifier
+// CHECK: Match CXXConstructorDecl: :C3(void ())(9) to CXXConstructorDecl: 
:C3(void (int))(6)
+class C3 { C3(int); };
+
 #endif
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -384,8 +384,7 @@
   // Strip the qualifier, if Val refers to somthing in the current scope.
   // But leave one leading ':' in place, so that we know that this is a
   // relative path.
-  if (!ContextPrefix.empty() &&
-  StringRef(Val).startswith(ContextPrefix))
+  if (!ContextPrefix.empty() && StringRef(Val).startswith(ContextPrefix))
 Val = Val.substr(ContextPrefix.size() + 1);
   return Val;
 }
@@ -715,14 +714,18 @@
   if (auto *ND = ASTNode.get()) {
 if (ND->getDeclName().isIdentifier())
   return ND->getQualifiedNameAsString();
+else
+  return std::string();
   }
   return llvm::None;
 }
 
 llvm::Optional Node::getIdentifier() const {
   if (auto *ND = ASTNode.get()) {
 if (ND->getDeclName().isIdentifier())
   return ND->getName();
+else
+  return StringRef();
   }
   return llvm::None;
 }


Index: test/Tooling/clang-diff-heuristics.cpp
===
--- test/Tooling/clang-diff-heuristics.cpp
+++ test/Tooling/clang-diff-heuristics.cpp
@@ -10,6 +10,8 @@
 
 void f2(int) {;}
 
+class C3 { C3(); };
+
 #else
 
 // same parents, same value
@@ -22,4 +24,8 @@
 // CHECK: Match CompoundStmt
 void f2() {}
 
+// same parents, same identifier
+// CHECK: Match CXXConstructorDecl: :C3(void ())(9) to CXXConstructorDecl: :C3(void (int))(6)
+class C3 { C3(int); };
+
 #endif
Index: lib/Tooling/ASTDiff/ASTDiff.cpp
===
--- lib/Tooling/ASTDiff/ASTDiff.cpp
+++ lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -384,8 +384,7 @@
   // Strip the qualifier, if Val refers to somthing in the current scope.
   // But leave one leading ':' in place, so that we know that this is a
   // relative path.
-  if (!ContextPrefix.empty() &&
-  StringRef(Val).startswith(ContextPrefix))
+  if (!ContextPrefix.empty() && StringRef(Val).startswith(ContextPrefix))
 Val = Val.substr(ContextPrefix.size() + 1);
   return Val;
 }
@@ -715,14 +714,18 @@
   if (auto *ND = ASTNode.get()) {
 if (ND->getDeclName().isIdentifier())
   return ND->getQualifiedNameAsString();
+else
+  return std::string();
   }
   return llvm::None;
 }
 
 llvm::Optional Node::getIdentifier() const {
   if (auto *ND = ASTNode.get()) {
 if (ND->getDeclName().isIdentifier())
   return ND->getName();
+else
+  return StringRef();
   }
   return llvm::None;
 }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


  1   2   >