Просмотр исходного кода

Specialize noreturn diagnostics for lambda expressions.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150586 91177308-0d34-0410-b5e6-96231b3b80d8
Douglas Gregor 13 лет назад
Родитель
Сommit
793cd1c4cd

+ 8 - 0
include/clang/Basic/DiagnosticSemaKinds.td

@@ -4045,6 +4045,14 @@ let CategoryName = "Lambda Issue" in {
   def ext_lambda_default_arguments : ExtWarn<
   def ext_lambda_default_arguments : ExtWarn<
     "C++11 forbids default arguments for lambda expressions">,
     "C++11 forbids default arguments for lambda expressions">,
     InGroup<LambdaExtensions>;
     InGroup<LambdaExtensions>;
+  def err_noreturn_lambda_has_return_expr : Error<
+    "lambda declared 'noreturn' should not return">;
+  def warn_maybe_falloff_nonvoid_lambda : Warning<
+    "control may reach end of non-void lambda">,
+    InGroup<ReturnType>;
+  def warn_falloff_nonvoid_lambda : Warning<
+    "control reaches end of non-void lambda">,
+    InGroup<ReturnType>;
 }
 }
 
 
 def err_operator_arrow_circular : Error<
 def err_operator_arrow_circular : Error<

+ 28 - 8
lib/Sema/AnalysisBasedWarnings.cpp

@@ -218,7 +218,8 @@ struct CheckFallThroughDiagnostics {
   unsigned diag_AlwaysFallThrough_HasNoReturn;
   unsigned diag_AlwaysFallThrough_HasNoReturn;
   unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
   unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
   unsigned diag_NeverFallThroughOrReturn;
   unsigned diag_NeverFallThroughOrReturn;
-  bool funMode;
+  enum { Function, Block, Lambda } funMode;
+  bool IsLambda;
   SourceLocation FuncLoc;
   SourceLocation FuncLoc;
 
 
   static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
   static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
@@ -250,7 +251,7 @@ struct CheckFallThroughDiagnostics {
     else
     else
       D.diag_NeverFallThroughOrReturn = 0;
       D.diag_NeverFallThroughOrReturn = 0;
     
     
-    D.funMode = true;
+    D.funMode = Function;
     return D;
     return D;
   }
   }
 
 
@@ -266,13 +267,28 @@ struct CheckFallThroughDiagnostics {
       diag::err_falloff_nonvoid_block;
       diag::err_falloff_nonvoid_block;
     D.diag_NeverFallThroughOrReturn =
     D.diag_NeverFallThroughOrReturn =
       diag::warn_suggest_noreturn_block;
       diag::warn_suggest_noreturn_block;
-    D.funMode = false;
+    D.funMode = Block;
+    return D;
+  }
+
+  static CheckFallThroughDiagnostics MakeForLambda() {
+    CheckFallThroughDiagnostics D;
+    D.diag_MaybeFallThrough_HasNoReturn =
+      diag::err_noreturn_lambda_has_return_expr;
+    D.diag_MaybeFallThrough_ReturnsNonVoid =
+      diag::warn_maybe_falloff_nonvoid_lambda;
+    D.diag_AlwaysFallThrough_HasNoReturn =
+      diag::err_noreturn_lambda_has_return_expr;
+    D.diag_AlwaysFallThrough_ReturnsNonVoid =
+      diag::warn_falloff_nonvoid_lambda;
+    D.diag_NeverFallThroughOrReturn = 0;
+    D.funMode = Lambda;
     return D;
     return D;
   }
   }
 
 
   bool checkDiagnostics(DiagnosticsEngine &D, bool ReturnsVoid,
   bool checkDiagnostics(DiagnosticsEngine &D, bool ReturnsVoid,
                         bool HasNoReturn) const {
                         bool HasNoReturn) const {
-    if (funMode) {
+    if (funMode == Function) {
       return (ReturnsVoid ||
       return (ReturnsVoid ||
               D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
               D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
                                    FuncLoc) == DiagnosticsEngine::Ignored)
                                    FuncLoc) == DiagnosticsEngine::Ignored)
@@ -284,9 +300,9 @@ struct CheckFallThroughDiagnostics {
               == DiagnosticsEngine::Ignored);
               == DiagnosticsEngine::Ignored);
     }
     }
 
 
-    // For blocks.
-    return  ReturnsVoid && !HasNoReturn
-            && (!ReturnsVoid ||
+    // For blocks / lambdas.
+    return ReturnsVoid && !HasNoReturn
+            && ((funMode == Lambda) ||
                 D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
                 D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
                   == DiagnosticsEngine::Ignored);
                   == DiagnosticsEngine::Ignored);
   }
   }
@@ -888,7 +904,11 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
   if (P.enableCheckFallThrough) {
   if (P.enableCheckFallThrough) {
     const CheckFallThroughDiagnostics &CD =
     const CheckFallThroughDiagnostics &CD =
       (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
       (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
-                         : CheckFallThroughDiagnostics::MakeForFunction(D));
+       : (isa<CXXMethodDecl>(D) &&
+          cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call &&
+          cast<CXXMethodDecl>(D)->getParent()->isLambda())
+            ? CheckFallThroughDiagnostics::MakeForLambda()
+            : CheckFallThroughDiagnostics::MakeForFunction(D));
     CheckFallThroughForBody(S, D, Body, blkExpr, CD, AC);
     CheckFallThroughForBody(S, D, Body, blkExpr, CD, AC);
   }
   }
 
 

+ 8 - 2
lib/Sema/SemaStmt.cpp

@@ -1857,12 +1857,18 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
   QualType FnRetType = CurCap->ReturnType;
   QualType FnRetType = CurCap->ReturnType;
   assert(!FnRetType.isNull());
   assert(!FnRetType.isNull());
 
 
-  if (BlockScopeInfo *CurBlock = dyn_cast<BlockScopeInfo>(CurCap))
+  if (BlockScopeInfo *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
     if (CurBlock->FunctionType->getAs<FunctionType>()->getNoReturnAttr()) {
     if (CurBlock->FunctionType->getAs<FunctionType>()->getNoReturnAttr()) {
       Diag(ReturnLoc, diag::err_noreturn_block_has_return_expr);
       Diag(ReturnLoc, diag::err_noreturn_block_has_return_expr);
       return StmtError();
       return StmtError();
     }
     }
-  // FIXME: [[noreturn]] for lambdas!
+  } else {
+    LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(CurCap);
+    if (LSI->CallOperator->getType()->getAs<FunctionType>()->getNoReturnAttr()){
+      Diag(ReturnLoc, diag::err_noreturn_lambda_has_return_expr);
+      return StmtError();
+    }
+  }
 
 
   // Otherwise, verify that this result type matches the previous one.  We are
   // Otherwise, verify that this result type matches the previous one.  We are
   // pickier with blocks than for normal functions because we don't have GCC
   // pickier with blocks than for normal functions because we don't have GCC

+ 3 - 1
test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp

@@ -3,7 +3,9 @@
 // An attribute-specifier-seq in a lambda-declarator appertains to the
 // An attribute-specifier-seq in a lambda-declarator appertains to the
 // type of the corresponding function call operator.
 // type of the corresponding function call operator.
 void test_attributes() {
 void test_attributes() {
-  auto nrl = []() [[noreturn]] {}; // expected-warning{{function declared 'noreturn' should not return}}
+  auto nrl = [](int x) -> int { if (x > 0) return x; }; // expected-warning{{control may reach end of non-void lambda}}
+
+  auto nrl2 = []() [[noreturn]] { return; }; // expected-error{{lambda declared 'noreturn' should not return}}
 }
 }
 
 
 template<typename T>
 template<typename T>

+ 1 - 1
test/CXX/expr/expr.prim/expr.prim.lambda/p7.cpp

@@ -2,7 +2,7 @@
 
 
 // Check that analysis-based warnings work in lambda bodies.
 // Check that analysis-based warnings work in lambda bodies.
 void analysis_based_warnings() {
 void analysis_based_warnings() {
-  (void)[]() -> int { }; // expected-warning{{control reaches end of non-void function}}
+  (void)[]() -> int { }; // expected-warning{{control reaches end of non-void lambda}}
 }
 }
 
 
 // Check that we get the right types of captured variables (the
 // Check that we get the right types of captured variables (the

+ 1 - 1
test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp

@@ -2,7 +2,7 @@
 
 
 template<typename T>
 template<typename T>
 void test_attributes() {
 void test_attributes() {
-  auto nrl = []() [[noreturn]] {}; // expected-warning{{function declared 'noreturn' should not return}}
+  auto nrl = []() [[noreturn]] {}; // expected-error{{lambda declared 'noreturn' should not return}}
 }
 }
 
 
 template void test_attributes<int>(); // expected-note{{in instantiation of function}}
 template void test_attributes<int>(); // expected-note{{in instantiation of function}}