Skip to content
41 changes: 13 additions & 28 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -713,30 +713,21 @@ def err_thread_non_global : Error<
def err_thread_unsupported : Error<
"thread-local storage is not supported for the current target">;

// FIXME: Combine fallout warnings to just one warning.
def warn_maybe_falloff_nonvoid_function : Warning<
"non-void function does not return a value in all control paths">,
InGroup<ReturnType>;
def warn_falloff_nonvoid_function : Warning<
"non-void function does not return a value">,
def warn_falloff_nonvoid : Warning<
"non-void "
"%enum_select<FunModes>{%Function{function}|%Block{block}|%Lambda{lambda}|%Coroutine{coroutine}}0"
" does not return a value%select{| in all control paths}1">,
InGroup<ReturnType>;
def err_falloff_nonvoid : Error<
"non-void %select{function|block|lambda|coroutine}0 "
"does not return a value%select{| in all control paths}1">;
def warn_const_attr_with_pure_attr : Warning<
"'const' attribute imposes more restrictions; 'pure' attribute ignored">,
InGroup<IgnoredAttributes>;
def warn_pure_function_returns_void : Warning<
"'%select{pure|const}0' attribute on function returning 'void'; attribute ignored">,
InGroup<IgnoredAttributes>;

def err_maybe_falloff_nonvoid_block : Error<
"non-void block does not return a value in all control paths">;
def err_falloff_nonvoid_block : Error<
"non-void block does not return a value">;
def warn_maybe_falloff_nonvoid_coroutine : Warning<
"non-void coroutine does not return a value in all control paths">,
InGroup<ReturnType>;
def warn_falloff_nonvoid_coroutine : Warning<
"non-void coroutine does not return a value">,
InGroup<ReturnType>;
def warn_suggest_noreturn_function : Warning<
"%select{function|method}0 %1 could be declared with attribute 'noreturn'">,
InGroup<MissingNoreturn>, DefaultIgnore;
Expand Down Expand Up @@ -8406,14 +8397,6 @@ let CategoryName = "Lambda Issue" in {
"lambda expression in default argument cannot capture any entity">;
def err_lambda_incomplete_result : Error<
"incomplete result type %0 in lambda expression">;
def err_noreturn_lambda_has_return_expr : Error<
"lambda declared 'noreturn' should not return">;
def warn_maybe_falloff_nonvoid_lambda : Warning<
"non-void lambda does not return a value in all control paths">,
InGroup<ReturnType>;
def warn_falloff_nonvoid_lambda : Warning<
"non-void lambda does not return a value">,
InGroup<ReturnType>;
def err_access_lambda_capture : Error<
// The ERRORs represent other special members that aren't constructors, in
// hopes that someone will bother noticing and reporting if they appear
Expand Down Expand Up @@ -10603,14 +10586,16 @@ def err_ctor_dtor_returns_void : Error<
def warn_noreturn_function_has_return_expr : Warning<
"function %0 declared 'noreturn' should not return">,
InGroup<InvalidNoreturn>;
def warn_falloff_noreturn_function : Warning<
"function declared 'noreturn' should not return">,
def warn_noreturn_has_return_expr : Warning<
"%select{function|block|lambda|coroutine}0 "
"declared 'noreturn' should not return">,
InGroup<InvalidNoreturn>;
def err_noreturn_has_return_expr : Error<
"%select{function|block|lambda|coroutine}0 "
"declared 'noreturn' should not return">;
def warn_noreturn_coroutine : Warning<
"coroutine %0 cannot be declared 'noreturn' as it always returns a coroutine handle">,
InGroup<InvalidNoreturn>;
def err_noreturn_block_has_return_expr : Error<
"block declared 'noreturn' should not return">;
def err_carries_dependency_missing_on_first_decl : Error<
"%select{function|parameter}0 declared '[[carries_dependency]]' "
"after its first declaration">;
Expand Down
133 changes: 49 additions & 84 deletions clang/lib/Sema/AnalysisBasedWarnings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,25 +544,17 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
namespace {

struct CheckFallThroughDiagnostics {
unsigned diag_MaybeFallThrough_HasNoReturn;
unsigned diag_MaybeFallThrough_ReturnsNonVoid;
unsigned diag_AlwaysFallThrough_HasNoReturn;
unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
unsigned diag_NeverFallThroughOrReturn;
enum { Function, Block, Lambda, Coroutine } funMode;
unsigned diag_FallThrough_HasNoReturn = 0;
unsigned diag_FallThrough_ReturnsNonVoid = 0;
unsigned diag_NeverFallThroughOrReturn = 0;
unsigned FunMode; // TODO: use diag::FunModes
SourceLocation FuncLoc;

static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
CheckFallThroughDiagnostics D;
D.FuncLoc = Func->getLocation();
D.diag_MaybeFallThrough_HasNoReturn =
diag::warn_falloff_noreturn_function;
D.diag_MaybeFallThrough_ReturnsNonVoid =
diag::warn_maybe_falloff_nonvoid_function;
D.diag_AlwaysFallThrough_HasNoReturn =
diag::warn_falloff_noreturn_function;
D.diag_AlwaysFallThrough_ReturnsNonVoid =
diag::warn_falloff_nonvoid_function;
D.diag_FallThrough_HasNoReturn = diag::warn_noreturn_has_return_expr;
D.diag_FallThrough_ReturnsNonVoid = diag::warn_falloff_nonvoid;

// Don't suggest that virtual functions be marked "noreturn", since they
// might be overridden by non-noreturn functions.
Expand All @@ -576,76 +568,49 @@ struct CheckFallThroughDiagnostics {
isTemplateInstantiation = Function->isTemplateInstantiation();

if (!isVirtualMethod && !isTemplateInstantiation)
D.diag_NeverFallThroughOrReturn =
diag::warn_suggest_noreturn_function;
else
D.diag_NeverFallThroughOrReturn = 0;
D.diag_NeverFallThroughOrReturn = diag::warn_suggest_noreturn_function;

D.funMode = Function;
D.FunMode = diag::FunModes::Function;
return D;
}

static CheckFallThroughDiagnostics MakeForCoroutine(const Decl *Func) {
CheckFallThroughDiagnostics D;
D.FuncLoc = Func->getLocation();
D.diag_MaybeFallThrough_HasNoReturn = 0;
D.diag_MaybeFallThrough_ReturnsNonVoid =
diag::warn_maybe_falloff_nonvoid_coroutine;
D.diag_AlwaysFallThrough_HasNoReturn = 0;
D.diag_AlwaysFallThrough_ReturnsNonVoid =
diag::warn_falloff_nonvoid_coroutine;
D.diag_NeverFallThroughOrReturn = 0;
D.funMode = Coroutine;
D.diag_FallThrough_ReturnsNonVoid = diag::warn_falloff_nonvoid;
D.FunMode = diag::FunModes::Coroutine;
return D;
}

static CheckFallThroughDiagnostics MakeForBlock() {
CheckFallThroughDiagnostics D;
D.diag_MaybeFallThrough_HasNoReturn =
diag::err_noreturn_block_has_return_expr;
D.diag_MaybeFallThrough_ReturnsNonVoid =
diag::err_maybe_falloff_nonvoid_block;
D.diag_AlwaysFallThrough_HasNoReturn =
diag::err_noreturn_block_has_return_expr;
D.diag_AlwaysFallThrough_ReturnsNonVoid =
diag::err_falloff_nonvoid_block;
D.diag_NeverFallThroughOrReturn = 0;
D.funMode = Block;
D.diag_FallThrough_HasNoReturn = diag::err_noreturn_has_return_expr;
D.diag_FallThrough_ReturnsNonVoid = diag::err_falloff_nonvoid;
D.FunMode = diag::FunModes::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;
D.diag_FallThrough_HasNoReturn = diag::err_noreturn_has_return_expr;
D.diag_FallThrough_ReturnsNonVoid = diag::warn_falloff_nonvoid;
D.FunMode = diag::FunModes::Lambda;
return D;
}

bool checkDiagnostics(DiagnosticsEngine &D, bool ReturnsVoid,
bool HasNoReturn) const {
if (funMode == Function) {
if (FunMode == diag::FunModes::Function) {
return (ReturnsVoid ||
D.isIgnored(diag::warn_maybe_falloff_nonvoid_function,
FuncLoc)) &&
D.isIgnored(diag::warn_falloff_nonvoid, FuncLoc)) &&
(!HasNoReturn ||
D.isIgnored(diag::warn_noreturn_function_has_return_expr,
FuncLoc)) &&
D.isIgnored(diag::warn_noreturn_has_return_expr, FuncLoc)) &&
(!ReturnsVoid ||
D.isIgnored(diag::warn_suggest_noreturn_block, FuncLoc));
}
if (funMode == Coroutine) {
if (FunMode == diag::FunModes::Coroutine) {
return (ReturnsVoid ||
D.isIgnored(diag::warn_maybe_falloff_nonvoid_function, FuncLoc) ||
D.isIgnored(diag::warn_maybe_falloff_nonvoid_coroutine,
FuncLoc)) &&
D.isIgnored(diag::warn_falloff_nonvoid, FuncLoc)) &&
(!HasNoReturn);
}
// For blocks / lambdas.
Expand Down Expand Up @@ -696,12 +661,12 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn))
return;
SourceLocation LBrace = Body->getBeginLoc(), RBrace = Body->getEndLoc();
auto EmitDiag = [&](SourceLocation Loc, unsigned DiagID) {
auto EmitDiag = [&](SourceLocation Loc, unsigned DiagID, unsigned FunMode) {
if (IsCoroutine) {
if (DiagID != 0)
S.Diag(Loc, DiagID) << FSI->CoroutinePromise->getType();
} else {
S.Diag(Loc, DiagID);
S.Diag(Loc, DiagID) << FunMode;
}
};

Expand All @@ -711,34 +676,34 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,

// Either in a function body compound statement, or a function-try-block.
switch (CheckFallThrough(AC)) {
case UnknownFallThrough:
break;
case UnknownFallThrough:
break;

case MaybeFallThrough:
if (HasNoReturn)
EmitDiag(RBrace, CD.diag_MaybeFallThrough_HasNoReturn);
else if (!ReturnsVoid)
EmitDiag(RBrace, CD.diag_MaybeFallThrough_ReturnsNonVoid);
break;
case AlwaysFallThrough:
if (HasNoReturn)
EmitDiag(RBrace, CD.diag_AlwaysFallThrough_HasNoReturn);
else if (!ReturnsVoid)
EmitDiag(RBrace, CD.diag_AlwaysFallThrough_ReturnsNonVoid);
break;
case NeverFallThroughOrReturn:
if (ReturnsVoid && !HasNoReturn && CD.diag_NeverFallThroughOrReturn) {
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
S.Diag(LBrace, CD.diag_NeverFallThroughOrReturn) << 0 << FD;
} else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
S.Diag(LBrace, CD.diag_NeverFallThroughOrReturn) << 1 << MD;
} else {
S.Diag(LBrace, CD.diag_NeverFallThroughOrReturn);
}
case MaybeFallThrough:
if (HasNoReturn)
EmitDiag(RBrace, CD.diag_FallThrough_HasNoReturn, CD.FunMode);
else if (!ReturnsVoid)
S.Diag(RBrace, CD.diag_FallThrough_ReturnsNonVoid) << CD.FunMode << 1;
break;
case AlwaysFallThrough:
if (HasNoReturn)
EmitDiag(RBrace, CD.diag_FallThrough_HasNoReturn, CD.FunMode);
else if (!ReturnsVoid)
S.Diag(RBrace, CD.diag_FallThrough_ReturnsNonVoid) << CD.FunMode << 0;
break;
case NeverFallThroughOrReturn:
if (ReturnsVoid && !HasNoReturn && CD.diag_NeverFallThroughOrReturn) {
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
S.Diag(LBrace, CD.diag_NeverFallThroughOrReturn) << 0 << FD;
} else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
S.Diag(LBrace, CD.diag_NeverFallThroughOrReturn) << 1 << MD;
} else {
S.Diag(LBrace, CD.diag_NeverFallThroughOrReturn);
}
break;
case NeverFallThrough:
break;
}
break;
case NeverFallThrough:
break;
}
}

Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3590,7 +3590,8 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,

if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
Diag(ReturnLoc, diag::err_noreturn_block_has_return_expr);
Diag(ReturnLoc, diag::err_noreturn_has_return_expr)
<< diag::FunModes::Block;
return StmtError();
}
} else if (auto *CurRegion = dyn_cast<CapturedRegionScopeInfo>(CurCap)) {
Expand All @@ -3601,7 +3602,8 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
if (CurLambda->CallOperator->getType()
->castAs<FunctionType>()
->getNoReturnAttr()) {
Diag(ReturnLoc, diag::err_noreturn_lambda_has_return_expr);
Diag(ReturnLoc, diag::err_noreturn_has_return_expr)
<< diag::FunModes::Lambda;
return StmtError();
}
}
Expand Down