diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt index 170fedf52130e..6723e4a727b21 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 + DeprecatedPosixFunctionsCheck.cpp NoAssemblerCheck.cpp PortabilityTidyModule.cpp RestrictSystemIncludesCheck.cpp diff --git a/clang-tools-extra/clang-tidy/portability/DeprecatedPosixFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/portability/DeprecatedPosixFunctionsCheck.cpp new file mode 100644 index 0000000000000..74a8cfe94d813 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/DeprecatedPosixFunctionsCheck.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 "DeprecatedPosixFunctionsCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang::ast_matchers; +using namespace llvm; + +namespace clang::tidy::portability { + +static constexpr StringRef DeprecatedFunctionId = "DeprecatedFunctions"; +static constexpr StringRef DeclRefId = "DRE"; + +static StringRef getReplacementFor(StringRef FunctionName) { + // TODO: Suggest Annex K replacements when available. + return StringSwitch(FunctionName) + .Case("bcmp", "memcmp") + .Case("bcopy", "memmove") + .Case("bzero", "memset") + .Case("getpw", "getpwuid") + .Case("vfork", "posix_spawn") + .Default({}); +} + +void DeprecatedPosixFunctionsCheck::registerMatchers(MatchFinder *Finder) { + const auto FunctionNamesMatcher = + hasAnyName("::bcmp", "::bcopy", "::bzero", "::getpw", "::vfork"); + Finder->addMatcher( + declRefExpr( + to(functionDecl(FunctionNamesMatcher).bind(DeprecatedFunctionId))) + .bind(DeclRefId), + this); +} + +void DeprecatedPosixFunctionsCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *SourceExpr = Result.Nodes.getNodeAs(DeclRefId); + const auto *FuncDecl = + Result.Nodes.getNodeAs(DeprecatedFunctionId); + if (!SourceExpr || !FuncDecl) + return; + + const StringRef FunctionName = FuncDecl->getName(); + diag(SourceExpr->getBeginLoc(), + "function '%0' is deprecated; '%1' should be used instead") + << FunctionName << getReplacementFor(FunctionName); +} + +} // namespace clang::tidy::portability diff --git a/clang-tools-extra/clang-tidy/portability/DeprecatedPosixFunctionsCheck.h b/clang-tools-extra/clang-tidy/portability/DeprecatedPosixFunctionsCheck.h new file mode 100644 index 0000000000000..eb1aa2784173c --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/DeprecatedPosixFunctionsCheck.h @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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_DEPRECATEDPOSIXFUNCTIONSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_DEPRECATEDPOSIXFUNCTIONSCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::portability { + +/// Finds uses of deprecated or obsolete POSIX functions and suggests modern +/// replacements. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/portability/deprecated-posix-functions.html +class DeprecatedPosixFunctionsCheck : public ClangTidyCheck { +public: + DeprecatedPosixFunctionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::portability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_DEPRECATEDPOSIXFUNCTIONSCHECK_H diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp index 1f2340502f685..962a5337c5c35 100644 --- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "AvoidPragmaOnceCheck.h" +#include "DeprecatedPosixFunctionsCheck.h" #include "NoAssemblerCheck.h" #include "RestrictSystemIncludesCheck.h" #include "SIMDIntrinsicsCheck.h" @@ -24,6 +25,8 @@ class PortabilityModule : public ClangTidyModule { void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "portability-avoid-pragma-once"); + CheckFactories.registerCheck( + "portability-deprecated-posix-functions"); CheckFactories.registerCheck("portability-no-assembler"); CheckFactories.registerCheck( "portability-restrict-system-includes"); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index c9749df481bcd..cc3ace2c74200 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -175,6 +175,12 @@ New checks Suggests insertion of ``std::move(...)`` to turn copy assignment operator calls into move assignment ones, when deemed valid and profitable. +- New :doc:`portability-deprecated-posix-functions + ` check. + + Finds uses of deprecated or obsolete POSIX functions and suggests modern + replacements. + - New :doc:`readability-redundant-lambda-parameter-list ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 053ce6f0779d9..d19cbfff4598a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -371,6 +371,7 @@ Clang-Tidy Checks :doc:`performance-unnecessary-value-param `, "Yes" :doc:`performance-use-std-move `, "Yes" :doc:`portability-avoid-pragma-once `, + :doc:`portability-deprecated-posix-functions `, :doc:`portability-no-assembler `, :doc:`portability-restrict-system-includes `, "Yes" :doc:`portability-simd-intrinsics `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/deprecated-posix-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/deprecated-posix-functions.rst new file mode 100644 index 0000000000000..c02dac67c8fd8 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/portability/deprecated-posix-functions.rst @@ -0,0 +1,15 @@ +.. title:: clang-tidy - portability-deprecated-posix-functions + +portability-deprecated-posix-functions +====================================== + +Finds uses of deprecated or obsolete POSIX functions and suggests modern +replacements. + +The following functions are checked: + +- ``bcmp``, suggested replacement: ``memcmp`` +- ``bcopy``, suggested replacement: ``memmove`` +- ``bzero``, suggested replacement: ``memset`` +- ``getpw``, suggested replacement: ``getpwuid`` +- ``vfork``, suggested replacement: ``posix_spawn`` diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/deprecated-posix-functions.c b/clang-tools-extra/test/clang-tidy/checkers/portability/deprecated-posix-functions.c new file mode 100644 index 0000000000000..1c984246645a9 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/deprecated-posix-functions.c @@ -0,0 +1,68 @@ +// RUN: %check_clang_tidy -std=c99-or-later %s portability-deprecated-posix-functions %t + +typedef __SIZE_TYPE__ size_t; +typedef int pid_t; +typedef int uid_t; + +int bcmp(const void *S1, const void *S2, size_t N); +void bcopy(const void *Src, void *Dest, size_t N); +void bzero(void *S, size_t N); +int getpw(uid_t UId, char *Buf); +pid_t vfork(void); + +#define CALL_BZERO(Buffer, Size) bzero(Buffer, Size) +#define DEPRECATED_BCMP bcmp + +void deprecated_posix_functions(void) { + char Buf1[128] = {0}; + char Buf2[128] = {0}; + + bcmp(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead [portability-deprecated-posix-functions] + + bcopy(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcopy' is deprecated; 'memmove' should be used instead + + bzero(Buf1, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bzero' is deprecated; 'memset' should be used instead + + getpw(0, Buf1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'getpw' is deprecated; 'getpwuid' should be used instead + + vfork(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'vfork' is deprecated; 'posix_spawn' should be used instead + + int (*BcmpPtr)(const void *, const void *, size_t) = bcmp; + // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead + + CALL_BZERO(Buf1, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bzero' is deprecated; 'memset' should be used instead + + DEPRECATED_BCMP(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead +} + +void deprecated_posix_functions_in_control_flow(int Flag) { + char Buf1[128] = {0}; + char Buf2[128] = {0}; + + if (bcmp(Buf1, Buf2, sizeof(Buf1)) == 0) + return; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead + + for (int I = 0; I < 2; ++I) + bzero(Buf1, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'bzero' is deprecated; 'memset' should be used instead + + while (getpw(0, Buf1) == 0) + break; + // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: function 'getpw' is deprecated; 'getpwuid' should be used instead + + Flag ? bcopy(Buf1, Buf2, sizeof(Buf1)) : bzero(Buf2, sizeof(Buf2)); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: function 'bcopy' is deprecated; 'memmove' should be used instead + // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: function 'bzero' is deprecated; 'memset' should be used instead + + if (Flag && vfork() == 0) + return; + // CHECK-MESSAGES: :[[@LINE-2]]:15: warning: function 'vfork' is deprecated; 'posix_spawn' should be used instead +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/deprecated-posix-functions.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/deprecated-posix-functions.cpp new file mode 100644 index 0000000000000..f1d1c0a0e5bf3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/deprecated-posix-functions.cpp @@ -0,0 +1,91 @@ +// RUN: %check_clang_tidy -std=c++98-or-later %s portability-deprecated-posix-functions %t + +typedef __SIZE_TYPE__ size_t; +typedef int pid_t; +typedef int uid_t; + +int bcmp(const void *S1, const void *S2, size_t N); +void bcopy(const void *Src, void *Dest, size_t N); +void bzero(void *S, size_t N); +int getpw(uid_t UId, char *Buf); +pid_t vfork(void); + +#define CALL_BZERO(Buffer, Size) bzero(Buffer, Size) +#define DEPRECATED_BCMP bcmp + +void deprecated_posix_functions() { + char Buf1[128] = {0}; + char Buf2[128] = {0}; + + bcmp(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead [portability-deprecated-posix-functions] + + bcopy(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcopy' is deprecated; 'memmove' should be used instead + + bzero(Buf1, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bzero' is deprecated; 'memset' should be used instead + + getpw(0, Buf1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'getpw' is deprecated; 'getpwuid' should be used instead + + vfork(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'vfork' is deprecated; 'posix_spawn' should be used instead + + int (*BcmpPtr)(const void *, const void *, size_t) = bcmp; + // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead + + CALL_BZERO(Buf1, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bzero' is deprecated; 'memset' should be used instead + + DEPRECATED_BCMP(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead + + ::bcmp(Buf1, Buf2, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead +} + +void deprecated_posix_functions_in_control_flow(bool Flag) { + char Buf1[128] = {0}; + char Buf2[128] = {0}; + + if (bcmp(Buf1, Buf2, sizeof(Buf1)) == 0) + return; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: function 'bcmp' is deprecated; 'memcmp' should be used instead + + for (int I = 0; I < 2; ++I) + bzero(Buf1, sizeof(Buf1)); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'bzero' is deprecated; 'memset' should be used instead + + while (getpw(0, Buf1) == 0) + break; + // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: function 'getpw' is deprecated; 'getpwuid' should be used instead + + Flag ? bcopy(Buf1, Buf2, sizeof(Buf1)) : bzero(Buf2, sizeof(Buf2)); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: function 'bcopy' is deprecated; 'memmove' should be used instead + // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: function 'bzero' is deprecated; 'memset' should be used instead + + if (Flag && vfork() == 0) + return; + // CHECK-MESSAGES: :[[@LINE-2]]:15: warning: function 'vfork' is deprecated; 'posix_spawn' should be used instead +} + +namespace non_posix { +int bcmp(const void *S1, const void *S2, size_t N); +void bcopy(const void *Src, void *Dest, size_t N); +void bzero(void *S, size_t N); +int getpw(uid_t UId, char *Buf); +pid_t vfork(void); + +void same_unqualified_names() { + char Buf1[128] = {0}; + char Buf2[128] = {0}; + + // No warnings for functions with the same unqualified names outside the global namespace. + bcmp(Buf1, Buf2, sizeof(Buf1)); + bcopy(Buf1, Buf2, sizeof(Buf1)); + bzero(Buf1, sizeof(Buf1)); + getpw(0, Buf1); + vfork(); +} +} // namespace non_posix