[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-09 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
ymandel added reviewers: aaron.ballman, JonasToth, ioeric.
Herald added subscribers: cfe-commits, xazax.hun, mgorny.

Adds a new check readability-const-value-return, which checks for functions with
a ``const``-qualified return type and recommends removal of the `const` keyword.
Such use of ``const`` is superfluous, and prevents valuable compiler
optimizations.

Based in part on the (abandoned) review https://reviews.llvm.org/D33531.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,139 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- \
+// RUN:   -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const`` for values, as it often disables optimizations and causes unnecessary copies
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``c
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Strukt p6() {}
+
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: avoid marking return types as ``co
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``cons
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+//another decl at top.
+const int p15();
+// CHECK-FIXES: int p15();
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+const int n1 = 1;
+const Clazz n2 = Clazz();
+const Clazz* n3 = new Clazz();
+Clazz *const n4 = new Clazz();
+const Clazz *const n5 = new Clazz();
+constexpr int n6 = 6;
+constexpr int n7() { return 8; }
+const int eight = 8;
+constexpr const int* n8() { re

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-09 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 168856.
ymandel added a comment.

Minor changes addressing comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,139 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- \
+// RUN:   -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const`` for values, as it often disables optimizations and causes unnecessary copies
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``c
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Strukt p6() {}
+
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: avoid marking return types as ``co
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``cons
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+//another decl at top.
+const int p15();
+// CHECK-FIXES: int p15();
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+const int n1 = 1;
+const Clazz n2 = Clazz();
+const Clazz* n3 = new Clazz();
+Clazz *const n4 = new Clazz();
+const Clazz *const n5 = new Clazz();
+constexpr int n6 = 6;
+constexpr int n7() { return 8; }
+const int eight = 8;
+constexpr const int* n8() { return &eight; }
+Klazz n9();
+const Klazz* n10();
+const Klazz& Clazz::n11(const Klazz) const {}
+const int n14();
+
+#define CONSTINT const int
+CONSTINT n12() {}
+
+// DEFFF in header.
+DEFFF n13() {}
Index: test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
===
--- /dev/null
+++ t

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-09 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 168857.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Dropped unneeded clang qualifier.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,139 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- \
+// RUN:   -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const`` for values, as it often disables optimizations and causes unnecessary copies
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``c
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Strukt p6() {}
+
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: avoid marking return types as ``co
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``cons
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+//another decl at top.
+const int p15();
+// CHECK-FIXES: int p15();
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+const int n1 = 1;
+const Clazz n2 = Clazz();
+const Clazz* n3 = new Clazz();
+Clazz *const n4 = new Clazz();
+const Clazz *const n5 = new Clazz();
+constexpr int n6 = 6;
+constexpr int n7() { return 8; }
+const int eight = 8;
+constexpr const int* n8() { return &eight; }
+Klazz n9();
+const Klazz* n10();
+const Klazz& Clazz::n11(const Klazz) const {}
+const int n14();
+
+#define CONSTINT const int
+CONSTINT n12() {}
+
+// DEFFF in header.
+DEFFF n13() {}
Index: test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
==

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-09 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 168858.
ymandel added a comment.

Add missing const.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,139 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- \
+// RUN:   -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const`` for values, as it often disables optimizations and causes unnecessary copies
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``c
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Strukt p6() {}
+
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: avoid marking return types as ``co
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: avoid marking return types as ``con
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: avoid marking return types as ``co
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: avoid marking return types as ``cons
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``cons
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+//another decl at top.
+const int p15();
+// CHECK-FIXES: int p15();
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: avoid marking return types as ``const
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+const int n1 = 1;
+const Clazz n2 = Clazz();
+const Clazz* n3 = new Clazz();
+Clazz *const n4 = new Clazz();
+const Clazz *const n5 = new Clazz();
+constexpr int n6 = 6;
+constexpr int n7() { return 8; }
+const int eight = 8;
+constexpr const int* n8() { return &eight; }
+Klazz n9();
+const Klazz* n10();
+const Klazz& Clazz::n11(const Klazz) const {}
+const int n14();
+
+#define CONSTINT const int
+CONSTINT n12() {}
+
+// DEFFF in header.
+DEFFF n13() {}
Index: test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
===
--- /dev/null
+++ test/clang-tidy/I

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-09 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 3 inline comments as done.
ymandel added a comment.

Thanks for the review. I've addressed the comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D52971: [clang-tidy] Customize FileCheck prefix in check_clang-tidy.py to support multiple prefixes

2018-10-11 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Hi,

It looks like this change has disabled FileCheck for all clang-tidy lit tests 
that don't use check-prefixes.  So, they all trivially pass even if their 
CHECK... lines are wrong.  An easy repro is just to randomly modify any CHECK 
line in a lit file (e.g. 
llvm/tools/clang/tools/extra/test/clang-tidy/readability-avoid-const-params-in-decls.cpp)
 and run ninja check-clang-tools.

In check_clang_tidy.py, if you add back (slightly modified) lines 93-95 to the 
else branch (line 132), it seems to fix the problem.  For example, add:

  has_check_fixes = check_fixes_prefixes[0] in input_text
  has_check_messages = check_messages_prefixes[0] in input_text
  has_check_notes = check_notes_prefixes[0] in input_text


Repository:
  rL LLVM

https://reviews.llvm.org/D52971



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-11 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169277.
ymandel added a comment.

Adusted code in respone to comments.

Major differences are:

- more warning in situations where a FixIt can't be suggested.
- more tests.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/check_clang_tidy.py
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,186 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinder compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values h
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hind
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'const'-qualified return values hind
+// CHECK-FIXES: /* comment */
+// more
+/* anoth

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-11 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169280.
ymandel added a comment.

comment tweak


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/check_clang_tidy.py
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,186 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinder compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values h
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hind
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'const'-qualified return values hind
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// ind

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-11 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169282.
ymandel added a comment.

Spacing fix.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/check_clang_tidy.py
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,186 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinder compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values h
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hind
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'const'-qualified return values hind
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indi

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-11 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169290.
ymandel marked 11 inline comments as done.
ymandel added a comment.

Comment fix.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/check_clang_tidy.py
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,186 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinder compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values h
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hind
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'const'-qualified return values hind
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexic

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-11 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Thank you for the detailed comments!  They were quite helpful, especially as 
this is my first llvm/clang review.




Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:25
+
+// Finds the location of the relevant "const" token in `Def`.
+llvm::Optional findConstToRemove(

JonasToth wrote:
> s/"const"/`const`
here and throughout.  All comments mention const without quotes.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:60
+  const auto *Def = Result.Nodes.getNodeAs("func");
+  if (!Def->getReturnType().isLocalConstQualified()) {
+return;

JonasToth wrote:
> Please ellide the braces
Actually, i expanded this to include a diag() statement, so kept the braces.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:64
+
+  // Fix the definition.
+  llvm::Optional Loc = findConstToRemove(Def, Result);

JonasToth wrote:
> I feel that this comment doesn't add value. Could you please either make it 
> more expressive or remove it?
Agreed. I merged the comment below into this one, so one comment describes the 
rest of the control flow in this block.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:65
+  // Fix the definition.
+  llvm::Optional Loc = findConstToRemove(Def, Result);
+  if (!Loc)

JonasToth wrote:
> This check does not produce diagnostics if something in the lexing went wrong.
> I think even if its not possible to do transformation the warning could still 
> be emitted (at functionDecl location). What do you think?
Nice. I like this.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:79
+   Decl = Decl->getPreviousDecl()) {
+if (const llvm::Optional PrevLoc =
+findConstToRemove(Decl, Result)) {

JonasToth wrote:
> The `const` is not common in llvm-code. Please use it only for references and 
> pointers.
> 
> What do you think about emitting a `note` if this transformation can not be 
> done? It is relevant for the user as he might need to do manual fix-up. It 
> would complicate the code as there can only be one(?) diagnostic at the same 
> time (90% sure only tbh).
Not the most elegant, but I don't see any other way to display multiple 
diagnoses. Let me know what you think.



Comment at: docs/clang-tidy/checks/list.rst:12
abseil-redundant-strcat-calls
-   abseil-string-find-startswith
abseil-str-cat-append

JonasToth wrote:
> spurious change
right. this was done by the script, so I wonder why it reordered these.



Comment at: test/clang-tidy/Inputs/readability-const-value-return/macro-def.h:1
+#ifndef MACRO_DEF_H
+#define MACRO_DEF_H

JonasToth wrote:
> You can define these macros directly in the test-case, or do you intend 
> something special? Self-contained tests are prefered.
I added a comment explaining. Does that justify its existence? If not, I'm fine 
getting rid of this header.



Comment at: test/clang-tidy/readability-const-value-return.cpp:53
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+

JonasToth wrote:
> Missing warning?
No, but this is subtle, so added a comment.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-12 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169461.
ymandel marked 3 inline comments as done.
ymandel added a comment.

Adjusted reporting of warnings.

Also, addressed other comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,174 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning:
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indirection.
+
+#define CONSTINT const int
+CONSTINT p18() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+#define CONST const
+CONST int p19() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+using ty = const int;
+ty p21() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty' (aka 'const int') is
+
+typedef const int ty2;
+ty2 p22() {}
+// CHECK-MESSAGES:

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-12 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:25
+
+// Finds the location of the relevant "const" token in `Def`.
+llvm::Optional findConstToRemove(

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > s/"const"/`const`
> > here and throughout.  All comments mention const without quotes.
> I meant that you use the  \` thingie to mark const :)
> Its better to mark language constructs in the comments as well for better 
> clarity whats meant. Comments need to be full sentences and super clear, 
> otherwise the comment does more harm then helping :)
> I meant that you use the \` thingie to mark const :)
Ah. :)  Done now for real.

> Its better to mark language constructs in the comments as well for better 
> clarity whats meant. Comments need to be full sentences and super clear, 
> otherwise the comment does more harm then helping :)

Is there a specific issue w/ this comment, or are you remarking in general?  
I've rewritten with the goal or better clarity, but wasn't sure of your intent 
wrt full sentences.  





Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:79
+   Decl = Decl->getPreviousDecl()) {
+if (const llvm::Optional PrevLoc =
+findConstToRemove(Decl, Result)) {

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > The `const` is not common in llvm-code. Please use it only for references 
> > > and pointers.
> > > 
> > > What do you think about emitting a `note` if this transformation can not 
> > > be done? It is relevant for the user as he might need to do manual 
> > > fix-up. It would complicate the code as there can only be one(?) 
> > > diagnostic at the same time (90% sure only tbh).
> > Not the most elegant, but I don't see any other way to display multiple 
> > diagnoses. Let me know what you think.
> What do you want to achieve? I think you just want to append the `FixItHint` 
> do you?
> 
> you can do this with saving the diagnostic object in a variable.
> 
> ```
> auto Diag = diag(Loc, "Warning Message");
> 
> // Foo
> 
> if (HaveFix)
> Diag << FixItHint::CreateRemove();
> ```
> 
> When `Diag` goes out of scope the diagnostic is actually issued, calling just 
> `diag` produces a temporary that gets immediately destroyed. 
I was responding to your original suggestion to emit a `note` if the 
transformation can not be done; I changed the code to allow multiple 
diagnostics via scoping, but found it less than elegant.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:93
+Diagnostics << FixItHint::CreateRemoval(
+CharSourceRange::getTokenRange(*PrevLoc, *PrevLoc));
+  } else {

JonasToth wrote:
> Twice `*PrevLoc`?
Is there a better alternative? I thought that, since token ranges closed on 
both ends,  this constructs a SourceRange that spans the single token at 
PrevLoc.  Instead, I could rework `getConstTokLocations` to return the actual 
tokens instead, and then create CharSourceRanges from Tok.getLocation(), 
Tok.getLastLocation().



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:95
+  } else {
+UnfixabledDecls.emplace_back(
+Decl->getInnerLocStart(),

JonasToth wrote:
> Did you consider `diag(Loc, "could not transform this declaration", 
> DiagnosticIDs::Note)` which emits a `note` instead of `warning`.
> You dont need to store these in between.
Yes, not sure what I was thinking there (storing the same string repeatedly).  
Done, thanks.



Comment at: test/clang-tidy/Inputs/readability-const-value-return/macro-def.h:1
+#ifndef MACRO_DEF_H
+#define MACRO_DEF_H

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > You can define these macros directly in the test-case, or do you intend 
> > > something special? Self-contained tests are prefered.
> > I added a comment explaining. Does that justify its existence? If not, I'm 
> > fine getting rid of this header.
> This test is not necessary. If that would not work, the unit tests for the 
> frontend need to fail. The inclusions and everything are assumed to be 
> correctly done. clang-tidy itself only works on the translation units 
> (meaning the .cpp file and all included headers in one blob). You can remove 
> this test safely.
Sure.  Will do.  Just to explain a bit: my concern was whether my code properly 
recognizes that the source location at the macro use comes from a macro and I 
thought that the cross-file case might be reflected differently in source locs 
than the in-file case.  But, now that I think about it, I can't see any 
interesting difference.



Comment at: test/clang-tidy/readability-const-value-return.cpp:53
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > Missing

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-12 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 5 inline comments as done.
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:60
+diag(Def->getInnerLocStart(),
+ "return type is 'const'-qualifed (possibly behind a type alias), "
+ "which hinders compiler optimizations");

JonasToth wrote:
> This diagnostic is nice, but including the type would make it even better. 
> This will resolve typedefs as well (like "'uint32_t (a.k.a. 'unsigned int')").
> 
> Necessary code (more info in the cfe-internals manual)
> ```
> diag(Location, "return type %0 is 'const'-qualified hindering compiler 
> optimizations") << Def->getReturnType();
> ```
> The value passed in with `<<` must be a `QualType`.
> Also diagnostics don't have punctuation and are not full sentences and should 
> be as short as possible (but still clear).
Thanks, this is a significant improvement.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-12 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:64
+
+  // Fix the definition.
+  llvm::Optional Loc = findConstToRemove(Def, Result);

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > I feel that this comment doesn't add value. Could you please either make 
> > > it more expressive or remove it?
> > Agreed. I merged the comment below into this one, so one comment describes 
> > the rest of the control flow in this block.
> Did I tell you the "only one diagnostic in-flight" thing? :D
> I told you wrong stuff as you already figured out in the code. Please adjust 
> this comment and the additional scope is not necessary too.
But, I think you are *correct* in this assertion.  When I tried multiple 
in-flight diagnostics, it failed with error:

clang-tidy: 
/usr/local/google/home/yitzhakm/Projects/llvm/llvm/tools/clang/include/clang/Basic/Diagnostic.h:1297:
 clang::DiagnosticBuilder 
clang::DiagnosticsEngine::Report(clang::SourceLocation, unsigned int): 
Assertion `CurDiagID == std::numeric_limits::max() && "Multiple 
diagnostics in flight at once!"' failed.

Am I doing something wrong?




Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-15 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:64
+
+  // Fix the definition.
+  llvm::Optional Loc = findConstToRemove(Def, Result);

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > ymandel wrote:
> > > > JonasToth wrote:
> > > > > I feel that this comment doesn't add value. Could you please either 
> > > > > make it more expressive or remove it?
> > > > Agreed. I merged the comment below into this one, so one comment 
> > > > describes the rest of the control flow in this block.
> > > Did I tell you the "only one diagnostic in-flight" thing? :D
> > > I told you wrong stuff as you already figured out in the code. Please 
> > > adjust this comment and the additional scope is not necessary too.
> > But, I think you are *correct* in this assertion.  When I tried multiple 
> > in-flight diagnostics, it failed with error:
> > 
> > clang-tidy: 
> > /usr/local/google/home/yitzhakm/Projects/llvm/llvm/tools/clang/include/clang/Basic/Diagnostic.h:1297:
> >  clang::DiagnosticBuilder 
> > clang::DiagnosticsEngine::Report(clang::SourceLocation, unsigned int): 
> > Assertion `CurDiagID == std::numeric_limits::max() && "Multiple 
> > diagnostics in flight at once!"' failed.
> > 
> > Am I doing something wrong?
> > 
> > 
> Then let me revert what I said and claim the first thing again :D 
> 
> I think, the issue is, that you have the `auto Diag = diag()` object not 
> destroyed before the next one is created. So temporarily storing the 
> locations for the problematic transformations might be necessary to close the 
> scope of the `Diag` first and then emit the notes.
> 
> It would be a good idea, to make a function that returns you a list of FixIts 
> and the list of problematic transformations.
> Having these 2 lists (best is probably `llvm::SmallVector`, see 
> https://llvm.org/docs/ProgrammersManual.html#dss-smallvector) simplifies 
> creating the diagnostics a lot.
> Then you have 2 scopes for emitting, one scope for the actual warning and 
> replacement and the second scope for emitting the fail-notes.
> 
> These 2 scopes could even be methods (necessary to access `diag()`).
Sounds good. Here's a stab at this restructuring:
https://reviews.llvm.org/differential/diff/169718/

Doesn't seem worth factoring the actual diag() calls into methods, but let me 
know what you think.

I'll go ahead with other changes as well, just wanted to get this out there...


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-16 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169938.
ymandel marked 5 inline comments as done.
ymandel added a comment.

Refactors generation of clang-tidy fixits and notes.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,174 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning:
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indirection.
+
+#define CONSTINT const int
+CONSTINT p18() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+#define CONST const
+CONST int p19() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+using ty = const int;
+ty p21() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty' (aka 'const int') is
+
+typedef const int ty2;
+ty2 p22() {}
+// CHECK-MESSAGES: [[@LINE-1]]

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-16 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 169941.
ymandel added a comment.

Changed check result into struct (was pair).


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,174 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template  class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning:
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indirection.
+
+#define CONSTINT const int
+CONSTINT p18() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+#define CONST const
+CONST int p19() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+using ty = const int;
+ty p21() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty' (aka 'const int') is
+
+typedef const int ty2;
+ty2 p22() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty2' (aka 'const int') i

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-16 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:93
+Diagnostics << FixItHint::CreateRemoval(
+CharSourceRange::getTokenRange(*PrevLoc, *PrevLoc));
+  } else {

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > Twice `*PrevLoc`?
> > Is there a better alternative? I thought that, since token ranges closed on 
> > both ends,  this constructs a SourceRange that spans the single token at 
> > PrevLoc.  Instead, I could rework `getConstTokLocations` to return the 
> > actual tokens instead, and then create CharSourceRanges from 
> > Tok.getLocation(), Tok.getLastLocation().
> The Fixits work with sourcelocations, you dont need to get the tokenrange and 
> CharSourceRange things. This looks more complicated then necessary to me. 
> Your `findConstToRemove` can return a `Optional` that encloses 
> the `const` token. This range can then be used for the FixIts. I think this 
> would make the code a bit clearer as well.
After reworking to pipe the Token all the way through to the use, I ended up 
sticking with CharSourceRange::getCharRange(), because otherwise the fixithint 
will convert a plain SourceRange into a token range, which is not what we want 
(since we already have the lower-level char range from the lexed token).



Comment at: clang-tidy/utils/LexerUtils.cpp:41
+  const char *TokenBegin = File.data() + LocInfo.second;
+  Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
+ File.begin(), TokenBegin, File.end());

JonasToth wrote:
> I think this function can be simplified as the manual lexing stuff seems 
> unnecessary.
> Take a look at https://reviews.llvm.org/D51949#change-B_4XlTym3KPw that uses 
> `clang::Lexer` functionality quite a bit to do manual lexing.
> 
> You can use the public static methods from `clang::Lexer` to simplify this 
> function.
(FWIW, this code was taken (almost directly) from 
clang-tidy/readability/AvoidConstParamsInDecls.cpp.)

I agree that would potentially simplify the code here, but I'm not sure its the 
right thing to do. Basically all of the relevant functions create a 
clang::Lexer internally and do a bunch more work.  So, it seems like this case, 
where we are incrementally proceeding through a source, token by token, we 
really should be using a (stateful) clang::Lexer directly, rather than 
repeatedly calling into the static methods and creating/destroying a Lexer with 
each such call.




Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 170418.
ymandel added a comment.

Expanded test cases.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,201 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+template  class Klazz {};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning:
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indirection.
+
+#define CONSTINT const int
+CONSTINT p18() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+#define CONST const
+CONST int p19() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+using ty = const int;
+ty p21() {}
+// CHECK-MESSAGES: [[@L

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked an inline comment as done.
ymandel added inline comments.



Comment at: test/clang-tidy/readability-const-value-return.cpp:174
+int **const * n_multiple_ptr();
+int *const & n_pointer_ref();

aaron.ballman wrote:
> I'd like to see some more complex examples, like:
> ```
> int const foo();
> int const * const foo();
> 
> std::add_const foo();
> 
> template 
> std::add_const foo();
> 
> auto foo() -> const int;
> auto foo() -> int const;
> 
> template 
> auto foo() -> std::add_const;
> ```
> I'm also very curious to hear how often this triggers on large code bases, 
> especially ones that claim decent const-correctness.
I've added examples along the lines you suggested.  However, the code cannot 
currently catch the "auto foo() -> type" form, and I couldn't find an easy way 
to change this. I've noted as much in the comments.

Also, the template version (whether trailing or normal return) does not trigger 
the matchers at all, from which I surmise that clang doesn't consider the type 
const qualified when it is not materialized with an actual type. I've noted as 
much in the comments, but please correct me if I've misunderstood.

Finally, as for triggering: I ran this over Google's C++ codebase and found 
quite a few hits (10k < X < 100k;  not sure I'm allowed to specify a more 
precise value for X, but if you need it,  let me know and I'll ask).  I sampled 
100 of them and confirmed that all were correct.

I also looked at LLVM and found ~130 hits. I sampled 10 and again found them 
all correct. I'm happy to post all of the llvm hits if you think that would be 
helpful.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 170423.
ymandel marked an inline comment as done.
ymandel added a comment.

Fix some message comments in tests.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,203 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+template  class Klazz {};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning:
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indirection.
+
+#define CONSTINT const int
+CONSTINT p18() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+#define CONST const
+CONST int p19() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+
+us

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Correction: the code *catches* the trailing return cases, I just couldn't find 
a way to *fix* them; specifically, I was not able to get the necessary source 
ranges to re-lex the trailing return type.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171103.
ymandel added a comment.

Fix bug in const-token finding and add tests.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,226 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171105.
ymandel added a comment.

Formatting & comment tweaks.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

In https://reviews.llvm.org/D53025#1271367, @JonasToth wrote:

> In https://reviews.llvm.org/D53025#1270784, @ymandel wrote:
>
> > Correction: the code *catches* the trailing return cases, I just couldn't 
> > find a way to *fix* them; specifically, I was not able to get the necessary 
> > source ranges to re-lex the trailing return type.
>
>
> There is `fuchsia/TrailingReturnCheck.cpp` which you look if there is 
> something, I am currently not aware of other checks that handle trailing 
> return types in any way.
>  In principle it is ok to leave these for future improvements, but should be 
> noted as limitation in the docs.


Thanks. Unfortuantely, that code doesn't have what I want.  I've updated the 
.rst doc to mention this shortcoming.   I'm happy to come back in the future 
and add support if I can get it figured out.  However, I'd rather not black on 
this at the moment, if that's ok.

In https://reviews.llvm.org/D53025#1271388, @JonasToth wrote:

> > Also, the template version (whether trailing or normal return) does not 
> > trigger the matchers at all, from which I surmise that clang doesn't 
> > consider the type const qualified when it is not materialized with an 
> > actual type. I've noted as much in the comments, but please correct me if 
> > I've misunderstood.
>
> If the template is not instantiated no information on `const` exists and
>  that might vary between instantiations anyway (if you don't explicitly
>  write `const T`). It would be an interesting test to try and break the
>  check with an explicit `const` template and instantiaions with `const`.
>  (e.g. `template  const T my_function();` and an
>  instantiation like `gsl::span`)


I'm not sure I understand what you're trying to break in the check. If you're 
thinking of whether the code trips up on the lexical issues, I'm pretty sure 
that won't apply here.  Once the const-ness is hidden behind a template, the 
check doesn't try to fix it; so, lexical issues don't come into play.  It boils 
down to the whether the matcher for const-ness is implemented correctly.  But, 
I did add a new test based on this:
`template  const T p32(T t) { return t; }`
which *is* detected and fixed.  Let me know, though, if you want something more 
elaborate.  Also, see below, where I *did* find a bug in a related kind of type 
definition.

>> Finally, as for triggering: I ran this over Google's C++ codebase and found 
>> quite a few hits (10k < X < 100k;  not sure I'm allowed to specify a more 
>> precise value for X, but if you need it,  let me know and I'll ask).  I 
>> sampled 100 of them and confirmed that all were correct.
>> 
>> I also looked at LLVM and found ~130 hits. I sampled 10 and again found them 
>> all correct. I'm happy to post all of the llvm hits if you think that would 
>> be helpful.
> 
> Did you try the transformation and is it correct (does not break code)?
>  Enough to check on LLVM :)

Good call -- found bugs this way in one of the files, which had two errors of 
the sort you were asking about above:
https://reviews.llvm.org/source/test-suite/browse/test-suite/trunk/SingleSource/Benchmarks/Misc-C%2B%2B-EH/spirit.cpp;344155$2204
https://reviews.llvm.org/source/test-suite/browse/test-suite/trunk/SingleSource/Benchmarks/Misc-C%2B%2B-EH/spirit.cpp;344155$3133

I've modified the code that finds the const token to fix this bug.  In the 
process, I simplified it and (I think) found a more general solution to the 
problem.  I also noticed that this same bug exists in the  
AvoidConstParamsInDecls check, so I plan to send a follow up change that fixes 
that check by having it use the newly introduced `getConstQualifyingToken` 
function.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Thanks!  What's the next step in the process? Does someone need to approve this 
change?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171293.
ymandel marked 4 inline comments as done.
ymandel added a comment.

Reword comment on the check, for clarity.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified hindering compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* another comment*/ in

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

aaron.ballman wrote:
> It seems strange to me that this is in the readability module but the 
> diagnostic here is about compiler optimizations. Should this be in the 
> performance module instead? Or should this diagnostic be reworded about the 
> readability concerns?
Good point. The readability angle is that the const clutters the code to no 
benefit.  That is, even if it was performance neutral, I'd still want to 
discourage this practice.  But, it does seem like the primary impact is 
performance. 

I'm fine either way -- moving it to performance or emphasizing the clutter of 
the unhelpful "const".  I'm inclined to moving it, but don't have good sense of 
how strict these hierarchies are. What do you think?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

aaron.ballman wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > It seems strange to me that this is in the readability module but the 
> > > diagnostic here is about compiler optimizations. Should this be in the 
> > > performance module instead? Or should this diagnostic be reworded about 
> > > the readability concerns?
> > Good point. The readability angle is that the const clutters the code to no 
> > benefit.  That is, even if it was performance neutral, I'd still want to 
> > discourage this practice.  But, it does seem like the primary impact is 
> > performance. 
> > 
> > I'm fine either way -- moving it to performance or emphasizing the clutter 
> > of the unhelpful "const".  I'm inclined to moving it, but don't have good 
> > sense of how strict these hierarchies are. What do you think?
> I'm not sold that `const`-qualified return types always pessimize 
> optimizations. However, I'm also not sold that it's *always* a bad idea to 
> have a top-level cv-qualifier on a return type either (for instance, this is 
> one way to prevent callers from modifying a temp returned by a function). How 
> about leaving this in readability and changing the diagnostic into: 
> "top-level 'const' qualifier on a return type may reduce code readability 
> with limited benefit" or something equally weasely?
I talked this over with other google folks and seems like the consensus is:

1.  The readability benefits may be controversial.  Some folks might not view 
`const` as clutter and there are some corner cases where the qualifier may 
prevent something harmful.
2.  The performance implication is real, if not guaranteed to be a problem.

Based on this, seems best to move it to performance, but water down the 
performance claims.  That keeps the focus to an issue we can assume nearly 
everyone agrees on.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-30 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

aaron.ballman wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > It seems strange to me that this is in the readability module but the 
> > > > > diagnostic here is about compiler optimizations. Should this be in 
> > > > > the performance module instead? Or should this diagnostic be reworded 
> > > > > about the readability concerns?
> > > > Good point. The readability angle is that the const clutters the code 
> > > > to no benefit.  That is, even if it was performance neutral, I'd still 
> > > > want to discourage this practice.  But, it does seem like the primary 
> > > > impact is performance. 
> > > > 
> > > > I'm fine either way -- moving it to performance or emphasizing the 
> > > > clutter of the unhelpful "const".  I'm inclined to moving it, but don't 
> > > > have good sense of how strict these hierarchies are. What do you think?
> > > I'm not sold that `const`-qualified return types always pessimize 
> > > optimizations. However, I'm also not sold that it's *always* a bad idea 
> > > to have a top-level cv-qualifier on a return type either (for instance, 
> > > this is one way to prevent callers from modifying a temp returned by a 
> > > function). How about leaving this in readability and changing the 
> > > diagnostic into: "top-level 'const' qualifier on a return type may reduce 
> > > code readability with limited benefit" or something equally weasely?
> > I talked this over with other google folks and seems like the consensus is:
> > 
> > 1.  The readability benefits may be controversial.  Some folks might not 
> > view `const` as clutter and there are some corner cases where the qualifier 
> > may prevent something harmful.
> > 2.  The performance implication is real, if not guaranteed to be a problem.
> > 
> > Based on this, seems best to move it to performance, but water down the 
> > performance claims.  That keeps the focus to an issue we can assume nearly 
> > everyone agrees on.
> I'm not convinced the performance implications are real compared to how the 
> check is currently implemented. I know there are performance concerns when 
> you return a const value of class type because it pessimizes the ability to 
> use move constructors, but that's a very specific performance concern that I 
> don't believe is present in general. For instance, I don't know of any 
> performance concerns regarding `const int f();` as a declaration. I'd be fine 
> moving this to the performance module, but I think the check would need to be 
> tightened to only focus on actual performance concerns.
> 
> FWIW, I do agree that the readability benefits may be controversial, but I 
> kind of thought that was the point to the check and as such, it's a very 
> reasonable check. Almost everything in readability is subjective to some 
> degree and our metric has always been "if you agree with a style check, don't 
> enable it".
> 
> It's up to you how you want to proceed, but I see two ways forward: 1) keep 
> this as a readability check that broadly finds top-level const qualifiers and 
> removes them, 2) move this to the performance module and rework the check to 
> focus on only the scenarios that have concrete performance impact.
> It's up to you how you want to proceed, but I see two ways forward: 1) keep 
> this as a readability check that broadly finds top-level const qualifiers and 
> removes them, 2) move this to the performance module and rework the check to 
> focus on only the scenarios that have concrete performance impact.

Aaron, thank you for the detailed response. I'm inclined to agree with you that 
this belongs in readabilty and I spoke with sbenza who feels the same.  The 
high-level point is that the `const` is noise in most cases.

You suggested above a warning along the lines of:
 "top-level 'const' qualifier on a return type may reduce code readability with 
limited benefit"

I like this, but think it should be a little stronger. Perhaps:
 "top-level 'const' qualifier on a return type may reduce code readability 
while rarely having an effect"

I also propose updating the explanation in the documentation file from:

Such use of ``const`` is superfluous, and
prevents valuable compiler optimizations. 

to the weaker

Such use of ``const`` is usually superfluous, and can
prevent valuable compiler optimizations. 

WDYT?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-30 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171700.
ymandel added a comment.

Reworded diagnostic message and corresponding documentation.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified, which may reduce code readability without improving const correctness
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* comment */ /* anoth

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-30 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 3 inline comments as done.
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

aaron.ballman wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > ymandel wrote:
> > > > > > aaron.ballman wrote:
> > > > > > > It seems strange to me that this is in the readability module but 
> > > > > > > the diagnostic here is about compiler optimizations. Should this 
> > > > > > > be in the performance module instead? Or should this diagnostic 
> > > > > > > be reworded about the readability concerns?
> > > > > > Good point. The readability angle is that the const clutters the 
> > > > > > code to no benefit.  That is, even if it was performance neutral, 
> > > > > > I'd still want to discourage this practice.  But, it does seem like 
> > > > > > the primary impact is performance. 
> > > > > > 
> > > > > > I'm fine either way -- moving it to performance or emphasizing the 
> > > > > > clutter of the unhelpful "const".  I'm inclined to moving it, but 
> > > > > > don't have good sense of how strict these hierarchies are. What do 
> > > > > > you think?
> > > > > I'm not sold that `const`-qualified return types always pessimize 
> > > > > optimizations. However, I'm also not sold that it's *always* a bad 
> > > > > idea to have a top-level cv-qualifier on a return type either (for 
> > > > > instance, this is one way to prevent callers from modifying a temp 
> > > > > returned by a function). How about leaving this in readability and 
> > > > > changing the diagnostic into: "top-level 'const' qualifier on a 
> > > > > return type may reduce code readability with limited benefit" or 
> > > > > something equally weasely?
> > > > I talked this over with other google folks and seems like the consensus 
> > > > is:
> > > > 
> > > > 1.  The readability benefits may be controversial.  Some folks might 
> > > > not view `const` as clutter and there are some corner cases where the 
> > > > qualifier may prevent something harmful.
> > > > 2.  The performance implication is real, if not guaranteed to be a 
> > > > problem.
> > > > 
> > > > Based on this, seems best to move it to performance, but water down the 
> > > > performance claims.  That keeps the focus to an issue we can assume 
> > > > nearly everyone agrees on.
> > > I'm not convinced the performance implications are real compared to how 
> > > the check is currently implemented. I know there are performance concerns 
> > > when you return a const value of class type because it pessimizes the 
> > > ability to use move constructors, but that's a very specific performance 
> > > concern that I don't believe is present in general. For instance, I don't 
> > > know of any performance concerns regarding `const int f();` as a 
> > > declaration. I'd be fine moving this to the performance module, but I 
> > > think the check would need to be tightened to only focus on actual 
> > > performance concerns.
> > > 
> > > FWIW, I do agree that the readability benefits may be controversial, but 
> > > I kind of thought that was the point to the check and as such, it's a 
> > > very reasonable check. Almost everything in readability is subjective to 
> > > some degree and our metric has always been "if you agree with a style 
> > > check, don't enable it".
> > > 
> > > It's up to you how you want to proceed, but I see two ways forward: 1) 
> > > keep this as a readability check that broadly finds top-level const 
> > > qualifiers and removes them, 2) move this to the performance module and 
> > > rework the check to focus on only the scenarios that have concrete 
> > > performance impact.
> > > It's up to you how you want to proceed, but I see two ways forward: 1) 
> > > keep this as a readability check that broadly finds top-level const 
> > > qualifiers and removes them, 2) move this to the performance module and 
> > > rework the check to focus on only the scenarios that have concrete 
> > > performance impact.
> > 
> > Aaron, thank you for the detailed response. I'm inclined to agree with you 
> > that this belongs in readabilty and I spoke with sbenza who feels the same. 
> >  The high-level point is that the `const` is noise in most cases.
> > 
> > You suggested above a warning along the lines of:
> >  "top-level 'const' qualifier on a return type may reduce code readability 
> > with limited benefit"
> > 
> > I like this, but think it should be a little stronger. Perhaps:
> >  "top-level 'const' qualifier on a return type may reduce code readability 
> > while rarely having an effect"
> > 
> > I also propose updating the explanation in the documentation file from:
> > 
> > Such use of ``const`` is superfluous, and
> > prevents valuable compiler opti

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-30 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171764.
ymandel marked an inline comment as done.
ymandel added a comment.

Modify dialog printing to highlight const token.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: w

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-30 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171765.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Add comment to field.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /*

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-30 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

aaron.ballman wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > ymandel wrote:
> > > > > > aaron.ballman wrote:
> > > > > > > ymandel wrote:
> > > > > > > > aaron.ballman wrote:
> > > > > > > > > It seems strange to me that this is in the readability module 
> > > > > > > > > but the diagnostic here is about compiler optimizations. 
> > > > > > > > > Should this be in the performance module instead? Or should 
> > > > > > > > > this diagnostic be reworded about the readability concerns?
> > > > > > > > Good point. The readability angle is that the const clutters 
> > > > > > > > the code to no benefit.  That is, even if it was performance 
> > > > > > > > neutral, I'd still want to discourage this practice.  But, it 
> > > > > > > > does seem like the primary impact is performance. 
> > > > > > > > 
> > > > > > > > I'm fine either way -- moving it to performance or emphasizing 
> > > > > > > > the clutter of the unhelpful "const".  I'm inclined to moving 
> > > > > > > > it, but don't have good sense of how strict these hierarchies 
> > > > > > > > are. What do you think?
> > > > > > > I'm not sold that `const`-qualified return types always pessimize 
> > > > > > > optimizations. However, I'm also not sold that it's *always* a 
> > > > > > > bad idea to have a top-level cv-qualifier on a return type either 
> > > > > > > (for instance, this is one way to prevent callers from modifying 
> > > > > > > a temp returned by a function). How about leaving this in 
> > > > > > > readability and changing the diagnostic into: "top-level 'const' 
> > > > > > > qualifier on a return type may reduce code readability with 
> > > > > > > limited benefit" or something equally weasely?
> > > > > > I talked this over with other google folks and seems like the 
> > > > > > consensus is:
> > > > > > 
> > > > > > 1.  The readability benefits may be controversial.  Some folks 
> > > > > > might not view `const` as clutter and there are some corner cases 
> > > > > > where the qualifier may prevent something harmful.
> > > > > > 2.  The performance implication is real, if not guaranteed to be a 
> > > > > > problem.
> > > > > > 
> > > > > > Based on this, seems best to move it to performance, but water down 
> > > > > > the performance claims.  That keeps the focus to an issue we can 
> > > > > > assume nearly everyone agrees on.
> > > > > I'm not convinced the performance implications are real compared to 
> > > > > how the check is currently implemented. I know there are performance 
> > > > > concerns when you return a const value of class type because it 
> > > > > pessimizes the ability to use move constructors, but that's a very 
> > > > > specific performance concern that I don't believe is present in 
> > > > > general. For instance, I don't know of any performance concerns 
> > > > > regarding `const int f();` as a declaration. I'd be fine moving this 
> > > > > to the performance module, but I think the check would need to be 
> > > > > tightened to only focus on actual performance concerns.
> > > > > 
> > > > > FWIW, I do agree that the readability benefits may be controversial, 
> > > > > but I kind of thought that was the point to the check and as such, 
> > > > > it's a very reasonable check. Almost everything in readability is 
> > > > > subjective to some degree and our metric has always been "if you 
> > > > > agree with a style check, don't enable it".
> > > > > 
> > > > > It's up to you how you want to proceed, but I see two ways forward: 
> > > > > 1) keep this as a readability check that broadly finds top-level 
> > > > > const qualifiers and removes them, 2) move this to the performance 
> > > > > module and rework the check to focus on only the scenarios that have 
> > > > > concrete performance impact.
> > > > > It's up to you how you want to proceed, but I see two ways forward: 
> > > > > 1) keep this as a readability check that broadly finds top-level 
> > > > > const qualifiers and removes them, 2) move this to the performance 
> > > > > module and rework the check to focus on only the scenarios that have 
> > > > > concrete performance impact.
> > > > 
> > > > Aaron, thank you for the detailed response. I'm inclined to agree with 
> > > > you that this belongs in readabilty and I spoke with sbenza who feels 
> > > > the same.  The high-level point is that the `const` is noise in most 
> > > > cases.
> > > > 
> > > > You suggested above a warning along the lines of:
> > > >  "top-level 'const' qualifier on a return type may reduce code 
> > > > readability with limited benefit"
> > > > 
> > > > I like this, but think it should be a

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-31 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171897.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Small tweaks in response to comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+//

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-31 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

In https://reviews.llvm.org/D53025#1281097, @aaron.ballman wrote:

> I think this is getting really close! One question: would it make more sense 
> to name this `readability-const-type-return` or 
> `readability-const-return-type` instead? It's not that the functions return a 
> const *value* that's the issue, it's that the declared return type is 
> top-level const. I think removing "value" and using "type" instead would be 
> an improvement (and similarly, rename the files and the check).


Yes, that sounds good.  Will do that in a separate diff so you can see my 
changes in reply to your comments first.

In https://reviews.llvm.org/D53025#1281107, @JonasToth wrote:

> Am 30.10.18 um 21:47 schrieb Aaron Ballman via Phabricator:
>
> > aaron.ballman added a comment.
> > 
> > I think this is getting really close! One question: would it make more 
> > sense to name this `readability-const-type-return` or 
> > `readability-const-return-type` instead? It's not that the functions return 
> > a const *value* that's the issue, it's that the declared return type is 
> > top-level const. I think removing "value" and using "type" instead would be 
> > an improvement (and similarly, rename the files and the check).
>
> There is a `rename-check.py` script in the repository. Just to prevent a
>  lot of manual work ;)


Jonas -- thanks for the tip!




Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

JonasToth wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > ymandel wrote:
> > > > > > aaron.ballman wrote:
> > > > > > > ymandel wrote:
> > > > > > > > aaron.ballman wrote:
> > > > > > > > > ymandel wrote:
> > > > > > > > > > aaron.ballman wrote:
> > > > > > > > > > > It seems strange to me that this is in the readability 
> > > > > > > > > > > module but the diagnostic here is about compiler 
> > > > > > > > > > > optimizations. Should this be in the performance module 
> > > > > > > > > > > instead? Or should this diagnostic be reworded about the 
> > > > > > > > > > > readability concerns?
> > > > > > > > > > Good point. The readability angle is that the const 
> > > > > > > > > > clutters the code to no benefit.  That is, even if it was 
> > > > > > > > > > performance neutral, I'd still want to discourage this 
> > > > > > > > > > practice.  But, it does seem like the primary impact is 
> > > > > > > > > > performance. 
> > > > > > > > > > 
> > > > > > > > > > I'm fine either way -- moving it to performance or 
> > > > > > > > > > emphasizing the clutter of the unhelpful "const".  I'm 
> > > > > > > > > > inclined to moving it, but don't have good sense of how 
> > > > > > > > > > strict these hierarchies are. What do you think?
> > > > > > > > > I'm not sold that `const`-qualified return types always 
> > > > > > > > > pessimize optimizations. However, I'm also not sold that it's 
> > > > > > > > > *always* a bad idea to have a top-level cv-qualifier on a 
> > > > > > > > > return type either (for instance, this is one way to prevent 
> > > > > > > > > callers from modifying a temp returned by a function). How 
> > > > > > > > > about leaving this in readability and changing the diagnostic 
> > > > > > > > > into: "top-level 'const' qualifier on a return type may 
> > > > > > > > > reduce code readability with limited benefit" or something 
> > > > > > > > > equally weasely?
> > > > > > > > I talked this over with other google folks and seems like the 
> > > > > > > > consensus is:
> > > > > > > > 
> > > > > > > > 1.  The readability benefits may be controversial.  Some folks 
> > > > > > > > might not view `const` as clutter and there are some corner 
> > > > > > > > cases where the qualifier may prevent something harmful.
> > > > > > > > 2.  The performance implication is real, if not guaranteed to 
> > > > > > > > be a problem.
> > > > > > > > 
> > > > > > > > Based on this, seems best to move it to performance, but water 
> > > > > > > > down the performance claims.  That keeps the focus to an issue 
> > > > > > > > we can assume nearly everyone agrees on.
> > > > > > > I'm not convinced the performance implications are real compared 
> > > > > > > to how the check is currently implemented. I know there are 
> > > > > > > performance concerns when you return a const value of class type 
> > > > > > > because it pessimizes the ability to use move constructors, but 
> > > > > > > that's a very specific performance concern that I don't believe 
> > > > > > > is present in general. For instance, I don't know of any 
> > > > > > > performance concerns regarding `const int f();` as a declaration. 
> > > > > > > I'd be fine moving this to the performance module, but I think 
> > > > > > > the check would

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-31 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171900.
ymandel added a comment.

Rename readability-const-value-return to readability-const-return-type


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstReturnTypeCheck.cpp
  clang-tidy/readability/ConstReturnTypeCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-return-type.rst
  test/clang-tidy/readability-const-return-type.cpp

Index: test/clang-tidy/readability-const-return-type.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-return-type.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-return-type %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /*

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-31 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

I've renamed the check. I noticed that the script did not rename the header 
guard macro, though.  Please let me know if/where to file a bug.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025



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


[PATCH] D56786: Changes to `CXXMemberExpr` matchers.

2019-01-16 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
Herald added a subscriber: cfe-commits.

Assorted changes to matchers for `CXXMemberExpr`:

- fixes the comments on `hasObjectExpression`,
- clarifies comments on `thisPointerType` and `on`,
- adds comments to `onImplicitObjectArgument`
- adds new matcher `invokedAtType`, a variant of `thisPointerType` (like `on` 
is a variant of `onImplicitObjectArgument`).


Repository:
  rC Clang

https://reviews.llvm.org/D56786

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,124 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, On) {
+  auto MatchesType = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc",
+  MatchesType));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc",
+  MatchesType));
+
+  // Parens are ignored.
+  auto MatchesCall = cxxMemberCallExpr(on(callExpr()));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(matches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(notMatches(Snippet2, MatchesX));
+
+  // Parens are not ignored.
+  auto MatchesCall = cxxMemberCallExpr(onImplicitObjectArgument(callExpr()));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(MatcherCXXMemberCallExpr, InvokedAtType) {
+  auto M = cxxMemberCallExpr(invokedAtType(cxxRecordDecl(hasName("Y";
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc",
+  M));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y *y) { y->m(); }
+  )cc",
+  M));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc",
+  M));
+}
+
+TEST(Matcher, HasObjectExpr) {
+  auto M = memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(
+  R"cc(
+struct X {
+  int m;
+  int f(X x) { return x.m; }
+};
+  )cc",
+  M));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct X {
+  int m;
+  int f(X x) { return m; }
+};
+  )cc",
+  M));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
 declRefExpr(to(varDecl(hasName("y".bind("arg");
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -2887,14 +2887,22 @@
  InnerMatcher.matches(*UnderlyingDecl, Finder, Builder);
 }
 
-/// Matches on the implicit object argument of a member call expression.
+/// Matches on the implicit object argument of a member call expression, after
+/// stripping off any parentheses or implicit casts.
 ///
-/// Example matches y.x()
-///   (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))
+/// Given
 /// \code
-///   class Y { public: void x(); };
-///   void z() { Y y; y.x(); }
-/// \endcode
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y {};
+///   void z(Y y, X x) { y.m(); (g()).m(); x.m(); }
+/// \endcode
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `(g()).m()` and `x.m()`.
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")
+///   matches `x.m()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   matches `(g()).m()`
 ///
 /// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher,
@@ -3254,6 +3262,23 

[PATCH] D56786: Changes to `CXXMemberExpr` matchers.

2019-01-16 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

In D56786#1359879 , @steveire wrote:

> Can you break this up into multiple commits?


Sure, but any suggestions on granularity?  E.g. i can split into two: the 
fixes/clarifications in one and the new matcher in another; or i could split 
into four -- one for each bullet, etc.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56786/new/

https://reviews.llvm.org/D56786



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


[PATCH] D56786: [ASTMatchers] Changes to `CXXMemberExpr` matchers.

2019-01-16 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added a comment.

In D56786#1359920 , @lebedev.ri wrote:

> In D56786#1359903 , @ymandel wrote:
>
> > In D56786#1359879 , @steveire 
> > wrote:
> >
> > > Can you break this up into multiple commits?
> >
> >
> > Sure, but any suggestions on granularity?  E.g. i can split into two: the 
> > fixes/clarifications in one and the new matcher in another; or i could 
> > split into four -- one for each bullet, etc.
>
>
> Each matcher separately would be best, you then end up with 3x NFC doc-only 
> changes, and 2x matchers.


Once I'm splitting, I'd prefer to bundle the tests with the corresponding 
comment changes, but I see how that would break the NFC split (assuming tests 
count as "functional changes").  So, if the NFC changes are separate, should 
all the tests be bundled together, or split into separate patches?  For 
example, I can put the tests for existing matchers into one patch, and the new 
matcher and its test into a separate patch. WDYT?




Comment at: include/clang/ASTMatchers/ASTMatchers.h:3346
+
+/// Overloaded to match the type's declaration.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,

lebedev.ri wrote:
> How is this different from the other one?
> Presumably it should have it's own docs.
I was following what we've done elsewhere for overloads (e.g. line 3312 above), 
but am happy to expand this if you recommend. Personally, I think more 
documentation is better, but wanted to stick w/ the standard practice.



Comment at: include/clang/ASTMatchers/ASTMatchers.h:3350-3353
+  using ::clang::ast_matchers::on;
+  using ::clang::ast_matchers::anyOf;
+  using ::clang::ast_matchers::hasType;
+  using ::clang::ast_matchers::pointsTo;

lebedev.ri wrote:
> I don't think these are needed.
> You don't have them in the matcher above yet it presumably compiles.
Right -- those are leftover from development (which took place in a different 
namespace originally).  Will remove once I split this patch.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56786/new/

https://reviews.llvm.org/D56786



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


[PATCH] D56786: [ASTMatchers] Changes to `CXXMemberExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

In D56786#1360706 , @steveire wrote:

> It seems that you update docs for existing matchers without changing those 
> matchers. You could put all of that in one patch.
>
> Then, you seem to add some tests for existing matches. You could put that in 
> the second patch.
>
> Then your third patch would add the new matcher with its tests and its docs.
>
> That would be easy to review.


Done:
Comments: https://reviews.llvm.org/D56849
Tests: https://reviews.llvm.org/D56850
New matcher with tests: https://reviews.llvm.org/D56851


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56786/new/

https://reviews.llvm.org/D56786



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


[PATCH] D56850: [ASTMatchers][NFC] Add tests for assorted `CXXMemberCallExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

In D56850#1361436 , @lebedev.ri wrote:

> Please always subscribe lists.
>  The reviews that happen without lists are null and void.


Thanks for updating my diffs with the relevant list. Noted for next time.

> Is this testing pre-existing behavior, and thus NFC?

Yes.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56850/new/

https://reviews.llvm.org/D56850



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


[PATCH] D56850: [ASTMatchers][NFC] Add tests for assorted `CXXMemberCallExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 182295.
ymandel added a comment.

Extended test for matcher `on`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56850/new/

https://reviews.llvm.org/D56850

Files:
  clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,97 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, On) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(notMatches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(notMatches(Snippet1, MatchesX));
+  EXPECT_TRUE(matches(Snippet2, MatchesX));
+
+  // Parens are ignored.
+  auto MatchesCall = cxxMemberCallExpr(on(callExpr()));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(matches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(notMatches(Snippet2, MatchesX));
+
+  // Parens are not ignored.
+  auto MatchesCall = cxxMemberCallExpr(onImplicitObjectArgument(callExpr()));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(Matcher, HasObjectExpr) {
+  auto M = memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(
+  R"cc(
+struct X {
+  int m;
+  int f(X x) { return x.m; }
+};
+  )cc",
+  M));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct X {
+  int m;
+  int f(X x) { return m; }
+};
+  )cc",
+  M));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
 declRefExpr(to(varDecl(hasName("y".bind("arg");
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D56850: [ASTMatchers][NFC] Add tests for assorted `CXXMemberCallExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:475
+  auto MatchesType = 
cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(
+  R"cc(

steveire wrote:
> Could you add a test which uses the same c++ snippet as this, but with a 
> matcher using `hasName("Y")`?
Good suggestion -- this caught a mistake in the comments. I'll update the other 
diff to reflect.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56850/new/

https://reviews.llvm.org/D56850



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


[PATCH] D56849: [ASTMatchers][NFC] Update comments on assorted `CXXMemberCallExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 182299.
ymandel added a comment.

Respond to comments; fix mistake (revealed by test).


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56849/new/

https://reviews.llvm.org/D56849

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2887,14 +2887,22 @@
  InnerMatcher.matches(*UnderlyingDecl, Finder, Builder);
 }
 
-/// Matches on the implicit object argument of a member call expression.
+/// Matches on the implicit object argument of a member call expression, after
+/// stripping off any parentheses or implicit casts.
 ///
-/// Example matches y.x()
-///   (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))
+/// Given
 /// \code
-///   class Y { public: void x(); };
-///   void z() { Y y; y.x(); }
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y {};
+///   void z(Y y, X x) { y.m(); (g()).m(); x.m(); }
 /// \endcode
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")
+///   matches `y.m()` and `(g()).m()`.
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")
+///   matches `x.m()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   matches `(g()).m()`.
 ///
 /// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher,
@@ -3254,6 +3262,23 @@
   .matches(Node, Finder, Builder);
 }
 
+/// Matches on the implicit object argument of a member call expression. Unlike
+/// `on`, matches the argument directly without stripping away anything.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y { void g(); };
+///   void z(Y y, X x) { y.m(); x.m(); x.g(); (g()).m(); }
+/// \endcode
+/// cxxMemberCallExpr(onImplicitObjectArgument(hasType(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `x.m()` and (g()).m(), but not `x.g()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   does not match `(g()).m()`, because the parens are not ignored.
+///
+/// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument,
   internal::Matcher, InnerMatcher) {
   const Expr *ExprNode = Node.getImplicitObjectArgument();
@@ -3261,8 +3286,22 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
-/// Matches if the expression's type either matches the specified
-/// matcher, or is a pointer to a type that matches the InnerMatcher.
+/// Matches if the type of the expression's implicit object argument either
+/// matches the InnerMatcher, or is a pointer to a type that matches the
+/// InnerMatcher.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   class X : public Y { void g(); };
+///   void z() { Y y; y.m(); Y *p; p->m(); X x; x.m(); x.g(); }
+/// \endcode
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `p->m()` and `x.m()`.
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("X")
+///   matches `x.g()`.
 AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, thisPointerType,
internal::Matcher, InnerMatcher, 0) {
   return onImplicitObjectArgument(
@@ -4964,18 +5003,22 @@
   return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder);
 }
 
-/// Matches a member expression where the object expression is
-/// matched by a given matcher.
+/// Matches a member expression where the object expression is matched by a
+/// given matcher. Implicit object expressions are included; that is, it matches
+/// use of implicit `this`.
 ///
 /// Given
 /// \code
-///   struct X { int m; };
-///   void f(X x) { x.m; m; }
+///   struct X {
+/// int m;
+/// int f(X x) { x.m; return m; }
+///   };
 /// \endcode
-/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
-///   matches "x.m" and "m"
-/// with hasObjectExpression(...)
-///   matching "x" and the implicit object expression of "m" which has type X*.
+/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")
+///   matches "x.m", but not "m"; however,
+/// memberExpr(hasObjectExpression(hasType(pointsTo(
+//  cxxRecordDecl(hasName("X"))
+///   matches "m" (aka. "this->m"), but not "x.m".
 AST_POLYMORPHIC_MATCHER_P(
 hasObjectExpression,
 AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -4814,12 +4814,15 @@
 matched by a given matcher.
 
 Given
-  struct X { int m; };
-  void f(X x) { x.m; m; }
-mem

[PATCH] D56849: [ASTMatchers][NFC] Update comments on assorted `CXXMemberCallExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 182301.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Update HTML docs.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56849/new/

https://reviews.llvm.org/D56849

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2887,14 +2887,22 @@
  InnerMatcher.matches(*UnderlyingDecl, Finder, Builder);
 }
 
-/// Matches on the implicit object argument of a member call expression.
+/// Matches on the implicit object argument of a member call expression, after
+/// stripping off any parentheses or implicit casts.
 ///
-/// Example matches y.x()
-///   (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))
+/// Given
 /// \code
-///   class Y { public: void x(); };
-///   void z() { Y y; y.x(); }
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y {};
+///   void z(Y y, X x) { y.m(); (g()).m(); x.m(); }
 /// \endcode
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")
+///   matches `y.m()` and `(g()).m()`.
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")
+///   matches `x.m()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   matches `(g()).m()`.
 ///
 /// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher,
@@ -3254,6 +3262,23 @@
   .matches(Node, Finder, Builder);
 }
 
+/// Matches on the implicit object argument of a member call expression. Unlike
+/// `on`, matches the argument directly without stripping away anything.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y { void g(); };
+///   void z(Y y, X x) { y.m(); x.m(); x.g(); (g()).m(); }
+/// \endcode
+/// cxxMemberCallExpr(onImplicitObjectArgument(hasType(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `x.m()` and (g()).m(), but not `x.g()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   does not match `(g()).m()`, because the parens are not ignored.
+///
+/// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument,
   internal::Matcher, InnerMatcher) {
   const Expr *ExprNode = Node.getImplicitObjectArgument();
@@ -3261,8 +3286,22 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
-/// Matches if the expression's type either matches the specified
-/// matcher, or is a pointer to a type that matches the InnerMatcher.
+/// Matches if the type of the expression's implicit object argument either
+/// matches the InnerMatcher, or is a pointer to a type that matches the
+/// InnerMatcher.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   class X : public Y { void g(); };
+///   void z() { Y y; y.m(); Y *p; p->m(); X x; x.m(); x.g(); }
+/// \endcode
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `p->m()` and `x.m()`.
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("X")
+///   matches `x.g()`.
 AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, thisPointerType,
internal::Matcher, InnerMatcher, 0) {
   return onImplicitObjectArgument(
@@ -4964,18 +5003,22 @@
   return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder);
 }
 
-/// Matches a member expression where the object expression is
-/// matched by a given matcher.
+/// Matches a member expression where the object expression is matched by a
+/// given matcher. Implicit object expressions are included; that is, it matches
+/// use of implicit `this`.
 ///
 /// Given
 /// \code
-///   struct X { int m; };
-///   void f(X x) { x.m; m; }
+///   struct X {
+/// int m;
+/// int f(X x) { x.m; return m; }
+///   };
 /// \endcode
-/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
-///   matches "x.m" and "m"
-/// with hasObjectExpression(...)
-///   matching "x" and the implicit object expression of "m" which has type X*.
+/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")
+///   matches "x.m", but not "m"; however,
+/// memberExpr(hasObjectExpression(hasType(pointsTo(
+//  cxxRecordDecl(hasName("X"))
+///   matches "m" (aka. "this->m"), but not "x.m".
 AST_POLYMORPHIC_MATCHER_P(
 hasObjectExpression,
 AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -4810,16 +4810,20 @@
 
 
 Matcher

[PATCH] D56849: [ASTMatchers][NFC] Update comments on assorted `CXXMemberCallExpr` matchers.

2019-01-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 4 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:2905
+/// cxxMemberCallExpr(on(callExpr()))
+///   matches `(g()).m()`
 ///

aaron.ballman wrote:
> Missing a full stop.
also fixed mistake in first example (turned up by actually testing the claim. 
:) )


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56849/new/

https://reviews.llvm.org/D56849



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


[PATCH] D56933: [Tooling][RFC] Introduce Stencil library to simplify source code generation in refactorings.

2019-01-18 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
Herald added subscribers: cfe-commits, jfb.

REQUEST FOR COMMENT: this is not intended (yet) as a proper revision. It 
complements the design document for Transformer:
https://docs.google.com/document/d/1ppw0RhjwsrbBcHYhI85pe6ISDbA6r5d00ot3N8cQWeQ/edit?usp=sharing

This revision introduces the Stencil library, which is designed to complement 
the AST matchers in support of writing source-to-source transformations. A 
stencil is similar in spirit to a format string: it is composed of a series of 
raw text strings, references to node ids (bound in a corresponding matcher) and 
helper code-generation operations.  A stencil can be applied to a match result 
and uses that result to find all nodes referenced in the stencil and then 
produce a source string.

Stencil is part of the Transformer framework, but can also be used on its own, 
for example in a ClangTidy.  Please see the Transformer doc and the 
accompanying tests for example uses.


Repository:
  rC Clang

https://reviews.llvm.org/D56933

Files:
  clang/include/clang/Tooling/Refactoring/Stencil.h
  clang/lib/Tooling/Refactoring/Stencil.cpp
  clang/unittests/Tooling/StencilTest.cpp

Index: clang/unittests/Tooling/StencilTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/StencilTest.cpp
@@ -0,0 +1,624 @@
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+
+using ::clang::ast_matchers::compoundStmt;
+using ::clang::ast_matchers::decl;
+using ::clang::ast_matchers::declStmt;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::hasAnySubstatement;
+using ::clang::ast_matchers::hasCondition;
+using ::clang::ast_matchers::hasDescendant;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasInitializer;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasReturnValue;
+using ::clang::ast_matchers::hasSingleDecl;
+using ::clang::ast_matchers::hasThen;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::ignoringImplicit;
+using ::clang::ast_matchers::returnStmt;
+using ::clang::ast_matchers::stmt;
+using ::clang::ast_matchers::varDecl;
+
+using MatchResult = ::clang::ast_matchers::MatchFinder::MatchResult;
+
+using ::clang::tooling::stencil_generators::addInclude;
+using ::clang::tooling::stencil_generators::apply;
+using ::clang::tooling::stencil_generators::args;
+using ::clang::tooling::stencil_generators::asAddress;
+using ::clang::tooling::stencil_generators::asValue;
+using ::clang::tooling::stencil_generators::member;
+using ::clang::tooling::stencil_generators::name;
+using ::clang::tooling::stencil_generators::parens;
+using ::clang::tooling::stencil_generators::removeInclude;
+using ::clang::tooling::stencil_generators::text;
+
+using ::testing::Eq;
+
+// We can't directly match on llvm::Expected since its accessors mutate the
+// object. So, we collapse it to an Optional.
+llvm::Optional toOptional(llvm::Expected V) {
+  if (V)
+return *V;
+  ADD_FAILURE() << "Losing error in conversion to IsSomething: "
+<< llvm::toString(V.takeError());
+  return llvm::None;
+}
+
+// A very simple matcher for llvm::Optional values.
+MATCHER_P(IsSomething, ValueMatcher, "") {
+  if (!arg)
+return false;
+  return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener);
+}
+
+// Create a valid translation-unit from a statement.
+std::string wrapSnippet(llvm::StringRef StatementCode) {
+  return ("auto stencil_test_snippet = []{" + StatementCode + "};").str();
+}
+
+clang::ast_matchers::DeclarationMatcher
+wrapMatcher(const clang::ast_matchers::StatementMatcher &Matcher) {
+  return varDecl(hasName("stencil_test_snippet"),
+ hasDescendant(compoundStmt(hasAnySubstatement(Matcher;
+}
+
+struct TestMatch {
+  // The AST unit from which `result` is built. We bundle it because it backs
+  // the result. Users are not expected to access it.
+  std::unique_ptr AstUnit;
+  // The result to use in the test. References `ast_unit`.
+  MatchResult Result;
+};
+
+// Matches `matcher` against the statement `statement_code` and returns the
+// result. Handles putting the statement inside a function and modifying the
+// matcher correspondingly. `matcher` should match `statement_code` exactly --
+// that is, produce exactly one match.
+llvm::Optional
+matchStmt(llvm::StringRef StatementCode,
+  clang::ast_matchers::StatementMatcher Matcher) {
+  auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
+  if (AstUnit == nullptr) {
+ADD_FAILURE() << "AST construction failed";
+return llvm::None;
+  }
+  clang::ASTContext &Context = AstUnit->getASTContext();
+  auto Matches = clang::ast_matchers::match(wrapMatcher(Matcher), Context);
+  

[PATCH] D56849: [ASTMatchers][NFC] Update comments on assorted `CXXMemberCallExpr` matchers.

2019-01-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 182918.
ymandel marked an inline comment as done.
ymandel added a comment.

Use backticks instead of quotes for quoted code.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56849/new/

https://reviews.llvm.org/D56849

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h

Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2887,14 +2887,22 @@
  InnerMatcher.matches(*UnderlyingDecl, Finder, Builder);
 }
 
-/// Matches on the implicit object argument of a member call expression.
+/// Matches on the implicit object argument of a member call expression, after
+/// stripping off any parentheses or implicit casts.
 ///
-/// Example matches y.x()
-///   (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))
+/// Given
 /// \code
-///   class Y { public: void x(); };
-///   void z() { Y y; y.x(); }
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y {};
+///   void z(Y y, X x) { y.m(); (g()).m(); x.m(); }
 /// \endcode
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")
+///   matches `y.m()` and `(g()).m()`.
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")
+///   matches `x.m()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   matches `(g()).m()`.
 ///
 /// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher,
@@ -3254,6 +3262,23 @@
   .matches(Node, Finder, Builder);
 }
 
+/// Matches on the implicit object argument of a member call expression. Unlike
+/// `on`, matches the argument directly without stripping away anything.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y { void g(); };
+///   void z(Y y, X x) { y.m(); x.m(); x.g(); (g()).m(); }
+/// \endcode
+/// cxxMemberCallExpr(onImplicitObjectArgument(hasType(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `x.m()` and (g()).m(), but not `x.g()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   does not match `(g()).m()`, because the parens are not ignored.
+///
+/// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument,
   internal::Matcher, InnerMatcher) {
   const Expr *ExprNode = Node.getImplicitObjectArgument();
@@ -3261,8 +3286,22 @@
   InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
-/// Matches if the expression's type either matches the specified
-/// matcher, or is a pointer to a type that matches the InnerMatcher.
+/// Matches if the type of the expression's implicit object argument either
+/// matches the InnerMatcher, or is a pointer to a type that matches the
+/// InnerMatcher.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   class X : public Y { void g(); };
+///   void z() { Y y; y.m(); Y *p; p->m(); X x; x.m(); x.g(); }
+/// \endcode
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `p->m()` and `x.m()`.
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("X")
+///   matches `x.g()`.
 AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, thisPointerType,
internal::Matcher, InnerMatcher, 0) {
   return onImplicitObjectArgument(
@@ -4964,18 +5003,22 @@
   return InnerMatcher.matches(*Node.getMemberDecl(), Finder, Builder);
 }
 
-/// Matches a member expression where the object expression is
-/// matched by a given matcher.
+/// Matches a member expression where the object expression is matched by a
+/// given matcher. Implicit object expressions are included; that is, it matches
+/// use of implicit `this`.
 ///
 /// Given
 /// \code
-///   struct X { int m; };
-///   void f(X x) { x.m; m; }
+///   struct X {
+/// int m;
+/// int f(X x) { x.m; return m; }
+///   };
 /// \endcode
-/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
-///   matches "x.m" and "m"
-/// with hasObjectExpression(...)
-///   matching "x" and the implicit object expression of "m" which has type X*.
+/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")
+///   matches `x.m`, but not `m`; however,
+/// memberExpr(hasObjectExpression(hasType(pointsTo(
+//  cxxRecordDecl(hasName("X"))
+///   matches `m` (aka. `this->m`), but not `x.m`.
 AST_POLYMORPHIC_MATCHER_P(
 hasObjectExpression,
 AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -4810,16 +4810,20 @@
 
 
 Matcher

[PATCH] D56850: [ASTMatchers][NFC] Add tests for assorted `CXXMemberCallExpr` matchers.

2019-01-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 182919.
ymandel marked an inline comment as done.
ymandel added a comment.

Remove unnecessary expectation.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56850/new/

https://reviews.llvm.org/D56850

Files:
  clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,96 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, On) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(notMatches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(Snippet2, MatchesX));
+
+  // Parens are ignored.
+  auto MatchesCall = cxxMemberCallExpr(on(callExpr()));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(matches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(notMatches(Snippet2, MatchesX));
+
+  // Parens are not ignored.
+  auto MatchesCall = cxxMemberCallExpr(onImplicitObjectArgument(callExpr()));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(Matcher, HasObjectExpr) {
+  auto M = memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(
+  R"cc(
+struct X {
+  int m;
+  int f(X x) { return x.m; }
+};
+  )cc",
+  M));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct X {
+  int m;
+  int f(X x) { return m; }
+};
+  )cc",
+  M));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
 declRefExpr(to(varDecl(hasName("y".bind("arg");
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D56850: [ASTMatchers][NFC] Add tests for assorted `CXXMemberCallExpr` matchers.

2019-01-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 182933.
ymandel marked 3 inline comments as done.
ymandel added a comment.

Extended test of `hasObjectExpression`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56850/new/

https://reviews.llvm.org/D56850

Files:
  clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,100 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, On) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(notMatches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(Snippet2, MatchesX));
+
+  // Parens are ignored.
+  auto MatchesCall = cxxMemberCallExpr(on(callExpr()));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) {
+  auto Snippet1 = R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("Y");
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(matches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(
+  onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(notMatches(Snippet2, MatchesX));
+
+  // Parens are not ignored.
+  auto MatchesCall = cxxMemberCallExpr(onImplicitObjectArgument(callExpr()));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+Y g();
+void z(Y y) { (g()).m(); }
+  )cc",
+  MatchesCall));
+}
+
+TEST(Matcher, HasObjectExpr) {
+  auto Snippet1 = R"cc(
+struct X {
+  int m;
+  int f(X x) { return x.m; }
+};
+  )cc";
+  auto Snippet2 = R"cc(
+struct X {
+  int m;
+  int f(X x) { return m; }
+};
+  )cc";
+  auto MatchesX =
+  memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X");
+  EXPECT_TRUE(matches(Snippet1, MatchesX));
+  EXPECT_TRUE(notMatches(Snippet2, MatchesX));
+
+  auto MatchesXPointer = memberExpr(
+  hasObjectExpression(hasType(pointsTo(cxxRecordDecl(hasName("X"));
+  EXPECT_TRUE(notMatches(Snippet1, MatchesXPointer));
+  EXPECT_TRUE(matches(Snippet2, MatchesXPointer));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
 declRefExpr(to(varDecl(hasName("y".bind("arg");
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D56850: [ASTMatchers][NFC] Add tests for assorted `CXXMemberCallExpr` matchers.

2019-01-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:558
+  int m;
+  int f(X x) { return m; }
+};

steveire wrote:
> Are we missing a matcher that would reach the type of X in this case? 
> `hasImplicitObjectExpression`, or something equivalent?
Good question. The reason we're not reaching `X` in this case is that we're 
distinguishing between `T` and `T*` (like `thisPointerType` does). That's 
separate from the difference between `on` and `onImplicitObjectArgument`.  So, 
I think we'll at least want a matcher that elides this difference, e.g.
`hasObjectType`.

I'm less inclined to add matchers that distinguish the written from the coerced 
member expression (one variant each for the expression and type matchers) given 
that I think this issue comes up far less for member expressions that don't 
involve calls. I'm just afraid that the proliferation of matchers might confuse 
beginners.

Note that I extended the test to include the hasPointerType() case (especially 
since it's now mentioned in the comments).


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56850/new/

https://reviews.llvm.org/D56850



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


[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3300
+///   matches `x.m()` and `p->m()`.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,
+   clang::ast_matchers::internal::Matcher,

aaron.ballman wrote:
> alexfh wrote:
> > The name of the matcher doesn't tell me much. I had to carefully read the 
> > documentation to understand what is it about. I don't have a name that 
> > would raise no questions and wouldn't be too verbose at the same time, but 
> > a bit of verbosity wouldn't hurt I guess. How about `objectTypeAsWritten`?
> Yeah, I think this would be a better name. Also, having some examples that 
> demonstrate where this behavior differs from `thisPointerType` would be 
> helpful.
Agreed that it needs a new name, but I'm having trouble finding one I'm 
satisfied with.  Here's the full description: "the type of the written implicit 
object argument".  I base this phrasing on the class CXXMemberCallExpr's 
terminology.  In `x.f(5)`, `x` is the implicit object argument, whether or not 
it is also implicitly surrounded by a cast.  That is, "implicit" has two 
different meanings in this context.

So, with that, how about `writtenObjectType`? It's close to 
`objectTypeAsWritten` but I'm hoping it makes more clear that the "written" 
part is the object not the type.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851



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


[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 183582.
ymandel added a comment.

Remove unneeded qualifiers and clarify constrast with `thisPointerType`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,35 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, InvokedAtType) {
+  auto M = cxxMemberCallExpr(invokedAtType(cxxRecordDecl(hasName("Y";
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc",
+  M));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y *y) { y->m(); }
+  )cc",
+  M));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc",
+  M));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
 declRefExpr(to(varDecl(hasName("y".bind("arg");
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3278,6 +3278,40 @@
   .matches(Node, Finder, Builder);
 }
 
+/// Matches if the type of the object expression (as written, without any
+/// implicit casts) either matches the InnerMatcher, or is a pointer to a
+/// type that matches the InnerMatcher. Differs from `thisPointerType` in that
+/// it matches against the type of the written object expression, which could be
+/// a subclass of the thisPointerType (like when the invoked member is
+/// inherited).
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   class X : public Y {};
+///   void z() { Y y; y.m(); X x; x.m(); X *p; p->m(); }
+/// \endcode
+/// cxxMemberCallExpr(invokedAtType(hasDeclaration(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()` (while `thisPointerType` would match all three calls.)
+///
+/// cxxMemberCallExpr(invokedAtType(hasDeclaration(
+/// cxxRecordDecl(hasName("X")
+///   matches `x.m()` and `p->m()` (while `thisPointerType` would not match any
+///   of the calls, because `this`'s type is `Y` in those calls).
+AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, invokedAtType,
+   internal::Matcher, InnerMatcher, 0) {
+  return on(anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher
+  .matches(Node, Finder, Builder);
+}
+
+/// Overloaded to match the type's declaration.
+AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, invokedAtType, DeclarationMatcher,
+   InnerMatcher, 1) {
+  return on(anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher
+  .matches(Node, Finder, Builder);
+}
+
 /// Matches a DeclRefExpr that refers to a declaration that matches the
 /// specified matcher.
 ///
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -4224,6 +4224,11 @@
 
 
 
+MatcherinvokedAtTypeclang::ast_matchers::DeclarationMatcher InnerMatcher
+Overloaded to match the type's declaration.
+
+
+
 MatcherDecl>>isInstantiated
 Matches declarations that are template instantiations or are inside
 template instantiations.
@@ -6956,6 +6961,27 @@
 
 
 
+MatcherinvokedAtTypeclang::ast_matchers::Matcher InnerMatcher
+Matches if the type of the object expression (as written, without any
+implicit casts) either matches the InnerMatcher, or is a pointer to a
+type that matches the InnerMatcher. Differs from `thisPointerType` in that
+it matches against the type of the written object expression, which could be
+a subclass of the thisPointerType (like when the invoked member is
+inherited).
+
+Given
+  class Y { public: void m(); };
+  class X : public Y {};
+  void z() { Y y; y.m(); X x; x.m(); X *p; p->m(); }
+cxxMemberCallExpr(invokedAtType(hasDeclaration(
+cxxRecordDecl(hasName("Y")
+  matches `y.m()`.
+cxxMemberCallExpr(invokedAtType(hasDeclaration(
+cxxRecordDecl(hasName("X")
+  matches `x.m()` and `p->m()`.
+
+
+
 MatcherNestedNameSpecifierLoc>>locMatcher

[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 3 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3300
+///   matches `x.m()` and `p->m()`.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,
+   clang::ast_matchers::internal::Matcher,

ymandel wrote:
> aaron.ballman wrote:
> > alexfh wrote:
> > > The name of the matcher doesn't tell me much. I had to carefully read the 
> > > documentation to understand what is it about. I don't have a name that 
> > > would raise no questions and wouldn't be too verbose at the same time, 
> > > but a bit of verbosity wouldn't hurt I guess. How about 
> > > `objectTypeAsWritten`?
> > Yeah, I think this would be a better name. Also, having some examples that 
> > demonstrate where this behavior differs from `thisPointerType` would be 
> > helpful.
> Agreed that it needs a new name, but I'm having trouble finding one I'm 
> satisfied with.  Here's the full description: "the type of the written 
> implicit object argument".  I base this phrasing on the class 
> CXXMemberCallExpr's terminology.  In `x.f(5)`, `x` is the implicit object 
> argument, whether or not it is also implicitly surrounded by a cast.  That 
> is, "implicit" has two different meanings in this context.
> 
> So, with that, how about `writtenObjectType`? It's close to 
> `objectTypeAsWritten` but I'm hoping it makes more clear that the "written" 
> part is the object not the type.
I've contrasted the behavior with thisPointerType in both of the examples. Do 
you think this helps or do you want something more explicit?


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851



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


[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 183591.
ymandel added a comment.

Contrast to thisPointerType with explicit examples.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851

Files:
  clang/docs/LibASTMatchersReference.html
  clang/include/clang/ASTMatchers/ASTMatchers.h
  clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,35 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, InvokedAtType) {
+  auto M = cxxMemberCallExpr(invokedAtType(cxxRecordDecl(hasName("Y";
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y y) { y.m(); }
+  )cc",
+  M));
+  EXPECT_TRUE(matches(
+  R"cc(
+struct Y {
+  void m();
+};
+void z(Y *y) { y->m(); }
+  )cc",
+  M));
+  EXPECT_TRUE(notMatches(
+  R"cc(
+struct Y {
+  void m();
+};
+struct X : public Y {};
+void z(X x) { x.m(); }
+  )cc",
+  M));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
 declRefExpr(to(varDecl(hasName("y".bind("arg");
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3278,6 +3278,49 @@
   .matches(Node, Finder, Builder);
 }
 
+/// Matches if the type of the object expression (as written, without any
+/// implicit casts) either matches the InnerMatcher, or is a pointer to a
+/// type that matches the InnerMatcher. Differs from `thisPointerType` in that
+/// it matches against the type of the written object expression, which could be
+/// a subclass of the this-pointer type (like when the invoked member is
+/// inherited).
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   class X : public Y {};
+///   void z() { Y y; y.m(); X x; x.m(); X *p; p->m(); }
+/// \endcode
+/// cxxMemberCallExpr(onOrPointsToType(hasDeclaration(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`.
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("Y")
+///   matches `y.m()`, `x.m()` and `p->m()`.
+///
+/// cxxMemberCallExpr(onOrPointsToType(hasDeclaration(
+/// cxxRecordDecl(hasName("X")
+///   matches `x.m()` and `p->m()`.
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+/// cxxRecordDecl(hasName("X")
+///   matches nothing because the `this` type is `Y` in all three calls.
+AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, onOrPointsToType,
+   internal::Matcher, InnerMatcher, 0) {
+  return on(anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher
+  .matches(Node, Finder, Builder);
+}
+
+/// Overloaded to match the type's declaration. With this overload, the above
+/// examples could be simplified to:
+///
+/// cxxMemberCallExpr(onOrPointsToType(cxxRecordDecl(hasName("Y"
+/// cxxMemberCallExpr(onOrPointsToType(cxxRecordDecl(hasName("X"
+AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, onOrPointsToType, DeclarationMatcher,
+   InnerMatcher, 1) {
+  return on(anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher
+  .matches(Node, Finder, Builder);
+}
+
 /// Matches a DeclRefExpr that refers to a declaration that matches the
 /// specified matcher.
 ///
Index: clang/docs/LibASTMatchersReference.html
===
--- clang/docs/LibASTMatchersReference.html
+++ clang/docs/LibASTMatchersReference.html
@@ -4224,6 +4224,11 @@
 
 
 
+MatcherinvokedAtTypeclang::ast_matchers::DeclarationMatcher InnerMatcher
+Overloaded to match the type's declaration.
+
+
+
 MatcherDecl>>isInstantiated
 Matches declarations that are template instantiations or are inside
 template instantiations.
@@ -6956,6 +6961,27 @@
 
 
 
+MatcherinvokedAtTypeclang::ast_matchers::Matcher InnerMatcher
+Matches if the type of the object expression (as written, without any
+implicit casts) either matches the InnerMatcher, or is a pointer to a
+type that matches the InnerMatcher. Differs from `thisPointerType` in that
+it matches against the type of the written object expression, which could be
+a subclass of the thisPointerType (like when the invoked member is
+inherited).
+
+Given
+  class Y { public: void m(); };
+  class X : public Y {};
+  void z() { Y y; y.m(); X x; x.m(); X *p; p->m(); }
+cxxMemberCallExpr(invokedAtType(hasDeclaration(
+cxxRecordDecl(hasName("Y"))

[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 3 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3300
+///   matches `x.m()` and `p->m()`.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,
+   clang::ast_matchers::internal::Matcher,

aaron.ballman wrote:
> aaron.ballman wrote:
> > ymandel wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > alexfh wrote:
> > > > > > The name of the matcher doesn't tell me much. I had to carefully 
> > > > > > read the documentation to understand what is it about. I don't have 
> > > > > > a name that would raise no questions and wouldn't be too verbose at 
> > > > > > the same time, but a bit of verbosity wouldn't hurt I guess. How 
> > > > > > about `objectTypeAsWritten`?
> > > > > Yeah, I think this would be a better name. Also, having some examples 
> > > > > that demonstrate where this behavior differs from `thisPointerType` 
> > > > > would be helpful.
> > > > Agreed that it needs a new name, but I'm having trouble finding one I'm 
> > > > satisfied with.  Here's the full description: "the type of the written 
> > > > implicit object argument".  I base this phrasing on the class 
> > > > CXXMemberCallExpr's terminology.  In `x.f(5)`, `x` is the implicit 
> > > > object argument, whether or not it is also implicitly surrounded by a 
> > > > cast.  That is, "implicit" has two different meanings in this context.
> > > > 
> > > > So, with that, how about `writtenObjectType`? It's close to 
> > > > `objectTypeAsWritten` but I'm hoping it makes more clear that the 
> > > > "written" part is the object not the type.
> > > I've contrasted the behavior with thisPointerType in both of the 
> > > examples. Do you think this helps or do you want something more explicit?
> > Here's a totally different direction: `onOrPointsToType()`
> > ```
> > cxxMemberCallExpr(onOrPointsToType(hasDeclaration(cxxRecordDecl(hasName("Y")
> > ```
> > 
> I think more explicit would be better. e.g.,
> ```
> cxxMemberCallExpr(invokedAtType(hasDeclaration(cxxRecordDecl(hasName("X")
> matches 'x.m()' and 'p->m()'.
> cxxMemberCallExpr(on(thisPointerType(hasDeclaration(cxxRecordDecl(hasName("X"))
> matches nothing because the type of 'this' is 'Y' in both cases.
> ```
But, what about even simpler: onType? I think this parallels the intuition of 
the name thisPointerType.  onType(T) should match x.f and x->f, where x is type 
T.  


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851



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


[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3300
+///   matches `x.m()` and `p->m()`.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,
+   clang::ast_matchers::internal::Matcher,

aaron.ballman wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > aaron.ballman wrote:
> > > > ymandel wrote:
> > > > > ymandel wrote:
> > > > > > aaron.ballman wrote:
> > > > > > > alexfh wrote:
> > > > > > > > The name of the matcher doesn't tell me much. I had to 
> > > > > > > > carefully read the documentation to understand what is it 
> > > > > > > > about. I don't have a name that would raise no questions and 
> > > > > > > > wouldn't be too verbose at the same time, but a bit of 
> > > > > > > > verbosity wouldn't hurt I guess. How about 
> > > > > > > > `objectTypeAsWritten`?
> > > > > > > Yeah, I think this would be a better name. Also, having some 
> > > > > > > examples that demonstrate where this behavior differs from 
> > > > > > > `thisPointerType` would be helpful.
> > > > > > Agreed that it needs a new name, but I'm having trouble finding one 
> > > > > > I'm satisfied with.  Here's the full description: "the type of the 
> > > > > > written implicit object argument".  I base this phrasing on the 
> > > > > > class CXXMemberCallExpr's terminology.  In `x.f(5)`, `x` is the 
> > > > > > implicit object argument, whether or not it is also implicitly 
> > > > > > surrounded by a cast.  That is, "implicit" has two different 
> > > > > > meanings in this context.
> > > > > > 
> > > > > > So, with that, how about `writtenObjectType`? It's close to 
> > > > > > `objectTypeAsWritten` but I'm hoping it makes more clear that the 
> > > > > > "written" part is the object not the type.
> > > > > I've contrasted the behavior with thisPointerType in both of the 
> > > > > examples. Do you think this helps or do you want something more 
> > > > > explicit?
> > > > Here's a totally different direction: `onOrPointsToType()`
> > > > ```
> > > > cxxMemberCallExpr(onOrPointsToType(hasDeclaration(cxxRecordDecl(hasName("Y")
> > > > ```
> > > > 
> > > I think more explicit would be better. e.g.,
> > > ```
> > > cxxMemberCallExpr(invokedAtType(hasDeclaration(cxxRecordDecl(hasName("X")
> > > matches 'x.m()' and 'p->m()'.
> > > cxxMemberCallExpr(on(thisPointerType(hasDeclaration(cxxRecordDecl(hasName("X"))
> > > matches nothing because the type of 'this' is 'Y' in both cases.
> > > ```
> > But, what about even simpler: onType? I think this parallels the intuition 
> > of the name thisPointerType.  onType(T) should match x.f and x->f, where x 
> > is type T.  
> You've pointed out why I don't think `onType` works -- it doesn't match on 
> type T -- it matches on type T, or a pointer/reference to type T, which is 
> pretty different. Someone reading the matcher may expect an exact type match 
> and insert a `pointerType()` or something there thinking they need to do that 
> to match a call through a pointer.
> 
> @alexfh, opinions?
True.  I should have explained more.  

1. Ultimately, I think that none of these names really make sense on their own 
and the user will need some familiarity with the documentation. I spent quite a 
while trying to come up with better names and didn't find anything compelling.  
I think that `onType` benefits from not carrying much information -- reducing 
the likelihood of misunderstanding it (they'll have to read the documentation) 
while paralleling the meaning of the matcher `on` and the behavior of 
`thisPointerType` (which also allows either the type or the pointer to that 
type).  

2. My particular concern with `onOrPointsToType` is that it sounds like the 
"or" applies to the `on` but it really means "on (type or points to type)".  


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851



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


[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-01-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/ASTMatchers/ASTMatchers.h:3300
+///   matches `x.m()` and `p->m()`.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,
+   clang::ast_matchers::internal::Matcher,

alexfh wrote:
> ymandel wrote:
> > aaron.ballman wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > aaron.ballman wrote:
> > > > > > ymandel wrote:
> > > > > > > ymandel wrote:
> > > > > > > > aaron.ballman wrote:
> > > > > > > > > alexfh wrote:
> > > > > > > > > > The name of the matcher doesn't tell me much. I had to 
> > > > > > > > > > carefully read the documentation to understand what is it 
> > > > > > > > > > about. I don't have a name that would raise no questions 
> > > > > > > > > > and wouldn't be too verbose at the same time, but a bit of 
> > > > > > > > > > verbosity wouldn't hurt I guess. How about 
> > > > > > > > > > `objectTypeAsWritten`?
> > > > > > > > > Yeah, I think this would be a better name. Also, having some 
> > > > > > > > > examples that demonstrate where this behavior differs from 
> > > > > > > > > `thisPointerType` would be helpful.
> > > > > > > > Agreed that it needs a new name, but I'm having trouble finding 
> > > > > > > > one I'm satisfied with.  Here's the full description: "the type 
> > > > > > > > of the written implicit object argument".  I base this phrasing 
> > > > > > > > on the class CXXMemberCallExpr's terminology.  In `x.f(5)`, `x` 
> > > > > > > > is the implicit object argument, whether or not it is also 
> > > > > > > > implicitly surrounded by a cast.  That is, "implicit" has two 
> > > > > > > > different meanings in this context.
> > > > > > > > 
> > > > > > > > So, with that, how about `writtenObjectType`? It's close to 
> > > > > > > > `objectTypeAsWritten` but I'm hoping it makes more clear that 
> > > > > > > > the "written" part is the object not the type.
> > > > > > > I've contrasted the behavior with thisPointerType in both of the 
> > > > > > > examples. Do you think this helps or do you want something more 
> > > > > > > explicit?
> > > > > > Here's a totally different direction: `onOrPointsToType()`
> > > > > > ```
> > > > > > cxxMemberCallExpr(onOrPointsToType(hasDeclaration(cxxRecordDecl(hasName("Y")
> > > > > > ```
> > > > > > 
> > > > > I think more explicit would be better. e.g.,
> > > > > ```
> > > > > cxxMemberCallExpr(invokedAtType(hasDeclaration(cxxRecordDecl(hasName("X")
> > > > > matches 'x.m()' and 'p->m()'.
> > > > > cxxMemberCallExpr(on(thisPointerType(hasDeclaration(cxxRecordDecl(hasName("X"))
> > > > > matches nothing because the type of 'this' is 'Y' in both cases.
> > > > > ```
> > > > But, what about even simpler: onType? I think this parallels the 
> > > > intuition of the name thisPointerType.  onType(T) should match x.f and 
> > > > x->f, where x is type T.  
> > > You've pointed out why I don't think `onType` works -- it doesn't match 
> > > on type T -- it matches on type T, or a pointer/reference to type T, 
> > > which is pretty different. Someone reading the matcher may expect an 
> > > exact type match and insert a `pointerType()` or something there thinking 
> > > they need to do that to match a call through a pointer.
> > > 
> > > @alexfh, opinions?
> > True.  I should have explained more.  
> > 
> > 1. Ultimately, I think that none of these names really make sense on their 
> > own and the user will need some familiarity with the documentation. I spent 
> > quite a while trying to come up with better names and didn't find anything 
> > compelling.  I think that `onType` benefits from not carrying much 
> > information -- reducing the likelihood of misunderstanding it (they'll have 
> > to read the documentation) while paralleling the meaning of the matcher 
> > `on` and the behavior of `thisPointerType` (which also allows either the 
> > type or the pointer to that type).  
> > 
> > 2. My particular concern with `onOrPointsToType` is that it sounds like the 
> > "or" applies to the `on` but it really means "on (type or points to type)". 
> >  
> So far, my observations are:
> 1. three engineers quite familiar with the topic can't come up with a name 
> that would explain the concept behind this matcher
> 2. anyone reading that name would have to look up the documentation
> 3. the implementation of the matcher is straightforward and even shorter than 
> the documentation
> 
> Should we give up and let users just type `on(anyOf(hasType(Q), 
> hasType(pointsTo(Q`?
> 
> If we want a bit more brevity here, maybe introduce a `hasTypeOrPointsToType` 
> matcher (any bikeshed color will do ;) to shorten the expression above?
Yes to both suggestions (dropping this one and adding `hasTypeOrPointsToType`). 
It seems a rather obvious conclusion now that you've said it. :)  

Personally, I'd go with `hasOrPointsToType`, but agreed that its just bike 
shedding.

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-31 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 4 inline comments as done.
ymandel added a comment.

Aaron, Jonas,

Thank you both so much for being fantastic reviewers!  This was my first 
experience contributing to clang (upstream) and it has been extremely positive.

In https://reviews.llvm.org/D53025#1282194, @aaron.ballman wrote:

> This LGTM, thank you for working on this check!


Great! I don't have SVN commit access.  Can one of you commit this?




Comment at: clang-tidy/readability/ConstValueReturnCheck.cpp:107
+diag(Def->getInnerLocStart(), "return type %0 is 'const'-qualified "
+  "hindering compiler optimizations")
+<< Def->getReturnType();

JonasToth wrote:
> ymandel wrote:
> > JonasToth wrote:
> > > ymandel wrote:
> > > > aaron.ballman wrote:
> > > > > ymandel wrote:
> > > > > > aaron.ballman wrote:
> > > > > > > ymandel wrote:
> > > > > > > > aaron.ballman wrote:
> > > > > > > > > ymandel wrote:
> > > > > > > > > > aaron.ballman wrote:
> > > > > > > > > > > ymandel wrote:
> > > > > > > > > > > > aaron.ballman wrote:
> > > > > > > > > > > > > It seems strange to me that this is in the 
> > > > > > > > > > > > > readability module but the diagnostic here is about 
> > > > > > > > > > > > > compiler optimizations. Should this be in the 
> > > > > > > > > > > > > performance module instead? Or should this diagnostic 
> > > > > > > > > > > > > be reworded about the readability concerns?
> > > > > > > > > > > > Good point. The readability angle is that the const 
> > > > > > > > > > > > clutters the code to no benefit.  That is, even if it 
> > > > > > > > > > > > was performance neutral, I'd still want to discourage 
> > > > > > > > > > > > this practice.  But, it does seem like the primary 
> > > > > > > > > > > > impact is performance. 
> > > > > > > > > > > > 
> > > > > > > > > > > > I'm fine either way -- moving it to performance or 
> > > > > > > > > > > > emphasizing the clutter of the unhelpful "const".  I'm 
> > > > > > > > > > > > inclined to moving it, but don't have good sense of how 
> > > > > > > > > > > > strict these hierarchies are. What do you think?
> > > > > > > > > > > I'm not sold that `const`-qualified return types always 
> > > > > > > > > > > pessimize optimizations. However, I'm also not sold that 
> > > > > > > > > > > it's *always* a bad idea to have a top-level cv-qualifier 
> > > > > > > > > > > on a return type either (for instance, this is one way to 
> > > > > > > > > > > prevent callers from modifying a temp returned by a 
> > > > > > > > > > > function). How about leaving this in readability and 
> > > > > > > > > > > changing the diagnostic into: "top-level 'const' 
> > > > > > > > > > > qualifier on a return type may reduce code readability 
> > > > > > > > > > > with limited benefit" or something equally weasely?
> > > > > > > > > > I talked this over with other google folks and seems like 
> > > > > > > > > > the consensus is:
> > > > > > > > > > 
> > > > > > > > > > 1.  The readability benefits may be controversial.  Some 
> > > > > > > > > > folks might not view `const` as clutter and there are some 
> > > > > > > > > > corner cases where the qualifier may prevent something 
> > > > > > > > > > harmful.
> > > > > > > > > > 2.  The performance implication is real, if not guaranteed 
> > > > > > > > > > to be a problem.
> > > > > > > > > > 
> > > > > > > > > > Based on this, seems best to move it to performance, but 
> > > > > > > > > > water down the performance claims.  That keeps the focus to 
> > > > > > > > > > an issue we can assume nearly everyone agrees on.
> > > > > > > > > I'm not convinced the performance implications are real 
> > > > > > > > > compared to how the check is currently implemented. I know 
> > > > > > > > > there are performance concerns when you return a const value 
> > > > > > > > > of class type because it pessimizes the ability to use move 
> > > > > > > > > constructors, but that's a very specific performance concern 
> > > > > > > > > that I don't believe is present in general. For instance, I 
> > > > > > > > > don't know of any performance concerns regarding `const int 
> > > > > > > > > f();` as a declaration. I'd be fine moving this to the 
> > > > > > > > > performance module, but I think the check would need to be 
> > > > > > > > > tightened to only focus on actual performance concerns.
> > > > > > > > > 
> > > > > > > > > FWIW, I do agree that the readability benefits may be 
> > > > > > > > > controversial, but I kind of thought that was the point to 
> > > > > > > > > the check and as such, it's a very reasonable check. Almost 
> > > > > > > > > everything in readability is subjective to some degree and 
> > > > > > > > > our metric has always been "if you agree with a style check, 
> > > > > > > > > don't enable it".
> > > > > > > > > 
> > > > > > > > > It's up to you how you want to proceed, but I see two ways 
> > > > > > > > > forward: 1) keep this as a readabil

[PATCH] D53025: [clang-tidy] implement new check for const return types.

2018-10-31 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 171985.
ymandel marked an inline comment as done.
ymandel added a comment.

Rebased patch onto trunk.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstReturnTypeCheck.cpp
  clang-tidy/readability/ConstReturnTypeCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-return-type.rst
  test/clang-tidy/readability-const-return-type.cpp

Index: test/clang-tidy/readability-const-return-type.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-const-return-type.cpp
@@ -0,0 +1,227 @@
+// RUN: %check_clang_tidy %s readability-const-return-type %t -- -- -isystem
+
+//  p# = positive test
+//  n# = negative test
+
+#include 
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template 
+const int p31(T v) { return 2; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu
+// CHECK-FIXES: int p31(T v) { return 2; }
+
+// We detect const-ness even without instantiating T.
+template 
+const T p32(T t) { return t; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const T' is 'const'-qual
+// CHECK-FIXES: T p32(T t) { return t; }
+
+// However, if the return type is itself a template instantiation, Clang does
+// not consider it const-qualified without knowing `T`.
+template 
+typename std::add_const::type n15(T v) { return v; }
+
+template 
+class Klazz {
+public:
+  Klazz(A) {}
+};
+
+class Clazz {
+ public:
+  Clazz *const p2() {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'Clazz *const' is 'co
+// CHECK-FIXES: Clazz *p2() {
+return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const
+// CHECK-FIXES: int p4() const {
+return 4;
+  }
+
+  const Klazz* const p5() const;
+  // CHECK-FIXES: const Klazz* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz' is 'const
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  // const-qualifier is the first `const` token, but not the first token.
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const int' is 'const'-
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: return type 'const Clazz::Strukt' i
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz& n11(const Klazz) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz::Strukt *con
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'Clazz *const' is 'cons
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Clazz *const' is
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz'
+// CHECK-FIXES: Klazz p12() {}
+
+const Klazz* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz *
+// CHECK-FIXES: const Klazz* p13() {}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning:
+// CHECK-FIXES: /* co

[PATCH] D58556: [LibTooling] Add "smart" retrieval of AST-node source to FixIt library

2019-02-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 4 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/FixIt.h:60
+// future to include more associated text (like comments).
+CharSourceRange getSourceRangeAuto(const Stmt &S, ASTContext &Context);
+

ilya-biryukov wrote:
> Do you have alternative names in mind? It would be nice to (1) not mention 
> the SourceRange now that we return CharSourceRange, (2) change "auto" to 
> something more descriptive.
> 
> Was thinking about `getNodeRange()` or `getSpannedRange()`, but that 
> completely misses the "auto" part (maybe it's fine, though).
> WDYT? Maybe other ideas?
I completely agree. I went through quite a few iterations on this name and 
disliked this one the least.  ;)  I think you're right, though, that once we're 
choosing a different name, the "auto" part doesn't really need to be in it.  I 
like `getSpannedRange` better than this. Other thoughts:

getLogicalRange
getExtendedRange
getAssociatedRange

any preference?



Comment at: clang/include/clang/Tooling/FixIt.h:73
+// context. In contrast with \p getText(), this function selects a source range
+// "automatically", extracting text that a reader might intuitively associate
+// with a node.  Currently, only specialized for \p clang::Stmt, where it will

ilya-biryukov wrote:
> What are other tricky cases you have in mind for the future?
I just assumed that we'd hit more as we dig into them, but, I'm not so sure 
now.  The two others I can think of offhand are 1) extending to include 
associated comments, 2) semicolons after declarations.  Commas present a 
similar challenge (if a bit simpler) when used in a list (vs. the comma 
operator).  Are there any other separators in C++? 

At a higher level, it would be nice to align this with your work on tree 
transformations. That is, think of these functions as short-term shims to 
simulate the behavior we'll ultimately get from that new library. However, it 
might be premature to consider those details here.



Comment at: clang/include/clang/Tooling/FixIt.h:77
+template 
+StringRef getTextAuto(const T &Node, ASTContext &Context) {
+  return internal::getText(getSourceRangeAuto(Node, Context), Context);

ilya-biryukov wrote:
> Could you add an example of the API use here? The anticipated use-case are 
> removing or textually replacing a node, right?
Yes, that's right.  Will do (on next edit, once we've resolved naming, etc.)



Comment at: clang/lib/Tooling/FixIt.cpp:52
+
+  auto NotCondition = unless(hasCondition(equalsNode(&S)));
+  auto Standalone =

ilya-biryukov wrote:
> Do you expect this function to be on the hot path?
> If so, I'd advice against using the matchers here. They do add enough 
> overhead to be avoided in hot functions.
> 
> I guess the problem is that we can't get a hold of the parent node with using 
> the matchers, right?
> Not sure if there's an easy way out of it in that case.
In the context of transformer, I expect that this will be called in proportion 
to the number of times that a match callback is invoked.  so, I expect there to 
already be matcher use in the control flow.

Yes, I'm using the matchers almost entirely for the hasParent() functionality.

Note that we can change the order of the guards in lines 67-69 and first check 
for a trailing semi which I'd guess is cheaper than calling the matcher. In 
that case, matching will only happen for expressions followed by semicolons.

Alternatively, I think I could restructure the uses of this function to 
*provide* the parent node. In that case, callers can decide what makes the most 
sense performance-wise for getting the parent. 


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58556/new/

https://reviews.llvm.org/D58556



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


[PATCH] D58556: [LibTooling] Add "smart" retrieval of AST-node source to FixIt library

2019-03-05 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 5 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/FixIt.h:73
+// context. In contrast with \p getText(), this function selects a source range
+// "automatically", extracting text that a reader might intuitively associate
+// with a node.  Currently, only specialized for \p clang::Stmt, where it will

kimgr wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > What are other tricky cases you have in mind for the future?
> > I just assumed that we'd hit more as we dig into them, but, I'm not so sure 
> > now.  The two others I can think of offhand are 1) extending to include 
> > associated comments, 2) semicolons after declarations.  Commas present a 
> > similar challenge (if a bit simpler) when used in a list (vs. the comma 
> > operator).  Are there any other separators in C++? 
> > 
> > At a higher level, it would be nice to align this with your work on tree 
> > transformations. That is, think of these functions as short-term shims to 
> > simulate the behavior we'll ultimately get from that new library. However, 
> > it might be premature to consider those details here.
> Peanut gallery comment on this:
> 
> > The two others I can think of offhand are
> > 1) extending to include associated comments,
> > 2) semicolons after declarations.
> > Commas present a similar challenge (if a bit simpler) when used in a list 
> > (vs. the comma operator).
> > Are there any other separators in C++?
> 
> Would it make sense to let callers choose what level of expansion they want 
> with a flag mask? Somehow I think that makes it easier to name the function, 
> too, e.g.:
> ```
> StringRef getExpandedRange(const Stmt &S, ASTContext &Context, ExpansionFlags 
> Flags);
> ```
> 
Yes, I think that's a good idea. I even like the name except that it will 
likely be confused with Expansion locs.  Maybe `getExtendedRange`?


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58556/new/

https://reviews.llvm.org/D58556



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


[PATCH] D58556: [LibTooling] Add "smart" retrieval of AST-node source to FixIt library

2019-03-10 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 190037.
ymandel marked an inline comment as done.
ymandel edited the summary of this revision.
ymandel added a comment.

This update significantly simplifies the change.  It removes any "smarts", 
instead allowing the user to request the extension of the source range to 
include the next specified token (if present).  This turns out to be a more 
basic and useful function for the needs of the upcoming changes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58556/new/

https://reviews.llvm.org/D58556

Files:
  clang/include/clang/Tooling/FixIt.h
  clang/lib/Tooling/FixIt.cpp
  clang/unittests/Tooling/FixItTest.cpp

Index: clang/unittests/Tooling/FixItTest.cpp
===
--- clang/unittests/Tooling/FixItTest.cpp
+++ clang/unittests/Tooling/FixItTest.cpp
@@ -13,6 +13,7 @@
 using namespace clang;
 
 using tooling::fixit::getText;
+using tooling::fixit::getExtendedText;
 using tooling::fixit::createRemoval;
 using tooling::fixit::createReplacement;
 
@@ -77,6 +78,34 @@
   "void foo(int x, int y) { FOO(x,y) }");
 }
 
+TEST(FixItTest, getExtendedText) {
+  CallsVisitor Visitor;
+
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+EXPECT_EQ("foo(x, y);",
+  getExtendedText(*CE, tok::TokenKind::semi, *Context));
+
+Expr *P0 = CE->getArg(0);
+Expr *P1 = CE->getArg(1);
+EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context));
+EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context));
+EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context));
+  };
+  Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
+  Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }");
+  Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }");
+  Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }");
+  Visitor.runOver(
+  "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }");
+
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context));
+  };
+  Visitor.runOver("bool foo() { if (foo()) return true; return false; }");
+  Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }");
+  Visitor.runOver("int foo() { return foo() + 3; }");
+}
+
 TEST(FixItTest, createRemoval) {
   CallsVisitor Visitor;
 
Index: clang/lib/Tooling/FixIt.cpp
===
--- clang/lib/Tooling/FixIt.cpp
+++ clang/lib/Tooling/FixIt.cpp
@@ -11,6 +11,8 @@
 //
 //===--===//
 #include "clang/Tooling/FixIt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Lex/Lexer.h"
 
 namespace clang {
@@ -18,12 +20,20 @@
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context) {
-  return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
-  Context.getSourceManager(),
+StringRef getText(CharSourceRange Range, const ASTContext &Context) {
+  return Lexer::getSourceText(Range, Context.getSourceManager(),
   Context.getLangOpts());
 }
-} // end namespace internal
+
+CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next,
+ ASTContext &Context) {
+  Optional Tok = Lexer::findNextToken(
+  Range.getEnd(), Context.getSourceManager(), Context.getLangOpts());
+  if (!Tok || !Tok->is(Next))
+return Range;
+  return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation());
+}
+} // namespace internal
 
 } // end namespace fixit
 } // end namespace tooling
Index: clang/include/clang/Tooling/FixIt.h
===
--- clang/include/clang/Tooling/FixIt.h
+++ clang/include/clang/Tooling/FixIt.h
@@ -20,36 +20,81 @@
 #define LLVM_CLANG_TOOLING_FIXIT_H
 
 #include "clang/AST/ASTContext.h"
+#include "clang/Basic/TokenKinds.h"
 
 namespace clang {
 namespace tooling {
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context);
+StringRef getText(CharSourceRange Range, const ASTContext &Context);
 
-/// Returns the SourceRange of a SourceRange. This identity function is
-///used by the following template abstractions.
-inline SourceRange getSourceRange(const SourceRange &Range) { return Range; }
+/// Returns the token CharSourceRange corresponding to \p Range.
+inline CharSourceRange getSourceRange(const SourceRange &Range) {
+  return CharSourceRange::getTokenRange(Range);
+}
 
-/// Returns the SourceRange of the token at Location \p Loc.
-inline SourceRange getSourceRange(const SourceLocation &Loc) {
-  return SourceRange(Loc);
+/// Returns 

[PATCH] D58556: [LibTooling] Add "smart" retrieval of AST-node source to FixIt library

2019-03-10 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 7 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/FixIt.h:73
+// context. In contrast with \p getText(), this function selects a source range
+// "automatically", extracting text that a reader might intuitively associate
+// with a node.  Currently, only specialized for \p clang::Stmt, where it will

kimgr wrote:
> ymandel wrote:
> > kimgr wrote:
> > > ymandel wrote:
> > > > ilya-biryukov wrote:
> > > > > What are other tricky cases you have in mind for the future?
> > > > I just assumed that we'd hit more as we dig into them, but, I'm not so 
> > > > sure now.  The two others I can think of offhand are 1) extending to 
> > > > include associated comments, 2) semicolons after declarations.  Commas 
> > > > present a similar challenge (if a bit simpler) when used in a list (vs. 
> > > > the comma operator).  Are there any other separators in C++? 
> > > > 
> > > > At a higher level, it would be nice to align this with your work on 
> > > > tree transformations. That is, think of these functions as short-term 
> > > > shims to simulate the behavior we'll ultimately get from that new 
> > > > library. However, it might be premature to consider those details here.
> > > Peanut gallery comment on this:
> > > 
> > > > The two others I can think of offhand are
> > > > 1) extending to include associated comments,
> > > > 2) semicolons after declarations.
> > > > Commas present a similar challenge (if a bit simpler) when used in a 
> > > > list (vs. the comma operator).
> > > > Are there any other separators in C++?
> > > 
> > > Would it make sense to let callers choose what level of expansion they 
> > > want with a flag mask? Somehow I think that makes it easier to name the 
> > > function, too, e.g.:
> > > ```
> > > StringRef getExpandedRange(const Stmt &S, ASTContext &Context, 
> > > ExpansionFlags Flags);
> > > ```
> > > 
> > Yes, I think that's a good idea. I even like the name except that it will 
> > likely be confused with Expansion locs.  Maybe `getExtendedRange`?
> Extended sounds good to me too! 
I went with "getExtended..." but ended up drastically simplifying the function, 
since the "smarts" turned out to be not smart enough. Fundamentally, the caller 
needs to know when to look for a trailing token (and which one).



Comment at: clang/lib/Tooling/FixIt.cpp:52
+
+  auto NotCondition = unless(hasCondition(equalsNode(&S)));
+  auto Standalone =

ymandel wrote:
> ilya-biryukov wrote:
> > Do you expect this function to be on the hot path?
> > If so, I'd advice against using the matchers here. They do add enough 
> > overhead to be avoided in hot functions.
> > 
> > I guess the problem is that we can't get a hold of the parent node with 
> > using the matchers, right?
> > Not sure if there's an easy way out of it in that case.
> In the context of transformer, I expect that this will be called in 
> proportion to the number of times that a match callback is invoked.  so, I 
> expect there to already be matcher use in the control flow.
> 
> Yes, I'm using the matchers almost entirely for the hasParent() functionality.
> 
> Note that we can change the order of the guards in lines 67-69 and first 
> check for a trailing semi which I'd guess is cheaper than calling the 
> matcher. In that case, matching will only happen for expressions followed by 
> semicolons.
> 
> Alternatively, I think I could restructure the uses of this function to 
> *provide* the parent node. In that case, callers can decide what makes the 
> most sense performance-wise for getting the parent. 
Removed, so no longer relevant.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58556/new/

https://reviews.llvm.org/D58556



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


[PATCH] D56851: [ASTMatchers] Adds `CXXMemberCallExpr` matcher `invokedAtType`.

2019-03-10 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 190038.
ymandel added a comment.
Herald added a project: clang.

Removed unneeded includes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56851/new/

https://reviews.llvm.org/D56851

Files:
  clang/include/clang/Tooling/FixIt.h
  clang/lib/Tooling/FixIt.cpp
  clang/unittests/Tooling/FixItTest.cpp

Index: clang/unittests/Tooling/FixItTest.cpp
===
--- clang/unittests/Tooling/FixItTest.cpp
+++ clang/unittests/Tooling/FixItTest.cpp
@@ -13,6 +13,7 @@
 using namespace clang;
 
 using tooling::fixit::getText;
+using tooling::fixit::getExtendedText;
 using tooling::fixit::createRemoval;
 using tooling::fixit::createReplacement;
 
@@ -77,6 +78,34 @@
   "void foo(int x, int y) { FOO(x,y) }");
 }
 
+TEST(FixItTest, getExtendedText) {
+  CallsVisitor Visitor;
+
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+EXPECT_EQ("foo(x, y);",
+  getExtendedText(*CE, tok::TokenKind::semi, *Context));
+
+Expr *P0 = CE->getArg(0);
+Expr *P1 = CE->getArg(1);
+EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context));
+EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context));
+EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context));
+  };
+  Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
+  Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }");
+  Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }");
+  Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }");
+  Visitor.runOver(
+  "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }");
+
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context));
+  };
+  Visitor.runOver("bool foo() { if (foo()) return true; return false; }");
+  Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }");
+  Visitor.runOver("int foo() { return foo() + 3; }");
+}
+
 TEST(FixItTest, createRemoval) {
   CallsVisitor Visitor;
 
Index: clang/lib/Tooling/FixIt.cpp
===
--- clang/lib/Tooling/FixIt.cpp
+++ clang/lib/Tooling/FixIt.cpp
@@ -18,12 +18,20 @@
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context) {
-  return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
-  Context.getSourceManager(),
+StringRef getText(CharSourceRange Range, const ASTContext &Context) {
+  return Lexer::getSourceText(Range, Context.getSourceManager(),
   Context.getLangOpts());
 }
-} // end namespace internal
+
+CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next,
+ ASTContext &Context) {
+  Optional Tok = Lexer::findNextToken(
+  Range.getEnd(), Context.getSourceManager(), Context.getLangOpts());
+  if (!Tok || !Tok->is(Next))
+return Range;
+  return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation());
+}
+} // namespace internal
 
 } // end namespace fixit
 } // end namespace tooling
Index: clang/include/clang/Tooling/FixIt.h
===
--- clang/include/clang/Tooling/FixIt.h
+++ clang/include/clang/Tooling/FixIt.h
@@ -20,36 +20,81 @@
 #define LLVM_CLANG_TOOLING_FIXIT_H
 
 #include "clang/AST/ASTContext.h"
+#include "clang/Basic/TokenKinds.h"
 
 namespace clang {
 namespace tooling {
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context);
+StringRef getText(CharSourceRange Range, const ASTContext &Context);
 
-/// Returns the SourceRange of a SourceRange. This identity function is
-///used by the following template abstractions.
-inline SourceRange getSourceRange(const SourceRange &Range) { return Range; }
+/// Returns the token CharSourceRange corresponding to \p Range.
+inline CharSourceRange getSourceRange(const SourceRange &Range) {
+  return CharSourceRange::getTokenRange(Range);
+}
 
-/// Returns the SourceRange of the token at Location \p Loc.
-inline SourceRange getSourceRange(const SourceLocation &Loc) {
-  return SourceRange(Loc);
+/// Returns the CharSourceRange of the token at Location \p Loc.
+inline CharSourceRange getSourceRange(const SourceLocation &Loc) {
+  return CharSourceRange::getTokenRange(Loc, Loc);
 }
 
-/// Returns the SourceRange of an given Node. \p Node is typically a
+/// Returns the CharSourceRange of an given Node. \p Node is typically a
 ///'Stmt', 'Expr' or a 'Decl'.
-template  SourceRange getSourceRange(const T &Node) {
-  return Node.getSourceRange();
+template  CharSourceRange getSourceRange(const T &Node) {
+  return CharSourceRange::getTokenRange(Node.getSourceRange());
 }
+
+/// Extends \p Range t

[PATCH] D58556: [LibTooling] Add "smart" retrieval of AST-node source to FixIt library

2019-03-10 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 190039.
ymandel marked an inline comment as done.
ymandel edited the summary of this revision.
ymandel added a comment.

Remove unneeded includes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58556/new/

https://reviews.llvm.org/D58556

Files:
  clang/include/clang/Tooling/FixIt.h
  clang/lib/Tooling/FixIt.cpp
  clang/unittests/Tooling/FixItTest.cpp

Index: clang/unittests/Tooling/FixItTest.cpp
===
--- clang/unittests/Tooling/FixItTest.cpp
+++ clang/unittests/Tooling/FixItTest.cpp
@@ -13,6 +13,7 @@
 using namespace clang;
 
 using tooling::fixit::getText;
+using tooling::fixit::getExtendedText;
 using tooling::fixit::createRemoval;
 using tooling::fixit::createReplacement;
 
@@ -77,6 +78,34 @@
   "void foo(int x, int y) { FOO(x,y) }");
 }
 
+TEST(FixItTest, getExtendedText) {
+  CallsVisitor Visitor;
+
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+EXPECT_EQ("foo(x, y);",
+  getExtendedText(*CE, tok::TokenKind::semi, *Context));
+
+Expr *P0 = CE->getArg(0);
+Expr *P1 = CE->getArg(1);
+EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context));
+EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context));
+EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context));
+  };
+  Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
+  Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }");
+  Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }");
+  Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }");
+  Visitor.runOver(
+  "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }");
+
+  Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context));
+  };
+  Visitor.runOver("bool foo() { if (foo()) return true; return false; }");
+  Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }");
+  Visitor.runOver("int foo() { return foo() + 3; }");
+}
+
 TEST(FixItTest, createRemoval) {
   CallsVisitor Visitor;
 
Index: clang/lib/Tooling/FixIt.cpp
===
--- clang/lib/Tooling/FixIt.cpp
+++ clang/lib/Tooling/FixIt.cpp
@@ -18,12 +18,20 @@
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context) {
-  return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
-  Context.getSourceManager(),
+StringRef getText(CharSourceRange Range, const ASTContext &Context) {
+  return Lexer::getSourceText(Range, Context.getSourceManager(),
   Context.getLangOpts());
 }
-} // end namespace internal
+
+CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next,
+ ASTContext &Context) {
+  Optional Tok = Lexer::findNextToken(
+  Range.getEnd(), Context.getSourceManager(), Context.getLangOpts());
+  if (!Tok || !Tok->is(Next))
+return Range;
+  return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation());
+}
+} // namespace internal
 
 } // end namespace fixit
 } // end namespace tooling
Index: clang/include/clang/Tooling/FixIt.h
===
--- clang/include/clang/Tooling/FixIt.h
+++ clang/include/clang/Tooling/FixIt.h
@@ -20,36 +20,81 @@
 #define LLVM_CLANG_TOOLING_FIXIT_H
 
 #include "clang/AST/ASTContext.h"
+#include "clang/Basic/TokenKinds.h"
 
 namespace clang {
 namespace tooling {
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context);
+StringRef getText(CharSourceRange Range, const ASTContext &Context);
 
-/// Returns the SourceRange of a SourceRange. This identity function is
-///used by the following template abstractions.
-inline SourceRange getSourceRange(const SourceRange &Range) { return Range; }
+/// Returns the token CharSourceRange corresponding to \p Range.
+inline CharSourceRange getSourceRange(const SourceRange &Range) {
+  return CharSourceRange::getTokenRange(Range);
+}
 
-/// Returns the SourceRange of the token at Location \p Loc.
-inline SourceRange getSourceRange(const SourceLocation &Loc) {
-  return SourceRange(Loc);
+/// Returns the CharSourceRange of the token at Location \p Loc.
+inline CharSourceRange getSourceRange(const SourceLocation &Loc) {
+  return CharSourceRange::getTokenRange(Loc, Loc);
 }
 
-/// Returns the SourceRange of an given Node. \p Node is typically a
+/// Returns the CharSourceRange of an given Node. \p Node is typically a
 ///'Stmt', 'Expr' or a 'Decl'.
-template  SourceRange getSourceRange(const T &Node) {
-  return Node.getSourceRange();
+template  CharSourceRange getSourceRange(const T &Node) {
+  return CharSourceRange::getTokenRan

[PATCH] D58556: [LibTooling] Add retrieval of extended AST-node source to FixIt library

2019-03-13 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL356095: [LibTooling] Add retrieval of extended AST-node 
source to FixIt library (authored by ymandel, committed by ).
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D58556?vs=190039&id=190479#toc

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D58556/new/

https://reviews.llvm.org/D58556

Files:
  cfe/trunk/include/clang/Tooling/FixIt.h
  cfe/trunk/lib/Tooling/FixIt.cpp
  cfe/trunk/unittests/Tooling/FixItTest.cpp

Index: cfe/trunk/include/clang/Tooling/FixIt.h
===
--- cfe/trunk/include/clang/Tooling/FixIt.h
+++ cfe/trunk/include/clang/Tooling/FixIt.h
@@ -20,36 +20,81 @@
 #define LLVM_CLANG_TOOLING_FIXIT_H
 
 #include "clang/AST/ASTContext.h"
+#include "clang/Basic/TokenKinds.h"
 
 namespace clang {
 namespace tooling {
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context);
+StringRef getText(CharSourceRange Range, const ASTContext &Context);
 
-/// Returns the SourceRange of a SourceRange. This identity function is
-///used by the following template abstractions.
-inline SourceRange getSourceRange(const SourceRange &Range) { return Range; }
+/// Returns the token CharSourceRange corresponding to \p Range.
+inline CharSourceRange getSourceRange(const SourceRange &Range) {
+  return CharSourceRange::getTokenRange(Range);
+}
 
-/// Returns the SourceRange of the token at Location \p Loc.
-inline SourceRange getSourceRange(const SourceLocation &Loc) {
-  return SourceRange(Loc);
+/// Returns the CharSourceRange of the token at Location \p Loc.
+inline CharSourceRange getSourceRange(const SourceLocation &Loc) {
+  return CharSourceRange::getTokenRange(Loc, Loc);
 }
 
-/// Returns the SourceRange of an given Node. \p Node is typically a
+/// Returns the CharSourceRange of an given Node. \p Node is typically a
 ///'Stmt', 'Expr' or a 'Decl'.
-template  SourceRange getSourceRange(const T &Node) {
-  return Node.getSourceRange();
+template  CharSourceRange getSourceRange(const T &Node) {
+  return CharSourceRange::getTokenRange(Node.getSourceRange());
 }
+
+/// Extends \p Range to include the token \p Next, if it immediately follows the
+/// end of the range. Otherwise, returns \p Range unchanged.
+CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next,
+ ASTContext &Context);
 } // end namespace internal
 
-// Returns a textual representation of \p Node.
+/// Returns a textual representation of \p Node.
 template 
 StringRef getText(const T &Node, const ASTContext &Context) {
   return internal::getText(internal::getSourceRange(Node), Context);
 }
 
+/// Returns the source range spanning the node, extended to include \p Next, if
+/// it immediately follows \p Node. Otherwise, returns the normal range of \p
+/// Node.  See comments on `getExtendedText()` for examples.
+template 
+CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next,
+ ASTContext &Context) {
+  return internal::maybeExtendRange(internal::getSourceRange(Node), Next,
+Context);
+}
+
+/// Returns the source text of the node, extended to include \p Next, if it
+/// immediately follows the node. Otherwise, returns the text of just \p Node.
+///
+/// For example, given statements S1 and S2 below:
+/// \code
+///   {
+/// // S1:
+/// if (!x) return foo();
+/// // S2:
+/// if (!x) { return 3; }
+//}
+/// \endcode
+/// then
+/// \code
+///   getText(S1, Context) = "if (!x) return foo()"
+///   getExtendedText(S1, tok::TokenKind::semi, Context)
+/// = "if (!x) return foo();"
+///   getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context)
+/// = "return foo();"
+///   getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context)
+/// = getText(S2, Context) = "{ return 3; }"
+/// \endcode
+template 
+StringRef getExtendedText(const T &Node, tok::TokenKind Next,
+  ASTContext &Context) {
+  return internal::getText(getExtendedRange(Node, Next, Context), Context);
+}
+
 // Returns a FixItHint to remove \p Node.
 // TODO: Add support for related syntactical elements (i.e. comments, ...).
 template  FixItHint createRemoval(const T &Node) {
Index: cfe/trunk/lib/Tooling/FixIt.cpp
===
--- cfe/trunk/lib/Tooling/FixIt.cpp
+++ cfe/trunk/lib/Tooling/FixIt.cpp
@@ -18,12 +18,20 @@
 namespace fixit {
 
 namespace internal {
-StringRef getText(SourceRange Range, const ASTContext &Context) {
-  return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
-  Context.getSourceManager(),
+StringRef getText(CharSourceRange Range, const ASTContext &Context) {
+  re

[PATCH] D59329: [LibTooling] Add NodeId, a strong type for AST-matcher node identifiers.

2019-03-13 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
ymandel added a reviewer: ilya-biryukov.
ymandel added a project: clang.
Herald added subscribers: jfb, mgorny.

The standard matcher API uses StringRefs to identify bound nodes.  This patch
introduces a strong type thats allows distinguishing ids from arbitrary text in
APIs.  It additionally adds a templated version which can indicate the AST type
to which the id is bound.

This patch is the second in a series intended to improve the abstractions
available to users for writing source-to-source transformations.  A full
discussion of the end goal can be found on the cfe-dev list with subject "[RFC]
Easier source-to-source transformations with clang tooling".


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D59329

Files:
  clang/include/clang/Tooling/Refactoring/NodeId.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/NodeId.cpp

Index: clang/lib/Tooling/Refactoring/NodeId.cpp
===
--- /dev/null
+++ clang/lib/Tooling/Refactoring/NodeId.cpp
@@ -0,0 +1,27 @@
+//===--- NodeId.cpp - NodeId implementation -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/NodeId.h"
+#include 
+
+namespace clang {
+namespace tooling {
+
+// For guaranteeing unique ids on NodeId creation.
+static size_t nextId() {
+  // Start with a relatively high number to avoid bugs if the user mixes
+  // explicitly-numbered ids with those generated with `NextId()`. Similarly, we
+  // choose a number that allows generated ids to be easily recognized.
+  static std::atomic Next();
+  return Next.fetch_add(1, std::memory_order_relaxed);
+}
+
+NodeId::NodeId() : NodeId(nextId()) {}
+
+} // namespace tooling
+} // namespace clang
Index: clang/lib/Tooling/Refactoring/CMakeLists.txt
===
--- clang/lib/Tooling/Refactoring/CMakeLists.txt
+++ clang/lib/Tooling/Refactoring/CMakeLists.txt
@@ -12,6 +12,7 @@
   Rename/USRFinder.cpp
   Rename/USRFindingAction.cpp
   Rename/USRLocFinder.cpp
+  NodeId.cpp
 
   LINK_LIBS
   clangAST
Index: clang/include/clang/Tooling/Refactoring/NodeId.h
===
--- /dev/null
+++ clang/include/clang/Tooling/Refactoring/NodeId.h
@@ -0,0 +1,85 @@
+//===--- NodeId.h - NodeId class --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+///
+/// \file This file defines abstractions for the bound identifiers used in AST
+/// matchers.
+///
+//===--===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_NODE_ID_H_
+#define LLVM_CLANG_TOOLING_REFACTOR_NODE_ID_H_
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringRef.h"
+#include 
+
+namespace clang {
+namespace tooling {
+
+/// A strong type for AST node identifiers.  The standard API uses StringRefs
+/// for identifiers.  The strong type allows us to distinguish ids from
+/// arbitrary text snippets in various APIs.
+class NodeId {
+public:
+  explicit NodeId(std::string Id) : Id(std::move(Id)) {}
+
+  /// Creates a NodeId whose name is based on \p Id. Guarantees that unique ids
+  /// map to unique NodeIds.
+  explicit NodeId(size_t Id) : Id("id" + std::to_string(Id)) {}
+
+  /// Creates a NodeId with a generated name. The name is guaranteed to be
+  /// unique with respect to other generated names.
+  NodeId();
+
+  llvm::StringRef id() const { return Id; }
+
+  /// Gets the AST node in \p Result corresponding to this NodeId, if
+  /// any. Otherwise, returns null.
+  template 
+  const Node *
+  getNodeAs(const ast_matchers::MatchFinder::MatchResult &Result) const {
+return Result.Nodes.getNodeAs(Id);
+  }
+
+private:
+  std::string Id;
+};
+
+/// Refinement of NodeId that identifies the intended node type for the id. This
+/// additional information allows us to select appropriate overloads or
+/// constrain use of various combinators. It also allows us to distinguish when
+/// a \c Expr node is intended as a \c Stmt, which influences the intended
+/// source range for the node.  \p Node is the AST node type corresponding to
+/// this id.
+template  class TypedNodeId : public NodeId {
+public:
+  using NodeId::NodeId;
+  using MatcherType = ast_matchers::internal::Matcher;
+
+  /// Creates a matcher corresponding to the AST-node type of t

[PATCH] D59371: [LibTooling] Add Stencil library for format-string style codegen.

2019-03-14 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
ymandel added a reviewer: ilya-biryukov.
ymandel added a project: clang.
Herald added subscribers: jdoerfert, jfb, mgorny.

This file defines the *Stencil* abstraction: a code-generating object, 
parameterized by named references to (bound) AST nodes.  Given a match result, 
a stencil can be evaluated to a string of source code.

A stencil is similar in spirit to a format string: it is composed of a series 
of raw text strings, references to nodes (the parameters) and helper 
code-generation operations.

See thread on cfe-dev list with subject "[RFC] Easier source-to-source 
transformations with clang tooling" for background.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D59371

Files:
  clang/include/clang/Tooling/Refactoring/Stencil.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Stencil.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/StencilTest.cpp

Index: clang/unittests/Tooling/StencilTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/StencilTest.cpp
@@ -0,0 +1,250 @@
+//===- unittest/Tooling/StencilTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+
+using ::clang::ast_matchers::compoundStmt;
+using ::clang::ast_matchers::decl;
+using ::clang::ast_matchers::declStmt;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::hasAnySubstatement;
+using ::clang::ast_matchers::hasCondition;
+using ::clang::ast_matchers::hasDescendant;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasInitializer;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasReturnValue;
+using ::clang::ast_matchers::hasSingleDecl;
+using ::clang::ast_matchers::hasThen;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::ignoringImplicit;
+using ::clang::ast_matchers::returnStmt;
+using ::clang::ast_matchers::stmt;
+using ::clang::ast_matchers::varDecl;
+
+using MatchResult = ::clang::ast_matchers::MatchFinder::MatchResult;
+
+using ::clang::tooling::stencil_generators::node;
+using ::clang::tooling::stencil_generators::text;
+
+using ::testing::Eq;
+
+// In tests, we can't directly match on llvm::Expected since its accessors
+// mutate the object. So, we collapse it to an Optional.
+llvm::Optional toOptional(llvm::Expected V) {
+  if (V)
+return *V;
+  ADD_FAILURE() << "Losing error in conversion to IsSomething: "
+<< llvm::toString(V.takeError());
+  return llvm::None;
+}
+
+// A very simple matcher for llvm::Optional values.
+MATCHER_P(IsSomething, ValueMatcher, "") {
+  if (!arg)
+return false;
+  return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener);
+}
+
+// Create a valid translation-unit from a statement.
+std::string wrapSnippet(llvm::Twine StatementCode) {
+  return ("auto stencil_test_snippet = []{" + StatementCode + "};").str();
+}
+
+clang::ast_matchers::DeclarationMatcher
+wrapMatcher(const clang::ast_matchers::StatementMatcher &Matcher) {
+  return varDecl(hasName("stencil_test_snippet"),
+ hasDescendant(compoundStmt(hasAnySubstatement(Matcher;
+}
+
+struct TestMatch {
+  // The AST unit from which `result` is built. We bundle it because it backs
+  // the result. Users are not expected to access it.
+  std::unique_ptr AstUnit;
+  // The result to use in the test. References `ast_unit`.
+  MatchResult Result;
+};
+
+// Matches `Matcher` against the statement `StatementCode` and returns the
+// result. Handles putting the statement inside a function and modifying the
+// matcher correspondingly. `Matcher` should match `StatementCode` exactly --
+// that is, produce exactly one match.
+llvm::Optional
+matchStmt(llvm::Twine StatementCode,
+  clang::ast_matchers::StatementMatcher Matcher) {
+  auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
+  if (AstUnit == nullptr) {
+ADD_FAILURE() << "AST construction failed";
+return llvm::None;
+  }
+  clang::ASTContext &Context = AstUnit->getASTContext();
+  auto Matches = clang::ast_matchers::match(wrapMatcher(Matcher), Context);
+  // We expect a single, exact match for the statement.
+  if (Matches.size() != 1) {
+ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
+return llvm::None;
+  }
+  return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
+}
+
+class 

[PATCH] D59329: [LibTooling] Add NodeId, a strong type for AST-matcher node identifiers.

2019-03-14 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 190675.
ymandel added a comment.
Herald added a subscriber: jdoerfert.

Build-related fixes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59329/new/

https://reviews.llvm.org/D59329

Files:
  clang/include/clang/Tooling/Refactoring/NodeId.h
  clang/include/clang/Tooling/Refactoring/Stencil.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/NodeId.cpp
  clang/lib/Tooling/Refactoring/Stencil.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/StencilTest.cpp

Index: clang/unittests/Tooling/StencilTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/StencilTest.cpp
@@ -0,0 +1,250 @@
+//===- unittest/Tooling/StencilTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+
+using ::clang::ast_matchers::compoundStmt;
+using ::clang::ast_matchers::decl;
+using ::clang::ast_matchers::declStmt;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::hasAnySubstatement;
+using ::clang::ast_matchers::hasCondition;
+using ::clang::ast_matchers::hasDescendant;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasInitializer;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasReturnValue;
+using ::clang::ast_matchers::hasSingleDecl;
+using ::clang::ast_matchers::hasThen;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::ignoringImplicit;
+using ::clang::ast_matchers::returnStmt;
+using ::clang::ast_matchers::stmt;
+using ::clang::ast_matchers::varDecl;
+
+using MatchResult = ::clang::ast_matchers::MatchFinder::MatchResult;
+
+using ::clang::tooling::stencil_generators::node;
+using ::clang::tooling::stencil_generators::text;
+
+using ::testing::Eq;
+
+// In tests, we can't directly match on llvm::Expected since its accessors
+// mutate the object. So, we collapse it to an Optional.
+llvm::Optional toOptional(llvm::Expected V) {
+  if (V)
+return *V;
+  ADD_FAILURE() << "Losing error in conversion to IsSomething: "
+<< llvm::toString(V.takeError());
+  return llvm::None;
+}
+
+// A very simple matcher for llvm::Optional values.
+MATCHER_P(IsSomething, ValueMatcher, "") {
+  if (!arg)
+return false;
+  return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener);
+}
+
+// Create a valid translation-unit from a statement.
+std::string wrapSnippet(llvm::Twine StatementCode) {
+  return ("auto stencil_test_snippet = []{" + StatementCode + "};").str();
+}
+
+clang::ast_matchers::DeclarationMatcher
+wrapMatcher(const clang::ast_matchers::StatementMatcher &Matcher) {
+  return varDecl(hasName("stencil_test_snippet"),
+ hasDescendant(compoundStmt(hasAnySubstatement(Matcher;
+}
+
+struct TestMatch {
+  // The AST unit from which `result` is built. We bundle it because it backs
+  // the result. Users are not expected to access it.
+  std::unique_ptr AstUnit;
+  // The result to use in the test. References `ast_unit`.
+  MatchResult Result;
+};
+
+// Matches `Matcher` against the statement `StatementCode` and returns the
+// result. Handles putting the statement inside a function and modifying the
+// matcher correspondingly. `Matcher` should match `StatementCode` exactly --
+// that is, produce exactly one match.
+llvm::Optional
+matchStmt(llvm::Twine StatementCode,
+  clang::ast_matchers::StatementMatcher Matcher) {
+  auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
+  if (AstUnit == nullptr) {
+ADD_FAILURE() << "AST construction failed";
+return llvm::None;
+  }
+  clang::ASTContext &Context = AstUnit->getASTContext();
+  auto Matches = clang::ast_matchers::match(wrapMatcher(Matcher), Context);
+  // We expect a single, exact match for the statement.
+  if (Matches.size() != 1) {
+ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
+return llvm::None;
+  }
+  return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
+}
+
+class StencilTest : public ::testing::Test {
+protected:
+  // Verifies that the given stencil fails when evaluated on a valid match
+  // result. Binds a statement to "stmt", a (non-member) ctor-initializer to
+  // "init", an expression to "expr" and a (nameless) declaration to "decl".
+  void testError(const Stencil &Stencil,
+ testing::Matcher Matcher) {
+  

[PATCH] D59329: [LibTooling] Add NodeId, a strong type for AST-matcher node identifiers.

2019-03-14 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 190679.
ymandel added a comment.

"Revert" to original diff.  This undoes the previous diff, which associated 
with the wrong revision.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59329/new/

https://reviews.llvm.org/D59329

Files:
  clang/include/clang/Tooling/Refactoring/NodeId.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/NodeId.cpp

Index: clang/lib/Tooling/Refactoring/NodeId.cpp
===
--- /dev/null
+++ clang/lib/Tooling/Refactoring/NodeId.cpp
@@ -0,0 +1,27 @@
+//===--- NodeId.cpp - NodeId implementation -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/NodeId.h"
+#include 
+
+namespace clang {
+namespace tooling {
+
+// For guaranteeing unique ids on NodeId creation.
+static size_t nextId() {
+  // Start with a relatively high number to avoid bugs if the user mixes
+  // explicitly-numbered ids with those generated with `NextId()`. Similarly, we
+  // choose a number that allows generated ids to be easily recognized.
+  static std::atomic Next();
+  return Next.fetch_add(1, std::memory_order_relaxed);
+}
+
+NodeId::NodeId() : NodeId(nextId()) {}
+
+} // namespace tooling
+} // namespace clang
Index: clang/lib/Tooling/Refactoring/CMakeLists.txt
===
--- clang/lib/Tooling/Refactoring/CMakeLists.txt
+++ clang/lib/Tooling/Refactoring/CMakeLists.txt
@@ -12,6 +12,7 @@
   Rename/USRFinder.cpp
   Rename/USRFindingAction.cpp
   Rename/USRLocFinder.cpp
+  NodeId.cpp
 
   LINK_LIBS
   clangAST
Index: clang/include/clang/Tooling/Refactoring/NodeId.h
===
--- /dev/null
+++ clang/include/clang/Tooling/Refactoring/NodeId.h
@@ -0,0 +1,85 @@
+//===--- NodeId.h - NodeId class --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+///
+/// \file This file defines abstractions for the bound identifiers used in AST
+/// matchers.
+///
+//===--===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_NODE_ID_H_
+#define LLVM_CLANG_TOOLING_REFACTOR_NODE_ID_H_
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/StringRef.h"
+#include 
+
+namespace clang {
+namespace tooling {
+
+/// A strong type for AST node identifiers.  The standard API uses StringRefs
+/// for identifiers.  The strong type allows us to distinguish ids from
+/// arbitrary text snippets in various APIs.
+class NodeId {
+public:
+  explicit NodeId(std::string Id) : Id(std::move(Id)) {}
+
+  /// Creates a NodeId whose name is based on \p Id. Guarantees that unique ids
+  /// map to unique NodeIds.
+  explicit NodeId(size_t Id) : Id("id" + std::to_string(Id)) {}
+
+  /// Creates a NodeId with a generated name. The name is guaranteed to be
+  /// unique with respect to other generated names.
+  NodeId();
+
+  llvm::StringRef id() const { return Id; }
+
+  /// Gets the AST node in \p Result corresponding to this NodeId, if
+  /// any. Otherwise, returns null.
+  template 
+  const Node *
+  getNodeAs(const ast_matchers::MatchFinder::MatchResult &Result) const {
+return Result.Nodes.getNodeAs(Id);
+  }
+
+private:
+  std::string Id;
+};
+
+/// Refinement of NodeId that identifies the intended node type for the id. This
+/// additional information allows us to select appropriate overloads or
+/// constrain use of various combinators. It also allows us to distinguish when
+/// a \c Expr node is intended as a \c Stmt, which influences the intended
+/// source range for the node.  \p Node is the AST node type corresponding to
+/// this id.
+template  class TypedNodeId : public NodeId {
+public:
+  using NodeId::NodeId;
+  using MatcherType = ast_matchers::internal::Matcher;
+
+  /// Creates a matcher corresponding to the AST-node type of this id and bound
+  /// to this id. Intended for settings where the type of matcher is
+  /// obvious/uninteresting. For example,
+  ///
+  ///   ExprId Arg;
+  ///   auto Matcher = callExpr(callee(isFunctionNamed("foo")),
+  ///   hasArgument(0, Arg.bind()));
+  MatcherType bind() const {
+return ast_matchers::internal::BindableMatcher(
+   ast_matchers::internal::TrueMatcher())
+.bind(id());
+  }
+};
+
+

[PATCH] D59371: [LibTooling] Add Stencil library for format-string style codegen.

2019-03-14 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 190683.
ymandel added a comment.

Build fixes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59371/new/

https://reviews.llvm.org/D59371

Files:
  clang/include/clang/Tooling/Refactoring/Stencil.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Stencil.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/StencilTest.cpp

Index: clang/unittests/Tooling/StencilTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/StencilTest.cpp
@@ -0,0 +1,250 @@
+//===- unittest/Tooling/StencilTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+
+using ::clang::ast_matchers::compoundStmt;
+using ::clang::ast_matchers::decl;
+using ::clang::ast_matchers::declStmt;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::hasAnySubstatement;
+using ::clang::ast_matchers::hasCondition;
+using ::clang::ast_matchers::hasDescendant;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasInitializer;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasReturnValue;
+using ::clang::ast_matchers::hasSingleDecl;
+using ::clang::ast_matchers::hasThen;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::ignoringImplicit;
+using ::clang::ast_matchers::returnStmt;
+using ::clang::ast_matchers::stmt;
+using ::clang::ast_matchers::varDecl;
+
+using MatchResult = ::clang::ast_matchers::MatchFinder::MatchResult;
+
+using ::clang::tooling::stencil_generators::node;
+using ::clang::tooling::stencil_generators::text;
+
+using ::testing::Eq;
+
+// In tests, we can't directly match on llvm::Expected since its accessors
+// mutate the object. So, we collapse it to an Optional.
+llvm::Optional toOptional(llvm::Expected V) {
+  if (V)
+return *V;
+  ADD_FAILURE() << "Losing error in conversion to IsSomething: "
+<< llvm::toString(V.takeError());
+  return llvm::None;
+}
+
+// A very simple matcher for llvm::Optional values.
+MATCHER_P(IsSomething, ValueMatcher, "") {
+  if (!arg)
+return false;
+  return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener);
+}
+
+// Create a valid translation-unit from a statement.
+std::string wrapSnippet(llvm::Twine StatementCode) {
+  return ("auto stencil_test_snippet = []{" + StatementCode + "};").str();
+}
+
+clang::ast_matchers::DeclarationMatcher
+wrapMatcher(const clang::ast_matchers::StatementMatcher &Matcher) {
+  return varDecl(hasName("stencil_test_snippet"),
+ hasDescendant(compoundStmt(hasAnySubstatement(Matcher;
+}
+
+struct TestMatch {
+  // The AST unit from which `result` is built. We bundle it because it backs
+  // the result. Users are not expected to access it.
+  std::unique_ptr AstUnit;
+  // The result to use in the test. References `ast_unit`.
+  MatchResult Result;
+};
+
+// Matches `Matcher` against the statement `StatementCode` and returns the
+// result. Handles putting the statement inside a function and modifying the
+// matcher correspondingly. `Matcher` should match `StatementCode` exactly --
+// that is, produce exactly one match.
+llvm::Optional
+matchStmt(llvm::Twine StatementCode,
+  clang::ast_matchers::StatementMatcher Matcher) {
+  auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
+  if (AstUnit == nullptr) {
+ADD_FAILURE() << "AST construction failed";
+return llvm::None;
+  }
+  clang::ASTContext &Context = AstUnit->getASTContext();
+  auto Matches = clang::ast_matchers::match(wrapMatcher(Matcher), Context);
+  // We expect a single, exact match for the statement.
+  if (Matches.size() != 1) {
+ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
+return llvm::None;
+  }
+  return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
+}
+
+class StencilTest : public ::testing::Test {
+protected:
+  // Verifies that the given stencil fails when evaluated on a valid match
+  // result. Binds a statement to "stmt", a (non-member) ctor-initializer to
+  // "init", an expression to "expr" and a (nameless) declaration to "decl".
+  void testError(const Stencil &Stencil,
+ testing::Matcher Matcher) {
+using ::clang::ast_matchers::cxxConstructExpr;
+using ::clang::ast_matchers::cxxCtorInitializer;
+using ::clang::ast_matchers::has

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-14 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
ymandel added a reviewer: ilya-biryukov.
Herald added subscribers: cfe-commits, jdoerfert, jfb, mgorny.
Herald added a project: clang.
ymandel added a parent revision: D59329: [LibTooling] Add NodeId, a strong type 
for AST-matcher node identifiers..

Adds a basic version of Transformer, a library supporting the concise 
specification of clang-based source-to-source transformations.  A full 
discussion of the end goal can be found on the cfe-dev list with subject "[RFC] 
Easier source-to-source transformations with clang tooling".


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,428 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ::clang::ast_matchers::anyOf;
+using ::clang::ast_matchers::argumentCountIs;
+using ::clang::ast_matchers::callee;
+using ::clang::ast_matchers::callExpr;
+using ::clang::ast_matchers::cxxMemberCallExpr;
+using ::clang::ast_matchers::cxxMethodDecl;
+using ::clang::ast_matchers::cxxRecordDecl;
+using ::clang::ast_matchers::declRefExpr;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::functionDecl;
+using ::clang::ast_matchers::hasAnyName;
+using ::clang::ast_matchers::hasArgument;
+using ::clang::ast_matchers::hasDeclaration;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasType;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::member;
+using ::clang::ast_matchers::memberExpr;
+using ::clang::ast_matchers::namedDecl;
+using ::clang::ast_matchers::on;
+using ::clang::ast_matchers::pointsTo;
+using ::clang::ast_matchers::to;
+using ::clang::ast_matchers::unless;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+} // namespace
+
+static clang::ast_matchers::internal::Matcher
+isOrPointsTo(const DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(llvm::StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+void compareSnippets(llvm::StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos) {
+Actual.erase(I, HL.size());
+  }
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(llvm::StringRef S) { FileContents[0].second += S; }
+
+  void addFile(llvm::StringRef Filename, llvm::StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(llvm::StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change faile

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-19 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 4 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:54
+/// boolean expression language for constructing filters.
+class MatchFilter {
+public:

ilya-biryukov wrote:
> Intuitively, it feels that any filtering should be possible at the level of 
> the AST matchers. Is that not the case?
> Could you provide some motivating examples where AST matchers cannot be used 
> to nail down the matching nodes and we need `MatchFilter`? 
> 
> Please note I have limited experience with AST matchers, so there might be 
> some obvious things that I'm missing.
Good point. The examples I have would actually be perfectly suited to a 
matcher.  That said, there is not matcher support for a simple predicate of 
this form, along the lines of gtest's `Truly(predicate)`. I'll remove this and 
separately try to add something like `Truly` to the matchers.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:135
+// \endcode
+class RewriteRule {
+public:

ilya-biryukov wrote:
> Maybe consider separating the fluent API to build the rewrite rule from the 
> rewrite rule itself?
> 
> Not opposed to having the fluent builder API, this does look nice and seems 
> to nicely align with the matcher APIs.
> However, it is somewhat hard to figure out what can `RewriteRule` do 
> **after** it was built when looking at the code right now.
> ```
> class RewriteRule {
> public:
>   RewriteRule(DynTypedMatcher, TextGenerator Replacement, TextGenerator 
> Explanation);
> 
>   DynTypedMatcher matcher() const;
>   Expected replacement() const;
>   Expected explanation() const;
> };
> 
> struct RewriteRuleBuilder { // Having a better name than 'Builder' would be 
> nice.
>   RewriteRule finish() &&; // produce the final RewriteRule.
> 
>   template 
>   RewriteRuleBuilder &change(const TypedNodeId &Target,
>   NodePart Part = NodePart::Node) &;
>   RewriteRuleBuilder &replaceWith(TextGenerator Replacement) &;
>   RewriteRuleBuilder &because(TextGenerator Explanation) &;
> private:
>   RewriteRule RuleInProgress;
> };
> RewriteRuleBuilder makeRewriteRule();
> ```
I see your point, but do you think it might be enough to improve the comments 
on the class? My concern with a builder is the mental burden on the user of 
another concept (the builder) and the need for an extra `.build()` everywhere. 
To a lesser extent, I also don't love the cost of an extra copy, although I 
doubt it matters and I suppose we could support moves in the build method.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-20 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 4 inline comments as done.
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:135
+// \endcode
+class RewriteRule {
+public:

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > Maybe consider separating the fluent API to build the rewrite rule from 
> > > the rewrite rule itself?
> > > 
> > > Not opposed to having the fluent builder API, this does look nice and 
> > > seems to nicely align with the matcher APIs.
> > > However, it is somewhat hard to figure out what can `RewriteRule` do 
> > > **after** it was built when looking at the code right now.
> > > ```
> > > class RewriteRule {
> > > public:
> > >   RewriteRule(DynTypedMatcher, TextGenerator Replacement, TextGenerator 
> > > Explanation);
> > > 
> > >   DynTypedMatcher matcher() const;
> > >   Expected replacement() const;
> > >   Expected explanation() const;
> > > };
> > > 
> > > struct RewriteRuleBuilder { // Having a better name than 'Builder' would 
> > > be nice.
> > >   RewriteRule finish() &&; // produce the final RewriteRule.
> > > 
> > >   template 
> > >   RewriteRuleBuilder &change(const TypedNodeId &Target,
> > >   NodePart Part = NodePart::Node) &;
> > >   RewriteRuleBuilder &replaceWith(TextGenerator Replacement) &;
> > >   RewriteRuleBuilder &because(TextGenerator Explanation) &;
> > > private:
> > >   RewriteRule RuleInProgress;
> > > };
> > > RewriteRuleBuilder makeRewriteRule();
> > > ```
> > I see your point, but do you think it might be enough to improve the 
> > comments on the class? My concern with a builder is the mental burden on 
> > the user of another concept (the builder) and the need for an extra 
> > `.build()` everywhere. To a lesser extent, I also don't love the cost of an 
> > extra copy, although I doubt it matters and I suppose we could support 
> > moves in the build method.
> The issues with the builder interface is that it requires lots of 
> boilerplate, which is hard to throw away when reading the code of the class. 
> I agree that having a separate builder class is also annoying (more concepts, 
> etc).
> 
> Keeping them separate would be my personal preference, but if you'd prefer to 
> keep it in the same class than maybe move the non-builder pieces to the top 
> of the class and separate the builder methods with a comment. 
> WDYT? 
> 
> > To a lesser extent, I also don't love the cost of an extra copy, although I 
> > doubt it matters and I suppose we could support moves in the build method.
> I believe we can be as efficient (up to an extra move) with builders as 
> without them. If most usages are of the form `RewriteRule R = 
> rewriteRule(...).change(...).replaceWith(...).because(...);`
> Then we could make all builder functions return r-value reference to a 
> builder and have an implicit conversion operator that would consume the 
> builder, producing the final `RewriteRule`:
> ```
> class RewriteRuleBuilder {
>   operator RewriteRule () &&;
>   /// ...
> };
> RewriteRuleBuilder rewriteRule();
> 
> void addRule(RewriteRule R);
> void clientCode() {
>   addRule(rewriteRule().change(Matcher).replaceWith("foo").because("bar"));
> }
> ```
I didn't realize that implicit conversion functions are allowed. With that, I'm 
fine w/ splitting.   Thanks!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-22 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked an inline comment as done.
ymandel added a comment.

Working on the new version now. Will note with "PTAL" once that's ready. Sorry 
that wasn't clear in earlier responses.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 192172.
ymandel marked an inline comment as done.
ymandel added a comment.

Split RewriteRule into two classes, remove support for where clause, support 
multi-token targets, and add corresponding tests.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,447 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ::clang::ast_matchers::anyOf;
+using ::clang::ast_matchers::argumentCountIs;
+using ::clang::ast_matchers::callee;
+using ::clang::ast_matchers::callExpr;
+using ::clang::ast_matchers::cxxMemberCallExpr;
+using ::clang::ast_matchers::cxxMethodDecl;
+using ::clang::ast_matchers::cxxRecordDecl;
+using ::clang::ast_matchers::declRefExpr;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::functionDecl;
+using ::clang::ast_matchers::hasAnyName;
+using ::clang::ast_matchers::hasArgument;
+using ::clang::ast_matchers::hasDeclaration;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasType;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::member;
+using ::clang::ast_matchers::memberExpr;
+using ::clang::ast_matchers::namedDecl;
+using ::clang::ast_matchers::on;
+using ::clang::ast_matchers::pointsTo;
+using ::clang::ast_matchers::to;
+using ::clang::ast_matchers::unless;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+} // namespace
+
+static clang::ast_matchers::internal::Matcher
+isOrPointsTo(const DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(llvm::StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+void compareSnippets(llvm::StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos) {
+Actual.erase(I, HL.size());
+  }
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(llvm::StringRef S) { FileContents[0].second += S; }
+
+  void addFile(llvm::StringRef Filename, llvm::StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(llvm::StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  clang::ast_matchers::MatchFinder MatchFinder;
+  AtomicChanges Changes;
+
+private:
+  FileContentMappings FileContents = {{

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 10 inline comments as done.
ymandel added a comment.

Addressed the most major comments. Still working on some smaller ones. PTAL.  
Thanks!




Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:54
+/// boolean expression language for constructing filters.
+class MatchFilter {
+public:

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > Intuitively, it feels that any filtering should be possible at the level 
> > > of the AST matchers. Is that not the case?
> > > Could you provide some motivating examples where AST matchers cannot be 
> > > used to nail down the matching nodes and we need `MatchFilter`? 
> > > 
> > > Please note I have limited experience with AST matchers, so there might 
> > > be some obvious things that I'm missing.
> > Good point. The examples I have would actually be perfectly suited to a 
> > matcher.  That said, there is not matcher support for a simple predicate of 
> > this form, along the lines of gtest's `Truly(predicate)`. I'll remove this 
> > and separately try to add something like `Truly` to the matchers.
> Makes sense! Maybe put a `FIXME` here to let the readers know this is moving 
> to the ast matchers?
I've removed the where clause entirely. I'll separately add the matcher support 
(I've figured out how to do it within the existing framework).



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:85
+  Node,
+  /// Given a \c MemberExpr, selects the member's token.
+  Member,

ilya-biryukov wrote:
> What happens if member is composed of multiple tokens? E.g.
> ```
> foo.bar::baz; // member tokens are ['bar', '::', 'baz']
> foo.template baz; // member tokens are ['template', 'baz', '<', 'int', 
> '>']
> foo.operator +; // member tokens are ['operator', '+']
> ```
> 
> I might be misinterpreting the meaning of "member" token.
Good catch! I've updated to handle these correctly (I believe). Added some 
tests, plan to add some more.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:89
+  /// relevant name, not including qualifiers.
+  Name,
+};

ilya-biryukov wrote:
> Same here, what happens to the template arguments and multi-token names, e.g.
> `operator +` or `foo`?
Good point. This seems difficult to get right, since NamedDecl does not carry 
sufficient loc data.  However, I've updated the code to explicitly fail in such 
cases, so at least we won't have bad rewrites.

BTW, any idea whether constructor initializers can ever be multi token?



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:135
+// \endcode
+class RewriteRule {
+public:

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > ymandel wrote:
> > > > ilya-biryukov wrote:
> > > > > Maybe consider separating the fluent API to build the rewrite rule 
> > > > > from the rewrite rule itself?
> > > > > 
> > > > > Not opposed to having the fluent builder API, this does look nice and 
> > > > > seems to nicely align with the matcher APIs.
> > > > > However, it is somewhat hard to figure out what can `RewriteRule` do 
> > > > > **after** it was built when looking at the code right now.
> > > > > ```
> > > > > class RewriteRule {
> > > > > public:
> > > > >   RewriteRule(DynTypedMatcher, TextGenerator Replacement, 
> > > > > TextGenerator Explanation);
> > > > > 
> > > > >   DynTypedMatcher matcher() const;
> > > > >   Expected replacement() const;
> > > > >   Expected explanation() const;
> > > > > };
> > > > > 
> > > > > struct RewriteRuleBuilder { // Having a better name than 'Builder' 
> > > > > would be nice.
> > > > >   RewriteRule finish() &&; // produce the final RewriteRule.
> > > > > 
> > > > >   template 
> > > > >   RewriteRuleBuilder &change(const TypedNodeId &Target,
> > > > >   NodePart Part = NodePart::Node) &;
> > > > >   RewriteRuleBuilder &replaceWith(TextGenerator Replacement) &;
> > > > >   RewriteRuleBuilder &because(TextGenerator Explanation) &;
> > > > > private:
> > > > >   RewriteRule RuleInProgress;
> > > > > };
> > > > > RewriteRuleBuilder makeRewriteRule();
> > > > > ```
> > > > I see your point, but do you think it might be enough to improve the 
> > > > comments on the class? My concern with a builder is the mental burden 
> > > > on the user of another concept (the builder) and the need for an extra 
> > > > `.build()` everywhere. To a lesser extent, I also don't love the cost 
> > > > of an extra copy, although I doubt it matters and I suppose we could 
> > > > support moves in the build method.
> > > The issues with the builder interface is that it requires lots of 
> > > boilerplate, which is hard to throw away when reading the code of the 
> > > class. I agree that having a separate builder class is also annoying 
> > > (more concepts, etc).
> > > 
> > > Keeping them separate would be my personal preference, but if you'd 
> >

[PATCH] D59329: [LibTooling] Add NodeId, a strong type for AST-matcher node identifiers.

2019-03-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

gentle ping...


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59329/new/

https://reviews.llvm.org/D59329



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


[PATCH] D59371: [LibTooling] Add Stencil library for format-string style codegen.

2019-03-25 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

gentle ping...


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59371/new/

https://reviews.llvm.org/D59371



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 192267.
ymandel marked 5 inline comments as done.
ymandel added a comment.

- Remove lvalue-ref overloads in builder
- add StringRef overloads for TextGenerator-taking methods
- constrain Name targeting for ctor initializers to explicitly written 
initializers.
- add multi-token member test


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,416 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ::clang::ast_matchers::anyOf;
+using ::clang::ast_matchers::argumentCountIs;
+using ::clang::ast_matchers::callee;
+using ::clang::ast_matchers::callExpr;
+using ::clang::ast_matchers::cxxMemberCallExpr;
+using ::clang::ast_matchers::cxxMethodDecl;
+using ::clang::ast_matchers::cxxRecordDecl;
+using ::clang::ast_matchers::declRefExpr;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::functionDecl;
+using ::clang::ast_matchers::hasAnyName;
+using ::clang::ast_matchers::hasArgument;
+using ::clang::ast_matchers::hasDeclaration;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasType;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::member;
+using ::clang::ast_matchers::memberExpr;
+using ::clang::ast_matchers::namedDecl;
+using ::clang::ast_matchers::on;
+using ::clang::ast_matchers::pointsTo;
+using ::clang::ast_matchers::to;
+using ::clang::ast_matchers::unless;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+} // namespace
+
+static clang::ast_matchers::internal::Matcher
+isOrPointsTo(const DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(llvm::StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+void compareSnippets(llvm::StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos) {
+Actual.erase(I, HL.size());
+  }
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(llvm::StringRef S) { FileContents[0].second += S; }
+
+  void addFile(llvm::StringRef Filename, llvm::StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(llvm::StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  clang::ast_matchers::MatchFinder Mat

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked an inline comment as done.
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:89
+  /// relevant name, not including qualifiers.
+  Name,
+};

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > Same here, what happens to the template arguments and multi-token names, 
> > > e.g.
> > > `operator +` or `foo`?
> > Good point. This seems difficult to get right, since NamedDecl does not 
> > carry sufficient loc data.  However, I've updated the code to explicitly 
> > fail in such cases, so at least we won't have bad rewrites.
> > 
> > BTW, any idea whether constructor initializers can ever be multi token?
> > BTW, any idea whether constructor initializers can ever be multi token?
> 
> Two cases come to mind:
> 1. arbitrary names when initializing base classes,  something like 
> `::ns::X(10)`
> 2. template packs with ellipsis (although ellipsis shouldn't be normally part 
> that we replace, I guess): `Base(10)...`
> 
> Full example:
> ```
> namespace ns {
>   struct X {
> X(int);
>   };
> }
> 
> 
> template 
> struct Y : ns::X, Bases... {
>   Y() : ns::X(10), Bases(10)... {
>   }
> };
> 
> struct Z {
>   Z(int);
> };
> struct W {
>   W(int);
> };
> 
> Y y;
> ```
Turns out the code was already filtering these cases. I added an addition 
constrain of I->isWritten() for initializers. So, only explicit initialization 
of fields is allowed, and therefore I'd venture guaranteed to be a single 
token. I noticed that Kythe seems to make the same assumption.  

That said, I could change to code to specify the range as a char-range of 
`getMemberLoc(), getLParenLoc()` if we can't rely on that (single-token) 
guarantee.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 192619.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Simplified TextGenerator, changed namespace handling, collapsed verifyTarget() 
into getTarget(), and other changes in response to comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,416 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ::clang::ast_matchers::anyOf;
+using ::clang::ast_matchers::argumentCountIs;
+using ::clang::ast_matchers::callee;
+using ::clang::ast_matchers::callExpr;
+using ::clang::ast_matchers::cxxMemberCallExpr;
+using ::clang::ast_matchers::cxxMethodDecl;
+using ::clang::ast_matchers::cxxRecordDecl;
+using ::clang::ast_matchers::declRefExpr;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::functionDecl;
+using ::clang::ast_matchers::hasAnyName;
+using ::clang::ast_matchers::hasArgument;
+using ::clang::ast_matchers::hasDeclaration;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasType;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::member;
+using ::clang::ast_matchers::memberExpr;
+using ::clang::ast_matchers::namedDecl;
+using ::clang::ast_matchers::on;
+using ::clang::ast_matchers::pointsTo;
+using ::clang::ast_matchers::to;
+using ::clang::ast_matchers::unless;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+} // namespace
+
+static clang::ast_matchers::internal::Matcher
+isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(llvm::StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+void compareSnippets(llvm::StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos) {
+Actual.erase(I, HL.size());
+  }
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(llvm::StringRef S) { FileContents[0].second += S; }
+
+  void addFile(llvm::StringRef Filename, llvm::StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(llvm::StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  clang::ast_matchers::MatchFinder MatchFinder;
+  AtomicChanges Changes;
+
+private:
+  Fil

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 192623.
ymandel marked 26 inline comments as done.
ymandel added a comment.

Adjusted test to cover case discussed in comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,418 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ::clang::ast_matchers::anyOf;
+using ::clang::ast_matchers::argumentCountIs;
+using ::clang::ast_matchers::callee;
+using ::clang::ast_matchers::callExpr;
+using ::clang::ast_matchers::cxxMemberCallExpr;
+using ::clang::ast_matchers::cxxMethodDecl;
+using ::clang::ast_matchers::cxxRecordDecl;
+using ::clang::ast_matchers::declRefExpr;
+using ::clang::ast_matchers::expr;
+using ::clang::ast_matchers::functionDecl;
+using ::clang::ast_matchers::hasAnyName;
+using ::clang::ast_matchers::hasArgument;
+using ::clang::ast_matchers::hasDeclaration;
+using ::clang::ast_matchers::hasElse;
+using ::clang::ast_matchers::hasName;
+using ::clang::ast_matchers::hasType;
+using ::clang::ast_matchers::ifStmt;
+using ::clang::ast_matchers::member;
+using ::clang::ast_matchers::memberExpr;
+using ::clang::ast_matchers::namedDecl;
+using ::clang::ast_matchers::on;
+using ::clang::ast_matchers::pointsTo;
+using ::clang::ast_matchers::to;
+using ::clang::ast_matchers::unless;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+} // namespace
+
+static clang::ast_matchers::internal::Matcher
+isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(llvm::StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+void compareSnippets(llvm::StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos) {
+Actual.erase(I, HL.size());
+  }
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(llvm::StringRef S) { FileContents[0].second += S; }
+
+  void addFile(llvm::StringRef Filename, llvm::StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(llvm::StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  clang::ast_matchers::MatchFinder MatchFinder;
+  AtomicChanges Changes;
+
+private:
+  FileContentMappings FileContents = {{"header.h", ""}};
+};
+
+class TransformerTest : public

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Thanks for the detailed review and really helpful comments!




Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:38
+/// @{
+using ast_matchers::CXXCtorInitializerMatcher;
+using ast_matchers::DeclarationMatcher;

ilya-biryukov wrote:
> I'm not sure if this is in the LLVM style guide, but we might want to avoid 
> introducing these names into `clang::tooling` namespaces in the headers.
> 
> My fear is that users will rely on those using without knowing that 
> explicitly and won't add corresponding `using` directives or qualifiers to 
> their `.cpp` files, making refactoring and moving the code around harder.
> 
> Could you fully-qualify those names in the header instead? There does not 
> seem to be too many of them.
right. I'd intended to introduce these into the clang tooling namespace -- that 
is, these weren't just convenience aliases for the header file. But, I no 
longer think that's useful in any case, so dropping them is certainly best.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:65
+
+using TextGenerator = std::function(
+const ast_matchers::MatchFinder::MatchResult &)>;

ilya-biryukov wrote:
> Why would a `TextGenerator`  fail?
> I imagine all of the failure cases are programming errors (matchers in the 
> rewrite rule were not aligned with the corresponding text generating 
> function). For those cases, using the `assert` macro seems cleaner.
Sure.  I could go either way. I think some of these cases fall on the border 
between an invariant violation and "invalid argument" or some such.  But, let's 
keep it simpler for now.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:116
+  // never be the empty string.
+  std::string Target = RootId;
+  ast_type_traits::ASTNodeKind TargetKind;

ilya-biryukov wrote:
> NIT: maybe move all inits to the constructor?
> To have all initializers in one place.
nicer, thanks.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:170
+
+  RewriteRuleBuilder(const RewriteRuleBuilder &) = default;
+  RewriteRuleBuilder(RewriteRuleBuilder &&) = default;

ilya-biryukov wrote:
> NIT: maybe remove the `=default` copy and move ctors and assignments?
> They should be generated automatically anyway, right?
Sure. I was going based on google's style recommendations, but personally i 
prefer leaving them implicit.



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:28
+
+namespace clang {
+namespace tooling {

ilya-biryukov wrote:
> Other files in the tooling library seem to be adding `using namespace clang` 
> instead of putting the declaration into a namespace.
> Could you please change the new code to do the same for consistency?
> 
Done.  Agreed about being consistent. FWIW, I can't say I like this style.  
Perhaps because I'm not used to it, but it feels too implicit.  It forces the 
reader to figure out where each definition is being associated. Also, I 
discovered it only works for method definitions. Free functions still need to 
be explicitly namespaced.

Any idea what the reason for this style is?



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:30
+namespace tooling {
+namespace {
+using ::clang::ast_matchers::MatchFinder;

ilya-biryukov wrote:
> Why put using directives into an anonymous namespace?
> I have not seen this pattern before, could you point me to explanations on 
> why this is useful?
You gain an extra little bit of robustness against clashing with something 
declared in the enclosing namespace.  But, this is overkill even for me -- I 
generally only do this when I already have an anonymous namespace; there's no 
good reason to create one for this purpose.



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:111
+// Requires verifyTarget(node, target_part) == success.
+static CharSourceRange getTarget(const DynTypedNode &Node, ASTNodeKind Kind,
+ NodePart TargetPart, ASTContext &Context) {

ilya-biryukov wrote:
> NIT: consider merging `verifyTarget` into `getTarget` and making `getTarget` 
> return `Expected`.
> Would allow avoiding to write one of the complicated switches and 
> error-checking arguably looks just as natural in the `getTarget` as it is in 
> `verifyTarget`.
> 
> Also, having the invariants closer to the code using them makes it easier to 
> certify both are correct, e.g. seeing that `NamedDecl.isIdentifier()` was 
> checked before accessing the `NamedDecl.getName()` in the same function is 
> simpler.
Yes, I like it much better this way.  The split wasn't worth it.



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:134
+  auto R = CharSourceRange::getTokenRange(TokenLoc, TokenLoc);
+  // Verify that the range covers exa

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-29 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 192889.
ymandel marked an inline comment as done.
ymandel added a comment.

- Assorted changes in response to comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,388 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ast_matchers::anyOf;
+using ast_matchers::argumentCountIs;
+using ast_matchers::callee;
+using ast_matchers::callExpr;
+using ast_matchers::cxxMemberCallExpr;
+using ast_matchers::cxxMethodDecl;
+using ast_matchers::cxxRecordDecl;
+using ast_matchers::declRefExpr;
+using ast_matchers::expr;
+using ast_matchers::functionDecl;
+using ast_matchers::hasAnyName;
+using ast_matchers::hasArgument;
+using ast_matchers::hasDeclaration;
+using ast_matchers::hasElse;
+using ast_matchers::hasName;
+using ast_matchers::hasType;
+using ast_matchers::ifStmt;
+using ast_matchers::member;
+using ast_matchers::memberExpr;
+using ast_matchers::namedDecl;
+using ast_matchers::on;
+using ast_matchers::pointsTo;
+using ast_matchers::to;
+using ast_matchers::unless;
+
+using llvm::StringRef;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+
+ast_matchers::internal::Matcher
+isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+std::string format(StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+void compareSnippets(StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos)
+Actual.erase(I, HL.size());
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(StringRef S) { FileContents[0].second += S; }
+
+  void addFile(StringRef Filename, StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  void testRule(RewriteRule Rule, StringRef Input, StringRef Expected) {
+Transformer T(std::move(Rule),
+  [this](const AtomicChange &C) { Changes.push_back(C); });
+T.registerMatchers(&MatchFinder);
+compareSnippets(Expected, rewrite(Input));
+  }
+
+  clang::ast_matchers::MatchFinder MatchFinder;
+  AtomicChanges Changes;
+
+private:
+  FileContentMappings FileContents = {{"header.h", ""}};
+};
+
+class TransformerTest : pub

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-03-29 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 19 inline comments as done.
ymandel added a comment.

Thanks for (more) helpful comments.  I think the code is a lot better now than 
it started out. :)

Also, converted `RewriteRule` to a simple struct as per our discussion.




Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:70
+//
+// * Explanation: explanation of the rewrite.
+//

ilya-biryukov wrote:
> NIT: maybe mention what it should be used for? we plan to show to to the user 
> (e.g. in the clang-tidy fix description), right?
Done. But, given that we don't use this field yet, I'm fine deleting it until a 
future revision. I'm also fine leaving it given that we know we'll be needing 
it later.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:89
+// \code
+//   RewriteRule R = buildRule(functionDecl(...)).replaceWith(...);
+// \endcode

ilya-biryukov wrote:
> Could you also add examples on how to apply the rewrite rule here?
> So that the users can have an idea about the full workflow when reading the 
> code.
Is this what you had in mind? Unlike clang-tidy, there is no neat 
infrastructure into which we can drop it.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:97
+  ast_matchers::internal::DynTypedMatcher Matcher;
+  // The (bound) id of the node whose source will be replaced.  This id should
+  // never be the empty string.

ilya-biryukov wrote:
> NIT: maybe assert this invariant in the constructor?
The constructor sets this field, so no need to assert.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:122
+  // The bound id of the node corresponding to the matcher.
+  static llvm::StringRef matchedNode() { return RootId; }
+

ilya-biryukov wrote:
> This method does not seem to be used anywhere.
> Are we missing the usages in this patch? Maybe remove it from the initial 
> implementation and re-add later when we have the callsites?
It was used above in makeMatcher.  But, it was overkill -- users can just 
reference RootId directly, so I've removed it.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:175
+  RewriteRuleBuilder &&because(std::string Explanation) && {
+return std::move(std::move(*this).because(text(std::move(Explanation;
+  }

ilya-biryukov wrote:
> NIT: the top-level `std::move` looks redundant, maybe remove it?
Yes, not sure why did that. thanks.



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:133
+
+namespace clang {
+namespace tooling {

ilya-biryukov wrote:
> NIT: remove namespaces for consistency with the rest of the code.
> 
> (Probably a leftover from the previous version)
This is necessary as far as I can tell. W/o it, I get a linker failure (missing 
definition). Based on the declaration in the header, the compiler resolves the 
reference below to clang::tooling::applyRewriteRule() but this definition ends 
up in the global namespace.

I think the using decls only work for method definitions -- the type seems to 
resolve to be the one in the namespace. But free functions don't get the same 
treatment. Unless I"m doing something wrong?



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:150
+  llvm::handleErrors(TargetOrErr.takeError(), [&Rule](StringError &E) {
+return invalidArgumentError("Failure targeting node" +
+Rule.target() + ": " + E.getMessage());

ilya-biryukov wrote:
> NIT: consider simply propagating the original error up the stack in that case 
> to avoid boilerplate.
> Although adding the `.target()` information to the error might be useful, so 
> up to you.
integrated id into getTarget so we can avoid this error handling step.



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:157
+  isOriginMacroBody(*Match.SourceManager, Target.getBegin()))
+return Transformation();
+

ilya-biryukov wrote:
> Why not return an error in case of macros too? Is there any use of the empty 
> transformation to the clients? 
> Alternatively, we might want to document this behavior (applyRewriteRule can 
> return empty transformations) and it's rationale.
I think the common case is that we simply want to skip macros -- there's 
nothing erroneous about them, they're just usually hard to deal w/ correctly.  
That said, we might want to change this to *never* return an error and just 
assert when things go wrong. The advantage of the current design is that 
callers can in principle ignore failed rewrites.

However, in practice, if the rewrite fails that means it had a bug, so it might 
be best to asssert().  Only real advantage, then, is that this is easier to 
test since it doesn't crash the program.

WDYT?



Comment at: clang/unittests/Tooling/Tr

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-01 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 193219.
ymandel marked 28 inline comments as done.
ymandel added a comment.

Assorted changes in response to comments.

Most notably, dropped constructor from RewriteRule, in favor of putting 
meaningful initialization in the builder.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,388 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ast_matchers::anyOf;
+using ast_matchers::argumentCountIs;
+using ast_matchers::callee;
+using ast_matchers::callExpr;
+using ast_matchers::cxxMemberCallExpr;
+using ast_matchers::cxxMethodDecl;
+using ast_matchers::cxxRecordDecl;
+using ast_matchers::declRefExpr;
+using ast_matchers::expr;
+using ast_matchers::functionDecl;
+using ast_matchers::hasAnyName;
+using ast_matchers::hasArgument;
+using ast_matchers::hasDeclaration;
+using ast_matchers::hasElse;
+using ast_matchers::hasName;
+using ast_matchers::hasType;
+using ast_matchers::ifStmt;
+using ast_matchers::member;
+using ast_matchers::memberExpr;
+using ast_matchers::namedDecl;
+using ast_matchers::on;
+using ast_matchers::pointsTo;
+using ast_matchers::to;
+using ast_matchers::unless;
+
+using llvm::StringRef;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+
+static ast_matchers::internal::Matcher
+isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+static void compareSnippets(StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos)
+Actual.erase(I, HL.size());
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(StringRef S) { FileContents[0].second += S; }
+
+  void addFile(StringRef Filename, StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  void testRule(RewriteRule Rule, StringRef Input, StringRef Expected) {
+Transformer T(std::move(Rule),
+  [this](const AtomicChange &C) { Changes.push_back(C); });
+T.registerMatchers(&MatchFinder);
+compareSnippets(Expected, rewrite(Input));
+  }
+
+  clang::ast_matchers::MatchFinder MatchFi

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-01 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:70
+//
+// * Explanation: explanation of the rewrite.
+//

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > NIT: maybe mention what it should be used for? we plan to show to to the 
> > > user (e.g. in the clang-tidy fix description), right?
> > Done. But, given that we don't use this field yet, I'm fine deleting it 
> > until a future revision. I'm also fine leaving it given that we know we'll 
> > be needing it later.
> Up to you, to me it feels like the presence of this field defines what this 
> class is used for.
> 1. If there's an explanation, it feels like it should represent a complete 
> fix or refactoring that could be presented to the user.
> 2. Without an explanation, it might feel like something lower-level (e.g. one 
> could write a bunch of RewriteRule whose changes are later combined and 
> surfaced to the user as a full refactoring).
> 
> Both approaches make sense, and I assume (1) is the desired abstraction this 
> class represents, so keeping the field looks ok.
Agreed. Keeping it.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:89
+// \code
+//   RewriteRule R = buildRule(functionDecl(...)).replaceWith(...);
+// \endcode

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > Could you also add examples on how to apply the rewrite rule here?
> > > So that the users can have an idea about the full workflow when reading 
> > > the code.
> > Is this what you had in mind? Unlike clang-tidy, there is no neat 
> > infrastructure into which we can drop it.
> Yeah, exactly, but could we keep is a bit shorter by removing the pieces 
> which are non-relevant to the actual transformer usage.
> Something like:
> ```
> // To apply a rule to the clang AST, use Transformer class:
> // \code
> //   AtomicChanges Changes;
> //   Transformer T(
> //   MyRule, [&Changes](const AtomicChange &C) { Changes.push_back(C); 
> };);
> //   ast_matchers::MatchFinder MatchFinder;
> //   T.registerMatchers(&MatchFinder);
> //   // ... insert code to run the ast matchers.
> //   // Consume the changes.
> //   applyAtomicChanges(..., Changes);
> ```
> 
> Or just mention that `Transformer` class should be used to apply the rewrite 
> rule and obtain the corresponding replacements.
went w/ the second suggestion.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:136
+
+  RewriteRule(ast_matchers::internal::DynTypedMatcher M)
+  : Matcher(initMatcher(M)), Target(RootId), 
TargetKind(M.getSupportedKind()),

ilya-biryukov wrote:
> NIT: Maybe remove the constructor and let the builder handle this?
> Technically, users can break the invariants after creating the rewrite rules 
> anyway, so having this constructor does not buy much in terms of safer coding 
> practices, but makes it harder to use `RewriteRule` as a plain struct 
> (specifically, having no default constructor might make it awkward).
> 
> Up to you, of course.
Agreed, but DynTypedMatcher has no default constructor, so we have to provide 
something.  I dropped the constructor, but added an initializer to `Matcher` to 
enable the default constructor.



Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:184
+RewriteRuleBuilder buildRule(ast_matchers::internal::Matcher M) {
+  return RewriteRuleBuilder(M);
+}

ilya-biryukov wrote:
> Isn't this overload redundant in presence of `buildRule(DynTypedMatcher)`? 
> Both seem to call into the constructor that accept a `DynTypedMatcher`, so 
> `Matcher` is convertible to `DynTypedMatcher`, right?.
I think I was avoiding some extra construction, but even if so, I can't see its 
worth the added complexity. thx



Comment at: clang/lib/Tooling/Refactoring/Transformer.cpp:133
+
+namespace clang {
+namespace tooling {

ilya-biryukov wrote:
> ymandel wrote:
> > ilya-biryukov wrote:
> > > NIT: remove namespaces for consistency with the rest of the code.
> > > 
> > > (Probably a leftover from the previous version)
> > This is necessary as far as I can tell. W/o it, I get a linker failure 
> > (missing definition). Based on the declaration in the header, the compiler 
> > resolves the reference below to clang::tooling::applyRewriteRule() but this 
> > definition ends up in the global namespace.
> > 
> > I think the using decls only work for method definitions -- the type seems 
> > to resolve to be the one in the namespace. But free functions don't get the 
> > same treatment. Unless I"m doing something wrong?
> Yeah, you should explicitly qualify to let the compiler know the namespace of 
> a corresponding declaration:
> ```
> Expected
> clang::tooling::applyRewriteRule(...) {
>   // ...
> }
> ```
> 
> `tooling::applyRewriteRule` would also work since we have `using namespace 
> 

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-02 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 193260.
ymandel marked 3 inline comments as done.
ymandel added a comment.

Small tweaks in response to comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,388 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ast_matchers::anyOf;
+using ast_matchers::argumentCountIs;
+using ast_matchers::callee;
+using ast_matchers::callExpr;
+using ast_matchers::cxxMemberCallExpr;
+using ast_matchers::cxxMethodDecl;
+using ast_matchers::cxxRecordDecl;
+using ast_matchers::declRefExpr;
+using ast_matchers::expr;
+using ast_matchers::functionDecl;
+using ast_matchers::hasAnyName;
+using ast_matchers::hasArgument;
+using ast_matchers::hasDeclaration;
+using ast_matchers::hasElse;
+using ast_matchers::hasName;
+using ast_matchers::hasType;
+using ast_matchers::ifStmt;
+using ast_matchers::member;
+using ast_matchers::memberExpr;
+using ast_matchers::namedDecl;
+using ast_matchers::on;
+using ast_matchers::pointsTo;
+using ast_matchers::to;
+using ast_matchers::unless;
+
+using llvm::StringRef;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+
+static ast_matchers::internal::Matcher
+isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+static void compareSnippets(StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos)
+Actual.erase(I, HL.size());
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(StringRef S) { FileContents[0].second += S; }
+
+  void addFile(StringRef Filename, StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  void testRule(RewriteRule Rule, StringRef Input, StringRef Expected) {
+Transformer T(std::move(Rule),
+  [this](const AtomicChange &C) { Changes.push_back(C); });
+T.registerMatchers(&MatchFinder);
+compareSnippets(Expected, rewrite(Input));
+  }
+
+  clang::ast_matchers::MatchFinder MatchFinder;
+  AtomicChanges Changes;
+
+private:
+  FileContentMappings FileContents = {{"header.h", ""}};
+};
+
+class Transf

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-02 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Thank you for the extremely helpful and detailed review!!




Comment at: clang/include/clang/Tooling/Refactoring/Transformer.h:110
+  // constructed with the builder class.
+  static constexpr char RootId[] = "___root___";
+};

ilya-biryukov wrote:
> NIT: `llvm::StringLiteral` is a vocabulary type with compile-time size that 
> could be used here.
> Although I don't think it has any actual benefits over char array, so leaving 
> as is also looks good.
Went with StringLiteral -- given that we always wrap RootId in a stringref to 
use it, seems better just to define it that way to begin with.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-02 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 193306.
ymandel added a comment.

Remove dependency on NodeIds.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  clang/include/clang/Tooling/Refactoring/Transformer.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Transformer.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/TransformerTest.cpp

Index: clang/unittests/Tooling/TransformerTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/TransformerTest.cpp
@@ -0,0 +1,389 @@
+//===- unittest/Tooling/TransformerTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+namespace {
+using ast_matchers::anyOf;
+using ast_matchers::argumentCountIs;
+using ast_matchers::callee;
+using ast_matchers::callExpr;
+using ast_matchers::cxxMemberCallExpr;
+using ast_matchers::cxxMethodDecl;
+using ast_matchers::cxxRecordDecl;
+using ast_matchers::declRefExpr;
+using ast_matchers::expr;
+using ast_matchers::functionDecl;
+using ast_matchers::hasAnyName;
+using ast_matchers::hasArgument;
+using ast_matchers::hasDeclaration;
+using ast_matchers::hasElse;
+using ast_matchers::hasName;
+using ast_matchers::hasType;
+using ast_matchers::ifStmt;
+using ast_matchers::member;
+using ast_matchers::memberExpr;
+using ast_matchers::namedDecl;
+using ast_matchers::on;
+using ast_matchers::pointsTo;
+using ast_matchers::to;
+using ast_matchers::unless;
+
+using llvm::StringRef;
+
+constexpr char KHeaderContents[] = R"cc(
+  struct string {
+string(const char*);
+char* c_str();
+int size();
+  };
+  int strlen(const char*);
+
+  namespace proto {
+  struct PCFProto {
+int foo();
+  };
+  struct ProtoCommandLineFlag : PCFProto {
+PCFProto& GetProto();
+  };
+  }  // namespace proto
+)cc";
+
+static ast_matchers::internal::Matcher
+isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
+  return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
+}
+
+static std::string format(StringRef Code) {
+  const std::vector Ranges(1, Range(0, Code.size()));
+  auto Style = format::getLLVMStyle();
+  const auto Replacements = format::reformat(Style, Code, Ranges);
+  auto Formatted = applyAllReplacements(Code, Replacements);
+  if (!Formatted) {
+ADD_FAILURE() << "Could not format code: "
+  << llvm::toString(Formatted.takeError());
+return std::string();
+  }
+  return *Formatted;
+}
+
+static void compareSnippets(StringRef Expected,
+ const llvm::Optional &MaybeActual) {
+  ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
+  auto Actual = *MaybeActual;
+  std::string HL = "#include \"header.h\"\n";
+  auto I = Actual.find(HL);
+  if (I != std::string::npos)
+Actual.erase(I, HL.size());
+  EXPECT_EQ(format(Expected), format(Actual));
+}
+
+// FIXME: consider separating this class into its own file(s).
+class ClangRefactoringTestBase : public testing::Test {
+protected:
+  void appendToHeader(StringRef S) { FileContents[0].second += S; }
+
+  void addFile(StringRef Filename, StringRef Content) {
+FileContents.emplace_back(Filename, Content);
+  }
+
+  llvm::Optional rewrite(StringRef Input) {
+std::string Code = ("#include \"header.h\"\n" + Input).str();
+auto Factory = newFrontendActionFactory(&MatchFinder);
+if (!runToolOnCodeWithArgs(
+Factory->create(), Code, std::vector(), "input.cc",
+"clang-tool", std::make_shared(),
+FileContents)) {
+  return None;
+}
+auto ChangedCodeOrErr =
+applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
+if (auto Err = ChangedCodeOrErr.takeError()) {
+  llvm::errs() << "Change failed: " << llvm::toString(std::move(Err))
+   << "\n";
+  return None;
+}
+return *ChangedCodeOrErr;
+  }
+
+  void testRule(RewriteRule Rule, StringRef Input, StringRef Expected) {
+Transformer T(std::move(Rule),
+  [this](const AtomicChange &C) { Changes.push_back(C); });
+T.registerMatchers(&MatchFinder);
+compareSnippets(Expected, rewrite(Input));
+  }
+
+  clang::ast_matchers::MatchFinder MatchFinder;
+  AtomicChanges Changes;
+
+private:
+  FileContentMappings FileContents = {{"header.h", ""}};
+};
+
+class TransformerTest : public ClangRefactoringTestBase {
+pro

[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-02 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

I've removed the dependency on NodeIds given our (off thread) discussions 
regarding their overall value and appropriateness in clang::tooling vs 
clang::ast_matchers. PTAL.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376



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


[PATCH] D59376: [LibTooling] Add Transformer, a library for source-to-source transformations.

2019-04-03 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC357576: [LibTooling] Add Transformer, a library for 
source-to-source transformations. (authored by ymandel, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D59376?vs=193306&id=193480#toc

Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59376/new/

https://reviews.llvm.org/D59376

Files:
  include/clang/Tooling/Refactoring/Transformer.h
  lib/Tooling/Refactoring/CMakeLists.txt
  lib/Tooling/Refactoring/Transformer.cpp
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/TransformerTest.cpp

Index: lib/Tooling/Refactoring/Transformer.cpp
===
--- lib/Tooling/Refactoring/Transformer.cpp
+++ lib/Tooling/Refactoring/Transformer.cpp
@@ -0,0 +1,204 @@
+//===--- Transformer.cpp - Transformer library implementation ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Transformer.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include 
+#include 
+#include 
+#include 
+
+using namespace clang;
+using namespace tooling;
+
+using ast_matchers::MatchFinder;
+using ast_type_traits::ASTNodeKind;
+using ast_type_traits::DynTypedNode;
+using llvm::Error;
+using llvm::Expected;
+using llvm::Optional;
+using llvm::StringError;
+using llvm::StringRef;
+using llvm::Twine;
+
+using MatchResult = MatchFinder::MatchResult;
+
+// Did the text at this location originate in a macro definition (aka. body)?
+// For example,
+//
+//   #define NESTED(x) x
+//   #define MACRO(y) { int y  = NESTED(3); }
+//   if (true) MACRO(foo)
+//
+// The if statement expands to
+//
+//   if (true) { int foo = 3; }
+//   ^ ^
+//   Loc1  Loc2
+//
+// For SourceManager SM, SM.isMacroArgExpansion(Loc1) and
+// SM.isMacroArgExpansion(Loc2) are both true, but isOriginMacroBody(sm, Loc1)
+// is false, because "foo" originated in the source file (as an argument to a
+// macro), whereas isOriginMacroBody(SM, Loc2) is true, because "3" originated
+// in the definition of MACRO.
+static bool isOriginMacroBody(const clang::SourceManager &SM,
+  clang::SourceLocation Loc) {
+  while (Loc.isMacroID()) {
+if (SM.isMacroBodyExpansion(Loc))
+  return true;
+// Otherwise, it must be in an argument, so we continue searching up the
+// invocation stack. getImmediateMacroCallerLoc() gives the location of the
+// argument text, inside the call text.
+Loc = SM.getImmediateMacroCallerLoc(Loc);
+  }
+  return false;
+}
+
+static llvm::Error invalidArgumentError(Twine Message) {
+  return llvm::make_error(llvm::errc::invalid_argument, Message);
+}
+
+static llvm::Error typeError(StringRef Id, const ASTNodeKind &Kind,
+ Twine Message) {
+  return invalidArgumentError(
+  Message + " (node id=" + Id + " kind=" + Kind.asStringRef() + ")");
+}
+
+static llvm::Error missingPropertyError(StringRef Id, Twine Description,
+StringRef Property) {
+  return invalidArgumentError(Description + " requires property '" + Property +
+  "' (node id=" + Id + ")");
+}
+
+static Expected
+getTargetRange(StringRef Target, const DynTypedNode &Node, ASTNodeKind Kind,
+   NodePart TargetPart, ASTContext &Context) {
+  switch (TargetPart) {
+  case NodePart::Node: {
+// For non-expression statements, associate any trailing semicolon with the
+// statement text.  However, if the target was intended as an expression (as
+// indicated by its kind) then we do not associate any trailing semicolon
+// with it.  We only associate the exact expression text.
+if (Node.get() != nullptr) {
+  auto ExprKind = ASTNodeKind::getFromNodeKind();
+  if (!ExprKind.isBaseOf(Kind))
+return fixit::getExtendedRange(Node, tok::TokenKind::semi, Context);
+}
+return CharSourceRange::getTokenRange(Node.getSourceRange());
+  }
+  case NodePart::Member:
+if (auto *M = Node.get())
+  return CharSourceRange::getTokenRange(
+  M->getMemberNameInfo().getSourceRange());
+return typeError(Target, Node.getNodeKind(),
+

[PATCH] D59371: [LibTooling] Add Stencil library for format-string style codegen.

2019-04-03 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 193496.
ymandel added a comment.

Sever dependency on NodeId and some general cleanup.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59371/new/

https://reviews.llvm.org/D59371

Files:
  clang/include/clang/Tooling/Refactoring/Stencil.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Stencil.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/StencilTest.cpp

Index: clang/unittests/Tooling/StencilTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/StencilTest.cpp
@@ -0,0 +1,204 @@
+//===- unittest/Tooling/StencilTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace ast_matchers;
+
+namespace {
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using MatchResult = MatchFinder::MatchResult;
+using tooling::stencil_generators::node;
+using tooling::stencil_generators::snode;
+using tooling::stencil_generators::text;
+
+// In tests, we can't directly match on llvm::Expected since its accessors
+// mutate the object. So, we collapse it to an Optional.
+static llvm::Optional toOptional(llvm::Expected V) {
+  if (V)
+return *V;
+  ADD_FAILURE() << "Losing error in conversion to IsSomething: "
+<< llvm::toString(V.takeError());
+  return llvm::None;
+}
+
+// A very simple matcher for llvm::Optional values.
+MATCHER_P(IsSomething, ValueMatcher, "") {
+  if (!arg)
+return false;
+  return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener);
+}
+
+// Create a valid translation-unit from a statement.
+static std::string wrapSnippet(llvm::Twine StatementCode) {
+  return ("auto stencil_test_snippet = []{" + StatementCode + "};").str();
+}
+
+static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {
+  return varDecl(hasName("stencil_test_snippet"),
+ hasDescendant(compoundStmt(hasAnySubstatement(Matcher;
+}
+
+struct TestMatch {
+  // The AST unit from which `result` is built. We bundle it because it backs
+  // the result. Users are not expected to access it.
+  std::unique_ptr AstUnit;
+  // The result to use in the test. References `ast_unit`.
+  MatchResult Result;
+};
+
+// Matches `Matcher` against the statement `StatementCode` and returns the
+// result. Handles putting the statement inside a function and modifying the
+// matcher correspondingly. `Matcher` should match `StatementCode` exactly --
+// that is, produce exactly one match.
+static llvm::Optional matchStmt(llvm::Twine StatementCode,
+   StatementMatcher Matcher) {
+  auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
+  if (AstUnit == nullptr) {
+ADD_FAILURE() << "AST construction failed";
+return llvm::None;
+  }
+  ASTContext &Context = AstUnit->getASTContext();
+  auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context);
+  // We expect a single, exact match for the statement.
+  if (Matches.size() != 1) {
+ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
+return llvm::None;
+  }
+  return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
+}
+
+class StencilTest : public ::testing::Test {
+protected:
+  // Verifies that the given stencil fails when evaluated on a valid match
+  // result. Binds a statement to "stmt", a (non-member) ctor-initializer to
+  // "init", an expression to "expr" and a (nameless) declaration to "decl".
+  void testError(const Stencil &Stencil,
+ ::testing::Matcher Matcher) {
+const std::string Snippet = R"cc(
+  struct A {};
+  class F : public A {
+   public:
+F(int) {}
+  };
+  F(1);
+)cc";
+auto StmtMatch = matchStmt(
+Snippet,
+stmt(hasDescendant(
+ cxxConstructExpr(
+ hasDeclaration(decl(hasDescendant(cxxCtorInitializer(
+   isBaseInitializer())
+   .bind("init")))
+.bind("decl")))
+ .bind("expr")))
+.bind("stmt"));
+ASSERT_TRUE(StmtMatch);
+if (auto ResultOrErr = Stencil.eval(StmtMatch->Result)) {
+  ADD_FAILURE() << "Expected failure but succeeded: " 

[PATCH] D59371: [LibTooling] Add Stencil library for format-string style codegen.

2019-04-03 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 193498.
ymandel added a comment.

Remove noisy default-defined constructors/operators


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59371/new/

https://reviews.llvm.org/D59371

Files:
  clang/include/clang/Tooling/Refactoring/Stencil.h
  clang/lib/Tooling/Refactoring/CMakeLists.txt
  clang/lib/Tooling/Refactoring/Stencil.cpp
  clang/unittests/Tooling/CMakeLists.txt
  clang/unittests/Tooling/StencilTest.cpp

Index: clang/unittests/Tooling/StencilTest.cpp
===
--- /dev/null
+++ clang/unittests/Tooling/StencilTest.cpp
@@ -0,0 +1,204 @@
+//===- unittest/Tooling/StencilTest.cpp ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/Tooling/Refactoring/Stencil.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace ast_matchers;
+
+namespace {
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using MatchResult = MatchFinder::MatchResult;
+using tooling::stencil_generators::node;
+using tooling::stencil_generators::snode;
+using tooling::stencil_generators::text;
+
+// In tests, we can't directly match on llvm::Expected since its accessors
+// mutate the object. So, we collapse it to an Optional.
+static llvm::Optional toOptional(llvm::Expected V) {
+  if (V)
+return *V;
+  ADD_FAILURE() << "Losing error in conversion to IsSomething: "
+<< llvm::toString(V.takeError());
+  return llvm::None;
+}
+
+// A very simple matcher for llvm::Optional values.
+MATCHER_P(IsSomething, ValueMatcher, "") {
+  if (!arg)
+return false;
+  return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener);
+}
+
+// Create a valid translation-unit from a statement.
+static std::string wrapSnippet(llvm::Twine StatementCode) {
+  return ("auto stencil_test_snippet = []{" + StatementCode + "};").str();
+}
+
+static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {
+  return varDecl(hasName("stencil_test_snippet"),
+ hasDescendant(compoundStmt(hasAnySubstatement(Matcher;
+}
+
+struct TestMatch {
+  // The AST unit from which `result` is built. We bundle it because it backs
+  // the result. Users are not expected to access it.
+  std::unique_ptr AstUnit;
+  // The result to use in the test. References `ast_unit`.
+  MatchResult Result;
+};
+
+// Matches `Matcher` against the statement `StatementCode` and returns the
+// result. Handles putting the statement inside a function and modifying the
+// matcher correspondingly. `Matcher` should match `StatementCode` exactly --
+// that is, produce exactly one match.
+static llvm::Optional matchStmt(llvm::Twine StatementCode,
+   StatementMatcher Matcher) {
+  auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
+  if (AstUnit == nullptr) {
+ADD_FAILURE() << "AST construction failed";
+return llvm::None;
+  }
+  ASTContext &Context = AstUnit->getASTContext();
+  auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context);
+  // We expect a single, exact match for the statement.
+  if (Matches.size() != 1) {
+ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
+return llvm::None;
+  }
+  return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
+}
+
+class StencilTest : public ::testing::Test {
+protected:
+  // Verifies that the given stencil fails when evaluated on a valid match
+  // result. Binds a statement to "stmt", a (non-member) ctor-initializer to
+  // "init", an expression to "expr" and a (nameless) declaration to "decl".
+  void testError(const Stencil &Stencil,
+ ::testing::Matcher Matcher) {
+const std::string Snippet = R"cc(
+  struct A {};
+  class F : public A {
+   public:
+F(int) {}
+  };
+  F(1);
+)cc";
+auto StmtMatch = matchStmt(
+Snippet,
+stmt(hasDescendant(
+ cxxConstructExpr(
+ hasDeclaration(decl(hasDescendant(cxxCtorInitializer(
+   isBaseInitializer())
+   .bind("init")))
+.bind("decl")))
+ .bind("expr")))
+.bind("stmt"));
+ASSERT_TRUE(StmtMatch);
+if (auto ResultOrErr = Stencil.eval(StmtMatch->Result)) {
+  ADD_FAILURE() << "Expected failure but succeeded: " <

  1   2   3   4   5   6   7   8   9   10   >