aaron.ballman added a comment.

This is looking great! Some mostly minor feedback below. The only things I 
noticed that may require further consideration are throwing function parameters 
and throwing exception handler variables -- I don't think those scenarios 
should emit a diagnostic. The former is a bit chatty because of error handling 
functions, and the latter is correct regardless of the type being thrown 
(though is a bit silly because the user should really just use throw; instead 
of rethrowing the variable).

General nitpick: please make sure all comments in the code and tests have 
proper capitalization, punctuation, etc.


================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:23
@@ +22,3 @@
+    : ClangTidyCheck(Name, Context),
+      checkAnonymousTemporaries(Options.get("CheckThrowTemporaries", "true") ==
+                                "true") {}
----------------
Can we just use a bool instead of the string "true"? The constructor for 
AssertSideEffectCheck does this, for instance.

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:31
@@ +30,3 @@
+
+  Finder->addMatcher(throwExpr().bind("throw"), this);
+  Finder->addMatcher(catchStmt().bind("catch"), this);
----------------
I'm not certain whether this is feasible or not, but if you can check the 
options by this point, you could replace a lot of custom AST logic from check() 
by registering a different matcher if care about anonymous temporaries. For 
instance:

  if (checkAnonymousTemporaries)
    
Finder->addMatcher(throwExpr(unless(anyOf(hasDescendant(anyOf(declRefExpr(to(parmVarDecl())),
 declRefExpr(to(varDecl(isExceptionVar()))), 
constructExpr(hasDescendant(materializeTemporaryExpr())), 
expr(hasType(hasCanonicalType(builtinType()))))), unless(has(expr()))))), this);
  else
    Finder->addMatcher(throwExpr(), this);

(Leaving the bindings out, but you get the idea).

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:43
@@ +42,3 @@
+  const auto *throwExpr = Result.Nodes.getNodeAs<CXXThrowExpr>("throw");
+  if (throwExpr != nullptr) {
+    auto *subExpr = throwExpr->getSubExpr();
----------------
No need for the != nullptr.

May want to consider splitting the throw and catch logic into helper methods to 
reduce indenting. e.g.,

  if (throwExpr)
    doThrowStuff(throwExpr);
  
  if (catchExpr)
    doCatchStuff(catchExpr);
  

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:44
@@ +43,3 @@
+  if (throwExpr != nullptr) {
+    auto *subExpr = throwExpr->getSubExpr();
+    auto qualType = subExpr->getType();
----------------
I think we want throwExpr->getSubExpr()->IgnoreParenImpCasts(). This looks 
through *all* implicit casts (and paren expressions, etc), so you can get rid 
of the if (isa<ImplicitCastExpr>) from below and just look at 
isa<StringLiteral>. This will then allow code like the following to be properly 
checked: throw ("I am not a kind person");

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:55
@@ +54,3 @@
+      }
+      diag(subExpr->getLocStart(), "avoid throwing pointer");
+    }
----------------
How about: "throw expression throws a pointer value; should throw a non-pointer 
value instead"?

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:63
@@ +62,3 @@
+
+    // algorithm
+    // from CXXThrowExpr, move through all casts until you either encounter an
----------------
No need to state that this is an algorithm. The reader can likely guess as 
much. ;-)

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:74
@@ +73,3 @@
+      auto *currentSubExpr = subExpr;
+      while (isa<CastExpr>(currentSubExpr)) {
+        auto *currentCast = cast<CastExpr>(currentSubExpr);
----------------
I think we want IgnoreParenImpCasts() here as well.

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:98
@@ +97,3 @@
+          auto *currentSubExpr = *argIter;
+          while (isa<CastExpr>(currentSubExpr)) {
+            auto *currentCast = cast<CastExpr>(currentSubExpr);
----------------
IgnoreParenImpCasts(), again?

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:102
@@ +101,3 @@
+          }
+          // if the subexpr is now a DeclRefExpr, it's a real variable
+          if (isa<DeclRefExpr>(currentSubExpr))
----------------
I think the correct way to do this is check to see whether the constructor is 
passed a MaterializeTemporaryExpr. and if so, we don't want to emit the 
diagnostic (otherwise we do).

Also, this code does not look to see whether there is a DeclRefExpr that refers 
to a parameter variable declaration. In my testing, this accounted for a 
sizable number of false positives because of code like:

  void handle_error(const someObj &o) {
    throw o; // Shouldn't diagnose, this isn't dangerous
  }

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:108
@@ +107,3 @@
+      if (emit)
+        diag(subExpr->getLocStart(), "prefer throwing anonymous temporaries");
+    }
----------------
How about: "throw expression should throw an anonymous temporary value instead"?

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:112
@@ +111,3 @@
+  const auto *catchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");
+  if (catchStmt != nullptr) {
+    auto caughtType = catchStmt->getCaughtType();
----------------
No need for the != nullptr.

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:119
@@ +118,3 @@
+    if (type->isPointerType()) {
+      auto canonical = type->getCanonicalTypeInternal().getTypePtr();
+      // check if pointed-to-type is char, wchar_t, char16_t, char32_t
----------------
Should use getCanonicalType() instead of the internal version.

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:124
@@ +123,3 @@
+      if (pointeeType->isAnyCharacterType() == false) {
+        diag(varDecl->getLocStart(), "catch by reference");
+      }
----------------
How about: "catch handler catches a pointer value; should throw a non-pointer 
value and catch by reference instead"?

May also want to consider combining with the same diagnostic below (or, at the 
very least, only have the diagnostic text written once and referred to from 
both places).

================
Comment at: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp:128
@@ +127,3 @@
+      // not pointer, not reference this means it must be  "by value"
+      // do not advice on built-in types - catching them by value
+      // is ok
----------------
"do not advice" -> "do not diagnose"

================
Comment at: test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:3
@@ +2,3 @@
+
+//#include <utility>
+
----------------
Should remove this.

================
Comment at: test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:19
@@ +18,3 @@
+  throw new logic_error("by pointer");
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid throwing pointer
+  // [misc-throw-by-value-catch-by-reference]
----------------
Don't worry about 80-col limits for diagnostic messages -- keep the entire 
diagnostic on one line (it makes it easier to read the tests).

================
Comment at: test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:44
@@ +43,3 @@
+  // can be enabled once std::move can be included
+  // throw std::move(lvalue)  
+  int &ex = lastException;
----------------
You can add your own move definition to the file:

  template <class T> struct remove_reference      {typedef T type;};
  template <class T> struct remove_reference<T&>  {typedef T type;};
  template <class T> struct remove_reference<T&&> {typedef T type;};
  
  template <typename T>
  typename remove_reference<T>::type&& move(T&& arg) {
    return static_cast<typename remove_reference<T>::type&&>(arg);
  }

================
Comment at: test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:53
@@ +52,3 @@
+void throwReferenceFunc(logic_error &ref) {
+  throw ref;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: prefer throwing anonymous
----------------
I don't think we should diagnose on this case; I found it caused a lot of false 
positives for code bases that wrap throws in an error handling function.

================
Comment at: test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:138
@@ +137,2 @@
+  }
+}
----------------
I'd also like to see tests for:

  typedef logic_error& fine;
  try {
  } catch (int i) { // ok
    throw i; // ok
  } catch (fine e) { // ok
    throw e; // ok
  } catch (logic_error *e) { // diagnose for catching a pointer
    throw e; // ok, despite throwing a pointer
  } catch(...) { // ok
    throw; // ok
  }



http://reviews.llvm.org/D11328



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

Reply via email to