Author: Karl Friebel
Date: 2026-02-11T15:47:36Z
New Revision: 0776af16c57cecca98472888f1bfc6f83151c427

URL: 
https://github.com/llvm/llvm-project/commit/0776af16c57cecca98472888f1bfc6f83151c427
DIFF: 
https://github.com/llvm/llvm-project/commit/0776af16c57cecca98472888f1bfc6f83151c427.diff

LOG: [Clang] [Sema] Fix FixIt for implicit-int diagnostics. (#179356)

When encountering a declaration without a type specifier, in contexts
where they could reasonably be assumed to default to int, clang emits a
diagnostic with FixIt. This FixIt does not produce working code.

This patch updates `SemaType` to correctly insert a single int type
specifier per group of declarations, and adds coverage in the FixIt lit test 
suite.

Fixes #179354

Added: 
    clang/test/FixIt/fixit-missing-type-spec.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/SemaType.cpp
    clang/test/SemaOpenCL/invalid-pipes-cl2.0.cl

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 59fdbc80e8bed..982f866e228b6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -228,6 +228,9 @@ Improvements to Clang's diagnostics
   when accessing a member function on a past-the-end array element.
   (#GH179128)
 
+- Added a missing space to the FixIt for the ``implicit-int`` group of 
diagnostics and 
+  made sure that only one such diagnostic and FixIt is emitted per declaration 
group. (#GH179354)
+
 Improvements to Clang's time-trace
 ----------------------------------
 

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index be9e0a39a3781..5d18414b1a746 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1188,7 +1188,8 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator 
&D,
   // declaration-specifiers are completely optional in the grammar.
   if (getLangOpts().isImplicitIntRequired() && D.getDeclSpec().isEmpty()) {
     Diag(D.getIdentifierLoc(), diag::warn_missing_type_specifier)
-        << D.getDeclSpec().getSourceRange();
+        << D.getDeclSpec().getSourceRange()
+        << FixItHint::CreateInsertion(D.getDeclSpec().getBeginLoc(), "int ");
     const char *PrevSpec;
     unsigned DiagID;
     const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();

diff  --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 348823ab2e9ca..a6d4b989cae3d 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -978,17 +978,24 @@ static QualType ConvertDeclSpecToType(TypeProcessingState 
&state) {
     // parser already though by it pretending to have seen an 'int' in this
     // case.
     if (S.getLangOpts().isImplicitIntRequired()) {
-      S.Diag(DeclLoc, diag::warn_missing_type_specifier)
-          << DS.getSourceRange()
-          << FixItHint::CreateInsertion(DS.getBeginLoc(), "int");
+      // Only emit the diagnostic for the first declarator in a DeclGroup, as
+      // the warning is always implied for all subsequent declarators, and the
+      // fix must only be applied exactly once as well.
+      if (declarator.isFirstDeclarator()) {
+        S.Diag(DeclLoc, diag::warn_missing_type_specifier)
+            << DS.getSourceRange()
+            << FixItHint::CreateInsertion(DS.getBeginLoc(), "int ");
+      }
     } else if (!DS.hasTypeSpecifier()) {
       // C99 and C++ require a type specifier.  For example, C99 6.7.2p2 says:
       // "At least one type specifier shall be given in the declaration
-      // specifiers in each declaration, and in the specifier-qualifier list in
-      // each struct declaration and type name."
+      // specifiers in each declaration, and in the specifier-qualifier list
+      // in each struct declaration and type name."
       if (!S.getLangOpts().isImplicitIntAllowed() && !DS.isTypeSpecPipe()) {
-        S.Diag(DeclLoc, diag::err_missing_type_specifier)
-            << DS.getSourceRange();
+        if (declarator.isFirstDeclarator()) {
+          S.Diag(DeclLoc, diag::err_missing_type_specifier)
+              << DS.getSourceRange();
+        }
 
         // When this occurs, often something is very broken with the value
         // being declared, poison it as invalid so we don't get chains of
@@ -996,15 +1003,17 @@ static QualType 
ConvertDeclSpecToType(TypeProcessingState &state) {
         declarator.setInvalidType(true);
       } else if (S.getLangOpts().getOpenCLCompatibleVersion() >= 200 &&
                  DS.isTypeSpecPipe()) {
-        S.Diag(DeclLoc, diag::err_missing_actual_pipe_type)
-            << DS.getSourceRange();
+        if (declarator.isFirstDeclarator()) {
+          S.Diag(DeclLoc, diag::err_missing_actual_pipe_type)
+              << DS.getSourceRange();
+        }
         declarator.setInvalidType(true);
-      } else {
+      } else if (declarator.isFirstDeclarator()) {
         assert(S.getLangOpts().isImplicitIntAllowed() &&
                "implicit int is disabled?");
         S.Diag(DeclLoc, diag::ext_missing_type_specifier)
             << DS.getSourceRange()
-            << FixItHint::CreateInsertion(DS.getBeginLoc(), "int");
+            << FixItHint::CreateInsertion(DS.getBeginLoc(), "int ");
       }
     }
 

diff  --git a/clang/test/FixIt/fixit-missing-type-spec.c 
b/clang/test/FixIt/fixit-missing-type-spec.c
new file mode 100644
index 0000000000000..a8804593bc496
--- /dev/null
+++ b/clang/test/FixIt/fixit-missing-type-spec.c
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -std=c90 -pedantic -Wno-comment 
-Wno-deprecated-non-prototype -Wimplicit-int -fsyntax-only -verify=c90 -x c %s
+// RUN: cp %s %t
+// RUN: not %clang_cc1 -std=c90 -pedantic -Wno-comment 
-Wno-deprecated-non-prototype -Wimplicit-int -Werror -fixit %t
+// RUN: %clang_cc1 -std=c90 -pedantic -Wno-comment 
-Wno-deprecated-non-prototype -Wimplicit-int -fsyntax-only -verify -x c %t
+// RUN: cat %t | FileCheck %s
+//
+// RUN: %clang_cc1 -std=c99 -pedantic -Wno-deprecated-non-prototype 
-fsyntax-only -verify=c99 -x c %s
+// RUN: cp %s %t
+// RUN: not %clang_cc1 -std=c99 -pedantic -Wno-deprecated-non-prototype -fixit 
%t
+// RUN: %clang_cc1 -std=c99 -pedantic -Wno-deprecated-non-prototype 
-fsyntax-only -verify -x c %t
+// RUN: cat %t | FileCheck %s
+//
+// RUN: %clang_cc1 -std=c23 -pedantic -fsyntax-only -verify=c23 -x c %s
+
+// expected-no-diagnostics
+
+// CHECK: int imp0[4],imp1,imp2=5;
+imp0[4],imp1,imp2=5;
+// c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+// c99-error@-2 {{type specifier missing, defaults to 'int'}}
+// c23-error@-3 {{a type specifier is required for all declarations}}
+// c23-error@-4 {{expected ';' after top level declarator}}
+
+// CHECK: int const imp3;
+const imp3;
+// c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+// c99-error@-2 {{type specifier missing, defaults to 'int'}}
+// c23-error@-3 {{a type specifier is required for all declarations}}
+
+// CHECK: int static imp4;
+static imp4;
+// c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+// c99-error@-2 {{type specifier missing, defaults to 'int'}}
+// c23-error@-3 {{a type specifier is required for all declarations}}
+
+// CHECK: int static const imp5;
+static const imp5;
+// c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+// c99-error@-2 {{type specifier missing, defaults to 'int'}}
+// c23-error@-3 {{a type specifier is required for all declarations}}
+
+// CHECK: int volatile __attribute__ ((aligned (16))) imp6;
+volatile __attribute__ ((aligned (16))) imp6;
+// c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+// c99-error@-2 {{type specifier missing, defaults to 'int'}}
+// c23-error@-3 {{a type specifier is required for all declarations}}
+
+// CHECK-LABEL: int f2(void)
+f2(void)
+// c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+// c99-error@-2 {{type specifier missing, defaults to 'int'}}
+// c23-error@-3 {{a type specifier is required for all declarations}}
+{
+  // CHECK: int register __attribute__ ((uninitialized)) i;
+  register __attribute__ ((uninitialized)) i;
+  // c90-warning@-1 {{type specifier missing, defaults to 'int'}}
+  // c99-error@-2 {{type specifier missing, defaults to 'int'}}
+  // c23-error@-3 {{a type specifier is required for all declarations}}
+
+  return 0;
+}

diff  --git a/clang/test/SemaOpenCL/invalid-pipes-cl2.0.cl 
b/clang/test/SemaOpenCL/invalid-pipes-cl2.0.cl
index edfce02f2ea25..089b2f16765fd 100644
--- a/clang/test/SemaOpenCL/invalid-pipes-cl2.0.cl
+++ b/clang/test/SemaOpenCL/invalid-pipes-cl2.0.cl
@@ -16,6 +16,8 @@ extern pipe write_only int get_pipe(void); // expected-error 
{{'write_only' attr
 // expected-error-re@-5{{type '__private write_only pipe int ({{(void)?}})' 
can only be used as a function parameter in OpenCL}}
 #endif
 
+global pipe notype1, notype2; // expected-error {{missing actual type 
specifier for pipe}}
+
 kernel void test_invalid_reserved_id(reserve_id_t ID) { // expected-error 
{{'__private reserve_id_t' cannot be used as the type of a kernel parameter}}
 }
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to