r291905 - [Sema] Add warning for unused lambda captures

Malcolm Parsons via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 13 07:01:07 PST 2017


Author: malcolm.parsons
Date: Fri Jan 13 09:01:06 2017
New Revision: 291905

URL: http://llvm.org/viewvc/llvm-project?rev=291905&view=rev
Log:
[Sema] Add warning for unused lambda captures

Summary:
Warn when a lambda explicitly captures something that is not used in its body.

The warning is part of -Wunused and can be enabled with -Wunused-lambda-capture.

Reviewers: rsmith, arphaman, jbcoe, aaron.ballman

Subscribers: Quuxplusone, arphaman, cfe-commits

Differential Revision: https://reviews.llvm.org/D28467

Added:
    cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/ScopeInfo.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaLambda.cpp
    cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp
    cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
    cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp
    cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp
    cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp
    cfe/trunk/test/SemaCXX/uninitialized.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Fri Jan 13 09:01:06 2017
@@ -480,6 +480,7 @@ def UnusedFunction : DiagGroup<"unused-f
 def UnusedMemberFunction : DiagGroup<"unused-member-function",
                                      [UnneededMemberFunction]>;
 def UnusedLabel : DiagGroup<"unused-label">;
+def UnusedLambdaCapture : DiagGroup<"unused-lambda-capture">;
 def UnusedParameter : DiagGroup<"unused-parameter">;
 def UnusedResult : DiagGroup<"unused-result">;
 def PotentiallyEvaluatedExpression : DiagGroup<"potentially-evaluated-expression">;
@@ -617,8 +618,9 @@ def Unused : DiagGroup<"unused",
                        [UnusedArgument, UnusedFunction, UnusedLabel,
                         // UnusedParameter, (matches GCC's behavior)
                         // UnusedMemberFunction, (clean-up llvm before enabling)
-                        UnusedPrivateField, UnusedLocalTypedef,
-                        UnusedValue, UnusedVariable, UnusedPropertyIvar]>,
+                        UnusedPrivateField, UnusedLambdaCapture,
+                        UnusedLocalTypedef, UnusedValue, UnusedVariable,
+                        UnusedPropertyIvar]>,
                         DiagCategory<"Unused Entity Issue">;
 
 // Format settings.

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jan 13 09:01:06 2017
@@ -316,6 +316,9 @@ def warn_unneeded_member_function : Warn
   InGroup<UnneededMemberFunction>, DefaultIgnore;
 def warn_unused_private_field: Warning<"private field %0 is not used">,
   InGroup<UnusedPrivateField>, DefaultIgnore;
+def warn_unused_lambda_capture: Warning<"lambda capture %0 is not "
+  "%select{used|required to be captured for use in an unevaluated context}1">,
+  InGroup<UnusedLambdaCapture>, DefaultIgnore;
 
 def warn_parameter_size: Warning<
   "%0 is a large (%1 bytes) pass-by-value argument; "

Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
+++ cfe/trunk/include/clang/Sema/ScopeInfo.h Fri Jan 13 09:01:06 2017
@@ -452,6 +452,14 @@ public:
     /// non-static data member that would hold the capture.
     QualType CaptureType;
 
+    /// \brief Whether an explicit capture has been odr-used in the body of the
+    /// lambda.
+    bool ODRUsed;
+
+    /// \brief Whether an explicit capture has been non-odr-used in the body of
+    /// the lambda.
+    bool NonODRUsed;
+
   public:
     Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested,
             SourceLocation Loc, SourceLocation EllipsisLoc,
@@ -460,7 +468,8 @@ public:
           InitExprAndCaptureKind(
               Cpy, !Var ? Cap_VLA : Block ? Cap_Block : ByRef ? Cap_ByRef
                                                               : Cap_ByCopy),
-          Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {}
+          Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType),
+          ODRUsed(false), NonODRUsed(false) {}
 
     enum IsThisCapture { ThisCapture };
     Capture(IsThisCapture, bool IsNested, SourceLocation Loc,
@@ -468,7 +477,8 @@ public:
         : VarAndNestedAndThis(
               nullptr, (IsThisCaptured | (IsNested ? IsNestedCapture : 0))),
           InitExprAndCaptureKind(Cpy, ByCopy ? Cap_ByCopy : Cap_ByRef),
-          Loc(Loc), EllipsisLoc(), CaptureType(CaptureType) {}
+          Loc(Loc), EllipsisLoc(), CaptureType(CaptureType), ODRUsed(false),
+          NonODRUsed(false) {}
 
     bool isThisCapture() const {
       return VarAndNestedAndThis.getInt() & IsThisCaptured;
@@ -491,6 +501,9 @@ public:
     bool isNested() const {
       return VarAndNestedAndThis.getInt() & IsNestedCapture;
     }
+    bool isODRUsed() const { return ODRUsed; }
+    bool isNonODRUsed() const { return NonODRUsed; }
+    void markUsed(bool IsODRUse) { (IsODRUse ? ODRUsed : NonODRUsed) = true; }
 
     VarDecl *getVariable() const {
       return VarAndNestedAndThis.getPointer();

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Jan 13 09:01:06 2017
@@ -5323,6 +5323,9 @@ public:
   ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
                              Scope *CurScope);
 
+  /// \brief Diagnose if an explicit lambda capture is unused.
+  void DiagnoseUnusedLambdaCapture(const sema::LambdaScopeInfo::Capture &From);
+
   /// \brief Complete a lambda-expression having processed and attached the
   /// lambda body.
   ExprResult BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Jan 13 09:01:06 2017
@@ -13916,8 +13916,10 @@ bool Sema::tryCaptureVariable(
 
     // Check whether we've already captured it.
     if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, 
-                                             DeclRefType)) 
+                                             DeclRefType)) {
+      CSI->getCapture(Var).markUsed(BuildAndDiagnose);
       break;
+    }
     // If we are instantiating a generic lambda call operator body, 
     // we do not want to capture new variables.  What was captured
     // during either a lambdas transformation or initial parsing

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Fri Jan 13 09:01:06 2017
@@ -1106,6 +1106,7 @@ bool Sema::CheckCXXThisCapture(SourceLoc
             dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) {
       if (CSI->CXXThisCaptureIndex != 0) {
         // 'this' is already being captured; there isn't anything more to do.
+        CSI->Captures[CSI->CXXThisCaptureIndex - 1].markUsed(BuildAndDiagnose);
         break;
       }
       LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI);

Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Fri Jan 13 09:01:06 2017
@@ -1384,7 +1384,7 @@ static void addBlockPointerConversion(Se
 }
 
 static ExprResult performLambdaVarCaptureInitialization(
-    Sema &S, LambdaScopeInfo::Capture &Capture, FieldDecl *Field) {
+    Sema &S, const LambdaScopeInfo::Capture &Capture, FieldDecl *Field) {
   assert(Capture.isVariableCapture() && "not a variable capture");
 
   auto *Var = Capture.getVariable();
@@ -1438,6 +1438,21 @@ mapImplicitCaptureStyle(CapturingScopeIn
   llvm_unreachable("Unknown implicit capture style");
 }
 
+void Sema::DiagnoseUnusedLambdaCapture(const LambdaScopeInfo::Capture &From) {
+  if (!From.isVLATypeCapture()) {
+    Expr *Init = From.getInitExpr();
+    if (Init && Init->HasSideEffects(Context))
+      return;
+  }
+
+  auto diag = Diag(From.getLocation(), diag::warn_unused_lambda_capture);
+  if (From.isThisCapture())
+    diag << "'this'";
+  else
+    diag << From.getVariable();
+  diag << From.isNonODRUsed();
+}
+
 ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
                                  LambdaScopeInfo *LSI) {
   // Collect information from the lambda scope.
@@ -1476,10 +1491,14 @@ ExprResult Sema::BuildLambdaExpr(SourceL
     // Translate captures.
     auto CurField = Class->field_begin();
     for (unsigned I = 0, N = LSI->Captures.size(); I != N; ++I, ++CurField) {
-      LambdaScopeInfo::Capture From = LSI->Captures[I];
+      const LambdaScopeInfo::Capture &From = LSI->Captures[I];
       assert(!From.isBlockCapture() && "Cannot capture __block variables");
       bool IsImplicit = I >= LSI->NumExplicitCaptures;
 
+      // Warn about unused explicit captures.
+      if (!CurContext->isDependentContext() && !IsImplicit && !From.isODRUsed())
+        DiagnoseUnusedLambdaCapture(From);
+
       // Handle 'this' capture.
       if (From.isThisCapture()) {
         Captures.push_back(

Modified: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp Fri Jan 13 09:01:06 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify
+// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify
 
 void odr_used() {
   int i = 17;

Modified: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp Fri Jan 13 09:01:06 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify
+// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify
 
 void f2() {
   int i = 1;

Modified: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp Fri Jan 13 09:01:06 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify
+// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify
 
 
 struct X {

Modified: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp Fri Jan 13 09:01:06 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify
+// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify
 // expected-no-diagnostics
 
 template<typename T, typename U>

Modified: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp (original)
+++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp Fri Jan 13 09:01:06 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify
+// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify
 
 struct MoveOnly {
   MoveOnly(MoveOnly&&);

Modified: cfe/trunk/test/SemaCXX/uninitialized.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/uninitialized.cpp?rev=291905&r1=291904&r2=291905&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/uninitialized.cpp (original)
+++ cfe/trunk/test/SemaCXX/uninitialized.cpp Fri Jan 13 09:01:06 2017
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -std=c++11 -verify %s
 
 // definitions for std::move
 namespace std {

Added: cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp?rev=291905&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp Fri Jan 13 09:01:06 2017
@@ -0,0 +1,110 @@
+// RUN: %clang_cc1 -fsyntax-only -Wunused-lambda-capture -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++14 %s
+
+class NonTrivialConstructor {
+public:
+  NonTrivialConstructor() {}
+};
+
+class NonTrivialDestructor {
+public:
+  ~NonTrivialDestructor() {}
+};
+
+class Trivial {
+public:
+  Trivial() = default;
+  Trivial(int a) {}
+};
+
+int side_effect() {
+  return 42;
+}
+
+void test() {
+  int i = 0;
+
+  auto captures_nothing = [] {};
+
+  auto captures_nothing_by_value = [=] {};
+  auto captures_nothing_by_reference = [&] {};
+
+  auto implicit_by_value = [=]() mutable { i++; };
+  auto implicit_by_reference = [&] { i++; };
+
+  auto explicit_by_value_used = [i] { return i + 1; };
+  auto explicit_by_value_used_void = [i] { (void)i; };
+  auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}}
+  auto explicit_by_value_unused_sizeof = [i] { return sizeof(i); }; // expected-warning{{lambda capture 'i' is not required to be captured for use in an unevaluated context}}
+  auto explicit_by_value_unused_decltype = [i] { decltype(i) j = 0; }; // expected-warning{{lambda capture 'i' is not required to be captured for use in an unevaluated context}}
+
+  auto explicit_by_reference_used = [&i] { i++; };
+  auto explicit_by_reference_unused = [&i] {}; // expected-warning{{lambda capture 'i' is not used}}
+
+  auto explicit_initialized_reference_used = [&j = i] { return j + 1; };
+  auto explicit_initialized_reference_unused = [&j = i]{}; // expected-warning{{lambda capture 'j' is not used}}
+
+  auto explicit_initialized_value_used = [j = 1] { return j + 1; };
+  auto explicit_initialized_value_unused = [j = 1] {}; // expected-warning{{lambda capture 'j' is not used}}
+  auto explicit_initialized_value_non_trivial_constructor = [j = NonTrivialConstructor()]{};
+  auto explicit_initialized_value_non_trivial_destructor = [j = NonTrivialDestructor()]{};
+  auto explicit_initialized_value_trivial_init = [j = Trivial()]{}; // expected-warning{{lambda capture 'j' is not used}}
+  auto explicit_initialized_value_non_trivial_init = [j = Trivial(42)]{};
+  auto explicit_initialized_value_with_side_effect = [j = side_effect()]{};
+
+  auto nested = [&i] {
+    auto explicit_by_value_used = [i] { return i + 1; };
+    auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}}
+  };
+}
+
+class Foo
+{
+  void test() {
+    auto explicit_this_used = [this] { return i; };
+    auto explicit_this_used_void = [this] { (void)this; };
+    auto explicit_this_unused = [this] {}; // expected-warning{{lambda capture 'this' is not used}}
+  }
+  int i;
+};
+
+template <typename T>
+void test_templated() {
+  int i = 0;
+
+  auto captures_nothing = [] {};
+
+  auto captures_nothing_by_value = [=] {};
+  auto captures_nothing_by_reference = [&] {};
+
+  auto implicit_by_value = [=]() mutable { i++; };
+  auto implicit_by_reference = [&] { i++; };
+
+  auto explicit_by_value_used = [i] { return i + 1; };
+  auto explicit_by_value_used_void = [i] { (void)i; };
+  auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}}
+  auto explicit_by_value_unused_sizeof = [i] { return sizeof(i); }; // expected-warning{{lambda capture 'i' is not required to be captured for use in an unevaluated context}}
+  auto explicit_by_value_unused_decltype = [i] { decltype(i) j = 0; }; // expected-warning{{lambda capture 'i' is not used}}
+
+  auto explicit_by_reference_used = [&i] { i++; };
+  auto explicit_by_reference_unused = [&i] {}; // expected-warning{{lambda capture 'i' is not used}}
+
+  auto explicit_initialized_reference_used = [&j = i] { return j + 1; };
+  auto explicit_initialized_reference_unused = [&j = i]{}; // expected-warning{{lambda capture 'j' is not used}}
+
+  auto explicit_initialized_value_used = [j = 1] { return j + 1; };
+  auto explicit_initialized_value_unused = [j = 1] {}; // expected-warning{{lambda capture 'j' is not used}}
+  auto explicit_initialized_value_non_trivial_constructor = [j = NonTrivialConstructor()]{};
+  auto explicit_initialized_value_non_trivial_destructor = [j = NonTrivialDestructor()]{};
+  auto explicit_initialized_value_trivial_init = [j = Trivial()]{}; // expected-warning{{lambda capture 'j' is not used}}
+  auto explicit_initialized_value_non_trivial_init = [j = Trivial(42)]{};
+  auto explicit_initialized_value_with_side_effect = [j = side_effect()]{};
+
+  auto nested = [&i] {
+    auto explicit_by_value_used = [i] { return i + 1; };
+    auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}}
+  };
+}
+
+void test_use_template() {
+  test_templated<int>(); // expected-note{{in instantiation of function template specialization 'test_templated<int>' requested here}}
+}




More information about the cfe-commits mailing list