EXTRACLANGTOOLS(1) Extra Clang Tools EXTRACLANGTOOLS(1)
NAME
extraclangtools - Extra Clang Tools Documentation
Welcome to the clang-tools-extra project which contains extra tools
built using Clang's tooling APIs.
EXTRA CLANG TOOLS 22.1.8 RELEASE NOTES
o Introduction
o What's New in Extra Clang Tools 22.1.8?
o Major New Features
o Potentially Breaking Changes
o Improvements to clangd
o Compile flags
o Diagnostics
o Hover
o Cross-references
o Inlay hints
o Call hierarchy
o Document symbols
o Code folding
o Code completion
o Code actions
o C++20 Modules support
o Miscellaneous
o Improvements to clang-doc
o Improvements to clang-query
o Improvements to clang-tidy
o New checks
o New check aliases
o Changes in existing checks
o Removed checks
o Miscellaneous
o Improvements to include-fixer
o Improvements to clang-include-fixer
o Improvements to modularize
o Improvements to pp-trace
o Clang-tidy Visual Studio plugin
Written by the LLVM Team
Introduction
This document contains the release notes for the Extra Clang Tools,
part of the Clang release 22.1.8. Here we describe the status of the
Extra Clang Tools in some detail, including major improvements from the
previous release and new feature work. All LLVM releases may be
downloaded from the LLVM releases web site .
For more information about Clang or LLVM, including information about
the latest release, please see the Clang Web Site or the LLVM Web Site .
Note that if you are reading this file from a Git checkout or the main
Clang web page, this document applies to the next release, not the
current one. To see the release notes for a specific release, please
see the releases page .
What's New in Extra Clang Tools 22.1.8?
Some of the major new features and improvements to Extra Clang Tools
are listed here. Generic improvements to Extra Clang Tools as a whole
or to its underlying infrastructure are described first, followed by
tool-specific sections.
Major New Features
Potentially Breaking Changes
o Deprecated the clang-tidy hicpp module. All checks have been moved to
other modules instead. The hicpp module will be removed for the 23.x
release.
o Deprecated the clang-tidy zircon module. All checks have been moved
to the fuchsia module instead. The zircon module will be removed in
the 24th release.
o Removed clang-tidy's global options IgnoreMacros and StrictMode,
which were documented as deprecated since clang-tidy-20. Users should
use the check-specific options of the same name instead.
o Removed clang-analyzer-* checks from default checks in clang-tidy.
From now on, users should specify explicitly that they want CSA
checks to run in clang-tidy via clang-analyzer-*.
o Renamed a few clang-tidy check options, as they were misspelled:
o NamePrefixSuffixSilenceDissimilarityTreshold to
NamePrefixSuffixSilenceDissimilarityThreshold in
bugprone-easily-swappable-parameters <>
o CharTypdefsToIgnore to CharTypedefsToIgnore in
bugprone-signed-char-misuse <>
o Modified the custom message format of bugprone-unsafe-functions <> by
assigning a special meaning to the character > at the start of the
value of the option CustomFunctions. If the option value starts with
>, then the replacement suggestion part of the message (which would
be included by default) is omitted. (This does not change the warning
locations.)
o clang-tidy now displays warnings from all non-system headers by
default. Previously, users had to explicitly opt-in to header
warnings using -header-filter='.*'. To disable warnings from
non-system, set -header-filter to an empty string.
o The clang-tidy/ClangTidyModuleRegistry.h header has been deprecated
and will be removed in LLVM 24. All of the symbols it used to define
have been moved into clang-tidy/ClangTidyModule.h.
Improvements to clangd
Compile flags
o Added proper support for the -std=c++latest flag, often used by
Windows users writing C++23 code.
Diagnostics
o Fixed a bug that caused the quick-fix for the
readability-identifier-naming diagnostic not to work in some editors.
Hover
o Added support for parsing and presenting doxygen and markdown
documentation
o Added a MacroContentsLimit config option to allow users to opt into
showing macro definitions in hovers even if they're very long
o Fixed a bug that prevented documentation from being shown for members
of class templates defined in a header
o Fixed a bug that prevented documentation from being shown for
abbreviated function templates
Cross-references
o Improved "go to definition" support for dependent auto
o Improved "go to definition" support for C++23 "deducing this"
o Fixed a bug where attributes on a method broke various features
including "go to definition" on a method's name
o "Find references" on a constructor now finds call sites of forwarding
functions such as make_unique as well
o "Document links" now supports #include directives with a macro
argument
Inlay hints
o Show designator hints for aggregate initialization of structures with
parentheses-list initialization (CXXParenListInitExpr) syntax.
o Show type hints for simple cases of dependent auto
Call hierarchy
o Support invoking call hierarchy on enum constants
o When invoked on a virtual function, show callers of base functions as
well
Document symbols
o Include inline friend functions in document symbols
Code folding
o Support folding of preprocessor branches
o Support folding of #pragma region blocks
Code completion
o Added a new MacroFilter configuration option to Completion to allow
fuzzy-matching with the FuzzyMatch option when suggesting macros.
ExactPrefix is the default, which retains previous behavior of
suggesting macros which match the prefix exactly.
o Improved support for C++23 "deducing this"
o Header insertion now checks AngledHeaders and QuotedHeaders against
resolved rather than spelled paths, consistent with the existing
behaviour of include-cleaner diagnostics.
Code actions
o New Override pure virtual methods code action. When invoked on a
class definition, this action automatically generates C++ override
declarations for all pure virtual methods inherited from its base
classes that have not yet been implemented. The generated method
stubs prompts the user for the actual implementation. The overrides
are intelligently grouped under their original access specifiers
(e.g., public, protected), creating new access specifier blocks if
necessary.
o Improved the "move function body out-of-line" code action's logic for
choosing where to place the definition. It now tries to place it next
to the definition of a method with an adjacent declaration.
C++20 Modules support
o Fixed a crash when using --experimental-modules-support without a
compilation database
o Added a --debug-modules-builder option to help debug clangd's modules
support
o Improved diagnostics related to modules
o Clangd now tries to reuse a module from the project's build if it's
suitable
Miscellaneous
o Add wildcard .gitignore file to the clangd index directory.
Improvements to clang-doc
o Use the Mustache HTML generator as the default HTML option.
o Document concepts, typedefs, aliases, public class members, nested
records, nested namespaces, global functions, global variables,
protected methods and members and friends in HTML.
o Document throws comments in HTML.
o Add definition location for classes in HTML.
o Show class template declaration in HTML.
o Add a top-level index page in HTML. This page lists the top-level
namespaces and serves as the documentation's root URL.
o Add a Home button to the navigation bar which replaces non-useful
buttons. This new button links to the top-level index page.
o Add a breadcrumb navigation bar to the navbar. This helps navigate
through a documented entity's context.
o If a filename is too long, then the name will be truncated and the
documented entity's USR will be concatenated to guarantee its
uniqueness.
o Revert the flat directory structure to a nested directory structure.
Improvements to clang-query
o Matcher queries interpreted by clang-query are now support trailing
comma (,) in matcher arguments. Note that C++ still doesn't allow
this in function arguments. So when porting a query to C++, remove
all instances of trailing comma (otherwise C++ compiler will just
complain about "expected expression").
Improvements to clang-tidy
o The check_clang_tidy.py tool now recognizes the -std argument when
run over C files. If -std is not specified, it defaults to
c99-or-later.
o clang-tidy now displays warnings from all non-system headers by
default. Previously, users had to explicitly opt-in to header
warnings using -header-filter='.*'. To disable warnings from
non-system, set -header-filter to an empty string.
o clang-tidy no longer attempts to analyze code from system headers by
default, greatly improving performance. This behavior is disabled if
the SystemHeaders option is enabled.
o clang-tidy now supports query based custom checks by CustomChecks
configuration option. Query Based Custom Check Document <>
o The run-clang-tidy.py and clang-tidy-diff.py scripts now run checks
in parallel by default using all available hardware threads. Both
scripts display the number of threads being used in their output.
o Improved run-clang-tidy.py by adding a new option
enable-check-profile to enable per-check timing profiles and print a
report based on all analyzed files.
o Improved documentation of the -line-filter command-line flag of
clang-tidy and run-clang-tidy.py.
o Improved clang-tidy option -quiet by suppressing diagnostic count
messages.
o Improved clang-tidy by not crashing when an empty directory field is
used in a compilation database; the current working directory will be
used instead, and an error message will be printed.
o Removed clang-tidy's global options IgnoreMacros and StrictMode,
which were documented as deprecated since clang-tidy-20. Users should
use the check-specific options of the same name instead.
o Improved run-clang-tidy.py and clang-tidy-diff.py scripts by adding
the -hide-progress option to suppress progress and informational
messages.
o Removed clang-analyzer-* check from default checks in clang-tidy.
From now on, users should specify explicitly that they want CSA
checks to run in clang-tidy.
o Improved clang-tidy by adding the --removed-arg option to remove
arguments sent to the compiler when invoking Clang-Tidy. This option
was also added to run-clang-tidy.py and clang-tidy-diff.py and can be
configured in the config file through the RemovedArgs option.
o Deprecated the clang-tidy zircon module. All checks have been moved
to the fuchsia module instead. The zircon module will be removed in
the 24th release.
o Improved clang-tidy configuration parsing by allowing the same list
syntax in WarningsAsErrors as in Checks.
New checks
o New abseil-unchecked-statusor-access <> check.
Finds uses of absl::StatusOr without checking if a value is present.
o New bugprone-derived-method-shadowing-base-method <> check.
Finds derived class methods that shadow a (non-virtual) base class
method.
o New bugprone-invalid-enum-default-initialization <> check.
Detects default initialization (to 0) of variables with enum type
where the enum has no enumerator with value of 0.
o New cppcoreguidelines-pro-bounds-avoid-unchecked-container-access <>
check.
Finds calls to operator[] in STL containers and suggests replacing
them with safe alternatives.
o New google-runtime-float <> check.
Finds uses of long double and suggests against their use due to lack
of portability.
o New llvm-mlir-op-builder <> check.
Checks for uses of MLIR's old/to be deprecated OpBuilder::create
form and suggests using T::create instead.
o New llvm-use-ranges <> check.
Finds calls to STL library iterator algorithms that could be replaced
with LLVM range-based algorithms from llvm/ADT/STLExtras.h.
o New misc-override-with-different-visibility <> check.
Finds virtual function overrides with different visibility than the
function in the base class.
o New readability-inconsistent-ifelse-braces <> check.
Detects if/else statements where one branch uses braces and the other
does not.
o New readability-redundant-parentheses <> check.
Detect redundant parentheses.
o New readability-redundant-typename <> check.
Finds redundant uses of the typename keyword.
New check aliases
o Renamed cert-dcl50-cpp <> to modernize-avoid-variadic-functions <>
keeping initial check as an alias to the new one.
o Renamed cert-dcl58-cpp <> to bugprone-std-namespace-modification <>
keeping initial check as an alias to the new one.
o Renamed cert-env33-c <> to bugprone-command-processor <> keeping
initial check as an alias to the new one.
o Renamed cert-err34-c <> to
bugprone-unchecked-string-to-number-conversion <> keeping initial
check as an alias to the new one.
o Renamed cert-err52-cpp <> to modernize-avoid-setjmp-longjmp <>
keeping initial check as an alias to the new one.
o Renamed cert-err58-cpp <> to bugprone-throwing-static-initialization
<> keeping initial check as an alias to the new one.
o Renamed cert-err60-cpp <> to
bugprone-exception-copy-constructor-throws <>
o Renamed cert-flp30-c <> to bugprone-float-loop-counter <> keeping
initial check as an alias to the new one.
o Renamed cert-mem57-cpp <> to
bugprone-default-operator-new-on-overaligned-type <> keeping initial
check as an alias to the new one.
o Renamed cert-msc30-c <> to misc-predictable-rand <> keeping initial
check as an alias to the new one.
o Renamed cert-msc32-c <> to bugprone-random-generator-seed <> keeping
initial check as an alias to the new one.
o Renamed cert-msc50-cpp <> to misc-predictable-rand <> keeping initial
check as an alias to the new one.
o Renamed cert-msc51-cpp <> to bugprone-random-generator-seed <>
keeping initial check as an alias to the new one.
o Renamed cert-oop57-cpp <> to
bugprone-raw-memory-call-on-non-trivial-type <> keeping initial check
as an alias to the new one.
o Renamed cert-oop58-cpp <> to
bugprone-copy-constructor-mutates-argument <> keeping initial check
as an alias to the new one.
o Renamed fuchsia-multiple-inheritance <> to misc-multiple-inheritance
<> keeping initial check as an alias to the new one.
o Renamed google-build-namespaces <> to
misc-anonymous-namespace-in-header <> keeping initial check as an
alias to the new one.
o Renamed google-readability-casting <> to modernize-avoid-c-style-cast
<> keeping initial check as an alias to the new one.
Changes in existing checks
o Improved bugprone-chained-comparison <> check by adding a new option
IgnoreMacros to suppress warnings within macro expansions.
o Improved bugprone-dynamic-static-initializers <> check:
o Avoided false positives for constexpr variables whose initializers
are value-dependent.
o Added support for C++20 constinit variables.
o Improved bugprone-easily-swappable-parameters <> check by correcting
a spelling mistake on its option
NamePrefixSuffixSilenceDissimilarityTreshold.
o Improved bugprone-exception-escape <> check:
o Exceptions from lambda captures are now diagnosed, exceptions in
the bodies of lambdas that aren't actually invoked are not.
o Fixed an issue where the check wouldn't diagnose throws in
arguments to functions or constructors.
o Added fine-grained configuration via options CheckDestructors,
CheckMoveMemberFunctions, CheckMain, CheckedSwapFunctions, and
CheckNothrowFunctions.
o Improved bugprone-infinite-loop <> check by adding detection for
variables introduced by structured bindings.
o Improved bugprone-invalid-enum-default-initialization <> with new
IgnoredEnums option to ignore specified enums during analysis.
o Improved bugprone-macro-parentheses <> check by fixing false
positives when using C++ template parameters.
o Improved bugprone-narrowing-conversions <> check by fixing false
positive from analysis of a conditional expression in C.
o Improved bugprone-not-null-terminated-result <> check by fixing bogus
fix-its for strncmp and wcsncmp on Windows and a crash caused by
certain value-dependent expressions.
o Improved bugprone-reserved-identifier <> check by ignoring
declarations and macros in system headers.
o Improved bugprone-signed-char-misuse <> check by fixing false
positives on C23 enums with the fixed underlying type of signed char.
o Improved bugprone-sizeof-expression <> check by fixing a crash on
sizeof of an array of dependent type.
o Improved bugprone-suspicious-include <> check by adding IgnoredRegex
option.
o Improved bugprone-tagged-union-member-count <> by fixing a false
positive when enums or unions from system header files or the std
namespace are treated as the tag or the data part of a user-defined
tagged union respectively.
o Improved bugprone-throw-keyword-missing <> check:
o Now it only considers the canonical types of base classes as
written.
o Added a note on the base class that triggered the warning.
o Fixed an issue where the check wouldn't fire in constructors or (in
certain contexts) lambdas.
o Improved bugprone-unchecked-optional-access <> check:
o Added support for NullableValue::makeValue and
NullableValue::makeValueInplace to prevent false-positives for
BloombergLP::bdlb::NullableValue.
o Fixed false-positives for bsl::optional containing allocator-aware
type.
o Added the IgnoreValueCalls option to suppress diagnostics for
optional::value().
o Added the IgnoreSmartPointerDereference option to ignore optionals
reached via smart-pointer-like dereference, while still diagnosing
UB-prone dereferences via operator* and operator->.
o Improved bugprone-unhandled-self-assignment <> check by adding an
additional matcher that generalizes the copy-and-swap idiom pattern
detection.
o Improved bugprone-unsafe-functions <> check by hiding the default
suffix when the reason starts with the character > in the
CustomFunctions option.
o Improved bugprone-use-after-move <> check:
o Added InvalidationFunctions option to support custom invalidation
functions.
o Added ReinitializationFunctions option to support custom
reinitialization functions.
o Improved cppcoreguidelines-avoid-non-const-global-variables <> check
by adding a new option AllowThreadLocal that suppresses warnings on
non-const global variables with thread-local storage duration.
o Improved cppcoreguidelines-init-variables <> check by fixing the
insertion location for function pointers with multiple parameters.
o Improved cppcoreguidelines-macro-usage <> check by excluding macro
bodies that starts with __attribute__((..)) keyword. Such a macro
body is unlikely a proper expression and so suggesting users an
impossible rewrite into a template function should be avoided.
o Improved cppcoreguidelines-prefer-member-initializer <> check to
avoid false positives on inherited members in class templates.
o Improved cppcoreguidelines-pro-bounds-pointer-arithmetic <> check
adding AllowIncrementDecrementOperators option to allow pointer
arithmetic via prefix/postfix increment or decrement operators.
o Improved cppcoreguidelines-pro-type-member-init <> check to correctly
ignore std::array and other array-like containers when IgnoreArrays
option is set to true.
o Improved fuchsia-multiple-inheritance <> by fixing an issue where the
check would only analyze the first class with a given name in the
program, missing any subsequent classes with that same name (declared
in a different scope).
o Improved google-readability-casting <> check by adding fix-it notes
for downcasts and casts to void pointer.
o Improved google-readability-todo <> check to accept the new TODO
format from the Google Style Guide.
o Improved llvm-prefer-isa-or-dyn-cast-in-conditionals <> check:
o Fix-it handles callees with nested-name-specifier correctly.
o if statements with init-statement (if (auto X = ...; ...)) are
handled correctly.
o for loops are supported.
o Improved misc-const-correctness <> check:
o Fixed false positives when pointers is transferred to non-const
references.
o Fixed false positives of function pointer, on return of non-const
pointer, on pointer-to-member operator.
o Fixed false positives when the address of a variable is taken to be
passed to a function.
o Improved misc-coroutine-hostile-raii <> check by adding the option
AllowedCallees, that allows exempting safely awaitable callees from
the check.
o Improved misc-header-include-cycle <> check performance.
o Improved misc-use-internal-linkage <> check:
o Added suggestion of internal linkage for user-defined types
(structs, classes, unions, and enums).
o Added fine-grained options AnalyzeFunctions, AnalyzeVariables,
AnalyzeTypes to control whether the check should diagnose
functions, variables, and/or user-defined types.
o Enabled the check for C.
o Improved modernize-avoid-c-arrays <> to not diagnose array types
which are part of an implicit instantiation of a template.
o Improved modernize-use-constraints <> check by fixing a crash on uses
of non-standard enable_if with a signature different from
std::enable_if (such as boost::enable_if).
o Improved modernize-use-default-member-init <> check to enhance the
robustness of the member initializer detection.
o Improved modernize-use-designated-initializers <> check to suggest
using designated initializers for aliased aggregate types.
o Improved modernize-use-integer-sign-comparison <> by providing
correct fix-its when the right-hand side of a comparison contains a
non-C-style cast.
o Improved modernize-use-nullptr <> check by fixing a crash on Windows
when the check was enabled with a 32-bit clang-tidy binary.
o Improved modernize-use-override <> by fixing an issue where the check
would sometimes suggest inserting override in an invalid place.
o Improved modernize-use-ranges <> check to suggest using the more
idiomatic std::views::reverse where it used to suggest
std::ranges::reverse_view.
o Improved modernize-use-scoped-lock <> check by fixing a crash on
malformed code (common when using clang-tidy through clangd).
o Improved modernize-use-std-format <> check to correctly match when
the format string is converted to a different type by an implicit
constructor call.
o Improved modernize-use-std-print <> check to correctly match when the
format string is converted to a different type by an implicit
constructor call, and fixed a crash when handling format strings
containing non-ASCII characters.
o Improved modernize-use-using <> check to correctly provide fix-its
for typedefs of pointers or references to array types.
o Improved performance-move-const-arg <> check by avoiding false
positives on trivially copyable types with a non-public copy
constructor.
o Improved performance-unnecessary-copy-initialization <> by printing
the type of the diagnosed variable.
o Improved performance-unnecessary-value-param <> by printing the type
of the diagnosed variable and correctly generating fix-it hints for
parameter-pack arguments.
o Improved portability-template-virtual-member-function <> check to
avoid false positives on pure virtual member functions.
o Improved readability-container-contains <> to support string
comparisons to npos. Internal changes may cause new rare false
positives in non-standard containers.
o Improved readability-container-data-pointer <> check by correctly
adding parentheses when the container expression is a dereference.
o Improved readability-container-size-empty <> check by correctly
generating fix-it hints when size method is called from implicit
this, ignoring default constructors with user provided arguments and
adding detection in container's method except empty.
o Improved readability-duplicate-include <> check by adding the
IgnoredFilesList option (semicolon-separated list of regexes or
filenames) to allow intentional duplicates.
o Improved readability-identifier-naming <> check:
o Now ignores declarations and macros in system headers.
o The documentation is improved to differentiate the general options
from the specific ones.
o Options for fine-grained control over constexpr variables were
added.
o Added default options which simplify configs by removing the need
to specify each identifier kind separately.
o Improved readability-implicit-bool-conversion <> check:
o Now correctly adds parentheses when inner expressions are
implicitly converted multiple times.
o Enabled the check for C99 and later standards.
o Now allows implicit conversions from bool to integer when used as
operands of logical operators (&&, ||) in C.
o Improved readability-inconsistent-declaration-parameter-name <> check
by not enforcing parameter name consistency between a variadic
parameter pack in the primary template and specific parameters in its
specializations.
o Improved readability-math-missing-parentheses <> check by correctly
diagnosing operator precedence issues inside parenthesized
expressions.
o Improved readability-qualified-auto <> check by adding the option
IgnoreAliasing, that allows not looking at underlying types of type
aliases.
o Improved readability-redundant-casting <> check by fixing false
negatives when explicitly cast from function pointer.
o Improved readability-redundant-control-flow <> by fixing an issue
where the check would sometimes suggest deleting not only a redundant
return or continue, but also unrelated lines preceding it.
o Improved readability-uppercase-literal-suffix <> check to recognize
literal suffixes added in C++23 and C23.
o Improved readability-use-concise-preprocessor-directives <> check to
generate correct fix-its for forms without a space after the
directive.
o Improved readability-use-std-min-max <> check by ensuring that
comments between the if condition and the then block are preserved
when applying the fix.
Removed checks
Miscellaneous
Improvements to include-fixer
The improvements are...
Improvements to clang-include-fixer
The improvements are...
Improvements to modularize
The improvements are...
Improvements to pp-trace
Clang-tidy Visual Studio plugin
CLANG-TIDY
Contents
o Clang-Tidy
o Using Clang-Tidy
o Clang-Tidy Automation
o Running Clang-Tidy in Parallel
o Running Clang-Tidy on Diff
o Suppressing Undesired Diagnostics
See also:
Clang-Tidy Checks
abseil-cleanup-ctad
Suggests switching the initialization pattern of absl::Cleanup
instances from the factory function to class template argument
deduction (CTAD), in C++17 and higher.
auto c1 = absl::MakeCleanup([] {});
const auto c2 = absl::MakeCleanup(std::function([] {}));
becomes
absl::Cleanup c1 = [] {};
const absl::Cleanup c2 = std::function([] {});
abseil-duration-addition
Checks for cases where addition should be performed in the absl::Time
domain. When adding two values, and one is known to be an absl::Time,
we can infer that the other should be interpreted as an absl::Duration
of a similar scale, and make that inference explicit.
Examples:
// Original - Addition in the integer domain
int x;
absl::Time t;
int result = absl::ToUnixSeconds(t) + x;
// Suggestion - Addition in the absl::Time domain
int result = absl::ToUnixSeconds(t + absl::Seconds(x));
abseil-duration-comparison
Checks for comparisons which should be in the absl::Duration domain
instead of the floating point or integer domains.
N.B.: In cases where a Duration was being converted to an integer and
then compared against a floating-point value, truncation during the
Duration conversion might yield a different result. In practice this is
very rare, and still indicates a bug which should be fixed.
Examples:
// Original - Comparison in the floating point domain
double x;
absl::Duration d;
if (x < absl::ToDoubleSeconds(d)) ...
// Suggested - Compare in the absl::Duration domain instead
if (absl::Seconds(x) < d) ...
// Original - Comparison in the integer domain
int x;
absl::Duration d;
if (x < absl::ToInt64Microseconds(d)) ...
// Suggested - Compare in the absl::Duration domain instead
if (absl::Microseconds(x) < d) ...
abseil-duration-conversion-cast
Checks for casts of absl::Duration conversion functions, and recommends
the right conversion function instead.
Examples:
// Original - Cast from a double to an integer
absl::Duration d;
int i = static_cast(absl::ToDoubleSeconds(d));
// Suggested - Use the integer conversion function directly.
int i = absl::ToInt64Seconds(d);
// Original - Cast from a double to an integer
absl::Duration d;
double x = static_cast(absl::ToInt64Seconds(d));
// Suggested - Use the integer conversion function directly.
double x = absl::ToDoubleSeconds(d);
Note: In the second example, the suggested fix could yield a different
result, as the conversion to integer could truncate. In practice, this
is very rare, and you should use absl::Trunc to perform this operation
explicitly instead.
abseil-duration-division
absl::Duration arithmetic works like it does with integers. That means
that division of two absl::Duration objects returns an int64 with any
fractional component truncated toward 0. See this link for more information on arithmetic with
absl::Duration.
For example:
absl::Duration d = absl::Seconds(3.5);
int64 sec1 = d / absl::Seconds(1); // Truncates toward 0.
int64 sec2 = absl::ToInt64Seconds(d); // Equivalent to division.
assert(sec1 == 3 && sec2 == 3);
double dsec = d / absl::Seconds(1); // WRONG: Still truncates toward 0.
assert(dsec == 3.0);
If you want floating-point division, you should use either the
absl::FDivDuration() function, or one of the unit conversion functions
such as absl::ToDoubleSeconds(). For example:
absl::Duration d = absl::Seconds(3.5);
double dsec1 = absl::FDivDuration(d, absl::Seconds(1)); // GOOD: No truncation.
double dsec2 = absl::ToDoubleSeconds(d); // GOOD: No truncation.
assert(dsec1 == 3.5 && dsec2 == 3.5);
This check looks for uses of absl::Duration division that is done in a
floating-point context, and recommends the use of a function that
returns a floating-point value.
abseil-duration-factory-float
Checks for cases where the floating-point overloads of various
absl::Duration factory functions are called when the more-efficient
integer versions could be used instead.
This check will not suggest fixes for literals which contain fractional
floating point values or non-literals. It will suggest removing
superfluous casts.
Examples:
// Original - Providing a floating-point literal.
absl::Duration d = absl::Seconds(10.0);
// Suggested - Use an integer instead.
absl::Duration d = absl::Seconds(10);
// Original - Explicitly casting to a floating-point type.
absl::Duration d = absl::Seconds(static_cast(10));
// Suggested - Remove the explicit cast
absl::Duration d = absl::Seconds(10);
abseil-duration-factory-scale
Checks for cases where arguments to absl::Duration factory functions
are scaled internally and could be changed to a different factory
function. This check also looks for arguments with a zero value and
suggests using absl::ZeroDuration() instead.
Examples:
// Original - Internal multiplication.
int x;
absl::Duration d = absl::Seconds(60 * x);
// Suggested - Use absl::Minutes instead.
absl::Duration d = absl::Minutes(x);
// Original - Internal division.
int y;
absl::Duration d = absl::Milliseconds(y / 1000.);
// Suggested - Use absl:::Seconds instead.
absl::Duration d = absl::Seconds(y);
// Original - Zero-value argument.
absl::Duration d = absl::Hours(0);
// Suggested = Use absl::ZeroDuration instead
absl::Duration d = absl::ZeroDuration();
abseil-duration-subtraction
Checks for cases where subtraction should be performed in the
absl::Duration domain. When subtracting two values, and the first one
is known to be a conversion from absl::Duration, we can infer that the
second should also be interpreted as an absl::Duration, and make that
inference explicit.
Examples:
// Original - Subtraction in the double domain
double x;
absl::Duration d;
double result = absl::ToDoubleSeconds(d) - x;
// Suggestion - Subtraction in the absl::Duration domain instead
double result = absl::ToDoubleSeconds(d - absl::Seconds(x));
// Original - Subtraction of two Durations in the double domain
absl::Duration d1, d2;
double result = absl::ToDoubleSeconds(d1) - absl::ToDoubleSeconds(d2);
// Suggestion - Subtraction in the absl::Duration domain instead
double result = absl::ToDoubleSeconds(d1 - d2);
Note: As with other clang-tidy checks, it is possible that multiple
fixes may overlap (as in the case of nested expressions), so not all
occurrences can be transformed in one run. In particular, this may
occur for nested subtraction expressions. Running clang-tidy multiple
times will find and fix these overlaps.
abseil-duration-unnecessary-conversion
Finds and fixes cases where absl::Duration values are being converted
to numeric types and back again.
Floating-point examples:
// Original - Conversion to double and back again
absl::Duration d1;
absl::Duration d2 = absl::Seconds(absl::ToDoubleSeconds(d1));
// Suggestion - Remove unnecessary conversions
absl::Duration d2 = d1;
// Original - Division to convert to double and back again
absl::Duration d2 = absl::Seconds(absl::FDivDuration(d1, absl::Seconds(1)));
// Suggestion - Remove division and conversion
absl::Duration d2 = d1;
Integer examples:
// Original - Conversion to integer and back again
absl::Duration d1;
absl::Duration d2 = absl::Hours(absl::ToInt64Hours(d1));
// Suggestion - Remove unnecessary conversions
absl::Duration d2 = d1;
// Original - Integer division followed by conversion
absl::Duration d2 = absl::Seconds(d1 / absl::Seconds(1));
// Suggestion - Remove division and conversion
absl::Duration d2 = d1;
Unwrapping scalar operations:
// Original - Multiplication by a scalar
absl::Duration d1;
absl::Duration d2 = absl::Seconds(absl::ToInt64Seconds(d1) * 2);
// Suggestion - Remove unnecessary conversion
absl::Duration d2 = d1 * 2;
Note: Converting to an integer and back to an absl::Duration might be a
truncating operation if the value is not aligned to the scale of
conversion. In the rare case where this is the intended result,
callers should use absl::Trunc to truncate explicitly.
abseil-faster-strsplit-delimiter
Finds instances of absl::StrSplit() or absl::MaxSplits() where the
delimiter is a single character string literal and replaces with a
character. The check will offer a suggestion to change the string
literal into a character. It will also catch code using
absl::ByAnyChar() for just a single character and will transform that
into a single character as well.
These changes will give the same result, but using characters rather
than single character string literals is more efficient and readable.
Examples:
// Original - the argument is a string literal.
for (auto piece : absl::StrSplit(str, "B")) {
// Suggested - the argument is a character, which causes the more efficient
// overload of absl::StrSplit() to be used.
for (auto piece : absl::StrSplit(str, 'B')) {
// Original - the argument is a string literal inside absl::ByAnyChar call.
for (auto piece : absl::StrSplit(str, absl::ByAnyChar("B"))) {
// Suggested - the argument is a character, which causes the more efficient
// overload of absl::StrSplit() to be used and we do not need absl::ByAnyChar
// anymore.
for (auto piece : absl::StrSplit(str, 'B')) {
// Original - the argument is a string literal inside absl::MaxSplits call.
for (auto piece : absl::StrSplit(str, absl::MaxSplits("B", 1))) {
// Suggested - the argument is a character, which causes the more efficient
// overload of absl::StrSplit() to be used.
for (auto piece : absl::StrSplit(str, absl::MaxSplits('B', 1))) {
abseil-no-internal-dependencies
Warns if code using Abseil depends on internal details. If something is
in a namespace that includes the word "internal", code is not allowed
to depend upon it because it's an implementation detail. They cannot
friend it, include it, you mention it or refer to it in any way. Doing
so violates Abseil's compatibility guidelines and may result in
breakage. See for more
information.
The following cases will result in warnings:
absl::strings_internal::foo();
// warning triggered on this line
class foo {
friend struct absl::container_internal::faa;
// warning triggered on this line
};
absl::memory_internal::MakeUniqueResult();
// warning triggered on this line
abseil-no-namespace
Ensures code does not open namespace absl as that violates Abseil's
compatibility guidelines. Code should not open namespace absl as that
conflicts with Abseil's compatibility guidelines and may result in
breakage.
Any code that uses:
namespace absl {
...
}
will be prompted with a warning.
See the full Abseil compatibility guidelines for more information.
abseil-redundant-strcat-calls
Suggests removal of unnecessary calls to absl::StrCat when the result
is being passed to another call to absl::StrCat or absl::StrAppend.
The extra calls cause unnecessary temporary strings to be constructed.
Removing them makes the code smaller and faster.
Examples:
std::string s = absl::StrCat("A", absl::StrCat("B", absl::StrCat("C", "D")));
//before
std::string s = absl::StrCat("A", "B", "C", "D");
//after
absl::StrAppend(&s, absl::StrCat("E", "F", "G"));
//before
absl::StrAppend(&s, "E", "F", "G");
//after
abseil-str-cat-append
Flags uses of absl::StrCat() to append to a std::string. Suggests
absl::StrAppend() should be used instead.
The extra calls cause unnecessary temporary strings to be constructed.
Removing them makes the code smaller and faster.
a = absl::StrCat(a, b); // Use absl::StrAppend(&a, b) instead.
Does not diagnose cases where absl::StrCat() is used as a template
argument for a functor.
abseil-string-find-startswith
Checks whether a std::string::find() or std::string::rfind() (and
corresponding std::string_view methods) result is compared with 0, and
suggests replacing with absl::StartsWith(). This is both a readability
and performance issue.
starts_with was added as a built-in function on those types in C++20.
If available, prefer enabling modernize-use-starts-ends-with <> instead
of this check.
string s = "...";
if (s.find("Hello World") == 0) { /* do something */ }
if (s.rfind("Hello World", 0) == 0) { /* do something */ }
becomes
string s = "...";
if (absl::StartsWith(s, "Hello World")) { /* do something */ }
if (absl::StartsWith(s, "Hello World")) { /* do something */ }
Options
StringLikeClasses
Semicolon-separated list of names of string-like classes. By
default both std::basic_string and std::basic_string_view are
considered. The list of methods to be considered is fixed.
IncludeStyle
A string specifying which include-style is used, llvm or google.
Default is llvm.
AbseilStringsMatchHeader
The location of Abseil's strings/match.h. Defaults to
absl/strings/match.h.
abseil-string-find-str-contains
Finds s.find(...) == string::npos comparisons (for various string-like
types) and suggests replacing with absl::StrContains().
This improves readability and reduces the likelihood of accidentally
mixing find() and npos from different string-like types.
By default, "string-like types" includes ::std::basic_string,
::std::basic_string_view, and ::absl::string_view. See the
StringLikeClasses option to change this.
std::string s = "...";
if (s.find("Hello World") == std::string::npos) { /* do something */ }
absl::string_view a = "...";
if (absl::string_view::npos != a.find("Hello World")) { /* do something */ }
becomes
std::string s = "...";
if (!absl::StrContains(s, "Hello World")) { /* do something */ }
absl::string_view a = "...";
if (absl::StrContains(a, "Hello World")) { /* do something */ }
Options
StringLikeClasses
Semicolon-separated list of names of string-like classes. By
default includes ::std::basic_string, ::std::basic_string_view,
and ::absl::string_view.
IncludeStyle
A string specifying which include-style is used, llvm or google.
Default is llvm.
AbseilStringsMatchHeader
The location of Abseil's strings/match.h. Defaults to
absl/strings/match.h.
abseil-time-comparison
Prefer comparisons in the absl::Time domain instead of the integer
domain.
N.B.: In cases where an absl::Time is being converted to an integer,
alignment may occur. If the comparison depends on this alignment, doing
the comparison in the absl::Time domain may yield a different result.
In practice this is very rare, and still indicates a bug which should
be fixed.
Examples:
// Original - Comparison in the integer domain
int x;
absl::Time t;
if (x < absl::ToUnixSeconds(t)) ...
// Suggested - Compare in the absl::Time domain instead
if (absl::FromUnixSeconds(x) < t) ...
abseil-time-subtraction
Finds and fixes absl::Time subtraction expressions to do subtraction in
the Time domain instead of the numeric domain.
There are two cases of Time subtraction in which deduce additional type
information:
o When the result is an absl::Duration and the first argument is an
absl::Time.
o When the second argument is a absl::Time.
In the first case, we must know the result of the operation, since
without that the second operand could be either an absl::Time or an
absl::Duration. In the second case, the first operand must be an
absl::Time, because subtracting an absl::Time from an absl::Duration is
not defined.
Examples:
int x;
absl::Time t;
// Original - absl::Duration result and first operand is an absl::Time.
absl::Duration d = absl::Seconds(absl::ToUnixSeconds(t) - x);
// Suggestion - Perform subtraction in the Time domain instead.
absl::Duration d = t - absl::FromUnixSeconds(x);
// Original - Second operand is an absl::Time.
int i = x - absl::ToUnixSeconds(t);
// Suggestion - Perform subtraction in the Time domain instead.
int i = absl::ToInt64Seconds(absl::FromUnixSeconds(x) - t);
abseil-unchecked-statusor-access
This check identifies unsafe accesses to values contained in
absl::StatusOr objects. Below we will refer to this type as
StatusOr.
An access to the value of an StatusOr occurs when one of its value,
operator*, or operator-> member functions is invoked. To align with
common misconceptions, the check considers these member functions as
equivalent, even though there are subtle differences related to
exceptions vs. undefined behavior.
An access to the value of a StatusOr is considered safe if and only
if code in the local scope (e.g. function body) ensures that the status
of the StatusOr is ok in all possible execution paths that can reach
the access. That should happen either through an explicit check, using
the StatusOr::ok member function, or by constructing the StatusOr
in a way that shows that its status is unambiguously ok (e.g. by
passing a value to its constructor).
Below we list some examples of safe and unsafe StatusOr access
patterns.
Note: If the check isn't behaving as you would have expected on a code
snippet, please report it .
False negatives
This check generally does not generate false negatives. That means that
if an access is not marked as unsafe, it is provably safe. If it cannot
prove an access safe, it is assumed to be unsafe. In some cases, the
static analysis cannot prove an access safe even though it is, for a
variety of reasons (e.g. unmodelled invariants of functions called).
In these cases, the analysis does produce false positive reports.
That being said, there are some heuristics used that in very rare cases
might be incorrect:
o a const method accessor (without arguments) that returns different
values when called multiple times <#functionstability>.
If you think the check generated a false negative, please report it
.
Known limitations
This is a non-exhaustive list of constructs that are currently not
modelled in the check and will lead to false positives:
o Checking a StatusOr and then capturing it in a lambda <#lambdas>
o Indexing into a container with the same index <#containers>
o Project specific helper-functions <#uncommonapi>,
o Functions with a stable return value <#functionstability>
o Any cross-function reasoning <#crossfunction>. This is by design and
will not change in the future.
Checking if the status is ok, then accessing the value
The check recognizes all straightforward ways for checking the status
and accessing the value contained in a StatusOr object. For example:
void f(absl::StatusOr sor) {
if (sor.ok()) {
use(*sor);
}
}
Checking if the status is ok, then accessing the value from a copy
The criteria that the check uses is semantic, not syntactic. It
recognizes when a copy of the StatusOr object being accessed is
known to have ok status. For example:
void f(absl::StatusOr sor1) {
if (sor1.ok()) {
absl::optional sor2 = sor1;
use(*sor2);
}
}
Ensuring that the status is ok using common macros
The check is aware of common macros like ABSL_CHECK and ASSERT_THAT.
Those can be used to ensure that the status of a StatusOr object is
ok. For example:
void f(absl::StatusOr sor) {
ABSL_DCHECK_OK(sor);
use(*sor);
}
Ensuring that the status is ok, then accessing the value in a correlated
branch
The check is aware of correlated branches in the code and can figure
out when a StatusOr object is ensured to have ok status on all
execution paths that lead to an access. For example:
void f(absl::StatusOr sor) {
bool safe = false;
if (sor.ok() && SomeOtherCondition()) {
safe = true;
}
// ... more code...
if (safe) {
use(*sor);
}
}
Accessing the value without checking the status
The check flags accesses to the value that are not locally guarded by a
status check:
void f1(absl::StatusOr sor) {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
void f2(absl::StatusOr sor) {
use(sor->member); // unsafe: it is unclear whether the status of `sor` is ok.
}
void f3(absl::StatusOr sor) {
use(sor.value()); // unsafe: it is unclear whether the status of `sor` is ok.
}
Use ABSL_CHECK_OK to signal that you knowingly want to crash on non-OK
values.
NOTE: Even though using .value() on a non-ok() StatusOr is defined to
crash, it is often unintentional. That is why our checker flags those
as well.
Accessing the value in the wrong branch
The check is aware of the state of a StatusOr object in different
branches of the code. For example:
void f(absl::StatusOr sor) {
if (sor.ok()) {
} else {
use(*sor); // unsafe: it is clear that the status of `sor` is *not* ok.
}
}
Assuming a function result to be stable
The check is aware that function results might not be stable. That is,
consecutive calls to the same function might return different values.
For example:
void f(Foo foo) {
if (foo.sor().ok()) {
use(*foo.sor()); // unsafe: it is unclear whether the status of `foo.sor()` is ok.
}
}
In such cases it is best to store the result of the function call in a
local variable and use it to access the value. For example:
void f(Foo foo) {
if (const auto& foo_sor = foo.sor(); foo_sor.ok()) {
use(*foo_sor);
}
}
The check does assume that const-qualified accessor functions return a
stable value if no non-const function was called between the two calls:
class Foo {
const absl::StatusOr& get() const {
[...];
}
}
void f(Foo foo) {
if (foo.get().ok()) {
use(*foo.get());
}
}
If there is a call to a non-const-qualified function, the check assumes
the return value of the accessor was mutated.
class Foo {
const absl::StatusOr& get() const {
[...];
}
void mutate();
}
void f(Foo foo) {
if (foo.get().ok()) {
foo.mutate();
use(*foo.get()); // unsafe: mutate might have changed the state of the object
}
}
Relying on invariants of uncommon APIs
The check is unaware of invariants of uncommon APIs. For example:
void f(Foo foo) {
if (foo.HasProperty("bar")) {
use(*foo.GetProperty("bar")); // unsafe: it is unclear whether the status of `foo.GetProperty("bar")` is ok.
}
}
In such cases it is best to check explicitly that the status of the
StatusOr object is ok. For example:
void f(Foo foo) {
if (const auto& property = foo.GetProperty("bar"); property.ok()) {
use(*property);
}
}
Checking if the status is ok, then passing the StatusOr to another
function
The check relies on local reasoning. The check and value access must
both happen in the same function. An access is considered unsafe even
if the caller of the function performing the access ensures that the
status of the StatusOr is ok. For example:
void g(absl::StatusOr sor) {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
void f(absl::StatusOr sor) {
if (sor.ok()) {
g(sor);
}
}
In such cases it is best to either pass the value directly when calling
a function or check that the status of the StatusOr is ok in the
local scope of the callee. For example:
void g(int val) {
use(val);
}
void f(absl::StatusOr sor) {
if (sor.ok()) {
g(*sor);
}
}
Aliases created via using declarations
The check is aware of aliases of StatusOr types that are created via
using declarations. For example:
using StatusOrInt = absl::StatusOr;
void f(StatusOrInt sor) {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
Containers
The check is more strict than necessary when it comes to containers of
StatusOr values. Simply checking that the status of an element of a
container is ok is not sufficient to deem accessing it safe. For
example:
void f(std::vector> sors) {
if (sors[0].ok()) {
use(*sors[0]); // unsafe: it is unclear whether the status of `sors[0]` is ok.
}
}
One needs to grab a reference to a particular object and use that
instead:
void f(std::vector> sors) {
absl::StatusOr& sor0 = sors[0];
if (sor0.ok()) {
use(*sor0);
}
}
A future version could improve the understanding of more safe usage
patterns that involve containers.
Lambdas
The check is capable of reporting unsafe StatusOr accesses in
lambdas, but isn't smart enough to propagate information from the
surrounding context through the lambda. This means that the following
pattern will be reported as an unsafe access:
void f(absl::StatusOr sor) {
if (sor.ok()) {
[&sor]() {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
}
}
To avoid the issue, you should grab a reference to the contained object
and capture that instead
void f(absl::StatusOr sor) {
if (sor.ok()) {
auto& s = *sor;
[&s]() {
use(s);
}
}
}
Alternatively you could add a check inside the lambda where the value
is accessed:
void f(absl::StatusOr sor) {
[&sor]() {
if (sor.ok()) {
use(*sor);
}
}
}
abseil-upgrade-duration-conversions
Finds calls to absl::Duration arithmetic operators and factories whose
argument needs an explicit cast to continue compiling after upcoming
API changes.
The operators *=, /=, *, and / for absl::Duration currently accept an
argument of class type that is convertible to an arithmetic type. Such
a call currently converts the value to an int64_t, even in a case such
as std::atomic that would result in lossy conversion.
Additionally, the absl::Duration factory functions (absl::Hours,
absl::Minutes, etc) currently accept an int64_t or a floating-point
type. Similar to the arithmetic operators, calls with an argument of
class type that is convertible to an arithmetic type go through the
int64_t path.
These operators and factories will be changed to only accept arithmetic
types to prevent unintended behavior. After these changes are released,
passing an argument of class type will no longer compile, even if the
type is implicitly convertible to an arithmetic type.
Here are example fixes created by this check:
std::atomic a;
absl::Duration d = absl::Milliseconds(a);
d *= a;
becomes
std::atomic a;
absl::Duration d = absl::Milliseconds(static_cast(a));
d *= static_cast(a);
Note that this check always adds a cast to int64_t in order to preserve
the current behavior of user code. It is possible that this uncovers
unintended behavior due to types implicitly convertible to a
floating-point type.
altera-id-dependent-backward-branch
Finds ID-dependent variables and fields that are used within loops.
This causes branches to occur inside the loops, and thus leads to
performance degradation.
// The following code will produce a warning because this ID-dependent
// variable is used in a loop condition statement.
int ThreadID = get_local_id(0);
// The following loop will produce a warning because the loop condition
// statement depends on an ID-dependent variable.
for (int i = 0; i < ThreadID; ++i) {
std::cout << i << std::endl;
}
// The following loop will not produce a warning, because the ID-dependent
// variable is not used in the loop condition statement.
for (int i = 0; i < 100; ++i) {
std::cout << ThreadID << std::endl;
}
Based on the Altera SDK for OpenCL: Best Practices Guide .
altera-kernel-name-restriction
Finds kernel files and include directives whose filename is kernel.cl,
Verilog.cl, or VHDL.cl. The check is case insensitive.
Such kernel file names cause the offline compiler to generate
intermediate design files that have the same names as certain internal
files, which leads to a compilation error.
Based on the Guidelines for Naming the Kernel section in the Intel FPGA
SDK for OpenCL Pro Edition: Programming Guide .
altera-single-work-item-barrier
Finds OpenCL kernel functions that call a barrier function but do not
call an ID function (get_local_id, get_local_id, get_group_id, or
get_local_linear_id).
These kernels may be viable single work-item kernels, but will be
forced to execute as NDRange kernels if using a newer version of the
Altera Offline Compiler (>= v17.01).
If using an older version of the Altera Offline Compiler, these kernel
functions will be treated as single work-item kernels, which could be
inefficient or lead to errors if NDRange semantics were intended.
Based on the Altera SDK for OpenCL: Best Practices Guide .
Examples:
// error: function calls barrier but does not call an ID function.
void __kernel barrier_no_id(__global int * foo, int size) {
for (int i = 0; i < 100; i++) {
foo[i] += 5;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
// ok: function calls barrier and an ID function.
void __kernel barrier_with_id(__global int * foo, int size) {
for (int i = 0; i < 100; i++) {
int tid = get_global_id(0);
foo[tid] += 5;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
// ok with AOC Version 17.01: the reqd_work_group_size turns this into
// an NDRange.
__attribute__((reqd_work_group_size(2,2,2)))
void __kernel barrier_with_id(__global int * foo, int size) {
for (int i = 0; i < 100; i++) {
foo[tid] += 5;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
Options
AOCVersion
Defines the version of the Altera Offline Compiler. Defaults to
1600 (corresponding to version 16.00).
altera-struct-pack-align
Finds structs that are inefficiently packed or aligned, and recommends
packing and/or aligning of said structs as needed.
Structs that are not packed take up more space than they should, and
accessing structs that are not well aligned is inefficient.
Fix-its are provided to fix both of these issues by inserting and/or
amending relevant struct attributes.
Based on the Altera SDK for OpenCL: Best Practices Guide .
// The following struct is originally aligned to 4 bytes, and thus takes up
// 12 bytes of memory instead of 10. Packing the struct will make it use
// only 10 bytes of memory, and aligning it to 16 bytes will make it
// efficient to access.
struct example {
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
};
// The following struct is arranged in such a way that packing is not needed.
// However, it is aligned to 4 bytes instead of 8, and thus needs to be
// explicitly aligned.
struct implicitly_packed_example {
char a; // 1 byte
char b; // 1 byte
char c; // 1 byte
char d; // 1 byte
int e; // 4 bytes
};
// The following struct is explicitly aligned and packed.
struct good_example {
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
} __attribute__((packed)) __attribute__((aligned(16));
// Explicitly aligning a struct to the wrong value will result in a warning.
// The following example should be aligned to 16 bytes, not 32.
struct badly_aligned_example {
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
} __attribute__((packed)) __attribute__((aligned(32)));
altera-unroll-loops
Finds inner loops that have not been unrolled, as well as fully
unrolled loops with unknown loop bounds or a large number of
iterations.
Unrolling inner loops could improve the performance of OpenCL kernels.
However, if they have unknown loop bounds or a large number of
iterations, they cannot be fully unrolled, and should be partially
unrolled.
Notes:
o This check is unable to determine the number of iterations in a while
or do..while loop; hence if such a loop is fully unrolled, a note is
emitted advising the user to partially unroll instead.
o In for loops, our check only works with simple arithmetic increments
( +, -, *, /). For all other increments, partial unrolling is
advised.
o Depending on the exit condition, the calculations for determining if
the number of iterations is large may be off by 1. This should not be
an issue since the cut-off is generally arbitrary.
Based on the Altera SDK for OpenCL: Best Practices Guide .
for (int i = 0; i < 10; i++) { // ok: outer loops should not be unrolled
int j = 0;
do { // warning: this inner do..while loop should be unrolled
j++;
} while (j < 15);
int k = 0;
#pragma unroll
while (k < 20) { // ok: this inner loop is already unrolled
k++;
}
}
int A[1000];
#pragma unroll
// warning: this loop is large and should be partially unrolled
for (int a : A) {
printf("%d", a);
}
#pragma unroll 5
// ok: this loop is large, but is partially unrolled
for (int a : A) {
printf("%d", a);
}
#pragma unroll
// warning: this loop is large and should be partially unrolled
for (int i = 0; i < 1000; ++i) {
printf("%d", i);
}
#pragma unroll 5
// ok: this loop is large, but is partially unrolled
for (int i = 0; i < 1000; ++i) {
printf("%d", i);
}
#pragma unroll
// warning: << operator not supported, recommend partial unrolling
for (int i = 0; i < 1000; i<<1) {
printf("%d", i);
}
std::vector someVector (100, 0);
int i = 0;
#pragma unroll
// note: loop may be large, recommend partial unrolling
while (i < someVector.size()) {
someVector[i]++;
}
#pragma unroll
// note: loop may be large, recommend partial unrolling
while (true) {
printf("In loop");
}
#pragma unroll 5
// ok: loop may be large, but is partially unrolled
while (i < someVector.size()) {
someVector[i]++;
}
Options
MaxLoopIterations
Defines the maximum number of loop iterations that a fully
unrolled loop can have. By default, it is set to 100.
In practice, this refers to the integer value of the upper bound
within the loop statement's condition expression.
android-cloexec-accept
The usage of accept() is not recommended, it's better to use accept4().
Without this flag, an opened sensitive file descriptor would remain
open across a fork+exec to a lower-privileged SELinux domain.
Examples:
accept(sockfd, addr, addrlen);
// becomes
accept4(sockfd, addr, addrlen, SOCK_CLOEXEC);
android-cloexec-accept4
accept4() should include SOCK_CLOEXEC in its type argument to avoid the
file descriptor leakage. Without this flag, an opened sensitive file
would remain open across a fork+exec to a lower-privileged SELinux
domain.
Examples:
accept4(sockfd, addr, addrlen, SOCK_NONBLOCK);
// becomes
accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
android-cloexec-creat
The usage of creat() is not recommended, it's better to use open().
Examples:
int fd = creat(path, mode);
// becomes
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
android-cloexec-dup
The usage of dup() is not recommended, it's better to use fcntl(),
which can set the close-on-exec flag. Otherwise, an opened sensitive
file would remain open across a fork+exec to a lower-privileged SELinux
domain.
Examples:
int fd = dup(oldfd);
// becomes
int fd = fcntl(oldfd, F_DUPFD_CLOEXEC);
android-cloexec-epoll-create
The usage of epoll_create() is not recommended, it's better to use
epoll_create1(), which allows close-on-exec.
Examples:
epoll_create(size);
// becomes
epoll_create1(EPOLL_CLOEXEC);
android-cloexec-epoll-create1
epoll_create1() should include EPOLL_CLOEXEC in its type argument to
avoid the file descriptor leakage. Without this flag, an opened
sensitive file would remain open across a fork+exec to a
lower-privileged SELinux domain.
Examples:
epoll_create1(0);
// becomes
epoll_create1(EPOLL_CLOEXEC);
android-cloexec-fopen
fopen() should include e in their mode string; so re would be valid.
This is equivalent to having set FD_CLOEXEC on that descriptor.
Examples:
fopen("fn", "r");
// becomes
fopen("fn", "re");
android-cloexec-inotify-init
The usage of inotify_init() is not recommended, it's better to use
inotify_init1().
Examples:
inotify_init();
// becomes
inotify_init1(IN_CLOEXEC);
android-cloexec-inotify-init1
inotify_init1() should include IN_CLOEXEC in its type argument to avoid
the file descriptor leakage. Without this flag, an opened sensitive
file would remain open across a fork+exec to a lower-privileged SELinux
domain.
Examples:
inotify_init1(IN_NONBLOCK);
// becomes
inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
android-cloexec-memfd-create
memfd_create() should include MFD_CLOEXEC in its type argument to avoid
the file descriptor leakage. Without this flag, an opened sensitive
file would remain open across a fork+exec to a lower-privileged SELinux
domain.
Examples:
memfd_create(name, MFD_ALLOW_SEALING);
// becomes
memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
android-cloexec-open
A common source of security bugs is code that opens a file without
using the O_CLOEXEC flag. Without that flag, an opened sensitive file
would remain open across a fork+exec to a lower-privileged SELinux
domain, leaking that sensitive data. Open-like functions including
open(), openat(), and open64() should include O_CLOEXEC in their flags
argument.
Examples:
open("filename", O_RDWR);
open64("filename", O_RDWR);
openat(0, "filename", O_RDWR);
// becomes
open("filename", O_RDWR | O_CLOEXEC);
open64("filename", O_RDWR | O_CLOEXEC);
openat(0, "filename", O_RDWR | O_CLOEXEC);
android-cloexec-pipe
This check detects usage of pipe(). Using pipe() is not recommended,
pipe2() is the suggested replacement. The check also adds the O_CLOEXEC
flag that marks the file descriptor to be closed in child processes.
Without this flag a sensitive file descriptor can be leaked to a child
process, potentially into a lower-privileged SELinux domain.
Examples:
pipe(pipefd);
Suggested replacement:
pipe2(pipefd, O_CLOEXEC);
android-cloexec-pipe2
This check ensures that pipe2() is called with the O_CLOEXEC flag. The
check also adds the O_CLOEXEC flag that marks the file descriptor to be
closed in child processes. Without this flag a sensitive file
descriptor can be leaked to a child process, potentially into a
lower-privileged SELinux domain.
Examples:
pipe2(pipefd, O_NONBLOCK);
Suggested replacement:
pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);
android-cloexec-socket
socket() should include SOCK_CLOEXEC in its type argument to avoid the
file descriptor leakage. Without this flag, an opened sensitive file
would remain open across a fork+exec to a lower-privileged SELinux
domain.
Examples:
socket(domain, type, SOCK_STREAM);
// becomes
socket(domain, type, SOCK_STREAM | SOCK_CLOEXEC);
android-comparison-in-temp-failure-retry
Diagnoses comparisons that appear to be incorrectly placed in the
argument to the TEMP_FAILURE_RETRY macro. Having such a use is
incorrect in the vast majority of cases, and will often silently defeat
the purpose of the TEMP_FAILURE_RETRY macro.
For context, TEMP_FAILURE_RETRY is a convenience macro
provided by both glibc and Bionic. Its purpose is to repeatedly run a
syscall until it either succeeds, or fails for reasons other than being
interrupted.
Example buggy usage looks like:
char cs[1];
while (TEMP_FAILURE_RETRY(read(STDIN_FILENO, cs, sizeof(cs)) != 0)) {
// Do something with cs.
}
Because TEMP_FAILURE_RETRY will check for whether the result of the
comparison is -1, and retry if so.
If you encounter this, the fix is simple: lift the comparison out of
the TEMP_FAILURE_RETRY argument, like so:
char cs[1];
while (TEMP_FAILURE_RETRY(read(STDIN_FILENO, cs, sizeof(cs))) != 0) {
// Do something with cs.
}
Options
RetryMacros
A comma-separated list of the names of retry macros to be
checked. Default is TEMP_FAILURE_RETRY.
boost-use-ranges
Detects calls to standard library iterator algorithms that could be
replaced with a Boost ranges version instead.
Example
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(), std::begin(Items2),
std::end(Items2));
Transforms to:
auto Iter1 = boost::range::find(Items, 0);
auto AreSame = boost::range::equal(Items1, Items2);
Supported algorithms
Calls to the following std library algorithms are checked:
std::accumulate, std::adjacent_difference, std::adjacent_find,
std::all_of, std::any_of, std::binary_search, std::copy_backward,
std::copy_if, std::copy, std::count_if, std::count, std::equal_range,
std::equal, std::fill, std::find_end, std::find_first_of,
std::find_if_not, std::find_if, std::find, std::for_each,
std::generate, std::includes, std::iota, std::is_partitioned,
std::is_permutation, std::is_sorted_until, std::is_sorted,
std::lexicographical_compare, std::lower_bound, std::make_heap,
std::max_element, std::merge, std::min_element, std::mismatch,
std::next_permutation, std::none_of, std::partial_sum,
std::partial_sort_copy, std::partition_copy, std::partition_point,
std::partition, std::pop_heap, std::prev_permutation, std::push_heap,
std::random_shuffle, std::reduce, std::remove_copy_if,
std::remove_copy, std::remove_if, std::remove, std::replace_copy_if,
std::replace_copy, std::replace_if, std::replace, std::reverse_copy,
std::reverse, std::search, std::set_difference, std::set_intersection,
std::set_symmetric_difference, std::set_union, std::sort_heap,
std::sort, std::stable_partition, std::stable_sort, std::transform,
std::unique_copy, std::unique, std::upper_bound.
The check will also look for the following functions from the
boost::algorithm namespace:
all_of_equal, any_of_equal, any_of, apply_permutation,
apply_reverse_permutation, clamp_range, copy_if_until, copy_if_while,
copy_if, copy_until, copy_while, find_backward, find_if_backward,
find_if_not_backward, find_if_not, find_not_backward, hex_lower, hex,
iota, all_of, is_decreasing, is_increasing, is_palindrome,
is_partitioned_until, is_partitioned, is_permutation, is_sorted_until,
is_sorted, is_strictly_decreasing, is_strictly_increasing,
none_of_equal, none_of, one_of_equal, one_of, partition_copy,
partition_point, reduce, unhex.
Reverse Iteration
If calls are made using reverse iterators on containers, The code will
be fixed using the boost::adaptors::reverse adaptor.
auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
std::crbegin(Items2), std::crend(Items2));
Transforms to:
auto AreSame = boost::range::equal(boost::adaptors::reverse(Items1),
boost::adaptors::reverse(Items2));
Options
IncludeStyle
A string specifying which include-style is used, llvm or google.
Default is llvm.
IncludeBoostSystem
If true (default value) the boost headers are included as system
headers with angle brackets (#include ), otherwise
quotes are used (#include "boost.hpp").
UseReversePipe
When true (default false), fixes which involve reverse ranges
will use the pipe adaptor syntax instead of the function syntax.
std::find(Items.rbegin(), Items.rend(), 0);
Transforms to:
boost::range::find(Items | boost::adaptors::reversed, 0);
boost-use-to-string
This check finds conversion from integer type like int to std::string
or std::wstring using boost::lexical_cast, and replace it with calls to
std::to_string and std::to_wstring.
It doesn't replace conversion from floating points despite the
to_string overloads, because it would change the behavior.
auto str = boost::lexical_cast(42);
auto wstr = boost::lexical_cast(2137LL);
// Will be changed to
auto str = std::to_string(42);
auto wstr = std::to_wstring(2137LL);
bugprone-argument-comment
Checks that argument comments match parameter names.
The check understands argument comments in the form /*parameter_name=*/
that are placed right before the argument.
void f(bool foo);
...
f(/*bar=*/true);
// warning: argument name 'bar' in comment does not match parameter name 'foo'
The check tries to detect typos and suggest automated fixes for them.
Options
StrictMode
When false, the check will ignore leading and trailing
underscores and case when comparing names -- otherwise they are
taken into account. Default is false.
IgnoreSingleArgument
When true, the check will ignore the single argument. Default is
false.
CommentBoolLiterals
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the boolean literal argument.
Default is false.
Before:
void foo(bool TurnKey, bool PressButton);
foo(true, false);
After:
void foo(bool TurnKey, bool PressButton);
foo(/*TurnKey=*/true, /*PressButton=*/false);
CommentIntegerLiterals
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the integer literal argument.
Default is false.
Before:
void foo(int MeaningOfLife);
foo(42);
After:
void foo(int MeaningOfLife);
foo(/*MeaningOfLife=*/42);
CommentFloatLiterals
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the float/double literal
argument. Default is false.
Before:
void foo(float Pi);
foo(3.14159);
After:
void foo(float Pi);
foo(/*Pi=*/3.14159);
CommentStringLiterals
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the string literal argument.
Default is false.
Before:
void foo(const char *String);
void foo(const wchar_t *WideString);
foo("Hello World");
foo(L"Hello World");
After:
void foo(const char *String);
void foo(const wchar_t *WideString);
foo(/*String=*/"Hello World");
foo(/*WideString=*/L"Hello World");
CommentCharacterLiterals
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the character literal argument.
Default is false.
Before:
void foo(char *Character);
foo('A');
After:
void foo(char *Character);
foo(/*Character=*/'A');
CommentUserDefinedLiterals
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the user defined literal
argument. Default is false.
Before:
void foo(double Distance);
double operator"" _km(long double);
foo(402.0_km);
After:
void foo(double Distance);
double operator"" _km(long double);
foo(/*Distance=*/402.0_km);
CommentNullPtrs
When true, the check will add argument comments in the format
/*ParameterName=*/ right before the nullptr literal argument.
Default is false.
Before:
void foo(A* Value);
foo(nullptr);
After:
void foo(A* Value);
foo(/*Value=*/nullptr);
bugprone-assert-side-effect
Finds assert() with side effect.
The condition of assert() is evaluated only in debug builds so a
condition with side effect can cause different behavior in debug /
release builds.
Options
AssertMacros
A comma-separated list of the names of assert macros to be
checked. Default is assert,NSAssert,NSCAssert.
CheckFunctionCalls
Whether to treat non-const member and non-member functions as
they produce side effects. Disabled by default because it can
increase the number of false positive warnings.
IgnoredFunctions
A semicolon-separated list of the names of functions or methods
to be considered as not having side-effects. Regular expressions
are accepted, e.g. [Rr]ef(erence)?$ matches every type with
suffix Ref, ref, Reference and reference. The default is empty.
If a name in the list contains the sequence :: it is matched
against the qualified type name (i.e. namespace::Type),
otherwise it is matched against only the type name (i.e. Type).
bugprone-assignment-in-if-condition
Finds assignments within conditions of if statements. Such assignments
are bug-prone because they may have been intended as equality tests.
This check finds all assignments within if conditions, including ones
that are not flagged by -Wparentheses due to an extra set of
parentheses, and including assignments that call an overloaded
operator=(). The identified assignments violate BARR group "Rule 8.2.c"
.
int f = 3;
if(f = 4) { // This is identified by both `Wparentheses` and this check - should it have been: `if (f == 4)` ?
f = f + 1;
}
if((f == 5) || (f = 6)) { // the assignment here `(f = 6)` is identified by this check, but not by `-Wparentheses`. Should it have been `(f == 6)` ?
f = f + 2;
}
bugprone-bad-signal-to-kill-thread
Finds pthread_kill function calls when a thread is terminated by
raising SIGTERM signal and the signal kills the entire process, not
just the individual thread. Use any signal except SIGTERM.
pthread_kill(thread, SIGTERM);
This check corresponds to the CERT C Coding Standard rule POS44-C. Do
not use signals to terminate threads .
cert-pos44-c redirects here as an alias of this check.
bugprone-bitwise-pointer-cast
Warns about code that tries to cast between pointers by means of
std::bit_cast or memcpy.
The motivation is that std::bit_cast is advertised as the safe
alternative to type punning via reinterpret_cast in modern C++.
However, one should not blindly replace reinterpret_cast with
std::bit_cast, as follows:
int x{};
-float y = *reinterpret_cast(&x);
+float y = *std::bit_cast(&x);
The drop-in replacement behaves exactly the same as reinterpret_cast,
and Undefined Behavior is still invoked. std::bit_cast is copying the
bytes of the input pointer, not the pointee, into an output pointer of
a different type, which may violate the strict aliasing rules. However,
simply looking at the code, it looks "safe", because it uses
std::bit_cast which is advertised as safe.
The solution to safe type punning is to apply std::bit_cast on value
types, not on pointer types:
int x{};
float y = std::bit_cast(x);
This way, the bytes of the input object are copied into the output
object, which is much safer. Do note that Undefined Behavior can still
occur, if there is no value of type To corresponding to the value
representation produced. Compilers may be able to optimize this copy
and generate identical assembly to the original reinterpret_cast
version.
Code before C++20 may backport std::bit_cast by means of memcpy, or
simply call memcpy directly, which is equally problematic. This is also
detected by this check:
int* x{};
float* y{};
std::memcpy(&y, &x, sizeof(x));
Alternatively, if a cast between pointers is truly wanted,
reinterpret_cast should be used, to clearly convey the intent and
enable warnings from compilers and linters, which should be addressed
accordingly.
bugprone-bool-pointer-implicit-conversion
Checks for conditions based on implicit conversion from a bool pointer
to bool.
Example:
bool *p;
if (p) {
// Never used in a pointer-specific way.
}
bugprone-branch-clone
Checks for repeated branches in if/else if/else chains, consecutive
repeated branches in switch statements and identical true and false
branches in conditional operators.
if (test_value(x)) {
y++;
do_something(x, y);
} else {
y++;
do_something(x, y);
}
In this simple example (which could arise e.g. as a copy-paste error)
the then and else branches are identical and the code is equivalent the
following shorter and cleaner code:
test_value(x); // can be omitted unless it has side effects
y++;
do_something(x, y);
If this is the intended behavior, then there is no reason to use a
conditional statement; otherwise the issue can be solved by fixing the
branch that is handled incorrectly.
The check detects repeated branches in longer if/else if/else chains
where it would be even harder to notice the problem.
The check also detects repeated inner and outer if statements that may
be a result of a copy-paste error. This check cannot currently detect
identical inner and outer if statements if code is between the if
conditions. An example is as follows.
void test_warn_inner_if_1(int x) {
if (x == 1) { // warns, if with identical inner if
if (x == 1) // inner if is here
;
if (x == 1) { // does not warn, cannot detect
int y = x;
if (x == 1)
;
}
}
In switch statements the check only reports repeated branches when they
are consecutive, because it is relatively common that the case: labels
have some natural ordering and rearranging them would decrease the
readability of the code. For example:
switch (ch) {
case 'a':
return 10;
case 'A':
return 10;
case 'b':
return 11;
case 'B':
return 11;
default:
return 10;
}
Here the check reports that the 'a' and 'A' branches are identical (and
that the 'b' and 'B' branches are also identical), but does not report
that the default: branch is also identical to the first two branches.
If this is indeed the correct behavior, then it could be implemented
as:
switch (ch) {
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
default:
return 10;
}
Here the check does not warn for the repeated return 10;, which is good
if we want to preserve that 'a' is before 'b' and default: is the last
branch.
Switch cases marked with the [[fallthrough]] attribute are ignored.
Finally, the check also examines conditional operators and reports code
like:
return test_value(x) ? x : x;
Unlike if statements, the check does not detect chains of conditional
operators.
Note: This check also reports situations where branches become
identical only after preprocessing.
bugprone-capturing-this-in-member-variable
Finds lambda captures that capture the this pointer and store it as
class members without handle the copy and move constructors and the
assignments.
Capture this in a lambda and store it as a class member is dangerous
because the lambda can outlive the object it captures. Especially when
the object is copied or moved, the captured this pointer will be
implicitly propagated to the new object. Most of the time, people will
believe that the captured this pointer points to the new object, which
will lead to bugs.
struct C {
C() : Captured([this]() -> C const * { return this; }) {}
std::function Captured;
};
void foo() {
C v1{};
C v2 = v1; // v2.Captured capture v1's 'this' pointer
assert(v2.Captured() == v1.Captured()); // v2.Captured capture v1's 'this' pointer
assert(v2.Captured() == &v2); // assertion failed.
}
Possible fixes:
o marking copy and move constructors and assignment operators
deleted.
o using class member method instead of class member variable
with function object types.
o passing this pointer as parameter.
Options
FunctionWrapperTypes
A semicolon-separated list of names of types. Used to specify
function wrapper that can hold lambda expressions. Default is
::std::function;::std::move_only_function;::boost::function.
BindFunctions
A semicolon-separated list of fully qualified names of functions
that can capture this pointer. Default is
::std::bind;::boost::bind;::std::bind_front;::std::bind_back;
::boost::compat::bind_front;::boost::compat::bind_back.
bugprone-casting-through-void
Detects unsafe or redundant two-step casting operations involving
void*, which is equivalent to reinterpret_cast as per the C++ Standard
.
Two-step type conversions via void* are discouraged for several
reasons.
o They obscure code and impede its understandability, complicating
maintenance.
o These conversions bypass valuable compiler support, erasing warnings
related to pointer alignment. It may violate strict aliasing rule and
leading to undefined behavior.
o In scenarios involving multiple inheritance, ambiguity and unexpected
outcomes can arise due to the loss of type information, posing
runtime issues.
In summary, avoiding two-step type conversions through void* ensures
clearer code, maintains essential compiler warnings, and prevents
ambiguity and potential runtime errors, particularly in complex
inheritance scenarios. If such a cast is wanted, it shall be done via
reinterpret_cast, to express the intent more clearly.
Note: it is expected that, after applying the suggested fix and using
reinterpret_cast, the check cppcoreguidelines-pro-type-reinterpret-cast
<> will emit a warning. This is intentional: reinterpret_cast is a
dangerous operation that can easily break the strict aliasing rules
when dereferencing the casted pointer, invoking Undefined Behavior. The
warning is there to prompt users to carefully analyze whether the usage
of reinterpret_cast is safe, in which case the warning may be
suppressed.
Examples:
using IntegerPointer = int *;
double *ptr;
static_cast(static_cast(ptr)); // WRONG
reinterpret_cast(reinterpret_cast(ptr)); // WRONG
(IntegerPointer)(void *)ptr; // WRONG
IntegerPointer(static_cast(ptr)); // WRONG
reinterpret_cast(ptr); // OK, clearly expresses intent.
// NOTE: dereferencing this pointer violates
// the strict aliasing rules, invoking
// Undefined Behavior.
bugprone-chained-comparison
Check detects chained comparison operators that can lead to unintended
behavior or logical errors.
Chained comparisons are expressions that use multiple comparison
operators to compare three or more values. For example, the expression
a < b < c compares the values of a, b, and c. However, this expression
does not evaluate as (a < b) && (b < c), which is probably what the
developer intended. Instead, it evaluates as (a < b) < c, which may
produce unintended results, especially when the types of a, b, and c
are different.
To avoid such errors, the check will issue a warning when a chained
comparison operator is detected, suggesting to use parentheses to
specify the order of evaluation or to use a logical operator to
separate comparison expressions.
Consider the following examples:
int a = 2, b = 6, c = 4;
if (a < b < c) {
// This block will be executed
}
In this example, the developer intended to check if a is less than b
and b is less than c. However, the expression a < b < c is equivalent
to (a < b) < c. Since a < b is true, the expression (a < b) < c is
evaluated as 1 < c, which is equivalent to true < c and is invalid in
this case as b < c is false.
Even that above issue could be detected as comparison of int to bool,
there is more dangerous example:
bool a = false, b = false, c = true;
if (a == b == c) {
// This block will be executed
}
In this example, the developer intended to check if a, b, and c are all
equal. However, the expression a == b == c is evaluated as (a == b) ==
c. Since a == b is true, the expression (a == b) == c is evaluated as
true == c, which is equivalent to true == true. This comparison yields
true, even though a and b are false, and are not equal to c.
To avoid this issue, the developer can use a logical operator to
separate the comparison expressions, like this:
if (a == b && b == c) {
// This block will not be executed
}
Alternatively, use of parentheses in the comparison expressions can
make the developer's intention more explicit and help avoid
misunderstanding.
if ((a == b) == c) {
// This block will be executed
}
Options
IgnoreMacros
If true, the check will not warn on chained comparisons inside
macros. Default is false.
bugprone-command-processor
Flags calls to system(), popen(), and _popen(), which execute a command
processor. It does not flag calls to system() with a null pointer
argument, as such a call checks for the presence of a command processor
but does not actually attempt to execute a command.
References
This check corresponds to the CERT C Coding Standard rule ENV33-C. Do
not call system() .
bugprone-compare-pointer-to-member-virtual-function
Detects unspecified behavior about equality comparison between pointer
to member virtual function and anything other than
null-pointer-constant.
struct A {
void f1();
void f2();
virtual void f3();
virtual void f4();
void g1(int);
};
void fn() {
bool r1 = (&A::f1 == &A::f2); // ok
bool r2 = (&A::f1 == &A::f3); // bugprone
bool r3 = (&A::f1 != &A::f3); // bugprone
bool r4 = (&A::f3 == nullptr); // ok
bool r5 = (&A::f3 == &A::f4); // bugprone
void (A::*v1)() = &A::f3;
bool r6 = (v1 == &A::f1); // bugprone
bool r6 = (v1 == nullptr); // ok
void (A::*v2)() = &A::f2;
bool r7 = (v2 == &A::f1); // false positive, but potential risk if assigning other value to v2.
void (A::*v3)(int) = &A::g1;
bool r8 = (v3 == &A::g1); // ok, no virtual function match void(A::*)(int) signature.
}
Provide warnings on equality comparisons involve pointers to member
virtual function or variables which is potential pointer to member
virtual function and any entity other than a null-pointer constant.
In certain compilers, virtual function addresses are not conventional
pointers but instead consist of offsets and indexes within a virtual
function table (vtable). Consequently, these pointers may vary between
base and derived classes, leading to unpredictable behavior when
compared directly. This issue becomes particularly challenging when
dealing with pointers to pure virtual functions, as they may not even
have a valid address, further complicating comparisons.
Instead, it is recommended to utilize the typeid operator or other
appropriate mechanisms for comparing objects to ensure robust and
predictable behavior in your codebase. By heeding this detection and
adopting a more reliable comparison method, you can mitigate potential
issues related to unspecified behavior, especially when dealing with
pointers to member virtual functions or pure virtual functions, thereby
improving the overall stability and maintainability of your code. In
scenarios involving pointers to member virtual functions, it's only
advisable to employ nullptr for comparisons.
Limitations
Does not analyze values stored in a variable. For variable, only
analyze all virtual methods in the same class or struct and diagnose
when assigning a pointer to member virtual function to this variable is
possible.
bugprone-copy-constructor-init
Finds copy constructors where the constructor doesn't call the copy
constructor of the base class.
class Copyable {
public:
Copyable() = default;
Copyable(const Copyable &) = default;
int memberToBeCopied = 0;
};
class X2 : public Copyable {
X2(const X2 &other) {} // Copyable(other) is missing
};
Also finds copy constructors where the constructor of the base class
don't have parameter.
class X3 : public Copyable {
X3(const X3 &other) : Copyable() {} // other is missing
};
Failure to properly initialize base class sub-objects during copy
construction can result in undefined behavior, crashes, data
corruption, or other unexpected outcomes. The check ensures that the
copy constructor of a derived class properly calls the copy constructor
of the base class, helping to prevent bugs and improve code quality.
Limitations
o It won't generate warnings for empty classes, as there are no class
members (including base class sub-objects) to worry about.
o It won't generate warnings for base classes that have copy
constructor private or deleted.
o It won't generate warnings for base classes that are initialized
using other non-default constructor, as this could be intentional.
The check also suggests a fix-its in some cases.
bugprone-copy-constructor-mutates-argument
Finds assignments to the copied object and its direct or indirect
members in copy constructors and copy assignment operators.
This check corresponds to the CERT C Coding Standard rule OOP58-CPP.
Copy operations must not mutate the source object .
bugprone-crtp-constructor-accessibility
Detects error-prone Curiously Recurring Template Pattern usage, when
the CRTP can be constructed outside itself and the derived class.
The CRTP is an idiom, in which a class derives from a template class,
where itself is the template argument. It should be ensured that if a
class is intended to be a base class in this idiom, it can only be
instantiated if the derived class is its template argument.
Example:
template class CRTP {
private:
CRTP() = default;
friend T;
};
class Derived : CRTP {};
Below can be seen some common mistakes that will allow the breaking of
the idiom.
If the constructor of a class intended to be used in a CRTP is public,
then it allows users to construct that class on its own.
Example:
template class CRTP {
public:
CRTP() = default;
};
class Good : CRTP {};
Good GoodInstance;
CRTP BadInstance;
If the constructor is protected, the possibility of an accidental
instantiation is prevented, however it can fade an error, when a
different class is used as the template parameter instead of the
derived one.
Example:
template class CRTP {
protected:
CRTP() = default;
};
class Good : CRTP {};
Good GoodInstance;
class Bad : CRTP {};
Bad BadInstance;
To ensure that no accidental instantiation happens, the best practice
is to make the constructor private and declare the derived class as
friend. Note that as a tradeoff, this also gives the derived class
access to every other private members of the CRTP. However,
constructors can still be public or protected if they are deleted.
Example:
template class CRTP {
CRTP() = default;
friend T;
};
class Good : CRTP {};
Good GoodInstance;
class Bad : CRTP {};
Bad CompileTimeError;
CRTP AlsoCompileTimeError;
Limitations
o The check is not supported below C++11
o The check does not handle when the derived class is passed as a
variadic template argument
o Accessible functions that can construct the CRTP, like factory
functions are not checked
The check also suggests a fix-its in some cases.
bugprone-dangling-handle
Detect dangling references in value handles like std::string_view.
These dangling references can be a result of constructing handles from
temporary values, where the temporary is destroyed soon after the
handle is created.
Examples:
string_view View = string(); // View will dangle.
string A;
View = A + "A"; // still dangle.
vector V;
V.push_back(string()); // V[0] is dangling.
V.resize(3, string()); // V[1] and V[2] will also dangle.
string_view f() {
// All these return values will dangle.
return string();
string S;
return S;
char Array[10]{};
return Array;
}
span g() {
array V;
return {V};
int Array[10]{};
return {Array};
}
Options
HandleClasses
A semicolon-separated list of class names that should be treated
as handles. By default only std::basic_string_view,
std::experimental::basic_string_view and std::span are
considered.
bugprone-default-operator-new-on-overaligned-type
Flags uses of default operator new where the type has extended
alignment (an alignment greater than the fundamental alignment).
The default operator new is guaranteed to provide the correct alignment
if the requested alignment is less or equal to the fundamental
alignment. Only cases are detected (by design) where the operator new
is not user-defined and is not a placement new (the reason is that in
these cases we assume that the user provided the correct memory
allocation).
References
This check corresponds to the CERT C++ Coding Standard rule MEM57-CPP.
Avoid using default operator new for over-aligned types .
bugprone-derived-method-shadowing-base-method
Finds derived class methods that shadow a (non-virtual) base class
method.
In order to be considered "shadowing", methods must have the same
signature (i.e. the same name, same number of parameters, same
parameter types, etc). Only checks public, non-templated methods.
The below example is bugprone because consumers of the Derived class
will expect the reset method to do the work of Base::reset() in
addition to extra work required to reset the Derived class. Common
fixes include:
o Making the reset method polymorphic
o Re-naming Derived::reset if it's not meant to intersect with
Base::reset
o Using using Base::reset to change the access specifier
This is also a violation of the Liskov Substitution Principle.
struct Base {
void reset() {/* reset the base class */};
};
struct Derived : public Base {
void reset() {/* reset the derived class, but not the base class */};
};
bugprone-dynamic-static-initializers
Finds instances of static variables that are dynamically initialized in
header files.
This can pose problems in certain multithreaded contexts. For example,
when disabling compiler generated synchronization instructions for
static variables initialized at runtime (e.g. by
-fno-threadsafe-statics), even if a particular project takes the
necessary precautions to prevent race conditions during initialization
by providing their own synchronization, header files included from
other projects may not. Therefore, such a check is helpful for ensuring
that disabling compiler generated synchronization for static variable
initialization will not cause problems.
Consider the following code:
int foo() {
static int k = bar();
return k;
}
When synchronization of static initialization is disabled, if two
threads both call foo for the first time, there is the possibility that
k will be double initialized, creating a race condition.
bugprone-easily-swappable-parameters
Finds function definitions where parameters of convertible types follow
each other directly, making call sites prone to calling the function
with swapped (or badly ordered) arguments.
void drawPoint(int X, int Y) { /* ... */ }
FILE *open(const char *Dir, const char *Name, Flags Mode) { /* ... */ }
A potential call like drawPoint(-2, 5) or openPath("a.txt", "tmp",
Read) is perfectly legal from the language's perspective, but might not
be what the developer of the function intended.
More elaborate and type-safe constructs, such as opaque typedefs or
strong types should be used instead, to prevent a mistaken order of
arguments.
struct Coord2D { int X; int Y; };
void drawPoint(const Coord2D Pos) { /* ... */ }
FILE *open(const Path &Dir, const Filename &Name, Flags Mode) { /* ... */ }
Due to the potentially elaborate refactoring and API-breaking that is
necessary to strengthen the type safety of a project, no automatic
fix-its are offered.
Options
Extension/relaxation options
Relaxation (or extension) options can be used to broaden the scope of
the analysis and fine-tune the enabling of more mixes between types.
Some mixes may depend on coding style or preference specific to a
project, however, it should be noted that enabling all of these
relaxations model the way of mixing at call sites the most. These
options are expected to make the check report for more functions, and
report longer mixable ranges.
QualifiersMix
Whether to consider parameters of some cvr-qualified T and a
differently cvr-qualified T (i.e. T and const T, const T and
volatile T, etc.) mixable between one another. If false, the
check will consider differently qualified types unmixable. True
turns the warnings on. Defaults to false.
The following example produces a diagnostic only if
QualifiersMix is enabled:
void *memcpy(const void *Destination, void *Source, std::size_t N) { /* ... */ }
ModelImplicitConversions
Whether to consider parameters of type T and U mixable if there
exists an implicit conversion from T to U and U to T. If false,
the check will not consider implicitly convertible types for
mixability. True turns warnings for implicit conversions on.
Defaults to true.
The following examples produce a diagnostic only if
ModelImplicitConversions is enabled:
void fun(int Int, double Double) { /* ... */ }
void compare(const char *CharBuf, std::string String) { /* ... */ }
Note:
Changing the qualifiers of an expression's type (e.g. from
int to const int) is defined as an implicit conversion in the
C++ Standard. However, the check separates this
decision-making on the mixability of differently qualified
types based on whether QualifiersMix was enabled.
For example, the following code snippet will only produce a
diagnostic if both QualifiersMix and ModelImplicitConversions
are enabled:
void fun2(int Int, const double Double) { /* ... */ }
Filtering options
Filtering options can be used to lessen the size of the diagnostics
emitted by the checker, whether the aim is to ignore certain constructs
or dampen the noisiness.
MinimumLength
The minimum length required from an adjacent parameter sequence
to be diagnosed. Defaults to 2. Might be any positive integer
greater or equal to 2. If 0 or 1 is given, the default value 2
will be used instead.
For example, if 3 is specified, the examples above will not be
matched.
IgnoredParameterNames
The list of parameter names that should never be considered part
of a swappable adjacent parameter sequence. The value is a
;-separated list of names. To ignore unnamed parameters, add ""
to the list verbatim (not the empty string, but the two quotes,
potentially escaped!). This option is case-sensitive!
By default, the following parameter names, and their
Uppercase-initial variants are ignored: "" (unnamed parameters),
iterator, begin, end, first, last, lhs, rhs.
IgnoredParameterTypeSuffixes
The list of parameter type name suffixes that should never be
considered part of a swappable adjacent parameter sequence.
Parameters which type, as written in the source code, end with
an element of this option will be ignored. The value is a
;-separated list of names. This option is case-sensitive!
By default, the following, and their lowercase-initial variants
are ignored: bool, It, Iterator, InputIt, ForwardIt, BidirIt,
RandomIt, random_iterator, ReverseIt, reverse_iterator,
reverse_const_iterator, RandomIt, random_iterator, ReverseIt,
reverse_iterator, reverse_const_iterator, Const_Iterator,
ConstIterator, const_reverse_iterator, ConstReverseIterator. In
addition, _Bool (but not _bool) is also part of the default
value.
SuppressParametersUsedTogether
Suppresses diagnostics about parameters that are used together
or in a similar fashion inside the function's body. Defaults to
true. Specifying false will turn off the heuristics.
Currently, the following heuristics are implemented which will
suppress the warning about the parameter pair involved:
o The parameters are used in the same expression, e.g. f(a, b)
or a < b.
o The parameters are further passed to the same function to the
same parameter of that function, of the same overload. E.g.
f(a, 1) and f(b, 2) to some f(T, int).
Note:
The check does not perform path-sensitive analysis, and as
such, "same function" in this context means the same
function declaration. If the same member function of a
type on two distinct instances are called with the
parameters, it will still be regarded as "same function".
o The same member field is accessed, or member method is called
of the two parameters, e.g. a.foo() and b.foo().
o Separate return statements return either of the parameters on
different code paths.
NamePrefixSuffixSilenceDissimilarityThreshold
The number of characters two parameter names might be different
on either the head or the tail end with the rest of the name the
same so that the warning about the two parameters are silenced.
Defaults to 1. Might be any positive integer. If 0, the
filtering heuristic based on the parameters' names is turned
off.
This option can be used to silence warnings about parameters
where the naming scheme indicates that the order of those
parameters do not matter.
For example, the parameters LHS and RHS are 1-dissimilar
suffixes of each other: L and R is the different character,
while HS is the common suffix. Similarly, parameters text1,
text2, text3 are 1-dissimilar prefixes of each other, with the
numbers at the end being the dissimilar part. If the value is
at least 1, such cases will not be reported.
Limitations
This check is designed to check function signatures!
The check does not investigate functions that are generated by the
compiler in a context that is only determined from a call site. These
cases include variadic functions, functions in C code that do not have
an argument list, and C++ template instantiations. Most of these
cases, which are otherwise swappable from a caller's standpoint, have
no way of getting "fixed" at the definition point. In the case of C++
templates, only primary template definitions and explicit
specializations are matched and analyzed.
None of the following cases produce a diagnostic:
int printf(const char *Format, ...) { /* ... */ }
int someOldCFunction() { /* ... */ }
template
int add(T X, U Y) { return X + Y };
void theseAreNotWarnedAbout() {
printf("%d %d\n", 1, 2); // Two ints passed, they could be swapped.
someOldCFunction(1, 2, 3); // Similarly, multiple ints passed.
add(1, 2); // Instantiates 'add', but that's not a user-defined function.
}
Due to the limitation above, parameters which type are further
dependent upon template instantiations to prove that they mix with
another parameter's is not diagnosed.
template
struct Vector {
typedef T element_type;
};
// Diagnosed: Explicit instantiation was done by the user, we can prove it
// is the same type.
void instantiated(int A, Vector::element_type B) { /* ... */ }
// Diagnosed: The two parameter types are exactly the same.
template
void exact(typename Vector::element_type A,
typename Vector::element_type B) { /* ... */ }
// Skipped: The two parameters are both 'T' but we cannot prove this
// without actually instantiating.
template
void falseNegative(T A, typename Vector::element_type B) { /* ... */ }
In the context of implicit conversions (when ModelImplicitConversions
is enabled), the modelling performed by the check warns if the
parameters are swappable and the swapped order matches implicit
conversions. It does not model whether there exists an unrelated third
type from which both parameters can be given in a function call. This
means that in the following example, even while strs() clearly carries
the possibility to be called with swapped arguments (as long as the
arguments are string literals), will not be warned about.
struct String {
String(const char *Buf);
};
struct StringView {
StringView(const char *Buf);
operator const char *() const;
};
// Skipped: Directly swapping expressions of the two type cannot mix.
// (Note: StringView -> const char * -> String would be **two**
// user-defined conversions, which is disallowed by the language.)
void strs(String Str, StringView SV) { /* ... */ }
// Diagnosed: StringView implicitly converts to and from a buffer.
void cStr(StringView SV, const char *Buf() { /* ... */ }
bugprone-empty-catch
Detects and suggests addressing issues with empty catch statements.
try {
// Some code that can throw an exception
} catch(const std::exception&) {
}
Having empty catch statements in a codebase can be a serious problem
that developers should be aware of. Catch statements are used to handle
exceptions that are thrown during program execution. When an exception
is thrown, the program jumps to the nearest catch statement that
matches the type of the exception.
Empty catch statements, also known as "swallowing" exceptions, catch
the exception but do nothing with it. This means that the exception is
not handled properly, and the program continues to run as if nothing
happened. This can lead to several issues, such as:
o Hidden Bugs: If an exception is caught and ignored, it can lead to
hidden bugs that are difficult to diagnose and fix. The root cause of
the problem may not be apparent, and the program may continue to
behave in unexpected ways.
o Security Issues: Ignoring exceptions can lead to security issues,
such as buffer overflows or null pointer dereferences. Hackers can
exploit these vulnerabilities to gain access to sensitive data or
execute malicious code.
o Poor Code Quality: Empty catch statements can indicate poor code
quality and a lack of attention to detail. This can make the codebase
difficult to maintain and update, leading to longer development
cycles and increased costs.
o Unreliable Code: Code that ignores exceptions is often unreliable and
can lead to unpredictable behavior. This can cause frustration for
users and erode trust in the software.
To avoid these issues, developers should always handle exceptions
properly. This means either fixing the underlying issue that caused
the exception or propagating the exception up the call stack to a
higher-level handler. If an exception is not important, it should
still be logged or reported in some way so that it can be tracked and
addressed later.
If the exception is something that can be handled locally, then it
should be handled within the catch block. This could involve logging
the exception or taking other appropriate action to ensure that the
exception is not ignored.
Here is an example:
try {
// Some code that can throw an exception
} catch (const std::exception& ex) {
// Properly handle the exception, e.g.:
std::cerr << "Exception caught: " << ex.what() << std::endl;
}
If the exception cannot be handled locally and needs to be propagated
up the call stack, it should be re-thrown or new exception should be
thrown.
Here is an example:
try {
// Some code that can throw an exception
} catch (const std::exception& ex) {
// Re-throw the exception
throw;
}
In some cases, catching the exception at this level may not be
necessary, and it may be appropriate to let the exception propagate up
the call stack. This can be done simply by not using try/catch block.
Here is an example:
void function() {
// Some code that can throw an exception
}
void callerFunction() {
try {
function();
} catch (const std::exception& ex) {
// Handling exception on higher level
std::cerr << "Exception caught: " << ex.what() << std::endl;
}
}
Other potential solution to avoid empty catch statements is to modify
the code to avoid throwing the exception in the first place. This can
be achieved by using a different API, checking for error conditions
beforehand, or handling errors in a different way that does not involve
exceptions. By eliminating the need for try-catch blocks, the code
becomes simpler and less error-prone.
Here is an example:
// Old code:
try {
mapContainer["Key"].callFunction();
} catch(const std::out_of_range&) {
}
// New code
if (auto it = mapContainer.find("Key"); it != mapContainer.end()) {
it->second.callFunction();
}
In conclusion, empty catch statements are a bad practice that can lead
to hidden bugs, security issues, poor code quality, and unreliable
code. By handling exceptions properly, developers can ensure that their
code is robust, secure, and maintainable.
Options
IgnoreCatchWithKeywords
This option can be used to ignore specific catch statements
containing certain keywords. If a catch statement body contains
(case-insensitive) any of the keywords listed in this
semicolon-separated option, then the catch will be ignored, and
no warning will be raised. Default value: @TODO;@FIXME.
AllowEmptyCatchForExceptions
This option can be used to ignore empty catch statements for
specific exception types. By default, the check will raise a
warning if an empty catch statement is detected, regardless of
the type of exception being caught. However, in certain
situations, such as when a developer wants to intentionally
ignore certain exceptions or handle them in a different way, it
may be desirable to allow empty catch statements for specific
exception types. To configure this option, a
semicolon-separated list of exception type names should be
provided. If an exception type name in the list is caught in an
empty catch statement, no warning will be raised. Default
value: empty string.
bugprone-exception-copy-constructor-throws
Checks whether a thrown object's copy constructor can throw.
Exception objects are required to be copy constructible in C++.
However, an exception's copy constructor should not throw to avoid
potential issues when unwinding the stack. If an exception is thrown
during stack unwinding (such as from a copy constructor of an exception
object), the program will terminate via std::terminate.
class SomeException {
public:
SomeException() = default;
SomeException(const SomeException&) { /* may throw */ }
};
void f() {
throw SomeException(); // warning: thrown exception type's copy constructor can throw
}
References
This check corresponds to the CERT C++ Coding Standard rule ERR60-CPP.
Exception objects must be nothrow copy constructible .
bugprone-exception-escape
Finds functions which may throw an exception directly or indirectly,
but they should not. The functions which should not throw exceptions
are the following:
o Destructors
o Move constructors
o Move assignment operators
o The main() functions
o swap() functions
o iter_swap() functions
o iter_move() functions
o Functions marked with throw() or noexcept
o Other functions given as option
A destructor throwing an exception may result in undefined behavior,
resource leaks or unexpected termination of the program. Throwing move
constructor or move assignment also may result in undefined behavior or
resource leak. The swap() operations expected to be non throwing most
of the cases and they are always possible to implement in a non
throwing way. Non throwing swap() operations are also used to create
move operations. A throwing main() function also results in unexpected
termination.
Functions declared explicitly with noexcept(false) or throw(exception)
will be excluded from the analysis, as even though it is not
recommended for functions like swap(), main(), move constructors, move
assignment operators and destructors, it is a clear indication of the
developer's intention and should be respected.
WARNING! This check may be expensive on large source files.
Options
CheckDestructors
When true, destructors are analyzed to not throw exceptions.
Default value is true.
CheckMoveMemberFunctions
When true, move constructors and move assignment operators are
analyzed to not throw exceptions. Default value is true.
CheckMain
When true, the main() function is analyzed to not throw
exceptions. Default value is true.
CheckNothrowFunctions
When true, functions marked with noexcept or throw() exception
specifications are analyzed to not throw exceptions. Default
value is true.
CheckedSwapFunctions
Comma-separated list of swap function names which should not
throw exceptions. Default value is swap,iter_swap,iter_move.
FunctionsThatShouldNotThrow
Comma separated list containing function names which should not
throw. An example value for this parameter can be WinMain which
adds function WinMain() in the Windows API to the list of the
functions which should not throw. Default value is an empty
string.
IgnoredExceptions
Comma separated list containing type names which are not counted
as thrown exceptions in the check. Default value is an empty
string.
bugprone-float-loop-counter
Flags for loops where the induction expression has a floating-point
type.
References
This check corresponds to the CERT C Coding Standard rule FLP30-C. Do
not use floating-point variables as loop counters .
bugprone-fold-init-type
The check flags type mismatches in folds like std::accumulate that might
result in loss of precision. std::accumulate folds an input range into
an initial value using the type of the latter, with operator+ by
default. This can cause loss of precision through:
o Truncation: The following code uses a floating point range and an int
initial value, so truncation will happen at every application of
operator+ and the result will be 0, which might not be what the user
expected.
auto a = {0.5f, 0.5f, 0.5f, 0.5f};
return std::accumulate(std::begin(a), std::end(a), 0);
o Overflow: The following code also returns 0.
auto a = {65536LL * 65536 * 65536};
return std::accumulate(std::begin(a), std::end(a), 0);
bugprone-forward-declaration-namespace
Checks if an unused forward declaration is in a wrong namespace.
The check inspects all unused forward declarations and checks if there
is any declaration/definition with the same name existing, which could
indicate that the forward declaration is in a potentially wrong
namespace.
namespace na { struct A; }
namespace nb { struct A {}; }
nb::A a;
// warning : no definition found for 'A', but a definition with the same name
// 'A' found in another namespace 'nb::'
This check can only generate warnings, but it can't suggest a fix at
this point.
bugprone-forwarding-reference-overload
The check looks for perfect forwarding constructors that can hide copy
or move constructors. If a non const lvalue reference is passed to the
constructor, the forwarding reference parameter will be a better match
than the const reference parameter of the copy constructor, so the
perfect forwarding constructor will be called, which can be confusing.
For detailed description of this issue see: Scott Meyers, Effective
Modern C++, Item 26.
Consider the following example:
class Person {
public:
// C1: perfect forwarding ctor
template
explicit Person(T&& n) {}
// C2: perfect forwarding ctor with parameter default value
template
explicit Person(T&& n, int x = 1) {}
// C3: perfect forwarding ctor guarded with enable_if
template, void>>
explicit Person(T&& n) {}
// C4: variadic perfect forwarding ctor guarded with enable_if
template, A&&...>, int> = 0>
explicit Person(A&&... a) {}
// C5: perfect forwarding ctor guarded with requires expression
template
requires requires { is_special; }
explicit Person(T&& n) {}
// C6: perfect forwarding ctor guarded with concept requirement
template
explicit Person(T&& n) {}
// (possibly compiler generated) copy ctor
Person(const Person& rhs);
};
The check warns for constructors C1 and C2, because those can hide copy
and move constructors. We suppress warnings if the copy and the move
constructors are both disabled (deleted or private), because there is
nothing the perfect forwarding constructor could hide in this case. We
also suppress warnings for constructors like C3-C6 that are guarded
with an enable_if or a concept, assuming the programmer was aware of
the possible hiding.
Background
For deciding whether a constructor is guarded with enable_if, we
consider the types of the constructor parameters, the default values of
template type parameters and the types of non-type template parameters
with a default literal value. If any part of these types is
std::enable_if or std::enable_if_t, we assume the constructor is
guarded.
bugprone-implicit-widening-of-multiplication-result
The check diagnoses instances where a result of a multiplication is
implicitly widened, and suggests (with fix-it) to either silence the
code by making widening explicit, or to perform the multiplication in a
wider type, to avoid the widening afterwards.
This is mainly useful when operating on very large buffers. For
example, consider:
void zeroinit(char* base, unsigned width, unsigned height) {
for(unsigned row = 0; row != height; ++row) {
for(unsigned col = 0; col != width; ++col) {
char* ptr = base + row * width + col;
*ptr = 0;
}
}
}
This is fine in general, but if width * height overflows, you end up
wrapping back to the beginning of base instead of processing the entire
requested buffer.
Indeed, this only matters for pretty large buffers (4GB+), but that can
happen very easily for example in image processing, where for that to
happen you "only" need a ~269MPix image.
Options
UseCXXStaticCastsInCppSources
When suggesting fix-its for C++ code, should C++-style
static_cast<>()'s be suggested, or C-style casts. Defaults to
true.
UseCXXHeadersInCppSources
When suggesting to include the appropriate header in C++ code,
should header be suggested, or . Defaults
to true.
IgnoreConstantIntExpr
If the multiplication operands are compile-time constants (like
literals or are constexpr) and fit within the source expression
type, do not emit a diagnostic or suggested fix. Only considers
expressions where the source expression is a signed integer
type. Defaults to false.
Examples:
long mul(int a, int b) {
return a * b; // warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
}
char* ptr_add(char *base, int a, int b) {
return base + a * b; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
}
char ptr_subscript(char *base, int a, int b) {
return base[a * b]; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
}
bugprone-inaccurate-erase
Checks for inaccurate use of the erase() method.
Algorithms like remove() do not actually remove any element from the
container but return an iterator to the first redundant element at the
end of the container. These redundant elements must be removed using
the erase() method. This check warns when not all of the elements will
be removed due to using an inappropriate overload.
For example, the following code erases only one element:
std::vector xs;
...
xs.erase(std::remove(xs.begin(), xs.end(), 10));
Call the two-argument overload of erase() to remove the subrange:
std::vector xs;
...
xs.erase(std::remove(xs.begin(), xs.end(), 10), xs.end());
bugprone-inc-dec-in-conditions
Detects when a variable is both incremented/decremented and referenced
inside a complex condition and suggests moving them outside to avoid
ambiguity in the variable's value.
When a variable is modified and also used in a complex condition, it
can lead to unexpected behavior. The side-effect of changing the
variable's value within the condition can make the code difficult to
reason about. Additionally, the developer's intended timing for the
modification of the variable may not be clear, leading to
misunderstandings and errors. This can be particularly problematic when
the condition involves logical operators like && and ||, where the
order of evaluation can further complicate the situation.
Consider the following example:
int i = 0;
// ...
if (i++ < 5 && i > 0) {
// do something
}
In this example, the result of the expression may not be what the
developer intended. The original intention of the developer could be to
increment i after the entire condition is evaluated, but in reality, i
will be incremented before i > 0 is executed. This can lead to
unexpected behavior and bugs in the code. To fix this issue, the
developer should separate the increment operation from the condition
and perform it separately. For example, they can increment i in a
separate statement before or after the condition is evaluated. This
ensures that the value of i is predictable and consistent throughout
the code.
int i = 0;
// ...
i++;
if (i <= 5 && i > 0) {
// do something
}
Another common issue occurs when multiple increments or decrements are
performed on the same variable inside a complex condition. For example:
int i = 4;
// ...
if (i++ < 5 || --i > 2) {
// do something
}
There is a potential issue with this code due to the order of
evaluation in C++. The || operator used in the condition statement
guarantees that if the first operand evaluates to true, the second
operand will not be evaluated. This means that if i were initially 4,
the first operand i < 5 would evaluate to true and the second operand i
> 2 would not be evaluated. As a result, the decrement operation --i
would not be executed and i would hold value 5, which may not be the
intended behavior for the developer.
To avoid this potential issue, the both increment and decrement
operation on i should be moved outside the condition statement.
bugprone-incorrect-enable-if
Detects incorrect usages of std::enable_if that don't name the nested
type type.
In C++11 introduced std::enable_if as a convenient way to leverage
SFINAE. One form of using std::enable_if is to declare an unnamed
template type parameter with a default type equal to typename
std::enable_if::type. If the author forgets to name the
nested type type, then the code will always consider the candidate
template even if the condition is not met.
Below are some examples of code using std::enable_if correctly and
incorrect examples that this check flags.
template ::type>
void valid_usage() { ... }
template >
void valid_usage_with_trait_helpers() { ... }
// The below code is not a correct application of SFINAE. Even if
// T::some_trait is not true, the function will still be considered in the
// set of function candidates. It can either incorrectly select the function
// when it should not be a candidates, and/or lead to hard compile errors
// if the body of the template does not compile if the condition is not
// satisfied.
template >
void invalid_usage() { ... }
// The tool suggests the following replacement for 'invalid_usage':
template ::type>
void fixed_invalid_usage() { ... }
C++14 introduced the trait helper std::enable_if_t which reduces the
likelihood of this error. C++20 introduces constraints, which generally
supersede the use of std::enable_if. See modernize-type-traits <> for
another tool that will replace std::enable_if with std::enable_if_t,
and see modernize-use-constraints <> for another tool that replaces
std::enable_if with C++20 constraints. Consider these newer mechanisms
where possible.
bugprone-incorrect-enable-shared-from-this
Detect classes or structs that do not publicly inherit from
std::enable_shared_from_this, because unintended behavior will
otherwise occur when calling shared_from_this.
Consider the following code:
#include
// private inheritance
class BadExample : std::enable_shared_from_this {
// ``shared_from_this``` unintended behaviour
// `libstdc++` implementation returns uninitialized ``weak_ptr``
public:
BadExample* foo() { return shared_from_this().get(); }
void bar() { return; }
};
void using_not_public() {
auto bad_example = std::make_shared();
auto* b_ex = bad_example->foo();
b_ex->bar();
}
Using libstdc++ implementation, shared_from_this will throw
std::bad_weak_ptr. When using_not_public() is called, this code will
crash without exception handling.
bugprone-incorrect-roundings
Checks the usage of patterns known to produce incorrect rounding.
Programmers often use:
(int)(double_expression + 0.5)
to round the double expression to an integer. The problem with this:
1. It is unnecessarily slow.
2. It is incorrect. The number 0.499999975 (smallest representable
float number below 0.5) rounds to 1.0. Even worse behavior for
negative numbers where both -0.5f and -1.4f both round to 0.0.
bugprone-infinite-loop
Finds obvious infinite loops (loops where the condition variable is not
changed at all).
Finding infinite loops is well-known to be impossible (halting
problem). However, it is possible to detect some obvious infinite
loops, for example, if the loop condition is not changed. This check
detects such loops. A loop is considered infinite if it does not have
any loop exit statement (break, continue, goto, return, throw or a call
to a function called as [[noreturn]]) and all of the following
conditions hold for every variable in the condition:
o It is a local variable.
o It has no reference or pointer aliases.
o It is not a structure or class member.
Furthermore, the condition must not contain a function call to consider
the loop infinite since functions may return different values for
different calls.
For example, the following loop is considered infinite i is not changed
in the body:
int i = 0, j = 0;
while (i < 10) {
++j;
}
bugprone-integer-division
Finds cases where integer division in a floating point context is
likely to cause unintended loss of precision.
No reports are made if divisions are part of the following expressions:
o operands of operators expecting integral or bool types,
o call expressions of integral or bool types, and
o explicit cast expressions to integral or bool types,
as these are interpreted as signs of deliberateness from the
programmer.
Examples:
float floatFunc(float);
int intFunc(int);
double d;
int i = 42;
// Warn, floating-point values expected.
d = 32 * 8 / (2 + i);
d = 8 * floatFunc(1 + 7 / 2);
d = i / (1 << 4);
// OK, no integer division.
d = 32 * 8.0 / (2 + i);
d = 8 * floatFunc(1 + 7.0 / 2);
d = (double)i / (1 << 4);
// OK, there are signs of deliberateness.
d = 1 << (i / 2);
d = 9 + intFunc(6 * i / 32);
d = (int)(i / 32) - 8;
bugprone-invalid-enum-default-initialization
Detects default initialization (to 0) of variables with enum type where
the enum has no enumerator with value of 0.
In C++ a default initialization is performed if a variable is
initialized with initializer list or in other implicit ways, and no
value is specified at the initialization. In such cases the value 0 is
used for the initialization. This also applies to enumerations even if
it does not have an enumerator with value 0. In this way a variable
with the enum type may contain initially an invalid value (if the
program expects that it contains only the listed enumerator values).
The check emits a warning only if an enum variable is
default-initialized (contrary to not initialized) and the enum does not
have an enumerator with value of 0. The type can be a scoped or
non-scoped enum. Unions are not handled by the check (if it contains a
member of enumeration type).
Note that the enum std::errc is always ignored because it is expected
to be default initialized, despite not defining an enumerator with the
value 0.
enum class Enum1: int {
A = 1,
B
};
enum class Enum0: int {
A = 0,
B
};
void f() {
Enum1 X1{}; // warn: 'X1' is initialized to 0
Enum1 X2 = Enum1(); // warn: 'X2' is initialized to 0
Enum1 X3; // no warning: 'X3' is not initialized
Enum0 X4{}; // no warning: type has an enumerator with value of 0
}
struct S1 {
Enum1 A;
S(): A() {} // warn: 'A' is initialized to 0
};
struct S2 {
int A;
Enum1 B;
};
S2 VarS2{}; // warn: member 'B' is initialized to 0
The check applies to initialization of arrays or structures with
initialization lists in C code too. In these cases elements not
specified in the list (and have enum type) are set to 0.
enum Enum1 {
Enum1_A = 1,
Enum1_B
};
struct Struct1 {
int a;
enum Enum1 b;
};
enum Enum1 Array1[2] = {Enum1_A}; // warn: omitted elements are initialized to 0
enum Enum1 Array2[2][2] = {{Enum1_A}, {Enum1_A}}; // warn: last element of both nested arrays is initialized to 0
enum Enum1 Array3[2][2] = {{Enum1_A, Enum1_A}}; // warn: elements of second array are initialized to 0
struct Struct1 S1 = {1}; // warn: element 'b' is initialized to 0
Options
IgnoredEnums
Semicolon-separated list of regexes specifying enums for which
this check won't be enforced. Default is ::std::errc.
bugprone-lambda-function-name
Checks for attempts to get the name of a function from within a lambda
expression. The name of a lambda is always something like operator(),
which is almost never what was intended.
Example:
void FancyFunction() {
[] { printf("Called from %s\n", __func__); }();
[] { printf("Now called from %s\n", __FUNCTION__); }();
}
Output:
Called from operator()
Now called from operator()
Likely intended output:
Called from FancyFunction
Now called from FancyFunction
Options
IgnoreMacros
The value true specifies that attempting to get the name of a
function from within a macro should not be diagnosed. The
default value is false.
bugprone-macro-parentheses
Finds macros that can have unexpected behavior due to missing
parentheses.
Macros are expanded by the preprocessor as-is. As a result, there can
be unexpected behavior; operators may be evaluated in unexpected order
and unary operators may become binary operators, etc.
When the replacement list has an expression, it is recommended to
surround it with parentheses. This ensures that the macro result is
evaluated completely before it is used.
It is also recommended to surround macro arguments in the replacement
list with parentheses. This ensures that the argument value is
calculated properly.
This check corresponds to the CERT C Coding Standard rule PRE02-C.
Macro replacement lists should be parenthesized.
bugprone-macro-repeated-side-effects
Checks for repeated argument with side effects in macros.
bugprone-misleading-setter-of-reference
Finds setter-like member functions that take a pointer parameter and
set a reference member of the same class with the pointed value.
The check detects member functions that take a single pointer
parameter, and contain a single expression statement that dereferences
the parameter and assigns the result to a data member with a reference
type.
The fact that a setter function takes a pointer might cause the belief
that an internal reference (if it would be a pointer) is changed
instead of the pointed-to (or referenced) value.
Example:
class MyClass {
int &InternalRef; // non-const reference member
public:
MyClass(int &Value) : InternalRef(Value) {}
// Warning: This setter could lead to unintended behaviour.
void setRef(int *Value) {
InternalRef = *Value; // This assigns to the referenced value, not changing what InternalRef references.
}
};
int main() {
int Value1 = 42;
int Value2 = 100;
MyClass X(Value1);
// This might look like it changes what InternalRef references to,
// but it actually modifies Value1 to be 100.
X.setRef(&Value2);
}
Possible fixes:
o Change the parameter type of the "set" function to non-pointer
type (for example, a const reference).
o Change the type of the member variable to a pointer and in the
"set" function assign a value to the pointer (without
dereference).
bugprone-misplaced-operator-in-strlen-in-alloc
Finds cases where 1 is added to the string in the argument to strlen(),
strnlen(), strnlen_s(), wcslen(), wcsnlen(), and wcsnlen_s() instead of
the result and the value is used as an argument to a memory allocation
function (malloc(), calloc(), realloc(), alloca()) or the new[]
operator in C++. The check detects error cases even if one of these
functions (except the new[] operator) is called by a constant function
pointer. Cases where 1 is added both to the parameter and the result of
the strlen()-like function are ignored, as are cases where the whole
addition is surrounded by extra parentheses.
C example code:
void bad_malloc(char *str) {
char *c = (char*) malloc(strlen(str + 1));
}
The suggested fix is to add 1 to the return value of strlen() and not
to its argument. In the example above the fix would be
char *c = (char*) malloc(strlen(str) + 1);
C++ example code:
void bad_new(char *str) {
char *c = new char[strlen(str + 1)];
}
As in the C code with the malloc() function, the suggested fix is to
add 1 to the return value of strlen() and not to its argument. In the
example above the fix would be
char *c = new char[strlen(str) + 1];
Example for silencing the diagnostic:
void bad_malloc(char *str) {
char *c = (char*) malloc(strlen((str + 1)));
}
bugprone-misplaced-pointer-arithmetic-in-alloc
Finds cases where an integer expression is added to or subtracted from
the result of a memory allocation function (malloc(), calloc(),
realloc(), alloca()) instead of its argument. The check detects error
cases even if one of these functions is called by a constant function
pointer.
Example code:
void bad_malloc(int n) {
char *p = (char*) malloc(n) + 10;
}
The suggested fix is to add the integer expression to the argument of
malloc and not to its result. In the example above the fix would be
char *p = (char*) malloc(n + 10);
bugprone-misplaced-widening-cast
This check will warn when there is a cast of a calculation result to a
bigger type. If the intention of the cast is to avoid loss of precision
then the cast is misplaced, and there can be loss of precision.
Otherwise the cast is ineffective.
Example code:
long f(int x) {
return (long)(x * 1000);
}
The result x * 1000 is first calculated using int precision. If the
result exceeds int precision there is loss of precision. Then the
result is casted to long.
If there is no loss of precision then the cast can be removed or you
can explicitly cast to int instead.
If you want to avoid loss of precision then put the cast in a proper
location, for instance:
long f(int x) {
return (long)x * 1000;
}
Implicit casts
Forgetting to place the cast at all is at least as dangerous and at
least as common as misplacing it. If CheckImplicitCasts is enabled the
check also detects these cases, for instance:
long f(int x) {
return x * 1000;
}
Floating point
Currently warnings are only written for integer conversion. No warning
is written for this code:
double f(float x) {
return (double)(x * 10.0f);
}
Options
CheckImplicitCasts
If true, enables detection of implicit casts. Default is false.
bugprone-move-forwarding-reference
Warns if std::move is called on a forwarding reference, for example:
template
void foo(T&& t) {
bar(std::move(t));
}
Forwarding references should typically be passed to std::forward
instead of std::move, and this is the fix that will be suggested.
(A forwarding reference is an rvalue reference of a type that is a
deduced function template argument.)
In this example, the suggested fix would be
bar(std::forward(t));
Background
Code like the example above is sometimes written with the expectation
that T&& will always end up being an rvalue reference, no matter what
type is deduced for T, and that it is therefore not possible to pass an
lvalue to foo(). However, this is not true. Consider this example:
std::string s = "Hello, world";
foo(s);
This code compiles and, after the call to foo(), s is left in an
indeterminate state because it has been moved from. This may be
surprising to the caller of foo() because no std::move was used when
calling foo().
The reason for this behavior lies in the special rule for template
argument deduction on function templates like foo() -- i.e. on function
templates that take an rvalue reference argument of a type that is a
deduced function template argument. (See section [temp.deduct.call]/3
in the C++11 standard.)
If foo() is called on an lvalue (as in the example above), then T is
deduced to be an lvalue reference. In the example, T is deduced to be
std::string &. The type of the argument t therefore becomes
std::string& &&; by the reference collapsing rules, this collapses to
std::string&.
This means that the foo(s) call passes s as an lvalue reference, and
foo() ends up moving s and thereby placing it into an indeterminate
state.
bugprone-multi-level-implicit-pointer-conversion
Detects implicit conversions between pointers of different levels of
indirection.
Conversions between pointer types of different levels of indirection
can be dangerous and may lead to undefined behavior, particularly if
the converted pointer is later cast to a type with a different level of
indirection. For example, converting a pointer to a pointer to an int
(int**) to a void* can result in the loss of information about the
original level of indirection, which can cause problems when attempting
to use the converted pointer. If the converted pointer is later cast to
a type with a different level of indirection and dereferenced, it may
lead to access violations, memory corruption, or other undefined
behavior.
Consider the following example:
void foo(void* ptr);
int main() {
int x = 42;
int* ptr = &x;
int** ptr_ptr = &ptr;
foo(ptr_ptr); // warning will trigger here
return 0;
}
In this example, foo() is called with ptr_ptr as its argument. However,
ptr_ptr is a int** pointer, while foo() expects a void* pointer. This
results in an implicit pointer level conversion, which could cause
issues if foo() dereferences the pointer assuming it's a int* pointer.
Using an explicit cast is a recommended solution to prevent issues
caused by implicit pointer level conversion, as it allows the developer
to explicitly state their intention and show their reasoning for the
type conversion. Additionally, it is recommended that developers
thoroughly check and verify the safety of the conversion before using
an explicit cast. This extra level of caution can help catch potential
issues early on in the development process, improving the overall
reliability and maintainability of the code.
Options
EnableInC
If true, enables the check in C code (it is always enabled in
C++ code). Default is true.
bugprone-multiple-new-in-one-expression
Finds multiple new operator calls in a single expression, where the
allocated memory by the first new may leak if the second allocation
fails and throws exception.
C++ does often not specify the exact order of evaluation of the
operands of an operator or arguments of a function. Therefore if a
first allocation succeeds and a second fails, in an exception handler
it is not possible to tell which allocation has failed and free the
memory. Even if the order is fixed the result of a first new may be
stored in a temporary location that is not reachable at the time when a
second allocation fails. It is best to avoid any expression that
contains more than one operator new call, if exception handling is used
to check for allocation errors.
Different rules apply for are the short-circuit operators || and && and
the , operator, where evaluation of one side must be completed before
the other starts. Expressions of a list-initialization (initialization
or construction using { and } characters) are evaluated in fixed order.
Similarly, condition of a ? operator is evaluated before the branches
are evaluated.
The check reports warning if two new calls appear in one expression at
different sides of an operator, or if new calls appear in different
arguments of a function call (that can be an object construction with
() syntax). These new calls can be nested at any level. For any
warning to be emitted the new calls should be in a code block where
exception handling is used with catch for std::bad_alloc or
std::exception. At ||, &&, ,, ? (condition and one branch) operators no
warning is emitted. No warning is emitted if both of the memory
allocations are not assigned to a variable or not passed directly to a
function. The reason is that in this case the memory may be
intentionally not freed or the allocated objects can be
self-destructing objects.
Examples:
struct A {
int Var;
};
struct B {
B();
B(A *);
int Var;
};
struct C {
int *X1;
int *X2;
};
void f(A *, B *);
int f1(A *);
int f1(B *);
bool f2(A *);
void foo() {
A *PtrA;
B *PtrB;
try {
// Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated.
f(new A, new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// List (aggregate) initialization is used.
C C1{new int, new int}; // no warning
// Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated but not yet passed to function 'f1'.
int X = f1(new A) + f1(new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// Allocation of 'B' may fail after memory for 'A' was allocated.
// From C++17 on memory for 'B' is allocated first but still may leak if allocation of 'A' fails.
PtrB = new B(new A); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception
// 'new A' and 'new B' may be performed in any order.
// 'new B'/'new A' may fail after memory for 'A'/'B' was allocated but not assigned to 'PtrA'/'PtrB'.
(PtrA = new A)->Var = (PtrB = new B)->Var; // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// Evaluation of 'f2(new A)' must be finished before 'f1(new B)' starts.
// If 'new B' fails the allocated memory for 'A' is supposedly handled correctly because function 'f2' could take the ownership.
bool Z = f2(new A) || f1(new B); // no warning
X = (f2(new A) ? f1(new A) : f1(new B)); // no warning
// No warning if the result of both allocations is not passed to a function
// or stored in a variable.
(new A)->Var = (new B)->Var; // no warning
// No warning if at least one non-throwing allocation is used.
f(new(std::nothrow) A, new B); // no warning
} catch(std::bad_alloc) {
}
// No warning if the allocation is outside a try block (or no catch handler exists for std::bad_alloc).
// (The fact if exceptions can escape from 'foo' is not taken into account.)
f(new A, new B); // no warning
}
bugprone-multiple-statement-macro
Detect multiple statement macros that are used in unbraced
conditionals. Only the first statement of the macro will be inside the
conditional and the other ones will be executed unconditionally.
Example:
#define INCREMENT_TWO(x, y) (x)++; (y)++
if (do_increment)
INCREMENT_TWO(a, b); // (b)++ will be executed unconditionally.
bugprone-narrowing-conversions
cppcoreguidelines-narrowing-conversions redirects here as an alias for
this check.
Checks for silent narrowing conversions, e.g: int i = 0; i += 0.1;.
While the issue is obvious in this former example, it might not be so
in the following: void MyClass::f(double d) { int_member_ += d; }.
We flag narrowing conversions from:
o an integer to a narrower integer (e.g. char to unsigned char)
if WarnOnIntegerNarrowingConversion Option is set,
o an integer to a narrower floating-point (e.g. uint64_t to
float) if WarnOnIntegerToFloatingPointNarrowingConversion
Option is set,
o a floating-point to an integer (e.g. double to int),
o a floating-point to a narrower floating-point (e.g. double to
float) if WarnOnFloatingPointNarrowingConversion Option is
set.
This check will flag:
o All narrowing conversions that are not marked by an explicit
cast (c-style or static_cast). For example: int i = 0; i +=
0.1;, void f(int); f(0.1);,
o All applications of binary operators with a narrowing
conversions. For example: int i; i+= 0.1;.
Arithmetic with smaller integer types than int trigger implicit
conversions, as explained under "Integral Promotion" on
cppreference.com . This check diagnoses more instances of narrowing
than the compiler warning -Wconversion does. The example below
demonstrates this behavior.
// The following function definition demonstrates usage of arithmetic with
// integer types smaller than `int` and how the narrowing conversion happens
// implicitly.
void computation(short argument1, short argument2) {
// Arithmetic written by humans:
short result = argument1 + argument2;
// Arithmetic actually performed by C++:
short result = static_cast(static_cast(argument1) + static_cast(argument2));
}
void recommended_resolution(short argument1, short argument2) {
short result = argument1 + argument2;
// ^ warning: narrowing conversion from 'int' to signed type 'short' is implementation-defined
// The cppcoreguidelines recommend to resolve this issue by using the GSL
// in one of two ways. Either by a cast that throws if a loss of precision
// would occur.
short result = gsl::narrow(argument1 + argument2);
// Or it can be resolved without checking the result risking invalid results.
short result = gsl::narrow_cast(argument1 + argument2);
// A classical `static_cast` will silence the warning as well if the GSL
// is not available.
short result = static_cast(argument1 + argument2);
}
Options
WarnOnIntegerNarrowingConversion
When true, the check will warn on narrowing integer conversion
(e.g. int to size_t). true by default.
WarnOnIntegerToFloatingPointNarrowingConversion
When true, the check will warn on narrowing integer to
floating-point conversion (e.g. size_t to double). true by
default.
WarnOnFloatingPointNarrowingConversion
When true, the check will warn on narrowing floating point
conversion (e.g. double to float). true by default.
WarnWithinTemplateInstantiation
When true, the check will warn on narrowing conversions within
template instantiations. false by default.
WarnOnEquivalentBitWidth
When true, the check will warn on narrowing conversions that
arise from casting between types of equivalent bit width. (e.g.
int n = uint(0); or long long n = double(0);) true by default.
IgnoreConversionFromTypes
Narrowing conversions from any type in this semicolon-separated
list will be ignored. This may be useful to weed out commonly
occurring, but less commonly problematic assignments such as int
n = std::vector().size(); or int n = std::difference(it1,
it2);. The default list is empty, but one suggested list for a
legacy codebase would be
size_t;ptrdiff_t;size_type;difference_type.
PedanticMode
When true, the check will warn on assigning a floating point
constant to an integer value even if the floating point value is
exactly representable in the destination type (e.g. int i =
1.0;). false by default.
FAQ
o What does "narrowing conversion from 'int' to 'float'" mean?
An IEEE754 Floating Point number can represent all integer values in
the range [-2^PrecisionBits, 2^PrecisionBits] where PrecisionBits is
the number of bits in the mantissa.
For float this would be [-2^23, 2^23], where int can represent values
in the range [-2^31, 2^31-1].
o What does "implementation-defined" mean?
You may have encountered messages like "narrowing conversion from
'unsigned int' to signed type 'int' is implementation-defined". The
C/C++ standard does not mandate two's complement for signed integers,
and so the compiler is free to define what the semantics are for
converting an unsigned integer to signed integer. Clang's
implementation uses the two's complement format.
bugprone-no-escape
Finds pointers with the noescape attribute that are captured by an
asynchronously-executed block. The block arguments in dispatch_async()
and dispatch_after() are guaranteed to escape, so it is an error if a
pointer with the noescape attribute is captured by one of these blocks.
The following is an example of an invalid use of the noescape
attribute.
void foo(__attribute__((noescape)) int *p) {
dispatch_async(queue, ^{
*p = 123;
});
});
bugprone-non-zero-enum-to-bool-conversion
Detect implicit and explicit casts of enum type into bool where enum
type doesn't have a zero-value enumerator. If the enum is used only to
hold values equal to its enumerators, then conversion to bool will
always result in true value. This can lead to unnecessary code that
reduces readability and maintainability and can result in bugs.
May produce false positives if the enum is used to store other values
(used as a bit-mask or zero-initialized on purpose). To deal with them,
// NOLINT or casting first to the underlying type before casting to
bool can be used.
It is important to note that this check will not generate warnings if
the definition of the enumeration type is not available. Additionally,
C++11 enumeration classes are supported by this check.
Overall, this check serves to improve code quality and readability by
identifying and flagging instances where implicit or explicit casts
from enumeration types to boolean could cause potential issues.
Example
enum EStatus {
OK = 1,
NOT_OK,
UNKNOWN
};
void process(EStatus status) {
if (!status) {
// this true-branch won't be executed
return;
}
// proceed with "valid data"
}
Options
EnumIgnoreList
Option is used to ignore certain enum types when checking for
implicit/explicit casts to bool. It accepts a
semicolon-separated list of (fully qualified) enum type names or
regular expressions that match the enum type names. The default
value is an empty string, which means no enums will be ignored.
bugprone-nondeterministic-pointer-iteration-order
Finds nondeterministic usages of pointers in unordered containers.
One canonical example is iteration across a container of pointers.
{
int a = 1, b = 2;
std::unordered_set UnorderedPtrSet = {&a, &b};
for (auto i : UnorderedPtrSet)
f(i);
}
Another such example is sorting a container of pointers.
{
int a = 1, b = 2;
std::vector VectorOfPtr = {&a, &b};
std::sort(VectorOfPtr.begin(), VectorOfPtr.end());
}
Iteration of a containers of pointers may present the order of
different pointers differently across different runs of a program. In
some cases this may be acceptable behavior, in others this may be
unexpected behavior. This check is advisory for this reason.
This check only detects range-based for loops over unordered sets and
maps. It also detects calls sorting-like algorithms on containers
holding pointers. Other similar usages will not be found and are false
negatives.
Limitations
o This check currently does not check if a nondeterministic iteration
order is likely to be a mistake, and instead marks all such
iterations as bugprone.
o std::reference_wrapper is not considered yet.
o Only for loops are considered, other iterators can be included in
improvements.
bugprone-not-null-terminated-result
Finds function calls where it is possible to cause a not
null-terminated result. Usually the proper length of a string is
strlen(src) + 1 or equal length of this expression, because the null
terminator needs an extra space. Without the null terminator it can
result in undefined behavior when the string is read.
The following and their respective wchar_t based functions are checked:
memcpy, memcpy_s, memchr, memmove, memmove_s, strerror_s, strncmp,
strxfrm
The following is a real-world example where the programmer forgot to
increase the passed third argument, which is size_t length. That is why
the length of the allocated memory is not enough to hold the null
terminator.
static char *stringCpy(const std::string &str) {
char *result = reinterpret_cast(malloc(str.size()));
memcpy(result, str.data(), str.size());
return result;
}
In addition to issuing warnings, fix-it rewrites all the necessary
code. It also tries to adjust the capacity of the destination array:
static char *stringCpy(const std::string &str) {
char *result = reinterpret_cast(malloc(str.size() + 1));
strcpy(result, str.data());
return result;
}
Note: It cannot guarantee to rewrite every of the path-sensitive memory
allocations.
Transformation rules of 'memcpy()'
It is possible to rewrite the memcpy() and memcpy_s() calls as the
following four functions: strcpy(), strncpy(), strcpy_s(),
strncpy_s(), where the latter two are the safer versions of the former
two. It rewrites the wchar_t based memory handler functions
respectively.
Rewrite based on the destination array
o If copy to the destination array cannot overflow [1] the new function
should be the older copy function (ending with cpy), because it is
more efficient than the safe version.
o If copy to the destination array can overflow [1] and
WantToUseSafeFunctions is set to true and it is possible to obtain
the capacity of the destination array then the new function could be
the safe version (ending with cpy_s).
o If the new function is could be safe version and C++ files are
analyzed and the destination array is plain char/wchar_t without
un/signed then the length of the destination array can be omitted.
o If the new function is could be safe version and the destination
array is un/signed it needs to be casted to plain char */wchar_t *.
[1] It is possible to overflow:
o If the capacity of the destination array is unknown.
o If the given length is equal to the destination array's
capacity.
Rewrite based on the length of the source string
o If the given length is strlen(source) or equal length of this
expression then the new function should be the older copy function
(ending with cpy), as it is more efficient than the safe version
(ending with cpy_s).
o Otherwise we assume that the programmer wanted to copy 'N'
characters, so the new function is ncpy-like which copies 'N'
characters.
Transformations with 'strlen()' or equal length of this expression
It transforms the wchar_t based memory and string handler functions
respectively (where only strerror_s does not have wchar_t based alias).
Memory handler functions
memcpy Please visit the Transformation rules of 'memcpy()' section.
memchr Usually there is a C-style cast and it is needed to be removed,
because the new function strchr's return type is correct. The given
length is going to be removed.
memmove If safe functions are available the new function is memmove_s,
which has a new second argument which is the length of the destination
array, it is adjusted, and the length of the source string is
incremented by one. If safe functions are not available the given
length is incremented by one.
memmove_s The given length is incremented by one.
String handler functions
strerror_s The given length is incremented by one.
strncmp If the third argument is the first or the second argument's
length + 1 it has to be truncated without the + 1 operation.
strxfrm The given length is incremented by one.
Options
WantToUseSafeFunctions
The value true specifies that the target environment is
considered to implement '_s' suffixed memory and string handler
functions which are safer than older versions (e.g.
'memcpy_s()'). The default value is true.
bugprone-optional-value-conversion
Detects potentially unintentional and redundant conversions where a
value is extracted from an optional-like type and then used to create a
new instance of the same optional-like type.
These conversions might be the result of developer oversight, leftovers
from code refactoring, or other situations that could lead to
unintended exceptions or cases where the resulting optional is always
initialized, which might be unexpected behavior.
To illustrate, consider the following problematic code snippet:
#include
void print(std::optional);
int main()
{
std::optional opt;
// ...
// Unintentional conversion from std::optional to int and back to
// std::optional:
print(opt.value());
// ...
}
A better approach would be to directly pass opt to the print function
without extracting its value:
#include
void print(std::optional);
int main()
{
std::optional opt;
// ...
// Proposed code: Directly pass the std::optional to the print
// function.
print(opt);
// ...
}
By passing opt directly to the print function, unnecessary conversions
are avoided, and potential unintended behavior or exceptions are
minimized.
Value extraction using operator * is matched by default. The support
for non-standard optional types such as boost::optional or
absl::optional may be limited.
Options:
OptionalTypes
Semicolon-separated list of (fully qualified) optional type
names or regular expressions that match the optional types.
Default value is
::std::optional;::absl::optional;::boost::optional.
ValueMethods
Semicolon-separated list of (fully qualified) method names or
regular expressions that match the methods. Default value is
::value$;::get$.
bugprone-parent-virtual-call
Detects and fixes calls to grand-...parent virtual methods instead of
calls to overridden parent's virtual methods.
struct A {
int virtual foo() {...}
};
struct B: public A {
int foo() override {...}
};
struct C: public B {
int foo() override { A::foo(); }
// ^^^^^^^^
// warning: qualified name A::foo refers to a member overridden in subclass; did you mean 'B'? [bugprone-parent-virtual-call]
};
bugprone-pointer-arithmetic-on-polymorphic-object
Finds pointer arithmetic performed on classes that contain a virtual
function.
Pointer arithmetic on polymorphic objects where the pointer's static
type is different from its dynamic type is undefined behavior, as the
two types could have different sizes, and thus the vtable pointer could
point to an invalid address.
Finding pointers where the static type contains a virtual member
function is a good heuristic, as the pointer is likely to point to a
different, derived object.
Example:
struct Base {
virtual ~Base();
int i;
};
struct Derived : public Base {};
void foo(Base* b) {
b += 1;
// warning: pointer arithmetic on class that declares a virtual function can
// result in undefined behavior if the dynamic type differs from the
// pointer type
}
int bar(const Derived d[]) {
return d[1].i; // warning due to pointer arithmetic on polymorphic object
}
// Making Derived final suppresses the warning
struct FinalDerived final : public Base {};
int baz(const FinalDerived d[]) {
return d[1].i; // no warning as FinalDerived is final
}
Options
IgnoreInheritedVirtualFunctions
When true, objects that only inherit a virtual function are not
checked. Classes that do not declare a new virtual function are
excluded by default, as they make up the majority of false
positives. Default: false.
void bar(Base b[], Derived d[]) {
b += 1; // warning, as Base declares a virtual destructor
d += 1; // warning only if IgnoreVirtualDeclarationsOnly is set to false
}
References
This check corresponds to the SEI Cert rule CTR56-CPP. Do not use
pointer arithmetic on polymorphic objects .
bugprone-posix-return
Checks if any calls to pthread_* or posix_* functions (except
posix_openpt) expect negative return values. These functions return
either 0 on success or an errno on failure, which is positive only.
Example buggy usage looks like:
if (posix_fadvise(...) < 0) {
This will never happen as the return value is always non-negative. A
simple fix could be:
if (posix_fadvise(...) > 0) {
bugprone-random-generator-seed
Flags all pseudo-random number engines, engine adaptor instantiations
and srand() when initialized or seeded with default argument, constant
expression or any user-configurable type. Pseudo-random number engines
seeded with a predictable value may cause vulnerabilities e.g. in
security protocols.
Examples:
void foo() {
std::mt19937 engine1; // Diagnose, always generate the same sequence
std::mt19937 engine2(1); // Diagnose
engine1.seed(); // Diagnose
engine2.seed(1); // Diagnose
std::time_t t;
engine1.seed(std::time(&t)); // Diagnose, system time might be controlled by user
int x = atoi(argv[1]);
std::mt19937 engine3(x); // Will not warn
}
Options
DisallowedSeedTypes
A comma-separated list of the type names which are disallowed.
Default value is time_t,std::time_t.
References
This check corresponds to the CERT C++ Coding Standard rules MSC51-CPP.
Ensure your random number generator is properly seeded and MSC32-C.
Properly seed pseudorandom number generators .
bugprone-raw-memory-call-on-non-trivial-type
Flags use of the C standard library functions memset, memcpy and memcmp
and similar derivatives on non-trivial types.
The check will detect the following functions: memset, std::memset,
std::memcpy, memcpy, std::memmove, memmove, std::strcpy, strcpy,
memccpy, stpncpy, strncpy, std::memcmp, memcmp, std::strcmp, strcmp,
strncmp.
Options
MemSetNames
Specify extra functions to flag that act similarly to memset.
Specify names in a semicolon-delimited list. Default is an empty
string.
MemCpyNames
Specify extra functions to flag that act similarly to memcpy.
Specify names in a semicolon-delimited list. Default is an empty
string.
MemCmpNames
Specify extra functions to flag that act similarly to memcmp.
Specify names in a semicolon-delimited list. Default is an empty
string.
This check corresponds to the CERT C++ Coding Standard rule OOP57-CPP.
Prefer special member functions and overloaded operators to C Standard
Library functions .
bugprone-redundant-branch-condition
Finds condition variables in nested if statements that were also
checked in the outer if statement and were not changed.
Simple example:
bool onFire = isBurning();
if (onFire) {
if (onFire)
scream();
}
Here onFire is checked both in the outer if and the inner if statement
without a possible change between the two checks. The check warns for
this code and suggests removal of the second checking of variable
onFire.
The checker also detects redundant condition checks if the condition
variable is an operand of a logical "and" (&&) or a logical "or" (||)
operator:
bool onFire = isBurning();
if (onFire) {
if (onFire && peopleInTheBuilding > 0)
scream();
}
bool onFire = isBurning();
if (onFire) {
if (onFire || isCollapsing())
scream();
}
In the first case (logical "and") the suggested fix is to remove the
redundant condition variable and keep the other side of the &&. In the
second case (logical "or") the whole if is removed similarly to the
simple case on the top.
The condition of the outer if statement may also be a logical "and"
(&&) expression:
bool onFire = isBurning();
if (onFire && fireFighters < 10) {
if (someOtherCondition()) {
if (onFire)
scream();
}
}
The error is also detected if both the outer statement is a logical
"and" (&&) and the inner statement is a logical "and" (&&) or "or"
(||). The inner if statement does not have to be a direct descendant
of the outer one.
No error is detected if the condition variable may have been changed
between the two checks:
bool onFire = isBurning();
if (onFire) {
tryToExtinguish(onFire);
if (onFire && peopleInTheBuilding > 0)
scream();
}
Every possible change is considered, thus if the condition variable is
not a local variable of the function, it is a volatile or it has an
alias (pointer or reference) then no warning is issued.
Limitations
The else branch is not checked currently for negated condition
variable:
bool onFire = isBurning();
if (onFire) {
scream();
} else {
if (!onFire) {
continueWork();
}
}
The checker currently only detects redundant checking of single
condition variables. More complex expressions are not checked:
if (peopleInTheBuilding == 1) {
if (peopleInTheBuilding == 1) {
doSomething();
}
}
bugprone-reserved-identifier
cert-dcl37-c and cert-dcl51-cpp redirect here as an alias for this
check.
Checks for usages of identifiers reserved for use by the
implementation.
The C and C++ standards both reserve the following names for such use:
o identifiers that begin with an underscore followed by an uppercase
letter;
o identifiers in the global namespace that begin with an underscore.
The C standard additionally reserves names beginning with a double
underscore, while the C++ standard strengthens this to reserve names
with a double underscore occurring anywhere.
Violating the naming rules above results in undefined behavior.
namespace NS {
void __f(); // name is not allowed in user code
using _Int = int; // same with this
#define cool__macro // also this
}
int _g(); // disallowed in global namespace only
The check can also be inverted, i.e. it can be configured to flag any
identifier that is not a reserved identifier. This mode is for use by
e.g. standard library implementors, to ensure they don't infringe on
the user namespace.
This check does not (yet) check for other reserved names, e.g. macro
names identical to language keywords, and names specifically reserved
by language standards, e.g. C++ 'zombie names' and C future library
directions.
This check corresponds to CERT C Coding Standard rule DCL37-C. Do not
declare or define a reserved identifier as well as its C++
counterpart, DCL51-CPP. Do not declare or define a reserved identifier
.
Options
Invert If true, inverts the check, i.e. flags names that are not
reserved. Default is false.
AllowedIdentifiers
Semicolon-separated list of regular expressions that the check
ignores. Default is an empty list.
bugprone-return-const-ref-from-parameter
Detects return statements that return a constant reference parameter as
constant reference. This may cause use-after-free errors if the caller
uses xvalues as arguments.
In C++, constant reference parameters can accept xvalues which will be
destructed after the call. When the function returns such a parameter
also as constant reference, then the returned reference can be used
after the object it refers to has been destroyed.
Example
struct S {
int v;
S(int);
~S();
};
const S &fn(const S &a) {
return a;
}
const S& s = fn(S{1});
s.v; // use after free
This issue can be resolved by declaring an overload of the problematic
function where the const & parameter is instead declared as &&. The
developer has to ensure that the implementation of that function does
not produce a use-after-free, the exact error that this check is
warning against. Marking such an && overload as deleted, will silence
the warning as well. In the case of different const & parameters being
returned depending on the control flow of the function, an overload
where all problematic const & parameters have been declared as && will
resolve the issue.
This issue can also be resolved by adding [[clang::lifetimebound]].
Clang enable -Wdangling warning by default which can detect mis-uses of
the annotated function. See lifetimebound attribute for details.
const int &f(const int &a [[clang::lifetimebound]]) { return a; } // no warning
const int &v = f(1); // warning: temporary bound to local reference 'v' will be destroyed at the end of the full-expression [-Wdangling]
bugprone-shared-ptr-array-mismatch
Finds initializations of C++ shared pointers to non-array type that are
initialized with an array.
If a shared pointer std::shared_ptr is initialized with a
new-expression new T[] the memory is not deallocated correctly. The
pointer uses plain delete in this case to deallocate the target memory.
Instead a delete[] call is needed. A std::shared_ptr calls the
correct delete operator.
The check offers replacement of shared_ptr to shared_ptr if it
is used at a single variable declaration (one variable in one
statement).
Example:
std::shared_ptr x(new Foo[10]); // -> std::shared_ptr x(new Foo[10]);
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
std::shared_ptr x1(new Foo), x2(new Foo[10]); // no replacement
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
std::shared_ptr x3(new Foo[10], [](const Foo *ptr) { delete[] ptr; }); // no warning
struct S {
std::shared_ptr x(new Foo[10]); // no replacement in this case
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
};
This check partially covers the CERT C++ Coding Standard rule
MEM51-CPP. Properly deallocate dynamically allocated resources
However, only
the std::shared_ptr case is detected by this check.
bugprone-signal-handler
Finds specific constructs in signal handler functions that can cause
undefined behavior. The rules for what is allowed differ between C++
language versions.
Checked signal handler rules for C:
o Calls to non-asynchronous-safe functions are not allowed.
Checked signal handler rules for up to and including C++14:
o Calls to non-asynchronous-safe functions are not allowed.
o C++-specific code constructs are not allowed in signal handlers. In
other words, only the common subset of C and C++ is allowed to be
used.
o Calls to functions with non-C linkage are not allowed (including the
signal handler itself).
The check is disabled on C++17 and later.
Asynchronous-safety is determined by comparing the function's name
against a set of known functions. In addition, the function must come
from a system header include and in a global namespace. The (possible)
arguments passed to the function are not checked. Any function that
cannot be determined to be asynchronous-safe is assumed to be
non-asynchronous-safe by the check, including user functions for which
only the declaration is visible. Calls to user-defined functions with
visible definitions are checked recursively.
This check implements the CERT C Coding Standard rule SIG30-C. Call
only asynchronous-safe functions within signal handlers and the
rule MSC54-CPP. A signal handler must be a plain old function . It has the alias
names cert-sig30-c and cert-msc54-cpp.
Options
AsyncSafeFunctionSet
Selects which set of functions is considered as
asynchronous-safe (and therefore allowed in signal handlers). It
can be set to the following values:
o
minimal
Selects a minimal set that is defined in the CERT
SIG30-C rule. and includes functions abort(), _Exit(),
quick_exit() and signal().
o
POSIX Selects a larger set of functions that is listed in
POSIX.1-2017 (see this link for more information). The following
functions are included: _Exit, _exit, abort, accept,
access, aio_error, aio_return, aio_suspend, alarm,
bind, cfgetispeed, cfgetospeed, cfsetispeed,
cfsetospeed, chdir, chmod, chown, clock_gettime, close,
connect, creat, dup, dup2, execl, execle, execv,
execve, faccessat, fchdir, fchmod, fchmodat, fchown,
fchownat, fcntl, fdatasync, fexecve, ffs, fork, fstat,
fstatat, fsync, ftruncate, futimens, getegid, geteuid,
getgid, getgroups, getpeername, getpgrp, getpid,
getppid, getsockname, getsockopt, getuid, htonl, htons,
kill, link, linkat, listen, longjmp, lseek, lstat,
memccpy, memchr, memcmp, memcpy, memmove, memset,
mkdir, mkdirat, mkfifo, mkfifoat, mknod, mknodat,
ntohl, ntohs, open, openat, pause, pipe, poll,
posix_trace_event, pselect, pthread_kill, pthread_self,
pthread_sigmask, quick_exit, raise, read, readlink,
readlinkat, recv, recvfrom, recvmsg, rename, renameat,
rmdir, select, sem_post, send, sendmsg, sendto, setgid,
setpgid, setsid, setsockopt, setuid, shutdown,
sigaction, sigaddset, sigdelset, sigemptyset,
sigfillset, sigismember, siglongjmp, signal, sigpause,
sigpending, sigprocmask, sigqueue, sigset, sigsuspend,
sleep, sockatmark, socket, socketpair, stat, stpcpy,
stpncpy, strcat, strchr, strcmp, strcpy, strcspn,
strlen, strncat, strncmp, strncpy, strnlen, strpbrk,
strrchr, strspn, strstr, strtok_r, symlink, symlinkat,
tcdrain, tcflow, tcflush, tcgetattr, tcgetpgrp,
tcsendbreak, tcsetattr, tcsetpgrp, time,
timer_getoverrun, timer_gettime, timer_settime, times,
umask, uname, unlink, unlinkat, utime, utimensat,
utimes, wait, waitpid, wcpcpy, wcpncpy, wcscat, wcschr,
wcscmp, wcscpy, wcscspn, wcslen, wcsncat, wcsncmp,
wcsncpy, wcsnlen, wcspbrk, wcsrchr, wcsspn, wcsstr,
wcstok, wmemchr, wmemcmp, wmemcpy, wmemmove, wmemset,
write
The function quick_exit is not included in the POSIX
list but it is included here in the set of safe
functions.
The default value is POSIX.
bugprone-signed-char-misuse
cert-str34-c redirects here as an alias for this check. For the CERT
alias, the DiagnoseSignedUnsignedCharComparisons option is set to
false.
Finds those signed char -> integer conversions which might indicate a
programming error. The basic problem with the signed char, that it
might store the non-ASCII characters as negative values. This behavior
can cause a misunderstanding of the written code both when an explicit
and when an implicit conversion happens.
When the code contains an explicit signed char -> integer conversion,
the human programmer probably expects that the converted value matches
with the character code (a value from [0..255]), however, the actual
value is in [-128..127] interval. To avoid this kind of
misinterpretation, the desired way of converting from a signed char to
an integer value is converting to unsigned char first, which stores all
the characters in the positive [0..255] interval which matches the
known character codes.
In case of implicit conversion, the programmer might not actually be
aware that a conversion happened and char value is used as an integer.
There are some use cases when this unawareness might lead to a
functionally imperfect code. For example, checking the equality of a
signed char and an unsigned char variable is something we should avoid
in C++ code. During this comparison, the two variables are converted to
integers which have different value ranges. For signed char, the
non-ASCII characters are stored as a value in [-128..-1] interval,
while the same characters are stored in the [128..255] interval for an
unsigned char.
It depends on the actual platform whether plain char is handled as
signed char by default and so it is caught by this check or not. To
change the default behavior you can use -funsigned-char and
-fsigned-char compilation options.
Currently, this check warns in the following cases:
o signed char is assigned to an integer variable
o signed char and unsigned char are compared with equality/inequality
operator
o signed char is converted to an integer in the array subscript
See also: STR34-C. Cast characters to unsigned char before converting
to larger integer sizes
A good example from the CERT description when a char variable is used
to read from a file that might contain non-ASCII characters. The
problem comes up when the code uses the -1 integer value as EOF, while
the 255 character code is also stored as -1 in two's complement form of
char type. See a simple example of this below. This code stops not
only when it reaches the end of the file, but also when it gets a
character with the 255 code.
#define EOF (-1)
int read(void) {
char CChar;
int IChar = EOF;
if (readChar(CChar)) {
IChar = CChar;
}
return IChar;
}
A proper way to fix the code above is converting the char variable to
an unsigned char value first.
#define EOF (-1)
int read(void) {
char CChar;
int IChar = EOF;
if (readChar(CChar)) {
IChar = static_cast(CChar);
}
return IChar;
}
Another use case is checking the equality of two char variables with
different signedness. Inside the non-ASCII value range this comparison
between a signed char and an unsigned char always returns false.
bool compare(signed char SChar, unsigned char USChar) {
if (SChar == USChar)
return true;
return false;
}
The easiest way to fix this kind of comparison is casting one of the
arguments, so both arguments will have the same type.
bool compare(signed char SChar, unsigned char USChar) {
if (static_cast(SChar) == USChar)
return true;
return false;
}
Options
CharTypedefsToIgnore
A semicolon-separated list of typedef names. In this list, we
can list typedefs for char or signed char, which will be ignored
by the check. This is useful when a typedef introduces an
integer alias like sal_Int8 or int8_t. In this case, human
misinterpretation is not an issue. Default is an empty string.
DiagnoseSignedUnsignedCharComparisons
When true, the check will warn on signed char/unsigned char
comparisons, otherwise these comparisons are ignored. By
default, this option is set to true.
bugprone-sizeof-container
The check finds usages of sizeof on expressions of STL container types.
Most likely the user wanted to use .size() instead.
All class/struct types declared in namespace std:: having a const
size() method are considered containers, with the exception of
std::bitset and std::array.
Examples:
std::string s;
int a = 47 + sizeof(s); // warning: sizeof() doesn't return the size of the container. Did you mean .size()?
int b = sizeof(std::string); // no warning, probably intended.
std::string array_of_strings[10];
int c = sizeof(array_of_strings) / sizeof(array_of_strings[0]); // no warning, definitely intended.
std::array std_array;
int d = sizeof(std_array); // no warning, probably intended.
bugprone-sizeof-expression
The check finds usages of sizeof expressions which are most likely
errors.
The sizeof operator yields the size (in bytes) of its operand, which
may be an expression or the parenthesized name of a type. Misuse of
this operator may be leading to errors and possible software
vulnerabilities.
Suspicious usage of 'sizeof(K)'
A common mistake is to query the sizeof of an integer literal. This is
equivalent to query the size of its type (probably int). The intent of
the programmer was probably to simply get the integer and not its size.
#define BUFLEN 42
char buf[BUFLEN];
memset(buf, 0, sizeof(BUFLEN)); // sizeof(42) ==> sizeof(int)
Suspicious usage of 'sizeof(expr)'
In cases, where there is an enum or integer to represent a type, a
common mistake is to query the sizeof on the integer or enum that
represents the type that should be used by sizeof. This results in the
size of the integer and not of the type the integer represents:
enum data_type {
FLOAT_TYPE,
DOUBLE_TYPE
};
struct data {
data_type type;
void* buffer;
data_type get_type() {
return type;
}
};
void f(data d, int numElements) {
// should be sizeof(float) or sizeof(double), depending on d.get_type()
int numBytes = numElements * sizeof(d.get_type());
...
}
Suspicious usage of 'sizeof(this)'
The this keyword is evaluated to a pointer to an object of a given
type. The expression sizeof(this) is returning the size of a pointer.
The programmer most likely wanted the size of the object and not the
size of the pointer.
class Point {
[...]
size_t size() { return sizeof(this); } // should probably be sizeof(*this)
[...]
};
Suspicious usage of 'sizeof(char*)'
There is a subtle difference between declaring a string literal with
char* A = "" and char A[] = "". The first case has the type char*
instead of the aggregate type char[]. Using sizeof on an object
declared with char* type is returning the size of a pointer instead of
the number of characters (bytes) in the string literal.
const char* kMessage = "Hello World!"; // const char kMessage[] = "...";
void getMessage(char* buf) {
memcpy(buf, kMessage, sizeof(kMessage)); // sizeof(char*)
}
Suspicious usage of 'sizeof(A*)'
A common mistake is to compute the size of a pointer instead of its
pointee. These cases may occur because of explicit cast or implicit
conversion.
int A[10];
memset(A, 0, sizeof(A + 0));
struct Point point;
memset(point, 0, sizeof(&point));
Suspicious usage of 'sizeof(...)/sizeof(...)'
Dividing sizeof expressions is typically used to retrieve the number of
elements of an aggregate. This check warns on incompatible or
suspicious cases.
In the following example, the entity has 10-bytes and is incompatible
with the type int which has 4 bytes.
char buf[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // sizeof(buf) => 10
void getMessage(char* dst) {
memcpy(dst, buf, sizeof(buf) / sizeof(int)); // sizeof(int) => 4 [incompatible sizes]
}
In the following example, the expression sizeof(Values) is returning
the size of char*. One can easily be fooled by its declaration, but in
parameter declaration the size '10' is ignored and the function is
receiving a char*.
char OrderedValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return CompareArray(char Values[10]) {
return memcmp(OrderedValues, Values, sizeof(Values)) == 0; // sizeof(Values) ==> sizeof(char*) [implicit cast to char*]
}
Suspicious 'sizeof' by 'sizeof' expression
Multiplying sizeof expressions typically makes no sense and is probably
a logic error. In the following example, the programmer used * instead
of /.
const char kMessage[] = "Hello World!";
void getMessage(char* buf) {
memcpy(buf, kMessage, sizeof(kMessage) * sizeof(char)); // sizeof(kMessage) / sizeof(char)
}
This check may trigger on code using the arraysize macro. The following
code is working correctly but should be simplified by using only the
sizeof operator.
extern Object objects[100];
void InitializeObjects() {
memset(objects, 0, arraysize(objects) * sizeof(Object)); // sizeof(objects)
}
Suspicious usage of 'sizeof(sizeof(...))'
Getting the sizeof of a sizeof makes no sense and is typically an error
hidden through macros.
#define INT_SZ sizeof(int)
int buf[] = { 42 };
void getInt(int* dst) {
memcpy(dst, buf, sizeof(INT_SZ)); // sizeof(sizeof(int)) is suspicious.
}
Suspicious usages of 'sizeof(...)' in pointer arithmetic
Arithmetic operators on pointers automatically scale the result with
the size of the pointed typed. Further use of sizeof around pointer
arithmetic will typically result in an unintended result.
Scaling the result of pointer difference
Subtracting two pointers results in an integer expression (of type
ptrdiff_t) which expresses the distance between the two pointed objects
in "number of objects between". A common mistake is to think that the
result is "number of bytes between", and scale the difference with
sizeof, such as P1 - P2 == N * sizeof(T) (instead of P1 - P2 == N) or
(P1 - P2) / sizeof(T) instead of P1 - P2.
void splitFour(const Obj* Objs, size_t N, Obj Delimiter) {
const Obj *P = Objs;
while (P < Objs + N) {
if (*P == Delimiter) {
break;
}
}
if (P - Objs != 4 * sizeof(Obj)) { // Expecting a distance multiplied by sizeof is suspicious.
error();
}
}
void iterateIfEvenLength(int *Begin, int *End) {
auto N = (Begin - End) / sizeof(int); // Dividing by sizeof() is suspicious.
if (N % 2)
return;
// ...
}
Stepping a pointer with a scaled integer
Conversely, when performing pointer arithmetics to add or subtract from
a pointer, the arithmetic operator implicitly scales the value actually
added to the pointer with the size of the pointee, as Ptr + N expects N
to be "number of objects to step", and not "number of bytes to step".
Seeing the calculation of a pointer where sizeof appears is suspicious,
and the result is typically unintended, often out of bounds. Ptr +
sizeof(T) will offset the pointer by sizeof(T) elements, effectively
exponentiating the scaling factor to the power of 2.
Similarly, multiplying or dividing a numeric value with the sizeof of
an element or the whole buffer is suspicious, because the dimensional
connection between the numeric value and the actual sizeof result can
not always be deduced. While scaling an integer up (multiplying) with
sizeof is likely always an issue, a scaling down (division) is not
always inherently dangerous, in case the developer is aware that the
division happens between an appropriate number of _bytes_ and a sizeof
value. Turning WarnOnOffsetDividedBySizeOf off will restrict the
warnings to the multiplication case.
This case also checks suspicious alignof and offsetof usages in pointer
arithmetic, as both return the "size" in bytes and not elements,
potentially resulting in doubly-scaled offsets.
void printEveryEvenIndexElement(int *Array, size_t N) {
int *P = Array;
while (P <= Array + N * sizeof(int)) { // Suspicious pointer arithmetic using sizeof()!
printf("%d ", *P);
P += 2 * sizeof(int); // Suspicious pointer arithmetic using sizeof()!
}
}
struct Message { /* ... */; char Flags[8]; };
void clearFlags(Message *Array, size_t N) {
const Message *End = Array + N;
while (Array < End) {
memset(Array + offsetof(Message, Flags), // Suspicious pointer arithmetic using offsetof()!
0, sizeof(Message::Flags));
++Array;
}
}
For this checked bogus pattern, cert-arr39-c redirects here as an alias
of this check.
This check corresponds to the CERT C Coding Standard rule ARR39-C. Do
not add or subtract a scaled integer to a pointer .
Limitations
Cases where the pointee type has a size of 1 byte (such as, and most
importantly, char) are excluded.
Options
WarnOnSizeOfConstant
When true, the check will warn on an expression like
sizeof(CONSTANT). Default is true.
WarnOnSizeOfIntegerExpression
When true, the check will warn on an expression like
sizeof(expr) where the expression results in an integer. Default
is false.
WarnOnSizeOfThis
When true, the check will warn on an expression like
sizeof(this). Default is true.
WarnOnSizeOfCompareToConstant
When true, the check will warn on an expression like
sizeof(expr) <= k for a suspicious constant k while k is 0 or
greater than 0x8000. Default is true.
WarnOnSizeOfPointerToAggregate
When true, the check will warn when the argument of sizeof is
either a pointer-to-aggregate type, an expression returning a
pointer-to-aggregate value or an expression that returns a
pointer from an array-to-pointer conversion (that may be
implicit or explicit, for example array + 2 or (int *)array).
Default is true.
WarnOnSizeOfPointer
When true, the check will report all expressions where the
argument of sizeof is an expression that produces a pointer
(except for a few idiomatic expressions that are probably
intentional and correct). This detects occurrences of CWE 467.
Default is false.
WarnOnOffsetDividedBySizeOf
When true, the check will warn on pointer arithmetic where the
element count is obtained from a division with sizeof(...),
e.g., Ptr + Bytes / sizeof(*T). Default is true.
WarnOnSizeOfInLoopTermination
When true, the check will warn about incorrect use of sizeof
expression in loop termination condition. The warning triggers
if the sizeof expression appears to be incorrectly used to
determine the number of array/buffer elements. e.g, long
arr[10]; for(int i = 0; i < sizeof(arr); i++) { ... }. Default
is true.
bugprone-spuriously-wake-up-functions
Finds cnd_wait, cnd_timedwait, wait, wait_for, or wait_until function
calls when the function is not invoked from a loop that checks whether
a condition predicate holds or the function has a condition parameter.
if (condition_predicate) {
condition.wait(lk);
}
if (condition_predicate) {
if (thrd_success != cnd_wait(&condition, &lock)) {
}
}
This check corresponds to the CERT C++ Coding Standard rule CON54-CPP.
Wrap functions that can spuriously wake up in a loop . and CERT C
Coding Standard rule CON36-C. Wrap functions that can spuriously wake
up in a loop .
bugprone-standalone-empty
Warns when empty() is used on a range and the result is ignored.
Suggests clear() if it is an existing member function.
The empty() method on several common ranges returns a Boolean
indicating whether or not the range is empty, but is often mistakenly
interpreted as a way to clear the contents of a range. Some ranges
offer a clear() method for this purpose. This check warns when a call
to empty returns a result that is ignored, and suggests replacing it
with a call to clear() if it is available as a member function of the
range.
For example, the following code could be used to indicate whether a
range is empty or not, but the result is ignored:
std::vector v;
...
v.empty();
A call to clear() would appropriately clear the contents of the range:
std::vector v;
...
v.clear();
Limitations
o Doesn't warn if empty() is defined and used with the ignore result in
the class template definition (for example in the library
implementation). These error cases can be caught with [[nodiscard]]
attribute.
bugprone-std-namespace-modification
Warns on modifications of the std or posix namespaces which can result
in undefined behavior.
The std (or posix) namespace is allowed to be extended with (class or
function) template specializations that depend on an user-defined type
(a type that is not defined in the standard system headers).
The check detects the following (user provided) declarations in
namespace std or posix:
o Anything that is not a template specialization.
o Explicit specializations of any standard library function template or
class template, if it does not have any user-defined type as template
argument.
o Explicit specializations of any member function of a standard library
class template.
o Explicit specializations of any member function template of a
standard library class or class template.
o Explicit or partial specialization of any member class template of a
standard library class or class template.
Examples:
namespace std {
int x; // warning: modification of 'std' namespace can result in undefined behavior [bugprone-dont-modify-std-namespace]
}
namespace posix::a { // warning: modification of 'posix' namespace can result in undefined behavior
}
template <>
struct ::std::hash { // warning: modification of 'std' namespace can result in undefined behavior
unsigned long operator()(const long &K) const {
return K;
}
};
struct MyData { long data; };
template <>
struct ::std::hash { // no warning: specialization with user-defined type
unsigned long operator()(const MyData &K) const {
return K.data;
}
};
namespace std {
template <>
void swap(bool &a, bool &b); // warning: modification of 'std' namespace can result in undefined behavior
template <>
bool less::operator()(MyData &&, MyData &&) const { // warning: modification of 'std' namespace can result in undefined behavior
return true;
}
}
References
This check corresponds to the CERT C++ Coding Standard rule DCL58-CPP.
Do not modify the standard namespaces .
bugprone-string-constructor
Finds string constructors that are suspicious and probably errors.
A common mistake is to swap parameters to the 'fill'
string-constructor.
Examples:
std::string str('x', 50); // should be str(50, 'x')
Calling the string-literal constructor with a length bigger than the
literal is suspicious and adds extra random characters to the string.
Examples:
std::string("test", 200); // Will include random characters after "test".
std::string("test", 2, 5); // Will include random characters after "st".
std::string_view("test", 200);
Creating an empty string from constructors with parameters is
considered suspicious. The programmer should use the empty constructor
instead.
Examples:
std::string("test", 0); // Creation of an empty string.
std::string("test", 1, 0);
std::string_view("test", 0);
Passing an invalid first character position parameter to constructor
will cause std::out_of_range exception at runtime.
Examples:
std::string("test", -1, 10); // Negative first character position.
std::string("test", 10, 10); // First character position is bigger than string literal character range".
Options
WarnOnLargeLength
When true, the check will warn on a string with a length greater
than LargeLengthThreshold. Default is true.
LargeLengthThreshold
An integer specifying the large length threshold. Default is
0x800000.
StringNames
Default is ::std::basic_string;::std::basic_string_view.
Semicolon-delimited list of class names to apply this check to.
By default ::std::basic_string applies to std::string and
std::wstring. Set to e.g.
::std::basic_string;llvm::StringRef;QString to perform this
check on custom classes.
bugprone-string-integer-assignment
The check finds assignments of an integer to std::basic_string
(std::string, std::wstring, etc.). The source of the problem is the
following assignment operator of std::basic_string:
basic_string& operator=( CharT ch );
Numeric types can be implicitly casted to character types.
std::string s;
int x = 5965;
s = 6;
s = x;
Use the appropriate conversion functions or character literals.
std::string s;
int x = 5965;
s = '6';
s = std::to_string(x);
In order to suppress false positives, use an explicit cast.
std::string s;
s = static_cast(6);
bugprone-string-literal-with-embedded-nul
Finds occurrences of string literal with embedded NUL character and
validates their usage.
Invalid escaping
Special characters can be escaped within a string literal by using
their hexadecimal encoding like \x42. A common mistake is to escape
them like this \0x42 where the \0 stands for the NUL character.
const char* Example[] = "Invalid character: \0x12 should be \x12";
const char* Bytes[] = "\x03\0x02\0x01\0x00\0xFF\0xFF\0xFF";
Truncated literal
String-like classes can manipulate strings with embedded NUL as they
are keeping track of the bytes and the length. This is not the case for
a char* (NUL-terminated) string.
A common mistake is to pass a string-literal with embedded NUL to a
string constructor expecting a NUL-terminated string. The bytes after
the first NUL character are truncated.
std::string str("abc\0def"); // "def" is truncated
str += "\0"; // This statement is doing nothing
if (str == "\0abc") return; // This expression is always true
bugprone-stringview-nullptr
Checks for various ways that the const CharT* constructor of
std::basic_string_view can be passed a null argument and replaces them
with the default constructor in most cases. For the comparison
operators, braced initializer list does not compile so instead a call
to .empty() or the empty string literal are used, where appropriate.
This prevents code from invoking behavior which is unconditionally
undefined. The single-argument const CharT* constructor does not check
for the null case before dereferencing its input. The standard is
slated to add an explicitly-deleted overload to catch some of these
cases: wg21.link/p2166
To catch the additional cases of NULL (which expands to __null) and 0,
first run the modernize-use-nullptr check to convert the callers to
nullptr.
std::string_view sv = nullptr;
sv = nullptr;
bool is_empty = sv == nullptr;
bool isnt_empty = sv != nullptr;
accepts_sv(nullptr);
accepts_sv({{}}); // A
accepts_sv({nullptr, 0}); // B
is translated into...
std::string_view sv = {};
sv = {};
bool is_empty = sv.empty();
bool isnt_empty = !sv.empty();
accepts_sv("");
accepts_sv(""); // A
accepts_sv({nullptr, 0}); // B
Note:
The source pattern with trailing comment "A" selects the (const
CharT*) constructor overload and then value-initializes the pointer,
causing a null dereference. It happens to not include the nullptr
literal, but it is still within the scope of this ClangTidy check.
Note:
The source pattern with trailing comment "B" selects the (const
CharT*, size_type) constructor which is perfectly valid, since the
length argument is 0. It is not changed by this ClangTidy check.
bugprone-suspicious-enum-usage
The checker detects various cases when an enum is probably misused (as
a bitmask).
1. When "ADD" or "bitwise OR" is used between two enum which come from
different types and these types value ranges are not disjoint.
The following cases will be investigated only using StrictMode <#
cmdoption-arg-StrictMode>. We regard the enum as a (suspicious) bitmask
if the three conditions below are true at the same time:
o at most half of the elements of the enum are non pow-of-2 numbers
(because of short enumerations)
o there is another non pow-of-2 number than the enum constant
representing all choices (the result "bitwise OR" operation of all
enum elements)
o enum type variable/enumconstant is used as an argument of a + or
"bitwise OR" operator
So whenever the non pow-of-2 element is used as a bitmask element we
diagnose a misuse and give a warning.
2. Investigating the right hand side of += and |= operator.
3. Check only the enum value side of a | and + operator if one of them
is not enum val.
4. Check both side of | or + operator where the enum values are from
the same enum type.
Examples:
enum { A, B, C };
enum { D, E, F = 5 };
enum { G = 10, H = 11, I = 12 };
unsigned flag;
flag =
A |
H; // OK, disjoint value intervals in the enum types ->probably good use.
flag = B | F; // Warning, have common values so they are probably misused.
// Case 2:
enum Bitmask {
A = 0,
B = 1,
C = 2,
D = 4,
E = 8,
F = 16,
G = 31 // OK, real bitmask.
};
enum Almostbitmask {
AA = 0,
BB = 1,
CC = 2,
DD = 4,
EE = 8,
FF = 16,
GG // Problem, forgot to initialize.
};
unsigned flag = 0;
flag |= E; // OK.
flag |=
EE; // Warning at the decl, and note that it was used here as a bitmask.
Options
StrictMode
Default value: 0. When non-null the suspicious bitmask usage
will be investigated additionally to the different enum usage
check.
bugprone-suspicious-include
The check detects various cases when an include refers to what appears
to be an implementation file, which often leads to hard-to-track-down
ODR violations.
Examples:
#include "Dinosaur.hpp" // OK, .hpp files tend not to have definitions.
#include "Pterodactyl.h" // OK, .h files tend not to have definitions.
#include "Velociraptor.cpp" // Warning, filename is suspicious.
#include_next // Warning, filename is suspicious.
Options
IgnoredRegex
A regular expression for the file name to be ignored by the
check. Default is empty string.
bugprone-suspicious-memory-comparison
Finds potentially incorrect calls to memcmp() based on properties of
the arguments. The following cases are covered:
Case 1: Non-standard-layout type
Comparing the object representations of non-standard-layout objects may
not properly compare the value representations.
Case 2: Types with no unique object representation
Objects with the same value may not have the same object
representation. This may be caused by padding or floating-point types.
See also: EXP42-C. Do not compare padding data and
FLP37-C. Do not use object representations to compare floating-point
values
This check is also related to and partially overlaps the CERT C++
Coding Standard rules OOP57-CPP. Prefer special member functions and
overloaded operators to C Standard Library functions
and EXP62-CPP. Do not access the bits of an object representation that
are not part of the object's value representation
cert-exp42-c redirects here as an alias of this check.
bugprone-suspicious-memset-usage
This check finds memset() calls with potential mistakes in their
arguments. Considering the function as void* memset(void* destination,
int fill_value, size_t byte_count), the following cases are covered:
Case 1: Fill value is a character ``'0'``
Filling up a memory area with ASCII code 48 characters is not
customary, possibly integer zeroes were intended instead. The check
offers a replacement of '0' with 0. Memsetting character pointers with
'0' is allowed.
Case 2: Fill value is truncated
Memset converts fill_value to unsigned char before using it. If
fill_value is out of unsigned character range, it gets truncated and
memory will not contain the desired pattern.
Case 3: Byte count is zero
Calling memset with a literal zero in its byte_count argument is likely
to be unintended and swapped with fill_value. The check offers to swap
these two arguments.
Corresponding cpplint.py check name: runtime/memset.
Examples:
void foo() {
int i[5] = {1, 2, 3, 4, 5};
int *ip = i;
char c = '1';
char *cp = &c;
int v = 0;
// Case 1
memset(ip, '0', 1); // suspicious
memset(cp, '0', 1); // OK
// Case 2
memset(ip, 0xabcd, 1); // fill value gets truncated
memset(ip, 0x00, 1); // OK
// Case 3
memset(ip, sizeof(int), v); // zero length, potentially swapped
memset(ip, 0, 1); // OK
}
bugprone-suspicious-missing-comma
String literals placed side-by-side are concatenated at translation
phase 6 (after the preprocessor). This feature is used to represent
long string literal on multiple lines.
For instance, the following declarations are equivalent:
const char* A[] = "This is a test";
const char* B[] = "This" " is a " "test";
A common mistake done by programmers is to forget a comma between two
string literals in an array initializer list.
const char* Test[] = {
"line 1",
"line 2" // Missing comma!
"line 3",
"line 4",
"line 5"
};
The array contains the string "line 2line3" at offset 1 (i.e. Test[1]).
Clang won't generate warnings at compile time.
This check may warn incorrectly on cases like:
const char* SupportedFormat[] = {
"Error %s",
"Code " PRIu64, // May warn here.
"Warning %s",
};
Options
SizeThreshold
An unsigned integer specifying the minimum size of a string
literal to be considered by the check. Default is 5U.
RatioThreshold
A string specifying the maximum threshold ratio [0, 1.0] of
suspicious string literals to be considered. Default is ".2".
MaxConcatenatedTokens
An unsigned integer specifying the maximum number of
concatenated tokens. Default is 5U.
bugprone-suspicious-realloc-usage
This check finds usages of realloc where the return value is assigned
to the same expression as passed to the first argument: p = realloc(p,
size); The problem with this construct is that if realloc fails it
returns a null pointer but does not deallocate the original memory. If
no other variable is pointing to it, the original memory block is not
available any more for the program to use or free. In either case p =
realloc(p, size); indicates bad coding style and can be replaced by q =
realloc(p, size);.
The pointer expression (used at realloc) can be a variable or a field
member of a data structure, but can not contain function calls or
unresolved types.
In obvious cases when the pointer used at realloc is assigned to
another variable before the realloc call, no warning is emitted. This
happens only if a simple expression in form of q = p or void *q = p is
found in the same function where p = realloc(p, ...) is found. The
assignment has to be before the call to realloc (but otherwise at any
place) in the same function. This suppression works only if p is a
single variable.
Examples:
struct A {
void *p;
};
A &getA();
void foo(void *p, A *a, int new_size) {
p = realloc(p, new_size); // warning: 'p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer
a->p = realloc(a->p, new_size); // warning: 'a->p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer
getA().p = realloc(getA().p, new_size); // no warning
}
void foo1(void *p, int new_size) {
void *p1 = p;
p = realloc(p, new_size); // no warning
}
bugprone-suspicious-semicolon
Finds most instances of stray semicolons that unexpectedly alter the
meaning of the code. More specifically, it looks for if, while, for and
for-range statements whose body is a single semicolon, and then
analyzes the context of the code (e.g. indentation) in an attempt to
determine whether that is intentional.
if (x < y);
{
x++;
}
Here the body of the if statement consists of only the semicolon at the
end of the first line, and x will be incremented regardless of the
condition.
while ((line = readLine(file)) != NULL);
processLine(line);
As a result of this code, processLine() will only be called once, when
the while loop with the empty body exits with line == NULL. The
indentation of the code indicates the intention of the programmer.
if (x >= y);
x -= y;
While the indentation does not imply any nesting, there is simply no
valid reason to have an if statement with an empty body (but it can
make sense for a loop). So this check issues a warning for the code
above.
To solve the issue remove the stray semicolon or in case the empty body
is intentional, reflect this using code indentation or put the
semicolon in a new line. For example:
while (readWhitespace());
Token t = readNextToken();
Here the second line is indented in a way that suggests that it is
meant to be the body of the while loop - whose body is in fact empty,
because of the semicolon at the end of the first line.
Either remove the indentation from the second line:
while (readWhitespace());
Token t = readNextToken();
... or move the semicolon from the end of the first line to a new line:
while (readWhitespace())
;
Token t = readNextToken();
In this case the check will assume that you know what you are doing,
and will not raise a warning.
bugprone-suspicious-string-compare
Find suspicious usage of runtime string comparison functions. This
check is valid in C and C++.
Checks for calls with implicit comparator and proposed to explicitly
add it.
if (strcmp(...)) // Implicitly compare to zero
if (!strcmp(...)) // Won't warn
if (strcmp(...) != 0) // Won't warn
Checks that compare function results (i.e., strcmp) are compared to
valid constant. The resulting value is
< 0 when lower than,
> 0 when greater than,
== 0 when equals.
A common mistake is to compare the result to 1 or -1.
if (strcmp(...) == -1) // Incorrect usage of the returned value.
Additionally, the check warns if the results value is implicitly cast
to a suspicious non-integer type. It's happening when the returned
value is used in a wrong context.
if (strcmp(...) < 0.) // Incorrect usage of the returned value.
Options
WarnOnImplicitComparison
When true, the check will warn on implicit comparison. true by
default.
WarnOnLogicalNotComparison
When true, the check will warn on logical not comparison. false
by default.
StringCompareLikeFunctions
A string specifying the comma-separated names of the extra
string comparison functions. Default is an empty string. The
check will detect the following string comparison functions:
__builtin_memcmp, __builtin_strcasecmp, __builtin_strcmp,
__builtin_strncasecmp, __builtin_strncmp, _mbscmp, _mbscmp_l,
_mbsicmp, _mbsicmp_l, _mbsnbcmp, _mbsnbcmp_l, _mbsnbicmp,
_mbsnbicmp_l, _mbsncmp, _mbsncmp_l, _mbsnicmp, _mbsnicmp_l,
_memicmp, _memicmp_l, _stricmp, _stricmp_l, _strnicmp,
_strnicmp_l, _wcsicmp, _wcsicmp_l, _wcsnicmp, _wcsnicmp_l,
lstrcmp, lstrcmpi, memcmp, memicmp, strcasecmp, strcmp, strcmpi,
stricmp, strncasecmp, strncmp, strnicmp, wcscasecmp, wcscmp,
wcsicmp, wcsncmp, wcsnicmp, wmemcmp.
bugprone-suspicious-stringview-data-usage
Identifies suspicious usages of std::string_view::data() that could
lead to reading out-of-bounds data due to inadequate or incorrect
string null termination.
It warns when the result of data() is passed to a constructor or
function without also passing the corresponding result of size() or
length() member function. Such usage can lead to unintended behavior,
particularly when assuming the data pointed to by data() is
null-terminated.
The absence of a c_str() method in std::string_view often leads
developers to use data() as a substitute, especially when interfacing
with C APIs that expect null-terminated strings. However, since data()
does not guarantee null termination, this can result in unintended
behavior if the API relies on proper null termination for correct
string interpretation.
In today's programming landscape, this scenario can occur when
implicitly converting an std::string_view to an std::string. Since the
constructor in std::string designed for string-view-like objects is
explicit, attempting to pass an std::string_view to a function
expecting an std::string will result in a compilation error. As a
workaround, developers may be tempted to utilize the .data() method to
achieve compilation, introducing potential risks.
For instance:
void printString(const std::string& str) {
std::cout << "String: " << str << std::endl;
}
void something(std::string_view sv) {
printString(sv.data());
}
In this example, directly passing sv to the printString function would
lead to a compilation error due to the explicit nature of the
std::string constructor. Consequently, developers might opt for
sv.data() to resolve the compilation error, albeit introducing
potential hazards as discussed.
Options
StringViewTypes
Option allows users to specify custom string view-like types for
analysis. It accepts a semicolon-separated list of type names or
regular expressions matching these types. Default value is:
::std::basic_string_view;::llvm::StringRef.
AllowedCallees
Specifies methods, functions, or classes where the result of
.data() is passed to. Allows to exclude such calls from the
analysis. Accepts a semicolon-separated list of names or regular
expressions matching these entities. Default value is: empty
string.
bugprone-swapped-arguments
Finds potentially swapped arguments by examining implicit conversions.
It analyzes the types of the arguments being passed to a function and
compares them to the expected types of the corresponding parameters. If
there is a mismatch or an implicit conversion that indicates a
potential swap, a warning is raised.
void printNumbers(int a, float b);
int main() {
// Swapped arguments: float passed as int, int as float)
printNumbers(10.0f, 5);
return 0;
}
Covers a wide range of implicit conversions, including: - User-defined
conversions - Conversions from floating-point types to boolean or
integral types - Conversions from integral types to boolean or
floating-point types - Conversions from boolean to integer types or
floating-point types - Conversions from (member) pointers to boolean
It is important to note that for most argument swaps, the types need to
match exactly. However, there are exceptions to this rule.
Specifically, when the swapped argument is of integral type, an exact
match is not always necessary. Implicit casts from other integral
types are also accepted. Similarly, when dealing with floating-point
arguments, implicit casts between different floating-point types are
considered acceptable.
To avoid confusion, swaps where both swapped arguments are of integral
types or both are of floating-point types do not trigger the warning.
In such cases, it's assumed that the developer intentionally used
different integral or floating-point types and does not raise a
warning. This approach prevents false positives and provides
flexibility in handling situations where varying integral or
floating-point types are intentionally utilized.
bugprone-switch-missing-default-case
Ensures that switch statements without default cases are flagged,
focuses only on covering cases with non-enums where the compiler may
not issue warnings.
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch
statement lacks a default case, if a value is encountered that does not
match any of the specified cases, the switch statement will do nothing
and the program will continue execution without handling the value.
This check helps identify switch statements that are missing a default
case, allowing developers to ensure that all possible cases are handled
properly. Adding a default case allows for graceful handling of
unexpected or unmatched values, reducing the risk of program errors and
unexpected behavior.
Example:
// Example 1:
// warning: switching on non-enum value without default case may not cover all cases
switch (i) {
case 0:
break;
}
// Example 2:
enum E { eE1 };
E e = eE1;
switch (e) { // no-warning
case eE1:
break;
}
// Example 3:
int i = 0;
switch (i) { // no-warning
case 0:
break;
default:
break;
}
Note:
Enum types are already covered by compiler warnings (comes under
-Wswitch) when a switch statement does not handle all enum values.
This check focuses on non-enum types where the compiler warnings may
not be present.
See also:
The CppCoreGuideline ES.79 provide guidelines
on switch statements, including the recommendation to always provide
a default case.
bugprone-tagged-union-member-count
Gives warnings for tagged unions, where the number of tags is different
from the number of data members inside the union.
A struct or a class is considered to be a tagged union if it has
exactly one union data member and exactly one enum data member and any
number of other data members that are neither unions or enums.
Furthermore, the types of the union and the enum members must not come
from system header files nor the std namespace.
Example:
enum Tags {
Tag1,
Tag2,
};
struct TaggedUnion { // warning: tagged union has more data members (3) than tags (2)
enum Tags Kind;
union {
int I;
float F;
char *Str;
} Data;
};
The following example illustrates the exception for unions and enums
from system header files and the std namespace.
#include
struct NotTaggedUnion {
enum MyEnum { MyEnumConstant1, MyEnumConstant2 } En;
pthread_mutex_t Mutex;
};
The pthread_mutex_t type may be defined as a union behind a typedef, in
which case the check could mistake this type as a user-defined tagged
union. After all, it has exactly one enum data member and exactly one
union data member. To avoid false-positive cases originating from
this, unions and enums from system headers and the std namespace are
ignored when pinpointing the union part and the enum part of a
potential user-defined tagged union.
How enum constants are counted
The main complicating factor when counting the number of enum constants
is that some of them might be auxiliary values that purposefully don't
have a corresponding union data member and are used for something else.
For example the last enum constant sometimes explicitly "points to" the
last declared valid enum constant or tracks how many enum constants
have been declared.
For an illustration:
enum TagWithLast {
Tag1 = 0,
Tag2 = 1,
Tag3 = 2,
LastTag = 2
};
enum TagWithCounter {
Tag1, // is 0
Tag2, // is 1
Tag3, // is 2
TagCount, // is 3
};
The check counts the number of distinct values among the enum constants
and not the enum constants themselves. This way the enum constants that
are essentially just aliases of other enum constants are not included
in the final count.
Handling of counting enum constants (ones like TagCount in the previous
code example) is done by decreasing the number of enum values by one if
the name of the last enum constant starts with a prefix or ends with a
suffix specified in CountingEnumPrefixes, CountingEnumSuffixes and it's
value is one less than the total number of distinct values in the enum.
When the final count is adjusted based on this heuristic then a
diagnostic note is emitted that shows which enum constant matched the
criteria.
The heuristic can be disabled entirely (EnableCountingEnumHeuristic) or
configured to follow your naming convention (CountingEnumPrefixes,
CountingEnumSuffixes). The strings specified in CountingEnumPrefixes,
CountingEnumSuffixes are matched case insensitively.
Example counts:
// Enum count is 3, because the value 2 is counted only once
enum TagWithLast {
Tag1 = 0,
Tag2 = 1,
Tag3 = 2,
LastTag = 2
};
// Enum count is 3, because TagCount is heuristically excluded
enum TagWithCounter {
Tag1, // is 0
Tag2, // is 1
Tag3, // is 2
TagCount, // is 3
};
Options
EnableCountingEnumHeuristic
This option enables or disables the counting enum heuristic. It uses
the prefixes and suffixes specified in the options
CountingEnumPrefixes, CountingEnumSuffixes to find counting enum
constants by using them for prefix and suffix matching.
This option is enabled by default.
When EnableCountingEnumHeuristic is false:
enum TagWithCounter {
Tag1,
Tag2,
Tag3,
TagCount,
};
struct TaggedUnion {
TagWithCounter Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
When EnableCountingEnumHeuristic is true:
enum TagWithCounter {
Tag1,
Tag2,
Tag3,
TagCount,
};
struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3)
TagWithCounter Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
CountingEnumPrefixes
See CountingEnumSuffixes below.
CountingEnumSuffixes
CountingEnumPrefixes and CountingEnumSuffixes are lists of semicolon
separated strings that are used to search for possible counting enum
constants. These strings are matched case insensitively as prefixes
and suffixes respectively on the names of the enum constants. If
EnableCountingEnumHeuristic is false then these options do nothing.
The default value of CountingEnumSuffixes is count and of
CountingEnumPrefixes is the empty string.
When EnableCountingEnumHeuristic is true and CountingEnumSuffixes is
count;size:
enum TagWithCounterCount {
Tag1,
Tag2,
Tag3,
TagCount,
};
struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterCount Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
enum TagWithCounterSize {
Tag11,
Tag22,
Tag33,
TagSize,
};
struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterSize Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
When EnableCountingEnumHeuristic is true and CountingEnumPrefixes is
maxsize;last_
enum TagWithCounterLast {
Tag1,
Tag2,
Tag3,
last_tag,
};
struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterLast tag;
union {
int I;
short S;
char *C;
float F;
} Data;
};
enum TagWithCounterMaxSize {
Tag1,
Tag2,
Tag3,
MaxSizeTag,
};
struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterMaxSize tag;
union {
int I;
short S;
char *C;
float F;
} Data;
};
StrictMode
When enabled, the check will also give a warning, when the number of
tags is greater than the number of union data members.
This option is disabled by default.
When StrictMode <#cmdoption-arg-StrictMode> is false:
struct TaggedUnion {
enum {
Tag1,
Tag2,
Tag3,
} Tags;
union {
int I;
float F;
} Data;
};
When StrictMode <#cmdoption-arg-StrictMode> is true:
struct TaggedUnion { // warning: tagged union has fewer data members (2) than tags (3)
enum {
Tag1,
Tag2,
Tag3,
} Tags;
union {
int I;
float F;
} Data;
};
bugprone-terminating-continue
Detects do while loops with a condition always evaluating to false that
have a continue statement, as this continue terminates the loop
effectively.
void f() {
do {
// some code
continue; // terminating continue
// some other code
} while(false);
bugprone-throw-keyword-missing
Warns about a potentially missing throw keyword. If a temporary object
is created, but the object's type derives from (or is the same as) a
class that has 'EXCEPTION', 'Exception' or 'exception' in its name, we
can assume that the programmer's intention was to throw that object.
Example:
void f(int i) {
if (i < 0) {
// Exception is created but is not thrown.
std::runtime_error("Unexpected argument");
}
}
bugprone-throwing-static-initialization
Finds all static or thread_local variable declarations where the
initializer for the object may throw an exception.
References
This check corresponds to the CERT C++ Coding Standard rule ERR58-CPP.
Handle all exceptions thrown before main() begins executing .
bugprone-too-small-loop-variable
Detects those for loops that have a loop variable with a "too small"
type which means this type can't represent all values which are part of
the iteration range.
int main() {
long size = 294967296l;
for (short i = 0; i < size; ++i) {}
}
This for loop is an infinite loop because the short type can't
represent all values in the [0..size] interval.
In a real use case size means a container's size which depends on the
user input.
int doSomething(const std::vector& items) {
for (short i = 0; i < items.size(); ++i) {}
}
This algorithm works for a small amount of objects, but will lead to
freeze for a larger user input.
It's recommended to enable the compiler warning
-Wtautological-constant-out-of-range-compare as well, since check does
not inspect compile-time constant loop boundaries to avoid overlaps
with the warning.
Options
MagnitudeBitsUpperLimit
Upper limit for the magnitude bits of the loop variable. If it's
set the check filters out those catches in which the loop
variable's type has more magnitude bits as the specified upper
limit. The default value is 16. For example, if the user sets
this option to 31 (bits), then a 32-bit unsigned int is ignored
by the check, however a 32-bit int is not (A 32-bit signed int
has 31 magnitude bits).
int main() {
long size = 294967296l;
for (unsigned i = 0; i < size; ++i) {} // no warning with MagnitudeBitsUpperLimit = 31 on a system where unsigned is 32-bit
for (int i = 0; i < size; ++i) {} // warning with MagnitudeBitsUpperLimit = 31 on a system where int is 32-bit
}
bugprone-unchecked-optional-access
Note: This check uses a flow-sensitive static analysis to produce its
results. Therefore, it may be more resource intensive (RAM, CPU) than
the average clang-tidy check.
This check identifies unsafe accesses to values contained in
std::optional, absl::optional, base::Optional,
folly::Optional, bsl::optional, or BloombergLP::bdlb::NullableValue
objects. Below we will refer to all these types collectively as
optional.
An access to the value of an optional occurs when one of its value,
operator*, or operator-> member functions is invoked. To align with
common misconceptions, the check considers these member functions as
equivalent, even though there are subtle differences related to
exceptions versus undefined behavior. See Additional notes, below, for
more information on this topic.
An access to the value of an optional is considered safe if and only
if code in the local scope (for example, a function body) ensures that
the optional has a value in all possible execution paths that can
reach the access. That should happen either through an explicit check,
using the optional::has_value member function, or by constructing
the optional in a way that shows that it unambiguously holds a value
(e.g using std::make_optional which always returns a populated
std::optional).
Below we list some examples, starting with unsafe optional access
patterns, followed by safe access patterns.
Unsafe access patterns
Access the value without checking if it exists
The check flags accesses to the value that are not locally guarded by
existence check:
void f(std::optional opt) {
use(*opt); // unsafe: it is unclear whether `opt` has a value.
}
Access the value in the wrong branch
The check is aware of the state of an optional object in different
branches of the code. For example:
void f(std::optional opt) {
if (opt.has_value()) {
} else {
use(opt.value()); // unsafe: it is clear that `opt` does *not* have a value.
}
}
Assume a function result to be stable
The check is aware that function results might not be stable. That is,
consecutive calls to the same function might return different values.
For example:
void f(Foo foo) {
if (foo.take().has_value()) {
use(*foo.take()); // unsafe: it is unclear whether `foo.take()` has a value.
}
}
Exception: accessor methods
The check assumes accessor methods of a class are stable, with a
heuristic to determine which methods are accessors. Specifically,
parameter-free const methods and smart pointer-like APIs (non const
overloads of * when there is a parallel const overload) are treated as
accessors. Note that this is not guaranteed to be safe -- but, it is
widely used (safely) in practice. Calls to non const methods are
assumed to modify the state of the object and affect the stability of
earlier accessor calls.
Rely on invariants of uncommon APIs
The check is unaware of invariants of uncommon APIs. For example:
void f(Foo foo) {
if (foo.HasProperty("bar")) {
use(*foo.GetProperty("bar")); // unsafe: it is unclear whether `foo.GetProperty("bar")` has a value.
}
}
Check if a value exists, then pass the optional to another function
The check relies on local reasoning. The check and value access must
both happen in the same function. An access is considered unsafe even
if the caller of the function performing the access ensures that the
optional has a value. For example:
void g(std::optional opt) {
use(*opt); // unsafe: it is unclear whether `opt` has a value.
}
void f(std::optional opt) {
if (opt.has_value()) {
g(opt);
}
}
Safe access patterns
Check if a value exists, then access the value
The check recognizes all straightforward ways for checking if a value
exists and accessing the value contained in an optional object. For
example:
void f(std::optional opt) {
if (opt.has_value()) {
use(*opt);
}
}
Check if a value exists, then access the value from a copy
The criteria that the check uses is semantic, not syntactic. It
recognizes when a copy of the optional object being accessed is known
to have a value. For example:
void f(std::optional opt1) {
if (opt1.has_value()) {
std::optional opt2 = opt1;
use(*opt2);
}
}
Ensure that a value exists using common macros
The check is aware of common macros like CHECK and DCHECK. Those can be
used to ensure that an optional object has a value. For example:
void f(std::optional opt) {
DCHECK(opt.has_value());
use(*opt);
}
Ensure that a value exists, then access the value in a correlated branch
The check is aware of correlated branches in the code and can figure
out when an optional object is ensured to have a value on all execution
paths that lead to an access. For example:
void f(std::optional opt) {
bool safe = false;
if (opt.has_value() && SomeOtherCondition()) {
safe = true;
}
// ... more code...
if (safe) {
use(*opt);
}
}
Stabilize function results
Function results are not assumed to be stable across calls, except for
const accessor methods. For more complex accessors (non-const, or
depend on multiple params) it is best to store the result of the
function call in a local variable and use that variable to access the
value. For example:
void f(Foo foo) {
if (const auto& foo_opt = foo.take(); foo_opt.has_value()) {
use(*foo_opt);
}
}
Do not rely on uncommon-API invariants
When uncommon APIs guarantee that an optional has contents, do not rely
on it -- instead, check explicitly that the optional object has a
value. For example:
void f(Foo foo) {
if (const auto& property = foo.GetProperty("bar")) {
use(*property);
}
}
instead of the HasProperty, GetProperty pairing we saw above.
Do not rely on caller-performed checks
If you know that all of a function's callers have checked that an
optional argument has a value, either change the function to take the
value directly or check the optional again in the local scope of the
callee. For example:
void g(int val) {
use(val);
}
void f(std::optional opt) {
if (opt.has_value()) {
g(*opt);
}
}
and
struct S {
std::optional opt;
int x;
};
void g(const S &s) {
if (s.opt.has_value() && s.x > 10) {
use(*s.opt);
}
void f(S s) {
if (s.opt.has_value()) {
g(s);
}
}
Additional notes
Aliases created via using declarations
The check is aware of aliases of optional types that are created via
using declarations. For example:
using OptionalInt = std::optional;
void f(OptionalInt opt) {
use(opt.value()); // unsafe: it is unclear whether `opt` has a value.
}
Lambdas
The check does not currently report unsafe optional accesses in
lambdas. A future version will expand the scope to lambdas, following
the rules outlined above. It is best to follow the same principles when
using optionals in lambdas.
Access with operator*() vs. value()
Given that value() has well-defined behavior (either throwing an
exception or terminating the program), why treat it the same as
operator*() which causes undefined behavior (UB)? That is, why is it
considered unsafe to access an optional with value(), if it's not
provably populated with a value? For that matter, why is CHECK()
followed by operator*() any better than value(), given that they are
semantically equivalent (on configurations that disable exceptions)?
The answer is that we assume most users do not realize the difference
between value() and operator*(). Shifting to operator*() and some form
of explicit value-presence check or explicit program termination has
two advantages:
o Readability. The check, and any potential side effects like
program shutdown, are very clear in the code. Separating access
from checks can actually make the checks more obvious.
o Performance. A single check can cover many or even all accesses
within scope. This gives the user the best of both worlds -- the
safety of a dynamic check, but without incurring redundant costs.
Options
IgnoreSmartPointerDereference
If set to true, the check ignores optionals that are reached
through overloaded smart-pointer-like dereference (operator*,
operator->) on classes other than the optional type itself. This
helps avoid false positives where the analysis cannot equate
results across such calls. This does not cover access through
operator[]. Default is false.
IgnoreValueCalls
If set to true, the check does not diagnose calls to
optional::value(). Diagnostics for operator*() and operator->()
remain enabled. This is useful for codebases that intentionally
rely on value() for defined, guarded access while still flagging
UB-prone operator dereferences. Default is false.
bugprone-unchecked-string-to-number-conversion
This check flags calls to string-to-number conversion functions that do
not verify the validity of the conversion, such as atoi() or scanf().
It does not flag calls to strtol(), or other, related conversion
functions that do perform better error checking.
#include
void func(const char *buff) {
int si;
if (buff) {
si = atoi(buff); /* 'atoi' used to convert a string to an integer, but function will
not report conversion errors; consider using 'strtol' instead. */
} else {
/* Handle error */
}
}
References
This check corresponds to the CERT C Coding Standard rule ERR34-C.
Detect errors when converting a string to a number .
bugprone-undefined-memory-manipulation
Finds calls of memory manipulation functions memset(), memcpy() and
memmove() on non-TriviallyCopyable objects resulting in undefined
behavior.
Using memory manipulation functions on non-TriviallyCopyable objects
can lead to a range of subtle and challenging issues in C++ code. The
most immediate concern is the potential for undefined behavior, where
the state of the object may become corrupted or invalid. This can
manifest as crashes, data corruption, or unexpected behavior at
runtime, making it challenging to identify and diagnose the root cause.
Additionally, misuse of memory manipulation functions can bypass
essential object-specific operations, such as constructors and
destructors, leading to resource leaks or improper initialization.
For example, when using memcpy to copy std::string, pointer data is
being copied, and it can result in a double free issue.
#include
#include
int main() {
std::string source = "Hello";
std::string destination;
std::memcpy(&destination, &source, sizeof(std::string));
// Undefined behavior may occur here, during std::string destructor call.
return 0;
}
bugprone-undelegated-constructor
Finds creation of temporary objects in constructors that look like a
function call to another constructor of the same class.
The user most likely meant to use a delegating constructor or base
class initializer.
bugprone-unhandled-exception-at-new
Finds calls to new with missing exception handler for std::bad_alloc.
Calls to new may throw exceptions of type std::bad_alloc that should be
handled. Alternatively, the nonthrowing form of new can be used. The
check verifies that the exception is handled in the function that calls
new.
If a nonthrowing version is used or the exception is allowed to
propagate out of the function no warning is generated.
The exception handler is checked if it catches a std::bad_alloc or
std::exception exception type, or all exceptions (catch-all). The
check assumes that any user-defined operator new is either noexcept or
may throw an exception of type std::bad_alloc (or one derived from it).
Other exception class types are not taken into account.
int *f() noexcept {
int *p = new int[1000]; // warning: missing exception handler for allocation failure at 'new'
// ...
return p;
}
int *f1() { // not 'noexcept'
int *p = new int[1000]; // no warning: exception can be handled outside
// of this function
// ...
return p;
}
int *f2() noexcept {
try {
int *p = new int[1000]; // no warning: exception is handled
// ...
return p;
} catch (std::bad_alloc &) {
// ...
}
// ...
}
int *f3() noexcept {
int *p = new (std::nothrow) int[1000]; // no warning: "nothrow" is used
// ...
return p;
}
bugprone-unhandled-self-assignment
cert-oop54-cpp redirects here as an alias for this check. For the CERT
alias, the WarnOnlyIfThisHasSuspiciousField option is set to false.
Finds user-defined copy assignment operators which do not protect the
code against self-assignment either by checking self-assignment
explicitly or using the copy-and-swap or the copy-and-move method.
By default, this check searches only those classes which have any
pointer or C array field to avoid false positives. In case of a pointer
or a C array, it's likely that self-copy assignment breaks the object
if the copy assignment operator was not written with care.
See also: OOP54-CPP. Gracefully handle self-copy assignment
A copy assignment operator must prevent that self-copy assignment ruins
the object state. A typical use case is when the class has a pointer
field and the copy assignment operator first releases the pointed
object and then tries to assign it:
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
T& operator=(const T &rhs) {
delete p;
p = new int(*rhs.p);
return *this;
}
};
There are two common C++ patterns to avoid this problem. The first is
the self-assignment check:
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
T& operator=(const T &rhs) {
if(this == &rhs)
return *this;
delete p;
p = new int(*rhs.p);
return *this;
}
};
The second one is the copy-and-swap method when we create a temporary
copy (using the copy constructor) and then swap this temporary object
with this:
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
void swap(T &rhs) {
using std::swap;
swap(p, rhs.p);
}
T& operator=(const T &rhs) {
T(rhs).swap(*this);
return *this;
}
};
There is a third pattern which is less common. Let's call it the
copy-and-move method when we create a temporary copy (using the copy
constructor) and then move this temporary object into this (needs a
move assignment operator):
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
T& operator=(const T &rhs) {
T t = rhs;
*this = std::move(t);
return *this;
}
T& operator=(T &&rhs) {
p = rhs.p;
rhs.p = nullptr;
return *this;
}
};
Options
WarnOnlyIfThisHasSuspiciousField
When true, the check will warn only if the container class of
the copy assignment operator has any suspicious fields (pointer,
C array and C++ smart pointer). This option is set to true by
default.
bugprone-unintended-char-ostream-output
Finds unintended character output from unsigned char and signed char to
an ostream.
Normally, when unsigned char (uint8_t) or signed char (int8_t) is used,
it is more likely a number than a character. However, when it is passed
directly to std::ostream's operator<<, the result is the character
output instead of the numeric value. This often contradicts the
developer's intent to print integer values.
uint8_t v = 65;
std::cout << v; // output 'A' instead of '65'
The check will suggest casting the value to an appropriate type to
indicate the intent, by default, it will cast to unsigned int for
unsigned char and int for signed char.
std::cout << static_cast(v); // when v is unsigned char
std::cout << static_cast(v); // when v is signed char
To avoid lengthy cast statements, add prefix + to the variable can also
suppress warnings because unary expression will promote the value to an
int.
std::cout << +v;
Or cast to char to explicitly indicate that output should be a
character.
std::cout << static_cast(v);
Options
AllowedTypes
A semicolon-separated list of type names that will be treated
like the char type: the check will not report variables declared
with with these types or explicit cast expressions to these
types. Note that this distinguishes type aliases from the
original type, so specifying e.g. unsigned char here will not
suppress reports about uint8_t even if it is defined as a
typedef alias for unsigned char. Default is unsigned
char;signed char.
CastTypeName
When CastTypeName is specified, the fix-it will use CastTypeName
as the cast target type. Otherwise, fix-it will automatically
infer the type.
bugprone-unique-ptr-array-mismatch
Finds initializations of C++ unique pointers to non-array type that are
initialized with an array.
If a pointer std::unique_ptr is initialized with a new-expression
new T[] the memory is not deallocated correctly. A plain delete is used
in this case to deallocate the target memory. Instead a delete[] call
is needed. A std::unique_ptr uses the correct delete operator. The
check does not emit warning if an unique_ptr with user-specified
deleter type is used.
The check offers replacement of unique_ptr to unique_ptr if it
is used at a single variable declaration (one variable in one
statement).
Example:
std::unique_ptr x(new Foo[10]); // -> std::unique_ptr x(new Foo[10]);
// ^ warning: unique pointer to non-array is initialized with array
std::unique_ptr x1(new Foo), x2(new Foo[10]); // no replacement
// ^ warning: unique pointer to non-array is initialized with array
D d;
std::unique_ptr x3(new Foo[10], d); // no warning (custom deleter used)
struct S {
std::unique_ptr x(new Foo[10]); // no replacement in this case
// ^ warning: unique pointer to non-array is initialized with array
};
This check partially covers the CERT C++ Coding Standard rule
MEM51-CPP. Properly deallocate dynamically allocated resources
However, only
the std::unique_ptr case is detected by this check.
bugprone-unsafe-functions
Checks for functions that have safer, more secure replacements
available, or are considered deprecated due to design flaws. The check
heavily relies on the functions from the Annex K. "Bounds-checking
interfaces" of C11.
The check implements the following rules from the CERT C Coding
Standard:
o Recommendation MSC24-C. Do not use deprecated or obsolescent
functions .
o Rule MSC33-C. Do not pass invalid data to the asctime()
function .
cert-msc24-c and cert-msc33-c redirect here as aliases of this check.
Unsafe functions
The following functions are reported if ReportDefaultFunctions is
enabled.
If Annex K. is available, a replacement from Annex K. is suggested for
the following functions:
asctime, asctime_r, bsearch, ctime, fopen, fprintf, freopen, fscanf,
fwprintf, fwscanf, getenv, gets, gmtime, localtime, mbsrtowcs,
mbstowcs, memcpy, memmove, memset, printf, qsort, scanf, snprintf,
sprintf, sscanf, strcat, strcpy, strerror, strlen, strncat, strncpy,
strtok, swprintf, swscanf, vfprintf, vfscanf, vfwprintf, vfwscanf,
vprintf, vscanf, vsnprintf, vsprintf, vsscanf, vswprintf, vswscanf,
vwprintf, vwscanf, wcrtomb, wcscat, wcscpy, wcslen, wcsncat, wcsncpy,
wcsrtombs, wcstok, wcstombs, wctomb, wmemcpy, wmemmove, wprintf,
wscanf.
If Annex K. is not available, replacements are suggested only for the
following functions from the previous list:
o asctime, asctime_r, suggested replacement: strftime
o gets, suggested replacement: fgets
The following functions are always checked, regardless of Annex K
availability:
o rewind, suggested replacement: fseek
o setbuf, suggested replacement: setvbuf
If ReportMoreUnsafeFunctions is enabled, the following functions are
also checked:
o bcmp, suggested replacement: memcmp
o bcopy, suggested replacement: memcpy_s if Annex K is available, or
memcpy
o bzero, suggested replacement: memset_s if Annex K is available, or
memset
o getpw, suggested replacement: getpwuid
o vfork, suggested replacement: posix_spawn
Although mentioned in the associated CERT rules, the following
functions are ignored by the check:
atof, atoi, atol, atoll, tmpfile.
The availability of Annex K is determined based on the following
macros:
o __STDC_LIB_EXT1__: feature macro, which indicates the presence of
Annex K. "Bounds-checking interfaces" in the library
implementation
o __STDC_WANT_LIB_EXT1__: user-defined macro, which indicates that
the user requests the functions from Annex K. to be defined.
Both macros have to be defined to suggest replacement functions from
Annex K. __STDC_LIB_EXT1__ is defined by the library implementation,
and __STDC_WANT_LIB_EXT1__ must be defined to 1 by the user before
including any system headers.
Custom functions
The option CustomFunctions allows the user to define custom functions
to be checked. The format is the following, without newlines:
bugprone-unsafe-functions.CustomFunctions="
functionRegex1[, replacement1[, reason1]];
functionRegex2[, replacement2[, reason2]];
...
"
The functions are matched using POSIX extended regular expressions.
(Note: The regular expressions do not support negative (?!) matches.)
The reason is optional and is used to provide additional information
about the reasoning behind the replacement. The default reason is is
marked as unsafe.
If replacement is empty, the default text it should not be used will be
shown instead of the suggestion for a replacement.
If the reason starts with the character >, the reason becomes fully
custom. The default suffix is disabled even if a replacement is
present, and only the reason message is shown after the matched
function, to allow better control over the suggestions. (The starting >
and whitespace directly after it are trimmed from the message.)
As an example, the following configuration matches only the function
original in the default namespace. A similar diagnostic can also be
printed using a fully custom reason.
// bugprone-unsafe-functions.CustomFunctions:
// ^original$, replacement, is deprecated;
// Using the fully custom message syntax:
// ^suspicious$,,> should be avoided if possible.
original(); // warning: function 'original' is deprecated; 'replacement' should be used instead.
suspicious(); // warning: function 'suspicious' should be avoided if possible.
::std::original(); // no-warning
original_function(); // no-warning
If the regular expression contains the character :, it is matched
against the qualified name (i.e. std::original), otherwise the regex is
matched against the unqualified name (original). If the regular
expression starts with :: (or ^::), it is matched against the fully
qualified name (::std::original).
One of the use cases for fully custom messages is suggesting compiler
options and warning flags:
// bugprone-unsafe-functions.CustomFunctions:
// ^memcpy$,,>is recommended to have compiler hardening using '_FORTIFY_SOURCE';
// ^printf$,,>is recommended to have the '-Werror=format-security' compiler warning flag;
memcpy(dest, src, 999'999); // warning: function 'memcpy' is recommended to have compiler hardening using '_FORTIFY_SOURCE'
printf(raw_str); // warning: function 'printf' is recommended to have the '-Werror=format-security' compiler warning flag
Note:
Fully qualified names can contain template parameters on certain C++
classes, but not on C++ functions. Type aliases are resolved before
matching.
As an example, the member function open in the class std::ifstream
has a fully qualified name of ::std::basic_ifstream::open.
The example could also be matched with the regex
::std::basic_ifstream<[^>]*>::open, which matches all potential
template parameters, but does not match nested template classes.
Options
ReportMoreUnsafeFunctions
When true, additional functions from widely used APIs (such as
POSIX) are added to the list of reported functions. See the
main documentation of the check for the complete list as to what
this option enables. Default is true.
ReportDefaultFunctions
When true, the check reports the default set of functions.
Consider changing the setting to false if you only want to see
custom functions matched via custom functions. Default is true.
CustomFunctions
A semicolon-separated list of custom functions to be matched. A
matched function contains a regular expression, an optional name
of the replacement function, and an optional reason, separated
by comma. For more information, see Custom functions.
Examples
#ifndef __STDC_LIB_EXT1__
#error "Annex K is not supported by the current standard library implementation."
#endif
#define __STDC_WANT_LIB_EXT1__ 1
#include // Defines functions from Annex K.
#include
enum { BUFSIZE = 32 };
void Unsafe(const char *Msg) {
static const char Prefix[] = "Error: ";
static const char Suffix[] = "\n";
char Buf[BUFSIZE] = {0};
strcpy(Buf, Prefix); // warning: function 'strcpy' is not bounds-checking; 'strcpy_s' should be used instead.
strcat(Buf, Msg); // warning: function 'strcat' is not bounds-checking; 'strcat_s' should be used instead.
strcat(Buf, Suffix); // warning: function 'strcat' is not bounds-checking; 'strcat_s' should be used instead.
if (fputs(buf, stderr) < 0) {
// error handling
return;
}
}
void UsingSafeFunctions(const char *Msg) {
static const char Prefix[] = "Error: ";
static const char Suffix[] = "\n";
char Buf[BUFSIZE] = {0};
if (strcpy_s(Buf, BUFSIZE, Prefix) != 0) {
// error handling
return;
}
if (strcat_s(Buf, BUFSIZE, Msg) != 0) {
// error handling
return;
}
if (strcat_s(Buf, BUFSIZE, Suffix) != 0) {
// error handling
return;
}
if (fputs(Buf, stderr) < 0) {
// error handling
return;
}
}
bugprone-unused-local-non-trivial-variable
Warns when a local non trivial variable is unused within a function.
The following types of variables are excluded from this check:
o trivial and trivially copyable
o references and pointers
o exception variables in catch clauses
o static or thread local
o structured bindings
o variables with [[maybe_unused]] attribute
o name-independent variables
This check can be configured to warn on all non-trivial variables by
setting IncludeTypes to .*, and excluding specific types using
ExcludeTypes.
In the this example, my_lock would generate a warning that it is
unused.
std::mutex my_lock;
// my_lock local variable is never used
In the next example, future2 would generate a warning that it is
unused.
std::future future1;
std::future future2;
// ...
MyObject foo = future1.get();
// future2 is not used.
Options
IncludeTypes
Semicolon-separated list of regular expressions matching types
of variables to check. By default the following types are
checked:
o ::std::.*mutex
o ::std::future
o ::std::basic_string
o ::std::basic_regex
o ::std::basic_istringstream
o ::std::basic_stringstream
o ::std::bitset
o ::std::filesystem::path
ExcludeTypes
A semicolon-separated list of regular expressions matching types
that are excluded from the IncludeTypes matches. By default it
is an empty list.
bugprone-unused-raii
Finds temporaries that look like RAII objects.
The canonical example for this is a scoped lock.
{
scoped_lock(&global_mutex);
critical_section();
}
The destructor of the scoped_lock is called before the critical_section
is entered, leaving it unprotected.
We apply a number of heuristics to reduce the false positive count of
this check:
o Ignore code expanded from macros. Testing frameworks make heavy use
of this.
o Ignore types with trivial destructors. They are very unlikely to be
RAII objects and there's no difference when they are deleted.
o Ignore objects at the end of a compound statement (doesn't change
behavior).
o Ignore objects returned from a call.
bugprone-unused-return-value
Warns on unused function return values. The checked functions can be
configured.
Operator overloading with assignment semantics are ignored.
Options
CheckedFunctions
Semicolon-separated list of functions to check. This parameter
supports regexp. The function is checked if the name and scope
matches, with any arguments. By default the following functions
are checked: ^::std::async$, ^::std::launder$, ^::std::remove$,
^::std::remove_if$, ^::std::unique$,
^::std::unique_ptr::release$, ^::std::basic_string::empty$,
^::std::vector::empty$, ^::std::back_inserter$,
^::std::distance$, ^::std::find$, ^::std::find_if$,
^::std::inserter$, ^::std::lower_bound$, ^::std::make_pair$,
^::std::map::count$, ^::std::map::find$,
^::std::map::lower_bound$, ^::std::multimap::equal_range$,
^::std::multimap::upper_bound$, ^::std::set::count$,
^::std::set::find$, ^::std::setfill$, ^::std::setprecision$,
^::std::setw$, ^::std::upper_bound$, ^::std::vector::at$,
^::bsearch$, ^::ferror$, ^::feof$, ^::isalnum$, ^::isalpha$,
^::isblank$, ^::iscntrl$, ^::isdigit$, ^::isgraph$, ^::islower$,
^::isprint$, ^::ispunct$, ^::isspace$, ^::isupper$,
^::iswalnum$, ^::iswprint$, ^::iswspace$, ^::isxdigit$,
^::memchr$, ^::memcmp$, ^::strcmp$, ^::strcoll$, ^::strncmp$,
^::strpbrk$, ^::strrchr$, ^::strspn$, ^::strstr$, ^::wcscmp$,
^::access$, ^::bind$, ^::connect$, ^::difftime$, ^::dlsym$,
^::fnmatch$, ^::getaddrinfo$, ^::getopt$, ^::htonl$, ^::htons$,
^::iconv_open$, ^::inet_addr$, isascii$, isatty$, ^::mmap$,
^::newlocale$, ^::openat$, ^::pathconf$, ^::pthread_equal$,
^::pthread_getspecific$, ^::pthread_mutex_trylock$, ^::readdir$,
^::readlink$, ^::recvmsg$, ^::regexec$, ^::scandir$, ^::semget$,
^::setjmp$, ^::shm_open$, ^::shmget$, ^::sigismember$,
^::strcasecmp$, ^::strsignal$, ^::ttyname$
o std::async(). Not using the return value makes the call
synchronous.
o std::launder(). Not using the return value usually means that
the function interface was misunderstood by the programmer.
Only the returned pointer is "laundered", not the argument.
o std::remove(), std::remove_if() and std::unique(). The
returned iterator indicates the boundary between elements to
keep and elements to be removed. Not using the return value
means that the information about which elements to remove is
lost.
o std::unique_ptr::release(). Not using the return value can
lead to resource leaks if the same pointer isn't stored
anywhere else. Often, ignoring the release() return value
indicates that the programmer confused the function with
reset().
o std::basic_string::empty() and std::vector::empty(). Not using
the return value often indicates that the programmer confused
the function with clear().
CheckedReturnTypes
Semicolon-separated list of function return types to check. By
default the following function return types are checked:
^::std::error_code$, ^::std::error_condition$, ^::std::errc$,
^::std::expected$, ^::boost::system::error_code$
AllowCastToVoid
Controls whether casting return values to void is permitted.
Default: false.
cert-err33-c <> is an alias of this check that checks a fixed and large
set of standard library functions.
bugprone-use-after-move
Warns if an object is used after it has been moved, for example:
std::string str = "Hello, world!\n";
std::vector messages;
messages.emplace_back(std::move(str));
std::cout << str;
The last line will trigger a warning that str is used after it has been
moved.
The check does not trigger a warning if the object is reinitialized
after the move and before the use. For example, no warning will be
output for this code:
messages.emplace_back(std::move(str));
str = "Greetings, stranger!\n";
std::cout << str;
Subsections below explain more precisely what exactly the check
considers to be a move, use, and reinitialization.
The check takes control flow into account. A warning is only emitted if
the use can be reached from the move. This means that the following
code does not produce a warning:
if (condition) {
messages.emplace_back(std::move(str));
} else {
std::cout << str;
}
On the other hand, the following code does produce a warning:
for (int i = 0; i < 10; ++i) {
std::cout << str;
messages.emplace_back(std::move(str));
}
(The use-after-move happens on the second iteration of the loop.)
In some cases, the check may not be able to detect that two branches
are mutually exclusive. For example (assuming that i is an int):
if (i == 1) {
messages.emplace_back(std::move(str));
}
if (i == 2) {
std::cout << str;
}
In this case, the check will erroneously produce a warning, even though
it is not possible for both the move and the use to be executed. More
formally, the analysis is flow-sensitive but not path-sensitive
.
Silencing erroneous warnings
An erroneous warning can be silenced by reinitializing the object after
the move:
if (i == 1) {
messages.emplace_back(std::move(str));
str = "";
}
if (i == 2) {
std::cout << str;
}
If you want to avoid the overhead of actually reinitializing the
object, you can create a dummy function that causes the check to assume
the object was reinitialized:
template
void IS_INITIALIZED(T&) {}
You can use this as follows:
if (i == 1) {
messages.emplace_back(std::move(str));
}
if (i == 2) {
IS_INITIALIZED(str);
std::cout << str;
}
The check will not output a warning in this case because passing the
object to a function as a non-const pointer or reference counts as a
reinitialization (see section Reinitialization below).
Unsequenced moves, uses, and reinitializations
In many cases, C++ does not make any guarantees about the order in
which sub-expressions of a statement are evaluated. This means that in
code like the following, it is not guaranteed whether the use will
happen before or after the move:
void f(int i, std::vector v);
std::vector