Skip to content

Commit 9f46305

Browse files
authored
[Clang] add ext warning for missing return in 'main' for C89 mode (#134617)
Fixes #21650 --- Clang currently inserts an implicit `return 0;` in `main()` when compiling in `C89` mode, even though the `C89` standard doesn't require this behavior. This patch changes that behavior by emitting a warning instead of silently inserting the implicit return under `-pedantic`.
1 parent 156e253 commit 9f46305

File tree

7 files changed

+55
-12
lines changed

7 files changed

+55
-12
lines changed

clang/docs/ReleaseNotes.rst

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ Improvements to Clang's diagnostics
321321

322322
- ``-Wc++98-compat`` no longer diagnoses use of ``__auto_type`` or
323323
``decltype(auto)`` as though it was the extension for ``auto``. (#GH47900)
324+
- Clang now issues a warning for missing return in ``main`` in C89 mode. (#GH21650)
324325

325326
- Now correctly diagnose a tentative definition of an array with static
326327
storage duration in pedantic mode in C. (#GH50661)

clang/include/clang/Basic/DiagnosticSemaKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,9 @@ def err_mainlike_template_decl : Error<"%0 cannot be a template">;
10311031
def err_main_returns_nonint : Error<"'main' must return 'int'">;
10321032
def ext_main_returns_nonint : ExtWarn<"return type of 'main' is not 'int'">,
10331033
InGroup<MainReturnType>;
1034+
def ext_main_no_return : Extension<
1035+
"implicit '0' return value from 'main' is a C99 extension">,
1036+
InGroup<MainReturnType>;
10341037
def note_main_change_return_type : Note<"change return type to 'int'">;
10351038
def err_main_surplus_args : Error<"too many parameters (%0) for 'main': "
10361039
"must be 0, 2, or 3">;

clang/lib/Sema/AnalysisBasedWarnings.cpp

+16-11
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,8 @@ struct CheckFallThroughDiagnostics {
550550
unsigned FunKind; // TODO: use diag::FalloffFunctionKind
551551
SourceLocation FuncLoc;
552552

553-
static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
553+
static CheckFallThroughDiagnostics MakeForFunction(Sema &S,
554+
const Decl *Func) {
554555
CheckFallThroughDiagnostics D;
555556
D.FuncLoc = Func->getLocation();
556557
D.diag_FallThrough_HasNoReturn = diag::warn_noreturn_has_return_expr;
@@ -564,8 +565,13 @@ struct CheckFallThroughDiagnostics {
564565

565566
// Don't suggest that template instantiations be marked "noreturn"
566567
bool isTemplateInstantiation = false;
567-
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(Func))
568+
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(Func)) {
568569
isTemplateInstantiation = Function->isTemplateInstantiation();
570+
if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().C99 &&
571+
Function->isMain()) {
572+
D.diag_FallThrough_ReturnsNonVoid = diag::ext_main_no_return;
573+
}
574+
}
569575

570576
if (!isVirtualMethod && !isTemplateInstantiation)
571577
D.diag_NeverFallThroughOrReturn = diag::warn_suggest_noreturn_function;
@@ -2737,15 +2743,14 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
27372743
// Warning: check missing 'return'
27382744
if (P.enableCheckFallThrough) {
27392745
const CheckFallThroughDiagnostics &CD =
2740-
(isa<BlockDecl>(D)
2741-
? CheckFallThroughDiagnostics::MakeForBlock()
2742-
: (isa<CXXMethodDecl>(D) &&
2743-
cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call &&
2744-
cast<CXXMethodDecl>(D)->getParent()->isLambda())
2745-
? CheckFallThroughDiagnostics::MakeForLambda()
2746-
: (fscope->isCoroutine()
2747-
? CheckFallThroughDiagnostics::MakeForCoroutine(D)
2748-
: CheckFallThroughDiagnostics::MakeForFunction(D)));
2746+
(isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
2747+
: (isa<CXXMethodDecl>(D) &&
2748+
cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call &&
2749+
cast<CXXMethodDecl>(D)->getParent()->isLambda())
2750+
? CheckFallThroughDiagnostics::MakeForLambda()
2751+
: (fscope->isCoroutine()
2752+
? CheckFallThroughDiagnostics::MakeForCoroutine(D)
2753+
: CheckFallThroughDiagnostics::MakeForFunction(S, D)));
27492754
CheckFallThroughForBody(S, D, Body, BlockType, CD, AC);
27502755
}
27512756

clang/lib/Sema/SemaDecl.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -16232,7 +16232,12 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1623216232

1623316233
// If the function implicitly returns zero (like 'main') or is naked,
1623416234
// don't complain about missing return statements.
16235-
if (FD->hasImplicitReturnZero() || FD->hasAttr<NakedAttr>())
16235+
// Clang implicitly returns 0 in C89 mode, but that's considered an
16236+
// extension. The check is necessary to ensure the expected extension
16237+
// warning is emitted in C89 mode.
16238+
if ((FD->hasImplicitReturnZero() &&
16239+
(getLangOpts().CPlusPlus || getLangOpts().C99 || !FD->isMain())) ||
16240+
FD->hasAttr<NakedAttr>())
1623616241
WP.disableCheckFallThrough();
1623716242

1623816243
// MSVC permits the use of pure specifier (=0) on function definition,
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -pedantic %s
2+
* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -Wmain-return-type %s
3+
* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=implicit-main-return -Wno-strict-prototypes -pedantic -Wno-main-return-type %s
4+
*/
5+
6+
/* implicit-main-return-no-diagnostics */
7+
8+
int main() {
9+
10+
} /* expected-warning {{implicit '0' return value from 'main' is a C99 extension}} */
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -Wmain-return-type %s
2+
*/
3+
4+
/* expected-no-diagnostics */
5+
6+
void exit(int);
7+
8+
int main() {
9+
if (1)
10+
exit(1);
11+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -Wmain-return-type %s
2+
*/
3+
4+
/* expected-no-diagnostics */
5+
6+
int main() {
7+
return 0;
8+
}

0 commit comments

Comments
 (0)