[clang-tidy] Add non-constant references in function parameters check.

Summary: This is implemented originally by Alexander Kornienko.

Reviewers: alexfh

Subscribers: cfe-commits


Patch by Haojian Wu!

Differential Revision: http://reviews.llvm.org/D16717

llvm-svn: 259530
diff --git a/clang-tools-extra/clang-tidy/google/CMakeLists.txt b/clang-tools-extra/clang-tidy/google/CMakeLists.txt
index 3597613..387e121 100644
--- a/clang-tools-extra/clang-tidy/google/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/google/CMakeLists.txt
@@ -8,6 +8,7 @@
   GoogleTidyModule.cpp
   IntegerTypesCheck.cpp
   MemsetZeroLengthCheck.cpp
+  NonConstReferences.cpp
   OverloadedUnaryAndCheck.cpp
   StringReferenceMemberCheck.cpp
   TodoCommentCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp b/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
index 395de36..1281e15 100644
--- a/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp
@@ -21,6 +21,7 @@
 #include "IntegerTypesCheck.h"
 #include "MemsetZeroLengthCheck.h"
 #include "OverloadedUnaryAndCheck.h"
+#include "NonConstReferences.h"
 #include "StringReferenceMemberCheck.h"
 #include "TodoCommentCheck.h"
 #include "UnnamedNamespaceInHeaderCheck.h"
@@ -47,6 +48,8 @@
         "google-runtime-int");
     CheckFactories.registerCheck<runtime::OverloadedUnaryAndCheck>(
         "google-runtime-operator");
+    CheckFactories.registerCheck<runtime::NonConstReferences>(
+        "google-runtime-references");
     CheckFactories.registerCheck<runtime::StringReferenceMemberCheck>(
         "google-runtime-member-string-references");
     CheckFactories.registerCheck<runtime::MemsetZeroLengthCheck>(
diff --git a/clang-tools-extra/clang-tidy/google/NonConstReferences.cpp b/clang-tools-extra/clang-tidy/google/NonConstReferences.cpp
new file mode 100644
index 0000000..a56d937
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/google/NonConstReferences.cpp
@@ -0,0 +1,119 @@
+//===--- NonConstReferences.cpp - clang-tidy --------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonConstReferences.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+void NonConstReferences::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      parmVarDecl(
+          unless(isInstantiated()),
+          hasType(references(
+              qualType(unless(isConstQualified())).bind("referenced_type"))),
+          unless(hasType(rValueReferenceType())))
+          .bind("param"),
+      this);
+}
+
+void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
+  const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+  const auto *Function =
+      dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
+
+  if (Function == nullptr || Function->isImplicit())
+    return;
+
+  if (!Function->isCanonicalDecl())
+    return;
+
+  if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Function)) {
+    // Don't warn on implementations of an interface using references.
+    if (Method->begin_overridden_methods() != Method->end_overridden_methods())
+      return;
+    // Don't warn on lambdas, as they frequently have to conform to the
+    // interface defined elsewhere.
+    if (Method->getParent()->isLambda())
+      return;
+  }
+
+  auto ReferencedType = *Result.Nodes.getNodeAs<QualType>("referenced_type");
+  // Don't warn on function references, they shouldn't be constant.
+  if (ReferencedType->isFunctionProtoType())
+    return;
+
+  // Don't warn on dependent types in templates.
+  if (ReferencedType->isDependentType())
+    return;
+
+  if (Function->isOverloadedOperator()) {
+    switch (Function->getOverloadedOperator()) {
+      case clang::OO_LessLess:
+      case clang::OO_PlusPlus:
+      case clang::OO_MinusMinus:
+      case clang::OO_PlusEqual:
+      case clang::OO_MinusEqual:
+      case clang::OO_StarEqual:
+      case clang::OO_SlashEqual:
+      case clang::OO_PercentEqual:
+      case clang::OO_LessLessEqual:
+      case clang::OO_GreaterGreaterEqual:
+      case clang::OO_PipeEqual:
+      case clang::OO_CaretEqual:
+      case clang::OO_AmpEqual:
+        // Don't warn on the first parameter of operator<<(Stream&, ...),
+        // operator++, operator-- and operation+assignment operators.
+        if (Function->getParamDecl(0) == Parameter)
+          return;
+        break;
+      case clang::OO_GreaterGreater: {
+        auto isNonConstRef = [](clang::QualType T) {
+          return T->isReferenceType() &&
+                 !T.getNonReferenceType().isConstQualified();
+        };
+        // Don't warn on parameters of stream extractors:
+        //   Stream& operator>>(Stream&, Value&);
+        // Both parameters should be non-const references by convention.
+        if (isNonConstRef(Function->getParamDecl(0)->getType()) &&
+            (Function->getNumParams() < 2 || // E.g. member operator>>.
+             isNonConstRef(Function->getParamDecl(1)->getType())) &&
+            isNonConstRef(Function->getReturnType()))
+          return;
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  // Some functions use references to comply with established standards.
+  if (Function->getDeclName().isIdentifier() && Function->getName() == "swap")
+    return;
+
+  // iostream parameters are typically passed by non-const reference.
+  if (StringRef(ReferencedType.getAsString()).endswith("stream"))
+    return;
+
+  diag(Parameter->getLocation(),
+       "non-const reference parameter '%0', make it const or use a pointer")
+      << Parameter->getName();
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/google/NonConstReferences.h b/clang-tools-extra/clang-tidy/google/NonConstReferences.h
new file mode 100644
index 0000000..3adb1f9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/google/NonConstReferences.h
@@ -0,0 +1,36 @@
+//===--- NonConstReferences.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_GOOGLE_NON_CONST_REFERENCES_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// \brief Checks the usage of non-constant references in function parameters.
+///
+/// https://google.github.io/styleguide/cppguide.html#Reference_Arguments
+class NonConstReferences : public ClangTidyCheck {
+public:
+  NonConstReferences(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H