Skip to content

Conversation

@AaronBallman
Copy link
Collaborator

This paper (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf) allows a source file to end without a newline. Clang has supported this as a conforming extension for a long time, so this suppresses the diagnotic in C2y mode but continues to diagnose as an extension in earlier language modes. It also continues to diagnose if the user passes -Wnewline-eof explicitly.

This paper allows a source file to end without a newline. Clang has
supported this as a conforming extension for a long time, so this
suppresses the diagnotic in C2y mode but continues to diagnose as an
extension in earlier language modes. It also continues to diagnose if
the user passes -Wnewline-eof explicitly.
@AaronBallman AaronBallman added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" c2y labels Mar 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 6, 2025

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

Changes

This paper (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf) allows a source file to end without a newline. Clang has supported this as a conforming extension for a long time, so this suppresses the diagnotic in C2y mode but continues to diagnose as an extension in earlier language modes. It also continues to diagnose if the user passes -Wnewline-eof explicitly.


Full diff: https://github.com/llvm/llvm-project/pull/130180.diff

4 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Lex/Lexer.cpp (+7-8)
  • (added) clang/test/C/C2y/n3411.c (+14)
  • (modified) clang/www/c_status.html (+1-1)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 38b5bdf1dd46b..36aeb6c736627 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -108,6 +108,9 @@ C Language Changes
 
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
+- Implemented N3411 which allows a source file to not end with a newline
+  character. This is still reported as a conforming extension in earlier
+  language modes.
 
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 087c6f13aea66..c62a9f5041183 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3192,23 +3192,22 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
   if (CurPtr != BufferStart && (CurPtr[-1] != '\n' && CurPtr[-1] != '\r')) {
     DiagnosticsEngine &Diags = PP->getDiagnostics();
     SourceLocation EndLoc = getSourceLocation(BufferEnd);
-    unsigned DiagID;
+    unsigned DiagID = diag::warn_no_newline_eof;
 
     if (LangOpts.CPlusPlus11) {
       // C++11 [lex.phases] 2.2 p2
       // Prefer the C++98 pedantic compatibility warning over the generic,
       // non-extension, user-requested "missing newline at EOF" warning.
-      if (!Diags.isIgnored(diag::warn_cxx98_compat_no_newline_eof, EndLoc)) {
+      if (!Diags.isIgnored(diag::warn_cxx98_compat_no_newline_eof, EndLoc))
         DiagID = diag::warn_cxx98_compat_no_newline_eof;
-      } else {
-        DiagID = diag::warn_no_newline_eof;
-      }
     } else {
-      DiagID = diag::ext_no_newline_eof;
+      // This is conforming in C2y, but is an extension in earlier language
+      // modes.
+      if (!LangOpts.C2y)
+        DiagID = diag::ext_no_newline_eof;
     }
 
-    Diag(BufferEnd, DiagID)
-      << FixItHint::CreateInsertion(EndLoc, "\n");
+    Diag(BufferEnd, DiagID) << FixItHint::CreateInsertion(EndLoc, "\n");
   }
 
   BufferPtr = CurPtr;
diff --git a/clang/test/C/C2y/n3411.c b/clang/test/C/C2y/n3411.c
new file mode 100644
index 0000000000000..edfc1c16fe365
--- /dev/null
+++ b/clang/test/C/C2y/n3411.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -verify=c2y -std=c2y -Wall -pedantic %s
+// RUN: %clang_cc1 -verify -Wnewline-eof -std=c2y -Wall -pedantic %s
+// RUN: %clang_cc1 -verify -std=c23 -Wall -pedantic %s
+
+/* WG14 N3411: Yes
+ * Slay Some Earthly Demons XII
+ *
+ * Allow a non-empty source file to end without a final newline character. Note
+ * that this file intentionally does not end with a trailing newline.
+ */
+// c2y-no-diagnostics
+
+int x; // Ensure the file contains at least one declaration.
+// expected-warning {{no newline at end of file}}
\ No newline at end of file
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 4e912987b69c6..e8b50a37093f9 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -294,7 +294,7 @@ <h2 id="c2y">C2y implementation status</h2>
     <tr>
       <td>Slay Some Earthly Demons XII</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf">N3411</a></td>
-      <td class="unknown" align="center">Unknown</td>
+      <td class="unreleased" align="center">Clang 21</td>
 	</tr>
     <tr>
       <td>Slay Some Earthly Demons XIII</td>

Comment on lines +3204 to +3207
// This is conforming in C2y, but is an extension in earlier language
// modes.
if (!LangOpts.C2y)
DiagID = diag::ext_no_newline_eof;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that the corresponding change in C++ was introduced by a CWG issue (CWG787). Would it be preferred to treat N3411 as a DR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, WG14 has a list of such things and this paper isn't on the list: https://www.open-std.org/jtc1/sc22/wg14/www/previous.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, GCC does not warn https://compiler-explorer.com/z/vWMf6W551 , so I question the value of bundling that warning in -fpedantic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-pedantic exists to diagnose extensions and this is definitively an extension. So that's a GCC bug IMO. :-)

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to approve as-is, but would you be willing to ask WG14 if they would consider encouraging backporting the feature?

@AaronBallman
Copy link
Collaborator Author

I'm happy to approve as-is, but would you be willing to ask WG14 if they would consider encouraging backporting the feature?

Absolutely!

@AaronBallman AaronBallman merged commit 9fc3310 into llvm:main Mar 7, 2025
10 of 11 checks passed
@AaronBallman AaronBallman deleted the aballman-wg14-n3411 branch March 7, 2025 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c2y clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants