diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 7e18f3806a143..d418195ec3e3b 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -20,6 +20,7 @@ #include "ClangTidyModuleRegistry.h" #include "ClangTidyProfiling.h" #include "ExpandModularHeadersPPCallbacks.h" +#include "GlobList.h" #include "clang-tidy-config.h" #include "clang/AST/ASTConsumer.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -581,7 +582,32 @@ runClangTidy(clang::tidy::ClangTidyContext &Context, return AdjustedArgs; }; + // Add extra arguments required by clang-additional-diagnostic-* checks. + ArgumentsAdjuster ClangAdditionalDiagnosticArgumentsInserter = + [&Context](const CommandLineArguments &Args, StringRef Filename) { + ClangTidyOptions Opts = Context.getOptionsForFile(Filename); + if (!Opts.Checks) + return Args; + + CommandLineArguments AdjustedArgs = Args; + CachedGlobList Filter(*Opts.Checks); + + for (StringRef Flag : clang::DiagnosticIDs::getDiagnosticFlags()) { + if (Flag.starts_with("-Wno")) + continue; + + std::string CheckName = "clang-additional-diagnostic-"; + CheckName += Flag.drop_front(2); + + if (Filter.contains(CheckName)) + AdjustedArgs.insert(AdjustedArgs.end(), Flag.str()); + } + + return AdjustedArgs; + }; + Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter); + Tool.appendArgumentsAdjuster(ClangAdditionalDiagnosticArgumentsInserter); Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); Context.setEnableProfiling(EnableCheckProfile); Context.setProfileStoragePrefix(StoreCheckProfile); 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")) { + 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("declaratorDecl"); + if (!MatchedDeclaratorDecl) { + return; + } + + if (const auto *MatchedFunctionDecl = + llvm::dyn_cast(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( "portability-avoid-pragma-once"); + CheckFactories.registerCheck( + "portability-avoid-unprototyped-functions"); CheckFactories.registerCheck( "portability-restrict-system-includes"); CheckFactories.registerCheck( 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 + ` 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 `, "Yes" :doc:`performance-unnecessary-value-param `, "Yes" :doc:`portability-avoid-pragma-once `, + :doc:`portability-avoid-unprototyped-functions `, :doc:`portability-restrict-system-includes `, "Yes" :doc:`portability-simd-intrinsics `, :doc:`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();