https://github.com/jchcrsp updated 
https://github.com/llvm/llvm-project/pull/109714

>From 8f512e2a8ad110b1a74c4283f81d4e28555e7567 Mon Sep 17 00:00:00 2001
From: Jeffrey Crowell <j...@apple.com>
Date: Mon, 23 Sep 2024 16:17:32 -0400
Subject: [PATCH] clang should have a warning to disallow re-externs

clang should have a warning to disallow re-externs from the main C translation 
unit

for example, it would be helpful to warn programmers in this situation

```c
// file1.c
extern int func(int a, int b);
int some_func() {
 func(1,2);
}
```

```c
// file2.c
int func(int a, int b, char *c, int d) {
  // function body
}
```
---
 clang/include/clang/Basic/DiagnosticGroups.td |  3 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  6 ++++
 clang/include/clang/Sema/Sema.h               |  9 +++++
 clang/lib/Sema/Sema.cpp                       |  2 ++
 clang/lib/Sema/SemaDecl.cpp                   | 34 +++++++++++++++++++
 .../Sema/warn-extern-func-not-in-header.c     | 13 +++++++
 6 files changed, 67 insertions(+)
 create mode 100644 clang/test/Sema/warn-extern-func-not-in-header.c

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 7d81bdf827ea0c..9e8bd587094423 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1582,3 +1582,6 @@ def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">;
 // Warnings about using the non-standard extension having an explicit 
specialization
 // with a storage class specifier.
 def ExplicitSpecializationStorageClass : 
DiagGroup<"explicit-specialization-storage-class">;
+
+// Warnings about extern functions not in header files.
+def ExternalDeclaration : DiagGroup<"external-declaration">;
\ No newline at end of file
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e4e04bff8b5120..16529dab1e245d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12614,4 +12614,10 @@ def err_acc_loop_spec_conflict
 // AMDGCN builtins diagnostics
 def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size 
value">;
 def note_amdgcn_global_load_lds_size_valid_value : Note<"size must be 1, 2, or 
4">;
+
+// SemaExternWarning diagnostics
+def warn_extern_func_not_in_header : Warning<
+  "extern function '%0' declared in main file '%1' instead of a header">,
+  InGroup<ExternalDeclaration>, DefaultIgnore;
+
 } // end of sema component.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e1c3a99cfa167e..76a9f4c84f47f7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4390,6 +4390,15 @@ class Sema final : public SemaBase {
   // linkage or not.
   static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD);
 
+  ///@}
+  /// \name CheckExternFunction
+  ///@{
+public:
+  void CheckExternDecl(Decl *D);
+  void CheckDeferredExternDecls();
+
+private:
+  std::vector<FunctionDecl *> ExternFuncDecls;
   ///@}
 
   //
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 6d7a57d7b5a41a..4a4aefcd38e4c7 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1177,6 +1177,8 @@ void Sema::ActOnEndOfTranslationUnit() {
   if (PP.isCodeCompletionEnabled())
     return;
 
+  CheckDeferredExternDecls();
+
   // Complete translation units and modules define vtables and perform implicit
   // instantiations. PCH files do not.
   if (TUKind != TU_Prefix) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 31bf50a32a83c3..e2dbf9521aec6c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6058,6 +6058,10 @@ Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
       Dcl && Dcl->getDeclContext()->isFileContext())
     Dcl->setTopLevelDeclInObjCContainer();
 
+  if (Dcl) {
+    CheckExternDecl(Dcl);
+  }
+
   if (!Bases.empty())
     OpenMP().ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(Dcl,
                                                                         Bases);
@@ -20354,3 +20358,33 @@ bool Sema::diagnoseConflictingFunctionEffect(
 
   return false;
 }
+
+void Sema::CheckExternDecl(Decl *D) {
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    SourceLocation Loc = FD->getLocation();
+    SourceManager &SM = Context.getSourceManager();
+
+    // Only consider extern function declarations (not definitions) in the main
+    // file
+    if (FD->isExternC() && !FD->isImplicit() && !FD->getBuiltinID() &&
+        !FD->hasBody() && !FD->isThisDeclarationADefinition() &&
+        FD->isFirstDecl() && SM.isInMainFile(Loc)) {
+      // Defer the warning check until the end of the translation unit
+      ExternFuncDecls.push_back(FD);
+    }
+  }
+}
+
+void Sema::CheckDeferredExternDecls() {
+  SourceManager &SM = Context.getSourceManager();
+  for (FunctionDecl *FD : ExternFuncDecls) {
+    // Check if there's a definition in the same file
+    const FunctionDecl *Definition = FD->getDefinition();
+    if (!Definition || !SM.isInMainFile(Definition->getLocation())) {
+      SourceLocation Loc = FD->getLocation();
+      Diag(Loc, diag::warn_extern_func_not_in_header)
+          << FD->getName() << SM.getFilename(Loc);
+    }
+  }
+  ExternFuncDecls.clear();
+}
diff --git a/clang/test/Sema/warn-extern-func-not-in-header.c 
b/clang/test/Sema/warn-extern-func-not-in-header.c
new file mode 100644
index 00000000000000..62ba2a00400dd6
--- /dev/null
+++ b/clang/test/Sema/warn-extern-func-not-in-header.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wexternal-declaration %s
+
+extern int
+foo(int); // expected-warning{{extern function 'foo' declared in main file}}
+
+int bar(int);
+int bar(int x) {
+  return x + 1;
+}
+
+int main() {
+  return foo(42) + bar(10);
+}
\ No newline at end of file

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to