[clang-tidy] Add check 'cert-err33-c'.

The CERT rule ERR33-C can be modeled partially by the existing check
'bugprone-unused-return-value'. The existing check is reused with
a fixed set of checked functions.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D112409
This commit is contained in:
Balázs Kéri 2021-11-02 10:30:14 +01:00
parent 1ca35fc89e
commit 4bcbb3d4d7
6 changed files with 426 additions and 2 deletions

View file

@ -16,6 +16,7 @@
#include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h"
#include "../bugprone/SuspiciousMemoryComparisonCheck.h"
#include "../bugprone/UnhandledSelfAssignmentCheck.h"
#include "../bugprone/UnusedReturnValueCheck.h"
#include "../concurrency/ThreadCanceltypeAsynchronousCheck.h"
#include "../google/UnnamedNamespaceInHeaderCheck.h"
#include "../misc/NewDeleteOverloadsCheck.h"
@ -39,6 +40,193 @@
#include "ThrownExceptionTypeCheck.h"
#include "VariadicFunctionDefCheck.h"
namespace {
// Checked functions for cert-err33-c.
// The following functions are deliberately excluded because they can be called
// with NULL argument and in this case the check is not applicable:
// `mblen, mbrlen, mbrtowc, mbtowc, wctomb, wctomb_s`.
// FIXME: The check can be improved to handle such cases.
const llvm::StringRef CertErr33CCheckedFunctions = "::aligned_alloc;"
"::asctime_s;"
"::at_quick_exit;"
"::atexit;"
"::bsearch;"
"::bsearch_s;"
"::btowc;"
"::c16rtomb;"
"::c32rtomb;"
"::calloc;"
"::clock;"
"::cnd_broadcast;"
"::cnd_init;"
"::cnd_signal;"
"::cnd_timedwait;"
"::cnd_wait;"
"::ctime_s;"
"::fclose;"
"::fflush;"
"::fgetc;"
"::fgetpos;"
"::fgets;"
"::fgetwc;"
"::fopen;"
"::fopen_s;"
"::fprintf;"
"::fprintf_s;"
"::fputc;"
"::fputs;"
"::fputwc;"
"::fputws;"
"::fread;"
"::freopen;"
"::freopen_s;"
"::fscanf;"
"::fscanf_s;"
"::fseek;"
"::fsetpos;"
"::ftell;"
"::fwprintf;"
"::fwprintf_s;"
"::fwrite;"
"::fwscanf;"
"::fwscanf_s;"
"::getc;"
"::getchar;"
"::getenv;"
"::getenv_s;"
"::gets_s;"
"::getwc;"
"::getwchar;"
"::gmtime;"
"::gmtime_s;"
"::localtime;"
"::localtime_s;"
"::malloc;"
"::mbrtoc16;"
"::mbrtoc32;"
"::mbsrtowcs;"
"::mbsrtowcs_s;"
"::mbstowcs;"
"::mbstowcs_s;"
"::memchr;"
"::mktime;"
"::mtx_init;"
"::mtx_lock;"
"::mtx_timedlock;"
"::mtx_trylock;"
"::mtx_unlock;"
"::printf_s;"
"::putc;"
"::putwc;"
"::raise;"
"::realloc;"
"::remove;"
"::rename;"
"::scanf;"
"::scanf_s;"
"::setlocale;"
"::setvbuf;"
"::signal;"
"::snprintf;"
"::snprintf_s;"
"::sprintf;"
"::sprintf_s;"
"::sscanf;"
"::sscanf_s;"
"::strchr;"
"::strerror_s;"
"::strftime;"
"::strpbrk;"
"::strrchr;"
"::strstr;"
"::strtod;"
"::strtof;"
"::strtoimax;"
"::strtok;"
"::strtok_s;"
"::strtol;"
"::strtold;"
"::strtoll;"
"::strtoul;"
"::strtoull;"
"::strtoumax;"
"::strxfrm;"
"::swprintf;"
"::swprintf_s;"
"::swscanf;"
"::swscanf_s;"
"::thrd_create;"
"::thrd_detach;"
"::thrd_join;"
"::thrd_sleep;"
"::time;"
"::timespec_get;"
"::tmpfile;"
"::tmpfile_s;"
"::tmpnam;"
"::tmpnam_s;"
"::tss_create;"
"::tss_get;"
"::tss_set;"
"::ungetc;"
"::ungetwc;"
"::vfprintf;"
"::vfprintf_s;"
"::vfscanf;"
"::vfscanf_s;"
"::vfwprintf;"
"::vfwprintf_s;"
"::vfwscanf;"
"::vfwscanf_s;"
"::vprintf_s;"
"::vscanf;"
"::vscanf_s;"
"::vsnprintf;"
"::vsnprintf_s;"
"::vsprintf;"
"::vsprintf_s;"
"::vsscanf;"
"::vsscanf_s;"
"::vswprintf;"
"::vswprintf_s;"
"::vswscanf;"
"::vswscanf_s;"
"::vwprintf_s;"
"::vwscanf;"
"::vwscanf_s;"
"::wcrtomb;"
"::wcschr;"
"::wcsftime;"
"::wcspbrk;"
"::wcsrchr;"
"::wcsrtombs;"
"::wcsrtombs_s;"
"::wcsstr;"
"::wcstod;"
"::wcstof;"
"::wcstoimax;"
"::wcstok;"
"::wcstok_s;"
"::wcstol;"
"::wcstold;"
"::wcstoll;"
"::wcstombs;"
"::wcstombs_s;"
"::wcstoul;"
"::wcstoull;"
"::wcstoumax;"
"::wcsxfrm;"
"::wctob;"
"::wctrans;"
"::wctype;"
"::wmemchr;"
"::wprintf_s;"
"::wscanf;"
"::wscanf_s;";
} // namespace
namespace clang {
namespace tidy {
namespace cert {
@ -99,6 +287,10 @@ public:
"cert-dcl37-c");
// ENV
CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
// ERR
CheckFactories.registerCheck<bugprone::UnusedReturnValueCheck>(
"cert-err33-c");
CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
// EXP
CheckFactories.registerCheck<bugprone::SuspiciousMemoryComparisonCheck>(
"cert-exp42-c");
@ -108,8 +300,6 @@ public:
"cert-flp37-c");
// FIO
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
// ERR
CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
// MSC
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
CheckFactories.registerCheck<ProperlySeededRandomGeneratorCheck>(
@ -131,6 +321,7 @@ public:
ClangTidyOptions Options;
ClangTidyOptions::OptionMap &Opts = Options.CheckOptions;
Opts["cert-dcl16-c.NewSuffixes"] = "L;LL;LU;LLU";
Opts["cert-err33-c.CheckedFunctions"] = CertErr33CCheckedFunctions;
Opts["cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField"] = "false";
Opts["cert-str34-c.DiagnoseSignedUnsignedCharComparisons"] = "false";
return Options;

View file

@ -103,6 +103,11 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^
- New alias :doc:`cert-err33-c
<clang-tidy/checks/cert-err33-c>` to
:doc:`bugprone-unused-return-value
<clang-tidy/checks/bugprone-unused-return-value>` was added.
- New alias :doc:`cert-exp42-c
<clang-tidy/checks/cert-exp42-c>` to
:doc:`bugprone-suspicious-memory-comparison

View file

@ -45,3 +45,6 @@ Options
- ``std::basic_string::empty()`` and ``std::vector::empty()``. Not using the
return value often indicates that the programmer confused the function with
``clear()``.
`cert-err33-c <cert-err33-c.html>`_ is an alias of this check that checks a
fixed and large set of standard library functions.

View file

@ -0,0 +1,199 @@
.. title:: clang-tidy - cert-err33-c
cert-err33-c
============
Warns on unused function return values. Many of the standard library functions
return a value that indicates if the call was successful. Ignoring the returned
value can cause unexpected behavior if an error has occured. The following
functions are checked:
* aligned_alloc()
* asctime_s()
* at_quick_exit()
* atexit()
* bsearch()
* bsearch_s()
* btowc()
* c16rtomb()
* c32rtomb()
* calloc()
* clock()
* cnd_broadcast()
* cnd_init()
* cnd_signal()
* cnd_timedwait()
* cnd_wait()
* ctime_s()
* fclose()
* fflush()
* fgetc()
* fgetpos()
* fgets()
* fgetwc()
* fopen()
* fopen_s()
* fprintf()
* fprintf_s()
* fputc()
* fputs()
* fputwc()
* fputws()
* fread()
* freopen()
* freopen_s()
* fscanf()
* fscanf_s()
* fseek()
* fsetpos()
* ftell()
* fwprintf()
* fwprintf_s()
* fwrite()
* fwscanf()
* fwscanf_s()
* getc()
* getchar()
* getenv()
* getenv_s()
* gets_s()
* getwc()
* getwchar()
* gmtime()
* gmtime_s()
* localtime()
* localtime_s()
* malloc()
* mbrtoc16()
* mbrtoc32()
* mbsrtowcs()
* mbsrtowcs_s()
* mbstowcs()
* mbstowcs_s()
* memchr()
* mktime()
* mtx_init()
* mtx_lock()
* mtx_timedlock()
* mtx_trylock()
* mtx_unlock()
* printf_s()
* putc()
* putwc()
* raise()
* realloc()
* remove()
* rename()
* setlocale()
* setvbuf()
* scanf()
* scanf_s()
* signal()
* snprintf()
* snprintf_s()
* sprintf()
* sprintf_s()
* sscanf()
* sscanf_s()
* strchr()
* strerror_s()
* strftime()
* strpbrk()
* strrchr()
* strstr()
* strtod()
* strtof()
* strtoimax()
* strtok()
* strtok_s()
* strtol()
* strtold()
* strtoll()
* strtoumax()
* strtoul()
* strtoull()
* strxfrm()
* swprintf()
* swprintf_s()
* swscanf()
* swscanf_s()
* thrd_create()
* thrd_detach()
* thrd_join()
* thrd_sleep()
* time()
* timespec_get()
* tmpfile()
* tmpfile_s()
* tmpnam()
* tmpnam_s()
* tss_create()
* tss_get()
* tss_set()
* ungetc()
* ungetwc()
* vfprintf()
* vfprintf_s()
* vfscanf()
* vfscanf_s()
* vfwprintf()
* vfwprintf_s()
* vfwscanf()
* vfwscanf_s()
* vprintf_s()
* vscanf()
* vscanf_s()
* vsnprintf()
* vsnprintf_s()
* vsprintf()
* vsprintf_s()
* vsscanf()
* vsscanf_s()
* vswprintf()
* vswprintf_s()
* vswscanf()
* vswscanf_s()
* vwprintf_s()
* vwscanf()
* vwscanf_s()
* wcrtomb()
* wcschr()
* wcsftime()
* wcspbrk()
* wcsrchr()
* wcsrtombs()
* wcsrtombs_s()
* wcsstr()
* wcstod()
* wcstof()
* wcstoimax()
* wcstok()
* wcstok_s()
* wcstol()
* wcstold()
* wcstoll()
* wcstombs()
* wcstombs_s()
* wcstoumax()
* wcstoul()
* wcstoull()
* wcsxfrm()
* wctob()
* wctrans()
* wctype()
* wmemchr()
* wprintf_s()
* wscanf()
* wscanf_s()
This check is an alias of check `bugprone-unused-return-value <bugprone-unused-return-value.html>`_
with a fixed set of functions.
The check corresponds to a part of CERT C Coding Standard rule `ERR33-C.
Detect and handle standard library errors
<https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors>`_.
The list of checked functions is taken from the rule, with following exception:
* The check can not differentiate if a function is called with ``NULL``
argument. Therefore the following functions are not checked:
``mblen``, ``mbrlen``, ``mbrtowc``, ``mbtowc``, ``wctomb``, ``wctomb_s``

View file

@ -333,6 +333,7 @@ Clang-Tidy Checks
`cert-dcl03-c <cert-dcl03-c.html>`_, `misc-static-assert <misc-static-assert.html>`_, "Yes"
`cert-dcl16-c <cert-dcl16-c.html>`_, `readability-uppercase-literal-suffix <readability-uppercase-literal-suffix.html>`_, "Yes"
`cert-dcl37-c <cert-dcl37-c.html>`_, `bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_, "Yes"
`cert-err33-c <cert-err33-c.html>`_, `bugprone-unused-return-value <bugprone-unused-return-value.html>`_,
`cert-dcl51-cpp <cert-dcl51-cpp.html>`_, `bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_, "Yes"
`cert-dcl54-cpp <cert-dcl54-cpp.html>`_, `misc-new-delete-overloads <misc-new-delete-overloads.html>`_,
`cert-dcl59-cpp <cert-dcl59-cpp.html>`_, `google-build-namespaces <google-build-namespaces.html>`_,

View file

@ -0,0 +1,25 @@
// RUN: %check_clang_tidy %s cert-err33-c %t
typedef __SIZE_TYPE__ size_t;
void *aligned_alloc(size_t alignment, size_t size);
void test_aligned_alloc() {
aligned_alloc(2, 10);
// CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used
// CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
}
long strtol(const char *restrict nptr, char **restrict endptr, int base);
void test_strtol() {
strtol("123", 0, 10);
// CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used
// CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
}
typedef char wchar_t;
int wscanf_s(const wchar_t *restrict format, ...);
void test_wscanf_s() {
int Val;
wscanf_s("%i", &Val);
// CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used
// CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
}