[clang] [Clang] add ext warning for missing return in 'main' for C89 mode (PR #134617)

Oleksandr T. via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 7 12:45:52 PDT 2025


https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/134617

>From 64210203803f7ba9deec06d467ee570bff761108 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sun, 6 Apr 2025 00:17:38 +0300
Subject: [PATCH 1/4] [Clang] add ext warning for missing return in 'main' for
 C89 mode

---
 clang/docs/ReleaseNotes.rst                   |  1 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 ++
 clang/lib/Sema/AnalysisBasedWarnings.cpp      | 27 +++++++++++--------
 clang/lib/Sema/SemaDecl.cpp                   |  4 ++-
 clang/test/Sema/main-no-return-c89.c          |  6 +++++
 5 files changed, 28 insertions(+), 12 deletions(-)
 create mode 100644 clang/test/Sema/main-no-return-c89.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5217e04b5e83f..6227b6133da0b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -318,6 +318,7 @@ Improvements to Clang's diagnostics
 
 - ``-Wc++98-compat`` no longer diagnoses use of ``__auto_type`` or
   ``decltype(auto)`` as though it was the extension for ``auto``. (#GH47900)
+- Clang now issues a warning for missing return in ``main`` in C89 mode. (#GH21650)
 
 Improvements to Clang's time-trace
 ----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 393bfecf9a36b..34f2ee883ca63 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1031,6 +1031,8 @@ def err_mainlike_template_decl : Error<"%0 cannot be a template">;
 def err_main_returns_nonint : Error<"'main' must return 'int'">;
 def ext_main_returns_nonint : ExtWarn<"return type of 'main' is not 'int'">,
     InGroup<MainReturnType>;
+def ext_main_no_return : Extension<"non-void 'main' function does not return a value">,
+    InGroup<MainReturnType>;
 def note_main_change_return_type : Note<"change return type to 'int'">;
 def err_main_surplus_args : Error<"too many parameters (%0) for 'main': "
     "must be 0, 2, or 3">;
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 3d6da4f70f99e..38aa3f0edf718 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -550,7 +550,8 @@ struct CheckFallThroughDiagnostics {
   unsigned FunKind; // TODO: use diag::FalloffFunctionKind
   SourceLocation FuncLoc;
 
-  static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
+  static CheckFallThroughDiagnostics MakeForFunction(Sema &S,
+                                                     const Decl *Func) {
     CheckFallThroughDiagnostics D;
     D.FuncLoc = Func->getLocation();
     D.diag_FallThrough_HasNoReturn = diag::warn_noreturn_has_return_expr;
@@ -564,8 +565,13 @@ struct CheckFallThroughDiagnostics {
 
     // Don't suggest that template instantiations be marked "noreturn"
     bool isTemplateInstantiation = false;
-    if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(Func))
+    if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(Func)) {
       isTemplateInstantiation = Function->isTemplateInstantiation();
+      if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().C99 &&
+          Function->isMain()) {
+        D.diag_FallThrough_ReturnsNonVoid = diag::ext_main_no_return;
+      }
+    }
 
     if (!isVirtualMethod && !isTemplateInstantiation)
       D.diag_NeverFallThroughOrReturn = diag::warn_suggest_noreturn_function;
@@ -2737,15 +2743,14 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   // Warning: check missing 'return'
   if (P.enableCheckFallThrough) {
     const CheckFallThroughDiagnostics &CD =
-        (isa<BlockDecl>(D)
-             ? CheckFallThroughDiagnostics::MakeForBlock()
-             : (isa<CXXMethodDecl>(D) &&
-                cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call &&
-                cast<CXXMethodDecl>(D)->getParent()->isLambda())
-                   ? CheckFallThroughDiagnostics::MakeForLambda()
-                   : (fscope->isCoroutine()
-                          ? CheckFallThroughDiagnostics::MakeForCoroutine(D)
-                          : CheckFallThroughDiagnostics::MakeForFunction(D)));
+        (isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
+         : (isa<CXXMethodDecl>(D) &&
+            cast<CXXMethodDecl>(D)->getOverloadedOperator() == OO_Call &&
+            cast<CXXMethodDecl>(D)->getParent()->isLambda())
+             ? CheckFallThroughDiagnostics::MakeForLambda()
+             : (fscope->isCoroutine()
+                    ? CheckFallThroughDiagnostics::MakeForCoroutine(D)
+                    : CheckFallThroughDiagnostics::MakeForFunction(S, D)));
     CheckFallThroughForBody(S, D, Body, BlockType, CD, AC);
   }
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d630f9bd409fd..b020a1b2fd3e0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16230,7 +16230,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
 
       // If the function implicitly returns zero (like 'main') or is naked,
       // don't complain about missing return statements.
-      if (FD->hasImplicitReturnZero() || FD->hasAttr<NakedAttr>())
+      if ((FD->hasImplicitReturnZero() &&
+           (getLangOpts().CPlusPlus || getLangOpts().C99 || !FD->isMain())) ||
+          FD->hasAttr<NakedAttr>())
         WP.disableCheckFallThrough();
 
       // MSVC permits the use of pure specifier (=0) on function definition,
diff --git a/clang/test/Sema/main-no-return-c89.c b/clang/test/Sema/main-no-return-c89.c
new file mode 100644
index 0000000000000..19d55916fc833
--- /dev/null
+++ b/clang/test/Sema/main-no-return-c89.c
@@ -0,0 +1,6 @@
+/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -pedantic -Wno-strict-prototypes %s
+ */
+
+int main() {
+
+} /* expected-warning {{non-void 'main' function does not return a value}} */

>From 42a8440b90f3a091326aa9f74b98bb5725176da2 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 7 Apr 2025 22:44:41 +0300
Subject: [PATCH 2/4] add an explanatory comment

---
 clang/lib/Sema/SemaDecl.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b020a1b2fd3e0..6f35873c6b69f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16230,6 +16230,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
 
       // If the function implicitly returns zero (like 'main') or is naked,
       // don't complain about missing return statements.
+      // Clang implicitly returns 0 in C89 mode, but that's considered an
+      // extension. The check is necessary to ensure the expected extension
+      // warning is emitted in C89 mode.
       if ((FD->hasImplicitReturnZero() &&
            (getLangOpts().CPlusPlus || getLangOpts().C99 || !FD->isMain())) ||
           FD->hasAttr<NakedAttr>())

>From 26987a788b471c50c09009d9a63b5b1dcaf7fac7 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 7 Apr 2025 22:44:59 +0300
Subject: [PATCH 3/4] update diagnostic message

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 34f2ee883ca63..aac2c10e502c4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1031,7 +1031,7 @@ def err_mainlike_template_decl : Error<"%0 cannot be a template">;
 def err_main_returns_nonint : Error<"'main' must return 'int'">;
 def ext_main_returns_nonint : ExtWarn<"return type of 'main' is not 'int'">,
     InGroup<MainReturnType>;
-def ext_main_no_return : Extension<"non-void 'main' function does not return a value">,
+def ext_main_no_return : Extension<"implicit '0' return value from 'main' is a C99 extension">,
     InGroup<MainReturnType>;
 def note_main_change_return_type : Note<"change return type to 'int'">;
 def err_main_surplus_args : Error<"too many parameters (%0) for 'main': "

>From 45821fd65f744df37daf3727ba624f8c171edff4 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 7 Apr 2025 22:45:12 +0300
Subject: [PATCH 4/4] add additional tests

---
 clang/test/Sema/main-no-return-c89-1.c | 10 ++++++++++
 clang/test/Sema/main-no-return-c89-2.c | 11 +++++++++++
 clang/test/Sema/main-no-return-c89-3.c |  8 ++++++++
 clang/test/Sema/main-no-return-c89.c   |  6 ------
 4 files changed, 29 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/Sema/main-no-return-c89-1.c
 create mode 100644 clang/test/Sema/main-no-return-c89-2.c
 create mode 100644 clang/test/Sema/main-no-return-c89-3.c
 delete mode 100644 clang/test/Sema/main-no-return-c89.c

diff --git a/clang/test/Sema/main-no-return-c89-1.c b/clang/test/Sema/main-no-return-c89-1.c
new file mode 100644
index 0000000000000..97ba040527025
--- /dev/null
+++ b/clang/test/Sema/main-no-return-c89-1.c
@@ -0,0 +1,10 @@
+/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -pedantic %s
+ * RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -Wmain-return-type %s
+ * RUN: %clang_cc1 -std=c89 -fsyntax-only -verify=implicit-main-return -Wno-strict-prototypes -pedantic -Wno-main-return-type %s
+ */
+
+/* implicit-main-return-no-diagnostics */
+
+int main() {
+
+} /* expected-warning {{implicit '0' return value from 'main' is a C99 extension}} */
diff --git a/clang/test/Sema/main-no-return-c89-2.c b/clang/test/Sema/main-no-return-c89-2.c
new file mode 100644
index 0000000000000..080229be8a5dc
--- /dev/null
+++ b/clang/test/Sema/main-no-return-c89-2.c
@@ -0,0 +1,11 @@
+/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -Wmain-return-type %s
+ */
+
+/* expected-no-diagnostics */
+
+void exit(int);
+
+int main() {
+    if (1)
+        exit(1);
+}
diff --git a/clang/test/Sema/main-no-return-c89-3.c b/clang/test/Sema/main-no-return-c89-3.c
new file mode 100644
index 0000000000000..e48e33c6ebd6c
--- /dev/null
+++ b/clang/test/Sema/main-no-return-c89-3.c
@@ -0,0 +1,8 @@
+/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -Wno-strict-prototypes -Wmain-return-type %s
+ */
+
+/* expected-no-diagnostics */
+
+int main() {
+    return 0;
+}
diff --git a/clang/test/Sema/main-no-return-c89.c b/clang/test/Sema/main-no-return-c89.c
deleted file mode 100644
index 19d55916fc833..0000000000000
--- a/clang/test/Sema/main-no-return-c89.c
+++ /dev/null
@@ -1,6 +0,0 @@
-/* RUN: %clang_cc1 -std=c89 -fsyntax-only -verify -pedantic -Wno-strict-prototypes %s
- */
-
-int main() {
-
-} /* expected-warning {{non-void 'main' function does not return a value}} */



More information about the cfe-commits mailing list