[clang-tools-extra] [clang-tidy] Portability Avoid Unprototyped Functions Check (PR #161023)

via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 27 14:17:50 PDT 2025


https://github.com/isuckatcs updated https://github.com/llvm/llvm-project/pull/161023

>From 51cf252c590d15c7be2ea5a25e7e86ea5e6772c8 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 27 Sep 2025 22:32:15 +0200
Subject: [PATCH] [clang-tidy] Portability Avoid Unprototyped Functions Check

---
 .../AvoidUnprototypedFunctionsCheck.cpp       | 58 +++++++++++++++
 .../AvoidUnprototypedFunctionsCheck.h         | 33 +++++++++
 .../clang-tidy/portability/CMakeLists.txt     |  1 +
 .../portability/PortabilityTidyModule.cpp     |  3 +
 clang-tools-extra/docs/ReleaseNotes.rst       |  5 ++
 .../docs/clang-tidy/checks/list.rst           |  1 +
 .../avoid-unprototyped-functions.rst          | 73 +++++++++++++++++++
 .../avoid-unprototyped-functions.c            | 32 ++++++++
 8 files changed, 206 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/portability/avoid-unprototyped-functions.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/portability/avoid-unprototyped-functions.c

diff --git a/clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.cpp
new file mode 100644
index 0000000000000..032986471e3e1
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.cpp
@@ -0,0 +1,58 @@
+//===--- AvoidUnprototypedFunctionsCheck.cpp - clang-tidy -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidUnprototypedFunctionsCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::portability {
+
+void AvoidUnprototypedFunctionsCheck::registerMatchers(MatchFinder *Finder) {
+  auto FunctionTypeMatcher =
+      forEachDescendant(functionType(unless(functionProtoType())));
+  Finder->addMatcher(declaratorDecl(FunctionTypeMatcher).bind("declaratorDecl"),
+                     this);
+  Finder->addMatcher(typedefDecl(FunctionTypeMatcher).bind("typedefDecl"),
+                     this);
+}
+
+void AvoidUnprototypedFunctionsCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *MatchedTypedefDecl =
+          Result.Nodes.getNodeAs<TypedefDecl>("typedefDecl")) {
+    diag(MatchedTypedefDecl->getLocation(),
+         "avoid unprototyped functions in typedef; explicitly add a 'void' "
+         "parameter if the function takes no arguments");
+    return;
+  }
+
+  const auto *MatchedDeclaratorDecl =
+      Result.Nodes.getNodeAs<Decl>("declaratorDecl");
+  if (!MatchedDeclaratorDecl) {
+    return;
+  }
+
+  if (const auto *MatchedFunctionDecl =
+          llvm::dyn_cast<FunctionDecl>(MatchedDeclaratorDecl)) {
+    if (MatchedFunctionDecl->isMain() ||
+        MatchedFunctionDecl->hasWrittenPrototype())
+      return;
+
+    diag(MatchedFunctionDecl->getLocation(),
+         "avoid unprototyped function declarations; explicitly spell out a "
+         "single 'void' parameter if the function takes no argument");
+    return;
+  }
+
+  diag(MatchedDeclaratorDecl->getLocation(),
+       "avoid unprototyped functions in type specifiers; explicitly add a "
+       "'void' parameter if the function takes no arguments");
+}
+
+} // namespace clang::tidy::portability
diff --git a/clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.h b/clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.h
new file mode 100644
index 0000000000000..fb89dc6526a24
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/AvoidUnprototypedFunctionsCheck.h
@@ -0,0 +1,33 @@
+//===--- AvoidUnprototypedFunctionsCheck.h - clang-tidy ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDUNPROTOTYPEDFUNCTIONSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDUNPROTOTYPEDFUNCTIONSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// Checks if unprototyped function types are used in the source code.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/avoid-unprototyped-functions.html
+class AvoidUnprototypedFunctionsCheck : public ClangTidyCheck {
+public:
+  AvoidUnprototypedFunctionsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return !LangOpts.CPlusPlus;
+  }
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDUNPROTOTYPEDFUNCTIONSCHECK_H
diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
index 73d74a550afc0..7140469ca1515 100644
--- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangTidyPortabilityModule STATIC
   AvoidPragmaOnceCheck.cpp
+  AvoidUnprototypedFunctionsCheck.cpp
   PortabilityTidyModule.cpp
   RestrictSystemIncludesCheck.cpp
   SIMDIntrinsicsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
index e73e95455d3a5..ee0c64712bacd 100644
--- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "AvoidPragmaOnceCheck.h"
+#include "AvoidUnprototypedFunctionsCheck.h"
 #include "RestrictSystemIncludesCheck.h"
 #include "SIMDIntrinsicsCheck.h"
 #include "StdAllocatorConstCheck.h"
@@ -23,6 +24,8 @@ class PortabilityModule : public ClangTidyModule {
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<AvoidPragmaOnceCheck>(
         "portability-avoid-pragma-once");
+    CheckFactories.registerCheck<AvoidUnprototypedFunctionsCheck>(
+        "portability-avoid-unprototyped-functions");
     CheckFactories.registerCheck<RestrictSystemIncludesCheck>(
         "portability-restrict-system-includes");
     CheckFactories.registerCheck<SIMDIntrinsicsCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9257dc6b98ba2..9997bb0ac60a7 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -208,6 +208,11 @@ New checks
 
   Detect redundant parentheses.
 
+- New :doc:`portability-avoid-unprototyped-functions
+  <clang-tidy/checks/portability/avoid-unprototyped-functions>` check.
+
+  Checks if unprototyped function types are used in the source code.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index f94696d4ef9c7..57c4321877e9d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -362,6 +362,7 @@ Clang-Tidy Checks
    :doc:`performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization>`, "Yes"
    :doc:`performance-unnecessary-value-param <performance/unnecessary-value-param>`, "Yes"
    :doc:`portability-avoid-pragma-once <portability/avoid-pragma-once>`,
+   :doc:`portability-avoid-unprototyped-functions <portability/avoid-unprototyped-functions>`,
    :doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
    :doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
    :doc:`portability-std-allocator-const <portability/std-allocator-const>`,
diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-unprototyped-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-unprototyped-functions.rst
new file mode 100644
index 0000000000000..3d7ed212b61d9
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-unprototyped-functions.rst
@@ -0,0 +1,73 @@
+.. title:: clang-tidy - portability-avoid-unprototyped-functions
+
+portability-avoid-unprototyped-functions
+========================================
+
+Checks if unprototyped function types are used in the source code.
+
+For example:
+
+.. code-block:: c
+
+  void foo();         // Bad: unprototyped function declaration
+  void foo(void);     // Good: a function declaration that takes no parameters
+
+  void (*ptr)();      // Bad: pointer to an unprototyped function
+  void (*ptr)(void);  // Good: pointer to a function that takes no parameters
+
+Before C23 ``void foo()`` means a function that takes any number of parameters, so the following snippet is valid.
+
+.. code-block:: c
+
+  // -std=c17
+  void foo();
+
+  int main() {
+    foo(1, 2, 3);
+
+    return 0;
+  }
+
+Starting from C23 however, ``void foo()`` means a function that takes no parameters, so the same snippet is invalid.
+
+.. code-block:: c
+
+  // -std=c23
+  void foo();
+
+  int main() {
+    foo(1, 2, 3);
+  //    ^ error: too many arguments to function call, expected 0, have 3
+
+    return 0;
+  }
+
+Similarly a pointer to an unprototyped function binds to any function before C23, so the following snippet is considered valid.
+
+.. code-block:: c
+
+  // -std=c17
+  void foo(int x, int y);
+
+  int main() {
+    void (*ptr)() = &foo;
+
+    return 0;
+  }
+
+From C23 however, the smae pointer will only bind to functions that take no parameters.
+
+.. code-block:: c
+
+  // -std=c23
+  void foo(int x, int y);
+
+  int main() {
+    void (*ptr)() = &foo;
+    //    ^ error: incompatible function pointer types initializing 'void (*)(void)' with an expression of type 'void (*)(int, int)'
+
+    return 0;
+  }
+
+Some codebases might explicitly take advantage of the pre-C23 behavior, but these codebases should also be aware that
+their solution might not be fully portable between compilers.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-unprototyped-functions.c b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-unprototyped-functions.c
new file mode 100644
index 0000000000000..0efc029f5c63f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-unprototyped-functions.c
@@ -0,0 +1,32 @@
+// RUN: %check_clang_tidy %s portability-avoid-unprototyped-functions %t
+struct S {
+  int (*UnprototypedFunctionPtrField)();
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid unprototyped functions in type specifiers; explicitly add a 'void' parameter if the function takes no arguments
+};
+
+void unprototypedFunction();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid unprototyped function declarations; explicitly spell out a single 'void' parameter if the function takes no argument
+
+void unprototypedParamter(int (*UnprototypedFunctionPtrParameter)());
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: avoid unprototyped functions in type specifiers; explicitly add a 'void' parameter if the function takes no arguments
+
+void unprototypedVariable() {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid unprototyped function declarations; explicitly spell out a single 'void' parameter if the function takes no argument
+
+  int (*UnprototypedFunctionPtrVariable)();
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid unprototyped functions in type specifiers; explicitly add a 'void' parameter if the function takes no arguments
+}
+
+typedef int (*UnprototypedFunctionPtrArray[13])();
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid unprototyped functions in typedef; explicitly add a 'void' parameter if the function takes no arguments
+
+void unprototypedTypeAliasParameter(UnprototypedFunctionPtrArray);
+
+#define ANYARG
+
+void unprototypedMacroParameter(ANYARG);
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid unprototyped function declarations; explicitly spell out a single 'void' parameter if the function takes no argument
+
+void functionWithNoArguments(void);
+
+int main();



More information about the cfe-commits mailing list