shuaiwang updated this revision to Diff 142730.
shuaiwang added a comment.

Updated mostly `isModified()`.
I'd like to mostly demonstrate that `isModified()` works given that there's 
https://reviews.llvm.org/D45444 and we'd like to merge.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/readability/UnmodifiedNonConstVariableCheck.cpp
  clang-tidy/readability/UnmodifiedNonConstVariableCheck.h
  clang-tidy/utils/ASTUtils.cpp
  clang-tidy/utils/ASTUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-unmodified-non-const-variable.rst
  test/clang-tidy/readability-unmodified-non-const-variable.cpp

Index: test/clang-tidy/readability-unmodified-non-const-variable.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-unmodified-non-const-variable.cpp
@@ -0,0 +1,459 @@
+// RUN: %check_clang_tidy %s readability-unmodified-non-const-variable %t \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: readability-unmodified-non-const-variable.MaxPerTranslationUnit,\
+// RUN:   value: '100000'}]}" --
+
+template <class T1, class T2>
+struct pair {
+  T1 first;
+  T2 second;
+
+  void set(T1 f, T2 s);
+};
+
+void touch(int&);
+void touch(int*);
+
+int simple() {
+  int x = 10;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it [readability-unmodified-non-const-variable]
+  return x + 1;
+}
+
+void modified1() {
+  int x = 10;
+  touch(x);
+}
+
+void modified2() {
+  int x = 10;
+  touch(&x);
+}
+
+int modified3() {
+  int x = 10;
+  x += 20;
+  return x;
+}
+
+int callNonConstMember() {
+  pair<int, int> p1;
+  p1.set(10, 20);
+  return p1.first + p1.second;
+}
+
+int followNonConstReference() {
+  pair<int, int> p1;
+  auto& p2 = p1;
+  p2.first = 10;
+  return p1.first;
+}
+
+bool global;
+char np_global = 0; // globals can't be known to be const
+
+namespace foo {
+int scoped;
+float np_scoped = 1; // namespace variables are like globals
+} // namespace foo
+
+void some_function(double, wchar_t);
+
+void some_function(double np_arg0, wchar_t np_arg1) {
+  int p_local0 = 2;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+
+  const int np_local1 = 42;
+
+  unsigned int np_local2 = 3;
+  np_local2 <<= 4;
+
+  int np_local3 = 4;
+  ++np_local3;
+  int np_local4 = 4;
+  np_local4++;
+
+  int np_local5 = 4;
+  --np_local5;
+  int np_local6 = 4;
+  np_local6--;
+}
+
+void some_lambda_environment_capture_all_by_reference(double np_arg0) {
+  int np_local0 = 0;
+  int p_local0 = 1;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+
+  const int np_local3 = 2;
+
+  // Capturing all variables by reference prohibits making them const.
+  [&]() { ++np_local0; };
+
+  int p_local1 = 0;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+}
+
+void some_lambda_environment_capture_all_by_value(double np_arg0) {
+  int np_local0 = 0;
+  int p_local0 = 1;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+
+  const int np_local2 = 2;
+
+  // Capturing by value has no influence on them.
+  [=]() { (void)p_local0; };
+
+  np_local0 += 10;
+}
+
+void function_inout_pointer(int *inout);
+void function_in_pointer(const int *in);
+
+void some_pointer_taking(int *out) {
+  int np_local0 = 42;
+  const int *const p0_np_local0 = &np_local0;
+  int *const p1_np_local0 = &np_local0;
+
+  int np_local1 = 42;
+  function_inout_pointer(&np_local1);
+
+  // Prevents const.
+  int np_local2 = 42;
+  out = &np_local2; // This returns and invalid address, its just about the AST
+
+  int p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+  const int *const p0_p_local0 = &p_local0;
+
+  int p_local1 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+  function_in_pointer(&p_local1);
+}
+
+void function_inout_ref(int &inout);
+void function_in_ref(const int &in);
+
+void some_reference_taking() {
+  int np_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+  const int &r0_np_local0 = np_local0;
+  int &r1_np_local0 = np_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: declaring a non-const variable but never modified it
+  const int &r2_np_local0 = r1_np_local0;
+
+  int np_local1 = 42;
+  function_inout_ref(np_local1);
+
+  int p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+  const int &r0_p_local0 = p_local0;
+
+  int p_local1 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+  function_in_ref(p_local1);
+}
+
+double *non_const_pointer_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  double np_local0 = 24.4;
+
+  return &np_local0;
+}
+
+const double *const_pointer_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  double p_local1 = 24.4;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  return &p_local1;
+}
+
+double &non_const_ref_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  double np_local0 = 42.42;
+  return np_local0;
+}
+
+const double &const_ref_return() {
+  double p_local0 = 0.0;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  double p_local1 = 24.4;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  return p_local1;
+}
+
+double *&return_non_const_pointer_ref() {
+  double *np_local0 = nullptr;
+  return np_local0;
+}
+
+void overloaded_arguments(const int &in);
+void overloaded_arguments(int &inout);
+void overloaded_arguments(const int *in);
+void overloaded_arguments(int *inout);
+
+void function_calling() {
+  int np_local0 = 42;
+  overloaded_arguments(np_local0);
+
+  const int np_local1 = 42;
+  overloaded_arguments(np_local1);
+
+  int np_local2 = 42;
+  overloaded_arguments(&np_local2);
+
+  const int np_local3 = 42;
+  overloaded_arguments(&np_local3);
+}
+
+template <typename T>
+void define_locals(T np_arg0, T &np_arg1, int np_arg2) {
+  T np_local0 = 0;
+  np_local0 += np_arg0 * np_arg1;
+
+  T np_local1 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:5: warning: declaring a non-const variable but never modified it
+  np_local0 += np_local1;
+
+  // Used as argument to an overloaded function with const and non-const.
+  T np_local2 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:5: warning: declaring a non-const variable but never modified it
+  overloaded_arguments(np_local2);
+
+  int np_local4 = 42;
+  // non-template values are ok still.
+  int p_local0 = 42;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it
+  np_local4 += p_local0;
+}
+
+void template_instantiation() {
+  const int np_local0 = 42;
+  int np_local1 = 42;
+
+  define_locals(np_local0, np_local1, np_local0);
+  define_locals(np_local1, np_local1, np_local1);
+}
+
+struct ConstNonConstClass {
+  ConstNonConstClass();
+  ConstNonConstClass(double &np_local0);
+  double nonConstMethod() {}
+  double constMethod() const {}
+  double modifyingMethod(double &np_arg0) const;
+
+  double NonConstMember;
+  const double ConstMember;
+
+  double &NonConstMemberRef;
+  const double &ConstMemberRef;
+
+  double *NonConstMemberPtr;
+  const double *ConstMemberPtr;
+};
+
+void direct_class_access() {
+  ConstNonConstClass np_local0;
+
+  np_local0.constMethod();
+  np_local0.nonConstMethod();
+
+  ConstNonConstClass p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it
+  p_local0.constMethod();
+
+  ConstNonConstClass p_local1;
+  // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it
+  double np_local1;
+  p_local1.modifyingMethod(np_local1);
+
+  double np_local2;
+  ConstNonConstClass p_local2(np_local2);
+  // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it
+
+  ConstNonConstClass np_local3;
+  np_local3.NonConstMember = 42.;
+
+  ConstNonConstClass np_local4;
+  np_local4.NonConstMemberRef = 42.;
+
+  ConstNonConstClass np_local5;
+  // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it
+  *np_local5.NonConstMemberPtr = 42.;
+
+  ConstNonConstClass p_local3;
+  // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it
+  const double val0 = p_local3.NonConstMember;
+  const double val1 = p_local3.NonConstMemberRef;
+  const double val2 = *p_local3.NonConstMemberPtr;
+}
+
+struct OperatorsAsConstAsPossible {
+  OperatorsAsConstAsPossible &operator+=(const OperatorsAsConstAsPossible &rhs);
+  OperatorsAsConstAsPossible operator+(const OperatorsAsConstAsPossible &rhs) const;
+};
+
+struct NonConstOperators {
+};
+NonConstOperators operator+(NonConstOperators &lhs, NonConstOperators &rhs);
+NonConstOperators operator-(NonConstOperators lhs, NonConstOperators rhs);
+
+void internal_operator_calls() {
+  OperatorsAsConstAsPossible np_local0;
+  OperatorsAsConstAsPossible np_local1;
+  OperatorsAsConstAsPossible p_local0;
+  // CHECK-MESSAGES: [[@LINE-1]]:30: warning: declaring a non-const variable but never modified it
+  OperatorsAsConstAsPossible p_local1;
+  // CHECK-MESSAGES: [[@LINE-1]]:30: warning: declaring a non-const variable but never modified it
+
+  np_local0 += p_local0;
+  np_local1 = p_local0 + p_local1;
+
+  NonConstOperators np_local2;
+  NonConstOperators np_local3;
+  NonConstOperators np_local4;
+
+  np_local2 = np_local3 + np_local4;
+
+  NonConstOperators p_local2;
+  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: declaring a non-const variable but never modified it
+  NonConstOperators p_local3 = p_local2 - p_local2;
+  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: declaring a non-const variable but never modified it
+}
+
+struct MyVector {
+  double *begin();
+  const double *begin() const;
+
+  double *end();
+  const double *end() const;
+
+  double &operator[](int index);
+  double operator[](int index) const;
+
+  double values[100];
+};
+
+void vector_usage() {
+  double np_local0[10];
+  np_local0[5] = 42.;
+
+  MyVector np_local1;
+  np_local1[5] = 42.;
+
+  double p_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  double p_local1 = p_local0[5];
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+
+  // The following subscript calls suprisingly choose the non-const operator
+  // version.
+  MyVector np_local2;
+  double p_local2 = np_local2[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+
+  MyVector np_local3;
+  const double np_local4 = np_local3[42];
+
+  // This subscript results in const overloaded operator.
+  const MyVector np_local5{};
+  double p_local3 = np_local5[42];
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+}
+
+void const_handle(const double &np_local0);
+void const_handle(const double *np_local0);
+
+void non_const_handle(double &np_local0);
+void non_const_handle(double *np_local0);
+
+void handle_from_array() {
+  // Non-const handle from non-const array forbids declaring the array as const
+  double np_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double *p_local0 = &np_local0[1];
+  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: declaring a non-const variable but never modified it
+
+  double np_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  double &non_const_ref = np_local1[1];
+  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: declaring a non-const variable but never modified it
+
+  double np_local2[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  double *np_local3;
+  np_local3 = &np_local2[5];
+
+  double np_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  non_const_handle(np_local4[2]);
+  double np_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  non_const_handle(&np_local5[2]);
+
+  // Constant handles are ok
+  double p_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  const double *p_local2 = &p_local1[2];
+  // CHECK-MESSAGES: [[@LINE-1]]:17: warning: declaring a non-const variable but never modified it
+
+  double p_local3[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  const double &const_ref = p_local3[2];
+
+  double p_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  const double *const_ptr;
+  const_ptr = &p_local4[2];
+
+  double p_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  const_handle(p_local5[2]);
+  double p_local6[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it
+  const_handle(&p_local6[2]);
+}
+
+void range_for() {
+  int np_local0[2] = {1, 2};
+  for (int &non_const_ref : np_local0) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: declaring a non-const variable but never modified it
+  }
+
+  int np_local1[2] = {1, 2};
+  for (auto &non_const_ref : np_local1) {
+    // CHECK-MESSAGES: [[@LINE-1]]:14: warning: declaring a non-const variable but never modified it
+  }
+
+  int np_local2[2] = {1, 2};
+  for (auto &&non_const_ref : np_local2) {
+    // CHECK-MESSAGES: [[@LINE-1]]:15: warning: declaring a non-const variable but never modified it
+  }
+
+  int *np_local3[2] = {&np_local0[0], &np_local0[1]};
+  for (int *non_const_ptr : np_local3) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: declaring a non-const variable but never modified it
+  }
+
+  int *np_local4[2] = {&np_local0[0], &np_local0[1]};
+  for (auto *non_const_ptr : np_local4) {
+    // CHECK-MESSAGES: [[@LINE-1]]:14: warning: declaring a non-const variable but never modified it
+  }
+
+  int p_local0[2] = {1, 2};
+  for (int value : p_local0) {
+    // CHECK-MESSAGES: [[@LINE-1]]:12: warning: declaring a non-const variable but never modified it
+  }
+
+  int p_local1[2] = {1, 2};
+  for (const int &const_ref : p_local1) {
+  }
+
+  int *p_local2[2] = {&np_local0[0], &np_local0[1]};
+  for (const int *con_ptr : p_local2) {
+    // CHECK-MESSAGES: [[@LINE-1]]:19: warning: declaring a non-const variable but never modified it
+  }
+
+  int *p_local3[2] = {nullptr, nullptr};
+  for (const auto *con_ptr : p_local3) {
+    // CHECK-MESSAGES: [[@LINE-1]]:20: warning: declaring a non-const variable but never modified it
+  }
+}
Index: docs/clang-tidy/checks/readability-unmodified-non-const-variable.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/readability-unmodified-non-const-variable.rst
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - readability-unmodified-non-const-variable
+
+readability-unmodified-non-const-variable
+=========================================
+
+Finds declarations of non-const variables that never get modified.
+
+For example:
+
+.. code-block:: c++
+
+  int simple() {
+    int x = 10; // x is declared as non-const but is never modified.
+    return x + 1;
+  }
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -91,8 +91,8 @@
    cppcoreguidelines-pro-type-vararg
    cppcoreguidelines-slicing
    cppcoreguidelines-special-member-functions
-   fuchsia-header-anon-namespaces (redirects to google-build-namespaces) <fuchsia-header-anon-namespaces>
    fuchsia-default-arguments
+   fuchsia-header-anon-namespaces (redirects to google-build-namespaces) <fuchsia-header-anon-namespaces>
    fuchsia-multiple-inheritance
    fuchsia-overloaded-operator
    fuchsia-statically-constructed-objects
@@ -229,4 +229,5 @@
    readability-static-definition-in-anonymous-namespace
    readability-string-compare
    readability-uniqueptr-delete-release
+   readability-unmodified-non-const-variable
    zircon-temporary-objects
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -57,6 +57,11 @@
 Improvements to clang-tidy
 --------------------------
 
+- New :doc:`readability-unmodified-non-const-variable
+  <clang-tidy/checks/readability-unmodified-non-const-variable>` check
+
+  Finds declarations of non-const variables that never get modified.
+
 - New module `abseil` for checks related to the `Abseil <https://abseil.io>`_
   library.
 
Index: clang-tidy/utils/ASTUtils.h
===================================================================
--- clang-tidy/utils/ASTUtils.h
+++ clang-tidy/utils/ASTUtils.h
@@ -27,6 +27,10 @@
 bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
                                 const LangOptions &LangOpts,
                                 StringRef FlagName);
+
+// Checks whether `Exp` is (potentially) modified within `Stm`.
+bool isModified(const Expr& Exp, const Stmt& Stm, ASTContext* Context);
+
 } // namespace utils
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/utils/ASTUtils.cpp
===================================================================
--- clang-tidy/utils/ASTUtils.cpp
+++ clang-tidy/utils/ASTUtils.cpp
@@ -67,6 +67,116 @@
   return true;
 }
 
+namespace {
+class MatchCallbackAdaptor : public MatchFinder::MatchCallback {
+public:
+  explicit MatchCallbackAdaptor(
+      std::function<void(const MatchFinder::MatchResult &)> Func)
+      : Func(std::move(Func)) {}
+  void run(const MatchFinder::MatchResult &Result) override { Func(Result); }
+
+private:
+  std::function<void(const MatchFinder::MatchResult &)> Func;
+};
+} // namespace
+
+bool isModified(const Expr &Exp, const Stmt &Stm, ASTContext *Context) {
+  // LHS of any assignment operators.
+  const auto AsAssignmentLhs =
+      binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(&Exp)));
+
+  // Operand of increment/decrement operators.
+  const auto AsIncDecOperand =
+      unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
+                    hasUnaryOperand(equalsNode(&Exp)));
+
+  // Invoking non-const member function.
+  const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
+  const auto AsNonConstThis = expr(
+      anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(&Exp))),
+            cxxOperatorCallExpr(callee(NonConstMethod),
+                                hasArgument(0, equalsNode(&Exp)))));
+
+  // Used as non-const-ref argument when calling a function.
+  const auto NonConstRefType =
+      referenceType(pointee(unless(isConstQualified())));
+  const auto NonConstRefParam = forEachArgumentWithParam(
+      equalsNode(&Exp), parmVarDecl(hasType(qualType(NonConstRefType))));
+  const auto AsNonConstRefArg =
+      anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam));
+
+  // Returned as non-const-ref.
+  // If we're returning 'Exp' directly then it's returned as non-const-ref.
+  // Otherwise there will be an ImplicitCastExpr <LValueToRValue> in between.
+  const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(&Exp)));
+
+  // Taking address of 'Exp'.
+  // In theory we can follow the pointer and see whether the pointer itself
+  // escaped 'Stm', or is dereferenced and the dereferencing expression is
+  // modified. This is left for future improvements.
+  const auto AsAmpersandOperand =
+      unaryOperator(hasOperatorName("&"),
+                    unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
+                    hasUnaryOperand(equalsNode(&Exp)));
+  const auto AsPointerFromArrayDecay =
+      castExpr(hasCastKind(CK_ArrayToPointerDecay),
+               unless(hasParent(arraySubscriptExpr())), has(equalsNode(&Exp)));
+
+  // Check whether 'Exp' is directly modified as a whole.
+  MatchFinder Finder;
+  bool HasMatch = false;
+  MatchCallbackAdaptor Callback(
+      [&HasMatch](const MatchFinder::MatchResult &) { HasMatch = true; });
+  Finder.addMatcher(
+      findAll(expr(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
+                         AsNonConstRefArg, AsAmpersandOperand,
+                         AsPointerFromArrayDecay))),
+      &Callback);
+  Finder.addMatcher(findAll(AsNonConstRefReturn), &Callback);
+  Finder.match(Stm, *Context);
+  if (HasMatch)
+    return true;
+
+  // Check whether any member of 'Exp' is modified.
+  const auto MemberExprs = match(
+      findAll(memberExpr(hasObjectExpression(equalsNode(&Exp))).bind("expr")),
+      Stm, *Context);
+  for (const auto &Node : MemberExprs) {
+    if (isModified(*Node.getNodeAs<Expr>("expr"), Stm, Context))
+      return true;
+  }
+
+  // In the case of 'Exp' being an array, check whether any element is modified.
+  const auto SubscriptExprs = match(
+      findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(&Exp))))
+                  .bind("expr")),
+      Stm, *Context);
+  for (const auto &Node : SubscriptExprs) {
+    if (isModified(*Node.getNodeAs<Expr>("expr"), Stm, Context))
+      return true;
+  }
+
+  // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
+  const auto Decls = match(
+      stmt(forEachDescendant(
+          varDecl(hasType(referenceType(pointee(unless(isConstQualified())))),
+                  hasInitializer(equalsNode(&Exp)))
+              .bind("decl"))),
+      Stm, *Context);
+  for (const auto &DeclNode : Decls) {
+    const auto Exprs = match(
+        findAll(declRefExpr(to(equalsNode(DeclNode.getNodeAs<Decl>("decl"))))
+                    .bind("expr")),
+        Stm, *Context);
+    for (const auto &ExprNode : Exprs) {
+      if (isModified(*ExprNode.getNodeAs<Expr>("expr"), Stm, Context))
+        return true;
+    }
+  }
+
+  return false;
+}
+
 } // namespace utils
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/readability/UnmodifiedNonConstVariableCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/readability/UnmodifiedNonConstVariableCheck.h
@@ -0,0 +1,47 @@
+//===--- UnmodifiedNonConstVariableCheck.h - clang-tidy----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNMODIFIEDNONCONSTVARIABLECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNMODIFIEDNONCONSTVARIABLECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds declarations of non-const variables that never get modified.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-unmodified-non-const-variable.html
+class UnmodifiedNonConstVariableCheck : public ClangTidyCheck {
+public:
+  UnmodifiedNonConstVariableCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        MaxPerTranslationUnit(Options.get("MaxPerTranslationUnit", 50)),
+        IgnorePointers(Options.get("IgnorePointers", false)) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onStartOfTranslationUnit() override { Count = 0; }
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
+    Options.store(Opts, "MaxPerTranslationUnit", MaxPerTranslationUnit);
+    Options.store(Opts, "IgnorePointers", IgnorePointers);
+  }
+
+private:
+  const int MaxPerTranslationUnit;
+  const bool IgnorePointers;
+  int Count;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNMODIFIEDNONCONSTVARIABLECHECK_H
Index: clang-tidy/readability/UnmodifiedNonConstVariableCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/readability/UnmodifiedNonConstVariableCheck.cpp
@@ -0,0 +1,60 @@
+//===--- UnmodifiedNonConstVariableCheck.cpp - clang-tidy------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnmodifiedNonConstVariableCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void UnmodifiedNonConstVariableCheck::registerMatchers(MatchFinder *Finder) {
+  const auto ConstTypes = qualType(
+      anyOf(isConstQualified(), referenceType(pointee(isConstQualified()))));
+  const auto TypeFilter = hasType(
+      qualType(unless(anyOf(substTemplateTypeParmType(),
+                            hasDescendant(substTemplateTypeParmType()))),
+               !IgnorePointers ? ConstTypes
+                               : qualType(anyOf(ConstTypes, pointerType()))));
+  const auto DeclFilter =
+      anyOf(parmVarDecl(), isImplicit(), isConstexpr(), TypeFilter);
+  Finder->addMatcher(
+      varDecl(unless(DeclFilter), hasAncestor(compoundStmt().bind("stmt")))
+          .bind("decl"),
+      this);
+}
+
+void UnmodifiedNonConstVariableCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (++Count > MaxPerTranslationUnit)
+    return;
+
+  const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("decl");
+  if (Decl->Decl::getLocStart().isMacroID())
+    return;
+  const auto *Compound = Result.Nodes.getNodeAs<Stmt>("stmt");
+  const auto Exprs =
+      match(findAll(declRefExpr(to(varDecl(equalsNode(Decl)))).bind("expr")),
+            *Compound, *Result.Context);
+  for (const auto &Node : Exprs) {
+    if (utils::isModified(*Node.getNodeAs<Expr>("expr"), *Compound,
+                          Result.Context))
+      return;
+  }
+  diag(Decl->getLocation(),
+       "declaring a non-const variable but never modified it");
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -36,6 +36,7 @@
 #include "StaticDefinitionInAnonymousNamespaceCheck.h"
 #include "StringCompareCheck.h"
 #include "UniqueptrDeleteReleaseCheck.h"
+#include "UnmodifiedNonConstVariableCheck.h"
 
 namespace clang {
 namespace tidy {
@@ -78,6 +79,8 @@
         "readability-static-definition-in-anonymous-namespace");
     CheckFactories.registerCheck<StringCompareCheck>(
         "readability-string-compare");
+    CheckFactories.registerCheck<UnmodifiedNonConstVariableCheck>(
+        "readability-unmodified-non-const-variable");
     CheckFactories.registerCheck<readability::NamedParameterCheck>(
         "readability-named-parameter");
     CheckFactories.registerCheck<NonConstParameterCheck>(
Index: clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tidy/readability/CMakeLists.txt
+++ clang-tidy/readability/CMakeLists.txt
@@ -29,6 +29,7 @@
   StaticDefinitionInAnonymousNamespaceCheck.cpp
   StringCompareCheck.cpp
   UniqueptrDeleteReleaseCheck.cpp
+  UnmodifiedNonConstVariableCheck.cpp
 
   LINK_LIBS
   clangAST
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to