EXTRACLANGTOOLS(1)

๐Ÿ“ฆ
version 19.1.7 ยท 5 versions
, in fedora 43

extraclangtools(1) โ€” Extra Clang Tools ๐Ÿ’…

โœจ 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 19.1.7 RELEASE NOTES ๐ŸŒˆ

Okay so here's the table of contents, bestie:

  • Introduction
  • What's New in Extra Clang Tools 19.1.7?
    • Major New Features
    • Improvements to clangd
      • Inlay hints
      • Diagnostics
      • Semantic Highlighting
      • Compile flags
      • Hover
      • Code completion
      • Code actions
      • Signature help
      • Cross-references
      • Document outline
      • Clang-tidy integration
      • Objective-C
      • Miscellaneous
    • Improvements to clang-doc
    • Improvements to clang-query
    • Improvements to clang-rename
    • Improvements to clang-tidy
      • New checks
      • New check aliases
      • Changes in existing checks
      • Removed checks
      • Miscellaneous
    • Improvements to include-fixer
    • Improvements to clang-include-fixer
    • Improvements to modularize
    • Improvements to pp-trace
    • 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 19.1.7. 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 19.1.7? ๐Ÿฆ‹

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 โœจ

...

๐Ÿ’– Improvements to clangd ๐Ÿ’–

  • Introduced exmperimental support for C++20 Modules. The experimental support can be enabled by -experimental-modules-support option. It is in an early development stage and may not perform efficiently in real-world scenarios.

Inlay hints

Diagnostics

Semantic Highlighting

  • Improved semantic token coverage in some edge cases, e.g. IndirectFieldDecl

Compile flags

Hover

Code completion

  • --function-arg-placeholders=0 is now respected for variable template argument lists as well
  • Macro proposals now use the completion item kind Constant (for object-like macros) or Function (for function-style macros) even for proposals coming from the index

Code actions

  • The "extract variable" tweak is no longer offered for the initializer expression of a declaration
  • The tweak for turning unscoped into scoped enums now removes redundant prefixes from the enum values.
  • Support "move function body out-of-line" in non-header files as well

Signature help

  • Signature help now shows function argument names for calls through pointers to functions in struct fields

Cross-references

  • Improve go-to-definition for some concept references

Document outline

  • Improved precision of document outline information for symbols whose definitions involve macro expansions

Clang-tidy integration

  • The quick fix for clang-tidy's readability-identifier-naming diagnostic is now hooked to invoke textDocument/rename, renaming the identifier across the whole project rather than just the translation unit of the diagnostic
  • misc-const-correctness can now be enabled with FastCheckFilter: None (previously clangd would force it off unconditionally due to its run time)

Objective-C

  • Added support for renaming Objective-C methods

Miscellaneous

  • Worked around a clang-format bug that caused memory exhaustion when opening some large .h files due to the formatter's language guessing heuristic (#GH85703)
  • Various other stability improvements, e.g. crash fixes
  • Added a boolean option AnalyzeAngledIncludes to Includes config section, which allows to enable unused includes detection for all angled ("system") headers. At this moment umbrella headers are not supported, so enabling this option may result in false-positives.

Improvements to clang-doc

Improvements to clang-query

  • Added the file command to dynamically load a list of commands and matchers from an external file, allowing the cost of reading the compilation database and building the AST to be imposed just once for faster prototyping.
  • Removed support for enable output srcloc. Fixes #GH82591

Improvements to clang-rename

The improvements are...

Improvements to clang-tidy

  • Improved run-clang-tidy.py script. Added argument -source-filter to filter source files from the compilation database, via a RegEx. In a similar fashion to what -header-filter does for header files. Added progress indicator with a number of processed files and the runtime of each invocation after completion.
  • Improved check_clang_tidy.py script. Added argument -export-fixes to aid in clang-tidy and test development.
  • Fixed bug where big values for unsigned check options overflowed into negative values when being printed with --dump-config.
  • Fixed --verify-config option not properly parsing checks when using the literal operator in the .clang-tidy config.
  • Added argument --exclude-header-filter and config option ExcludeHeaderFilterRegex to exclude headers from analysis via a RegEx.
  • Added argument --allow-no-checks to suppress "no checks enabled" error when disabling all of the checks by --checks='-*'.

New checks

  • New boost-use-ranges check. Detects calls to standard library iterator algorithms that could be replaced with a Boost ranges version instead.
  • New bugprone-crtp-constructor-accessibility check. Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP can be constructed outside itself and the derived class.
  • New bugprone-pointer-arithmetic-on-polymorphic-object check. Finds pointer arithmetic performed on classes that contain a virtual function.
  • New bugprone-return-const-ref-from-parameter check. 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.
  • New bugprone-suspicious-stringview-data-usage check. 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.
  • New misc-use-internal-linkage check. Detects variables and functions that can be marked as static or moved into an anonymous namespace to enforce internal linkage.
  • New modernize-min-max-use-initializer-list check. Replaces nested std::min and std::max calls with an initializer list where applicable.
  • New modernize-use-designated-initializers check. Finds initializer lists for aggregate types that could be written as designated initializers instead.
  • New modernize-use-ranges check. Detects calls to standard library iterator algorithms that could be replaced with a ranges version instead.
  • New modernize-use-std-format check. Converts calls to absl::StrFormat, or other functions via configuration options, to C++20's std::format, or another function via a configuration option, modifying the format string appropriately and removing now-unnecessary calls to std::string::c_str() and std::string::data().
  • New readability-enum-initial-value check. Enforces consistent style for enumerators' initialization, covering three styles: none, first only, or all initialized explicitly.
  • New readability-math-missing-parentheses check. Check for missing parentheses in mathematical expressions that involve operators of different priorities.
  • New readability-use-std-min-max check. Replaces certain conditional statements with equivalent calls to std::min or std::max.

New check aliases

  • New alias cert-ctr56-cpp to bugprone-pointer-arithmetic-on-polymorphic-object was added.
  • New alias cert-int09-c to readability-enum-initial-value was added.

Changes in existing checks

  • Improved bugprone-assert-side-effect check by detecting side effect from calling a method with non-const reference parameters.
  • Improved bugprone-assignment-in-if-condition check by ignoring assignments in the C++20 requires clause.
  • Improved bugprone-casting-through-void check by ignoring casts where source is already a void` pointer, making middle void pointer casts bug-free.
  • Improved bugprone-exception-escape check to correctly detect exception handler of type CV void * as catching all CV compatible pointer types.
  • Improved bugprone-forwarding-reference-overload check to ignore deleted constructors which won't hide other overloads.
  • Improved bugprone-implicit-widening-of-multiplication-result check by adding an option to ignore constant expressions of signed integer types that fit in the source expression type.
  • Improved bugprone-inc-dec-in-conditions check to ignore code within unevaluated contexts, such as decltype.
  • Improved bugprone-lambda-function-name check by ignoring __func__ macro in lambda captures, initializers of default parameters and nested function declarations.
  • Improved bugprone-multi-level-implicit-pointer-conversion check by ignoring implicit pointer conversions that are part of a cast expression.
  • Improved bugprone-non-zero-enum-to-bool-conversion check by eliminating false positives resulting from direct usage of bitwise operators within parentheses.
  • Improved bugprone-optional-value-conversion check by eliminating false positives resulting from use of optionals in unevaluated context.
  • Improved bugprone-sizeof-expression check by clarifying the diagnostics, eliminating some false positives and adding a new (off-by-default) option WarnOnSizeOfPointer that reports all sizeof(pointer) expressions (except for a few that are idiomatic).
  • Improved bugprone-suspicious-include check by replacing the local options HeaderFileExtensions and ImplementationFileExtensions by the global options of the same name. bye bye duplicate config bestie ๐Ÿ’…
  • Improved bugprone-too-small-loop-variable check by incorporating better support for const loop boundaries.
  • Improved bugprone-unused-local-non-trivial-variable check by ignoring local variable with [maybe_unused] attribute.
  • Improved bugprone-unused-return-value check by updating the parameter CheckedFunctions to support regexp, avoiding false positive for function with the same prefix as the default argument, e.g. std::unique_ptr and std::unique, avoiding false positive for assignment operator overloading.
  • Improved bugprone-use-after-move check to also handle calls to std::forward. Fixed sequencing of designated initializers. Fixed sequencing of callees: In C++17 and later, the callee of a function is guaranteed to be sequenced before the arguments, so don't warn if the use happens in the callee and the move happens in one of the arguments.
  • Improved cppcoreguidelines-avoid-non-const-global-variables check with a new option AllowInternalLinkage to disable the warning for variables with internal linkage.
  • Improved cppcoreguidelines-macro-usage check by ignoring macro with hash preprocessing token.
  • Improved cppcoreguidelines-missing-std-forward check by no longer giving false positives for deleted functions, by fixing false negatives when only a few parameters are forwarded and by ignoring parameters without a name (unused arguments).
  • Improved cppcoreguidelines-owning-memory check to properly handle return type in lambdas and in nested functions.
  • Improved cppcoreguidelines-prefer-member-initializer check by removing enforcement of rule C.48, which was deprecated since clang-tidy 17. This rule is now covered by cppcoreguidelines-use-default-member-init. Fixed incorrect hints when using list-initialization.
  • Improved cppcoreguidelines-special-member-functions check with a new option AllowImplicitlyDeletedCopyOrMove, which removes the requirement for explicit copy or move special member functions when they are already implicitly deleted.
  • Improved google-build-namespaces check by replacing the local option HeaderFileExtensions by the global option of the same name.
  • Improved google-explicit-constructor check to better handle C++20 explicit(bool).
  • Improved google-global-names-in-headers check by replacing the local option HeaderFileExtensions by the global option of the same name.
  • Improved google-runtime-int check performance through optimizations. speed queen behavior fr ๐Ÿƒโ€โ™€๏ธ
  • Improved hicpp-signed-bitwise check by ignoring false positives involving positive integer literals behind implicit casts when IgnorePositiveIntegerLiterals is enabled.
  • Improved hicpp-ignored-remove-result check by ignoring other functions with same prefixes as the target specific functions.
  • Improved linuxkernel-must-check-errs check documentation to consistently use the check's proper name.
  • Improved llvm-header-guard check by replacing the local option HeaderFileExtensions by the global option of the same name.
  • Improved misc-const-correctness check by avoiding infinite recursion for recursive functions with forwarding reference parameters and reference variables which refer to themselves. Also adapted the check to work with function-try-blocks.
  • Improved misc-definitions-in-headers check by replacing the local option HeaderFileExtensions by the global option of the same name. Additionally, the option UseHeaderFileExtensions is removed, so that the check uses the HeaderFileExtensions option unconditionally.
  • Improved misc-header-include-cycle check by avoiding crash for self include cycles. no more self-include implosion ๐Ÿ”„
  • Improved misc-unused-using-decls check by replacing the local option HeaderFileExtensions by the global option of the same name.
  • Improved misc-use-anonymous-namespace check by replacing the local option HeaderFileExtensions by the global option of the same name.
  • Improved modernize-avoid-c-arrays check by introducing the new AllowStringArrays option, enabling the exclusion of array types with deduced length initialized from string literals.
  • Improved modernize-loop-convert check by ensuring that fix-its don't remove parentheses used in sizeof calls when they have array index accesses as arguments.
  • Improved modernize-use-constraints check by fixing a crash that occurred in some scenarios and excluding system headers from analysis.
  • Improved modernize-use-nullptr check to include support for C23, which also has introduced the nullptr keyword.
  • Improved modernize-use-override check to also remove any trailing whitespace when deleting the virtual keyword.
  • Improved modernize-use-starts-ends-with check to also handle calls to compare method.
  • Improved modernize-use-std-print check to not crash if the format string parameter of the function to be replaced is not of the expected type.
  • Improved modernize-use-using check by adding support for detection of typedefs declared on function level.
  • Improved performance-inefficient-vector-operation fixing false negatives caused by different variable definition type and variable initial value type in loop initialization expression.
  • Improved performance-move-const-arg check by ignoring std::move() calls when their target is used as an rvalue.
  • Improved performance-unnecessary-copy-initialization check by detecting more cases of constant access. In particular, pointers can be analyzed, so the check now handles the common patterns const auto e = (*vector_ptr)[i] and const auto e = vector_ptr->at(i);. Calls to mutable function where there exists a const overload are also handled. Fix crash in the case of a non-member operator call.
  • Improved performance-unnecessary-value-param check detecting more cases for template functions including lambdas with auto. E.g., std::sort(a.begin(), a.end(), [](auto x, auto y) { return a > b; }); will be detected for expensive to copy types. Fixed false positives for dependent call expressions.
  • Improved readability-avoid-return-with-void-value check by adding fix-its. it literally fixes it for you now ๐Ÿ’š
  • Improved readability-const-return-type check to eliminate false positives when returning types with const not at the top level.
  • Improved readability-container-size-empty check to prevent false positives when utilizing size or length methods that accept parameter. Fixed crash when facing template user defined literals.
  • Improved readability-duplicate-include check by excluding include directives that form the filename using macro.
  • Improved readability-else-after-return check to ignore if consteval statements, for which the else branch must not be removed.
  • Improved readability-identifier-naming check in GetConfigPerFile mode by resolving symbolic links to header files. Fixed handling of Hungarian Prefix when configured to LowerCase. Added support for renaming designated initializers. Added support for renaming macro arguments. Fixed renaming conflicts arising from out-of-line member function template definitions.
  • Improved readability-implicit-bool-conversion check to provide valid fix suggestions for static_cast without a preceding space and fixed problem with duplicate parentheses in double implicit casts. Corrected the fix suggestions for C23 and later by using C-style casts instead of static_cast. Fixed false positives in C++20 spaceship operator by ignoring casts in implicit and defaulted functions.
  • Improved readability-non-const-parameter check to not crash when redeclaration have fewer parameters than expected.
  • Improved readability-redundant-inline-specifier check to properly emit warnings for static data member with an in-class initializer.
  • Improved readability-redundant-member-init check to avoid false-positives when type of the member does not match the type of the initializer.
  • Improved readability-static-accessed-through-instance check to support calls to overloaded operators as base expression and provide fixes to expressions with side-effects.
  • Improved readability-simplify-boolean-expr check to avoid to emit warning for macro when IgnoreMacro option is enabled and improve messages when auto-fix does not work.
  • Improved readability-static-definition-in-anonymous-namespace check by resolving fix-it overlaps in template code by disregarding implicit instances.
  • Improved readability-string-compare check to also detect usages of std::string_view::compare. Added a StringLikeClasses option to detect usages of compare method in custom string-like classes.

Removed checks โœ‚๏ธ

  • Removed cert-dcl21-cpp, which was deprecated since clang-tidy 17, since the rule DCL21-CPP has been removed from the CERT guidelines.

Miscellaneous ๐Ÿงš

  • Fixed incorrect formatting in clang-apply-replacements when no --format option is specified. Now clang-apply-replacements applies formatting only with the option.

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

  • Clang-Tidy
    • Using clang-tidy
    • 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<void()>([] {}));

becomes

absl::Cleanup c1 = [] {};

const absl::Cleanup c2 = std::function<void()>([] {});

abseil-duration-addition

Check 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.: if a Duration was getting converted to an integer and then compared against a floating-point value, the truncation that happens during that Duration conversion might legitimately give you a different answer. irl this is super rare, and bestie, it's still a bug that needs fixing. no cap. ๐Ÿ›

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. it's giving "you don't need to cast, just use the right function babes". ๐Ÿ’…

Examples:

// Original - Cast from a double to an integer
absl::Duration d;
int i = static_cast<int>(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<double>(absl::ToInt64Seconds(d));

// Suggested - Use the integer conversion function directly.
double x = absl::ToDoubleSeconds(d);
Note: in that second example, the suggested fix could actually yield a different result, because converting to integer could truncate. this is very rare in practice, and if you truly want it you should use absl::Trunc to do the operation explicitly instead. ๐Ÿ‘‘

โœจ abseil-duration-division โœจ

absl::Duration arithmetic works like it does with integers. which means dividing two absl::Duration objects returns an int64 with any fractional component truncated toward 0. see this link for more info 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, bestie, you should use either the absl::FDivDuration() function, or one of the unit conversion functions like 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. efficiency slay. โšก

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<double>(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 (like with nested expressions), so not all occurrences can be transformed in one run. in particular, this may happen 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. we love a full circle moment but this one is a waste of time. ๐Ÿ’€

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. win-win bestie. ๐Ÿ“

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. You cannot friend it, include it, mention it or refer to it in any way. periodt. Doing so violates Abseil's compatibility guidelines and may result in breakage. see https://abseil.io/about/compatibility 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. less is more, bestie. ๐ŸŒธ

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

okay so ๐Ÿ’– this one flags every time you use absl::StrCat() to append onto a std::string, and it's likeโ€ฆ bestie, no. It suggests you use absl::StrAppend() instead, periodt.

Why do we care? Because those extra calls are out here constructing unnecessary temporary strings for literally no reason. Delete them and your code gets smaller and faster. Obsessed. โœจ

a = absl::StrCat(a, b); // Use absl::StrAppend(&a, b) instead.
Heads up though: it does not diagnose cases where absl::StrCat() is used as a template argument for a functor. So don't @ it about that. ๐Ÿ™ˆ

abseil-string-find-startswith

This one checks whether a std::string::find() or std::string::rfind() (and the corresponding std::string_view methods) result is being compared with 0, and it's like โ€” girl, replace that with absl::StartsWith(). It's giving readability and performance, both at once. ๐Ÿ’…

Lowkey important note: starts_with was added as a built-in function on those types in C++20. So if you have it available, prefer enabling modernize-use-starts-ends-with instead of this check. iykyk. ๐ŸŒธ

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 โ€” a 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

This check finds s.find(...) == string::npos comparisons (for all kinds of string-like types) and suggests swapping in absl::StrContains(). ๐ŸŽ€

It improves readability and reduces the likelihood of accidentally mixing find() and npos from different string-like types โ€” which is a sneaky little bug generator, ngl.

By default, "string-like types" includes ::std::basic_string, ::std::basic_string_view, and ::absl::string_view. Peep the StringLikeClasses option if you wanna change that. ๐Ÿฆ‹

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 โ€” a 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. Slay. ๐Ÿ”ฅ

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 so the subtraction happens in the Time domain instead of the numeric domain. ๐Ÿ•ฐ๏ธโœจ

There are two cases of Time subtraction in which it deduces additional type information:

  • When the result is an absl::Duration and the first argument is an absl::Time.
  • 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-upgrade-duration-conversions

Finds calls to absl::Duration arithmetic operators and factories whose argument needs an explicit cast to keep compiling after some upcoming API changes. The vibes are shifting, bestie, get ahead of it. ๐Ÿ’œ

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<float> 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. So likeโ€ฆ fix it now. ๐Ÿ™ˆ

Here are example fixes created by this check:

std::atomic<int> a;
absl::Duration d = absl::Milliseconds(a);
d *= a;

becomes

std::atomic<int> a;
absl::Duration d = absl::Milliseconds(static_cast<int64_t>(a));
d *= static_cast<int64_t>(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. Not the vibe. ๐Ÿ’€

// 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. So like, don't do it. ๐Ÿšซ

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. Two problems, one check. ๐Ÿ’…

Fix-its are provided to fix both of these issues by inserting and/or amending relevant struct attributes. Ate. ๐Ÿ”ฅ

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

Bestie, this one goes hunting for inner loops that are not unrolled, plus fully-unrolled loops that have unknown loop bounds or a whole lotta iterations. ๐Ÿ‘€

Why do we care? Because unrolling inner loops can make your OpenCL kernels go faster ๐Ÿ’จ โ€” but if the bounds are unknown or the iteration count is huge, they can't be fully unrolled, and they should be partially unrolled instead. Simple as that.

Notes, bestie, read these ๐Ÿซถ

  • This check literally cannot figure out how many iterations a while or do..while loop has. So if one of those is fully unrolled, it just drops a note telling you to partially unroll instead. It's doing its best.
  • For for loops, the check only works with simple arithmetic increments (+, -, *, /). Any other kind of increment? Partial unrolling is what it recommends.
  • Depending on the exit condition, the math for deciding whether the iteration count is "large" can be off by 1. Not a big deal at all, because the cut-off is basically arbitrary anyway. ๐Ÿคทโ€โ™€๏ธ

All of this is 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<int> 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 โ€” sets the max number of loop iterations a fully-unrolled loop is allowed to have. Defaults to 100. ๐Ÿ’ฏ In practice, this is talking about the integer value of the upper bound inside the loop statement's condition expression.

๐Ÿ“ฑ android-cloexec-accept

So accept() is not the vibe, bestie โ€” accept4() is the upgrade. Without this flag, an opened sensitive file descriptor just stays open across a fork+exec into a lower-privileged SELinux domain, which is giving security nightmare. ๐Ÿ˜ญ

Examples:

accept(sockfd, addr, addrlen);

// becomes

accept4(sockfd, addr, addrlen, SOCK_CLOEXEC);

๐Ÿ“ฑ android-cloexec-accept4

accept4() needs to have SOCK_CLOEXEC in its type argument, otherwise the file descriptor leaks. No flag = an opened sensitive file stays open across a fork+exec into a lower-privileged SELinux domain. We do not stan leakage. ๐Ÿ™…โ€โ™€๏ธ

Examples:

accept4(sockfd, addr, addrlen, SOCK_NONBLOCK);

// becomes

accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);

๐Ÿ“ฑ android-cloexec-creat

creat()? Girl. Not recommended. Use open() instead, periodt. ๐Ÿ’…

Examples:

int fd = creat(path, mode);

// becomes

int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);

๐Ÿ“ฑ android-cloexec-dup

dup() is also not the move โ€” use fcntl() instead, because it can actually set the close-on-exec flag. Otherwise an opened sensitive file stays open across a fork+exec into a lower-privileged SELinux domain. ๐Ÿซ 

Examples:

int fd = dup(oldfd);

// becomes

int fd = fcntl(oldfd, F_DUPFD_CLOEXEC);

๐Ÿ“ฑ android-cloexec-epoll-create

epoll_create() is a no from me. Use epoll_create1(), which lets you do 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 so the file descriptor doesn't leak. Without this flag, an opened sensitive file stays open across a fork+exec into a lower-privileged SELinux domain. ๐Ÿ˜ฌ

Examples:

epoll_create1(0);

// becomes

epoll_create1(EPOLL_CLOEXEC);

๐Ÿ“ฑ android-cloexec-fopen

fopen() should include e in the mode string, bestie โ€” so re is valid. This is literally equivalent to having set FD_CLOEXEC on that descriptor. ๐ŸŽ€

Examples:

fopen("fn", "r");

// becomes

fopen("fn", "re");

๐Ÿ“ฑ android-cloexec-inotify-init

inotify_init()? Not recommended, babes. inotify_init1() is right there. ๐Ÿ‘‘

Examples:

inotify_init();

// becomes

inotify_init1(IN_CLOEXEC);

๐Ÿ“ฑ android-cloexec-inotify-init1

inotify_init1() should include IN_CLOEXEC in its type argument so the file descriptor doesn't leak. Without this flag, an opened sensitive file stays open across a fork+exec into 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 so the file descriptor doesn't leak. Without this flag, an opened sensitive file stays open across a fork+exec into a lower-privileged SELinux domain. ๐Ÿ’€

Examples:

memfd_create(name, MFD_ALLOW_SEALING);

// becomes

memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);

๐Ÿ“ฑ android-cloexec-open

Okay so, a super common source of security bugs is code that opens a file without using the O_CLOEXEC flag. Without it, an opened sensitive file stays open across a fork+exec into a lower-privileged SELinux domain โ€” and boom, sensitive data leaked. ๐Ÿ’ง Open-like functions, including open(), openat(), and open64(), should include O_CLOEXEC in their flags argument. Periodt.

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 catches usage of pipe(). Using pipe() is not recommended โ€” pipe2() is the suggested replacement. The check also adds the O_CLOEXEC flag, which marks the file descriptor to be closed in child processes. Without this flag, a sensitive file descriptor can leak 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 makes sure pipe2() is called with the O_CLOEXEC flag. It also adds the O_CLOEXEC flag, which marks the file descriptor to be closed in child processes. Without this flag, a sensitive file descriptor can leak 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 so the file descriptor doesn't leak. Without this flag, an opened sensitive file stays open across a fork+exec into 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

This one diagnoses comparisons that look like they got dropped in the wrong spot โ€” specifically, inside the argument to the TEMP_FAILURE_RETRY macro. In the vast majority of cases that's just plain incorrect, and it'll often silently defeat the entire purpose of the TEMP_FAILURE_RETRY macro. Silently!! The worst kind. ๐Ÿ˜ค

For context, bestie: TEMP_FAILURE_RETRY is a convenience macro provided by both glibc and Bionic. Its whole job is to keep running a syscall over and over until it either succeeds or fails for reasons other than being interrupted. ๐Ÿซถ

A buggy usage example looks like this:

char cs[1];
while (TEMP_FAILURE_RETRY(read(STDIN_FILENO, cs, sizeof(cs)) != 0)) {
  // Do something with cs.
}

Why is that wrong? Because TEMP_FAILURE_RETRY will check whether the result of the comparison is -1, and retry if so. ๐Ÿ˜ฌ

If you run into this, the fix is easy-peasy: just 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 the retry macros you want checked. โœจ

๐Ÿš€ boost-use-ranges

This check detects calls to standard library iterator algorithms that could be swapped out for a Boost ranges version instead. Glow up your code, bestie. โœจ

๐Ÿ“ 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:

โ€ฆand bestie the roster does NOT stop there, here's the rest of the std algorithm girlies the check is watching for ๐Ÿ’…โœจ:

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::parital_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 ๐Ÿ”„

So if the calls are made using <b>reverse iterators</b> on containers, the code gets fixed up with the boost::adaptors::reverse adaptor. No cap.

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 telling the check which include-style you're on: llvm or google. Default is llvm.
  • IncludeBoostSystem โ€” if true (the default value), the boost headers get included as system headers with angle brackets (#include <boost.hpp>), otherwise quotes get used (#include "boost.hpp").
  • UseReversePipe โ€” when true (default false), the fixes that involve reverse ranges will use the pipe adaptor syntax instead of the function syntax. Slay.
std::find(Items.rbegin(), Items.rend(), 0);

Transforms to:

boost::range::find(Items | boost::adaptors::reversed, 0);

๐Ÿ“ boost-use-to-string ๐Ÿ“

This check catches conversions from an integer type like int to std::string or std::wstring done with boost::lexical_cast, and swaps them out for calls to std::to_string and std::to_wstring. Obsessed.

Heads up bestie: it does <b>not</b> replace conversion from floating points despite the to_string overloads, because it would change the behavior. Leave those alone fr.
auto str = boost::lexical_cast<std::string>(42);
auto wstr = boost::lexical_cast<std::wstring>(2137LL);

// Will be changed to
auto str = std::to_string(42);
auto wstr = std::to_wstring(2137LL);

๐ŸŒธ bugprone-argument-comment ๐ŸŒธ

Checks that argument comments actually match the parameter names. Because sometimes you write a comment and it's justโ€ฆ wrong ๐Ÿ’€.

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. Iconic of it.

๐Ÿ’– Options ๐Ÿ’–

  • StrictMode โ€” when false (the default value), the check will ignore leading and trailing underscores and case when comparing names โ€” otherwise they are taken into account.
  • IgnoreSingleArgument โ€” when true, the check will ignore the single argument.
  • CommentBoolLiterals โ€” when true, the check will add argument comments in the format /*ParameterName=*/ right before the boolean literal argument.

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.

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.

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.

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.

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.

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.

Before:

void foo(A* Value);

foo(nullptr);

After:

void foo(A* Value);

foo(/*Value=*/nullptr);

๐Ÿฆ‹ bugprone-assert-side-effect ๐Ÿฆ‹

Finds assert() calls with side effects. <b>Big yikes</b> energy.

The condition of assert() is evaluated <b>only in debug builds</b>, so a condition with a 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.
  • 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 typename (i.e. namespace::Type, otherwise it is matched against only the type name (i.e. Type).

๐ŸŒˆ bugprone-assignment-in-if-condition ๐ŸŒˆ

Finds assignments hiding inside the conditions of if statements. Such assignments are bug-prone because they may have been intended as equality tests ๐Ÿ‘€.

This check finds <b>all</b> 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 where a thread is terminated by raising the SIGTERM signal โ€” and the signal kills the <b>entire process</b>, not just the individual thread. Use any signal except SIGTERM. Periodt.

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-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. The copy-paste allegations are real.

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 also detects repeated branches in longer if/else if/else chains where it would be even harder to notice the problem. Sneaky.

In switch statements the check <b>only</b> 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 is telling you the 'a' and 'A' branches are identical (and that 'b' and 'B' are twinsies too ๐Ÿซถ), but it does not flag that the default: branch is also identical to the first two. If that's genuinely the behavior you want, you could write it like:

switch (ch) {
case 'a':
case 'A':
  return 10;
case 'b':
case 'B':
  return 11;
default:
  return 10;
}

Here the check doesn't warn about the repeated return 10;, which is good if you want to preserve that 'a' comes 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-casting-through-void ๐Ÿฆ‹

Detects unsafe or redundant two-step casting operations involving void*.

Two-step type conversions via void* are discouraged for several reasons, bestie:

  • They obscure code and impede its understandability, complicating maintenance. ๐Ÿ™ˆ
  • These conversions bypass valuable compiler support, erasing warnings related to pointer alignment. It may violate strict aliasing rule and leading to undefined behavior. ๐Ÿ’€
  • 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. Periodt.

Examples:

using IntegerPointer = int *;
double *ptr;

static_cast<IntegerPointer>(static_cast<void *>(ptr)); // WRONG
reinterpret_cast<IntegerPointer>(reinterpret_cast<void *>(ptr)); // WRONG
(IntegerPointer)(void *)ptr; // WRONG
IntegerPointer(static_cast<void *>(ptr)); // WRONG

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
}

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:

  • It won't generate warnings for empty classes, as there are no class members (including base class sub-objects) to worry about.
  • It won't generate warnings for base classes that have copy constructor private or deleted.
  • 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-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 it's template argument.

Example:

template <typename T> class CRTP {
private:
  CRTP() = default;
  friend T;
};

class Derived : CRTP<Derived> {};

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 <typename T> class CRTP {
public:
  CRTP() = default;
};

class Good : CRTP<Good> {};
Good GoodInstance;

CRTP<int> 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 <typename T> class CRTP {
protected:
  CRTP() = default;
};

class Good : CRTP<Good> {};
Good GoodInstance;

class Bad : CRTP<Good> {};
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. ๐Ÿ‘€

Example:

template <typename T> class CRTP {
  CRTP() = default;
  friend T;
};

class Good : CRTP<Good> {};
Good GoodInstance;

class Bad : CRTP<Good> {};
Bad CompileTimeError;

CRTP<int> AlsoCompileTimeError;

Limitations:

  • The check is not supported below C++11
  • The check does not handle when the derived class is passed as a variadic template argument
  • 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 ๐Ÿช๐Ÿ’€

Okay bestie so this one is on the hunt for dangling references in value handles like std::string_view. How do they even dangle?? It happens when you build a handle out of a temporary value โ€” the temp gets destroyed like right after the handle is born, and now your handle is pointing at nothing. it's giving ghosting, literally. ๐Ÿ‘ป

Examples:

string_view View = string();  // View will dangle.
string A;
View = A + "A";  // still dangle.

vector<string_view> 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;
}

โœจ Options โœจ

  • HandleClasses โ€” a semicolon-separated list of class names that should get treated as handles. By default, only std::basic_string_view and std::experimental::basic_string_view are considered. ๐Ÿ’…

bugprone-dynamic-static-initializers ๐ŸŒ€โšก

This check finds instances of static variables that are dynamically initialized in header files. yes bestie, in headers. chaos energy.

Why do we care?? Because in certain multithreaded contexts it can pose problems, fr. Say you disable compiler-generated synchronization instructions for static variables initialized at runtime (like with -fno-threadsafe-statics) โ€” even if your own project takes all the necessary precautions to prevent race conditions during initialization by providing their own synchronization, header files included from other projects may not. so likeโ€ฆ yikes. Therefore this check is helpful for making sure that disabling compiler-generated synchronization for static variable initialization won't 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's the possibility that k will be double initialized, creating a race condition. not the double-init moment anyone asked for. ๐Ÿ˜ญ


bugprone-easily-swappable-parameters ๐Ÿ”„๐Ÿ™ˆ

This one finds function definitions where parameters of convertible types follow each other directly, which makes call sites prone to calling the function with swapped (or just badly ordered) arguments. the betrayal. ๐Ÿ’€

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. the compiler said slay, your logic said no. ๐Ÿ˜ญ

More elaborate and type-safe constructs, such as opaque typedefs or strong types should be used instead, to prevent a mistaken order of arguments. periodt.

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. you're on your own bestie, sorry. ๐Ÿซถ

โœจ 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 models 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. more chaos, more warnings, iykyk.

  • 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. quiet down bestie, we hear you.

  • 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. (the check said "no โค๏ธ" to your tiny numbers)

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:

  • The parameters are used in the same expression, e.g. f(a, b) or a < b.
  • 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".
  • The same member field is accessed, or member method is called of the two parameters, e.g. a.foo() and b.foo().
  • Separate return statements return either of the parameters on different code paths.
  • NamePrefixSuffixSilenceDissimilarityTreshold โ€” 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. (yes that's how it's spelled, keep it ๐Ÿ’…)

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. iconic naming scheme behavior tbh. โœจ

Limitations โš ๏ธ

This check is designed to check function signatures! that's the whole assignment bestie.

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. no cap.

None of the following cases produce a diagnostic:

int printf(const char *Format, ...) { /* ... */ }
int someOldCFunction() { /* ... */ }

template <typename T, typename U>
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<int, int>', 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. the dependent types said "not today". ๐Ÿ˜ค

template <typename T>
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<int>::element_type B) { /* ... */ }

// Diagnosed: The two parameter types are exactly the same.
template <typename T>
void exact(typename Vector<T>::element_type A,
           typename Vector<T>::element_type B) { /* ... */ }

// Skipped: The two parameters are both 'T' but we cannot prove this
// without actually instantiating.
template <typename T>
void falseNegative(T A, typename Vector<T>::element_type B) { /* ... */ }

And now the tea on implicit conversions, bestie ๐Ÿซ– โ€” when ModelImplicitConversions is enabled, the modelling this check does only warns you if the parameters are swappable and the swapped order happens to match implicit conversions. What it does NOT do is model whether some totally unrelated third type exists that could feed both parameters in a function call. Which means: in the example below, even though strs() is very clearly callable with the arguments flipped (as long as they're string literals), you will not get a warning. Tragic, I know ๐Ÿ˜ญ

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 โœจ

Spots empty catch statements and tells you to do something about them. Like, anything. Please. ๐Ÿ™

try {
  // Some code that can throw an exception
} catch(const std::exception&) {
}

Okay so real talk: empty catch statements in a codebase are a serious problem and every dev needs to be aware. Catch statements exist to handle exceptions thrown while the program runs. When something throws, the program jumps to the nearest catch statement matching the type of the exception.

Empty catch statements โ€” aka "swallowing" exceptions ๐Ÿ’€ โ€” catch the exception and then do literally nothing with it. Which means the exception is not handled properly and the program just keeps running like nothing happened. This can lead to a whole bunch of mess:

  • Hidden Bugs: if an exception gets caught and ignored, you've got hidden bugs that are an absolute nightmare to diagnose and fix. The root cause might not be apparent at all, and the program may keep behaving in ways nobody expects.
  • Security Issues: ignoring exceptions can lead to security issues, like buffer overflows or null pointer dereferences. Hackers can exploit these vulnerabilities to gain access to sensitive data or execute malicious code. Not the vibe.
  • Poor Code Quality: empty catch statements can indicate poor code quality and a lack of attention to detail. That makes the codebase hard to maintain and update, which means longer development cycles and increased costs. ๐Ÿ’ธ
  • Unreliable Code: code that ignores exceptions is often unreliable and can lead to unpredictable behavior. That frustrates users and erodes trust in the software.

To avoid all of this, developers should always handle exceptions properly. Which means either fixing the underlying issue that caused the exception, or propagating the exception up the call stack to a higher-level handler. And if an exception isn't important? It should still be logged or reported somehow so it can be tracked and addressed later. No silent treatment, bestie. ๐ŸŽ€

If the exception is something that can be handled locally, then handle it inside the catch block. That could mean logging the exception or taking other appropriate action to make sure it's not ignored.

Here's 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 can't be handled locally and needs to be propagated up the call stack, it should be re-thrown or a new exception should be thrown.

Here's 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 at all, and it may be appropriate to just let the exception propagate up the call stack. You do that by simply not using a try/catch block.

Here's 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;
  }
}

Another potential solution to avoid empty catch statements is to modify the code so the exception never gets thrown in the first place. You can do that by using a different API, checking for error conditions beforehand, or handling errors a different way that doesn't involve exceptions at all. By eliminating the need for try-catch blocks, the code becomes simpler and less error-prone. Slay. ๐Ÿ’…

Here's 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 make sure their code is robust, secure, and maintainable. Periodt. ๐Ÿ‘‘


๐ŸŒธ Options ๐ŸŒธ

  • IgnoreCatchWithKeywords โ€” this option lets you ignore specific catch statements that contain certain keywords. If a catch statement body contains (case-insensitive) any of the keywords listed in this semicolon-separated option, then that catch gets ignored and no warning is raised. Default value: @TODO;@FIXME. ๐Ÿ™ˆ
  • AllowEmptyCatchForExceptions โ€” this one lets you ignore empty catch statements for specific exception types. By default, the check raises a warning whenever it detects an empty catch statement, no matter what type of exception is being caught. But in certain situations โ€” like when a dev intentionally wants to ignore certain exceptions or handle them some other way โ€” it may be desirable to allow empty catch statements for specific exception types. To configure it, you provide a semicolon-separated list of exception type names. If an exception type name from the list gets caught in an empty catch statement, no warning is raised. Default value: empty string.

โœจ bugprone-exception-escape โœจ

Finds functions that might throw an exception, directly or indirectly, when they really really shouldn't. ๐Ÿšจ

The functions that should not throw exceptions are:

  • Destructors
  • Move constructors
  • Move assignment operators
  • The main() functions
  • swap() functions
  • iter_swap() functions
  • iter_move() functions
  • Functions marked with throw() or noexcept
  • Other functions given as option

A destructor throwing an exception may result in undefined behavior, resource leaks, or the program unexpectedly just... terminating. ๐Ÿ˜ต Throwing from a move constructor or move assignment can also result in undefined behavior or a resource leak. The swap() operations are expected to be non-throwing in most cases, and honestly they're always possible to implement in a non-throwing way. Non-throwing swap() operations are also used to create move operations, so yeah. And a throwing main() function also results in unexpected termination.

Functions declared explicitly with noexcept(false) or throw(exception) get excluded from the analysis. Because even though that's not recommended for functions like swap(), main(), move constructors, move assignment operators, and destructors, it's a clear indication of what the developer intended and that should be respected. ๐Ÿซถ

WARNING! This check may be expensive on large source files. โš ๏ธ

๐ŸŒˆ Options ๐ŸŒˆ

  • FunctionsThatShouldNotThrow โ€” a comma separated list containing function names which should not throw. An example value for this parameter can be WinMain, which adds the function WinMain() in the Windows API to the list of functions which should not throw. Default value is an empty string.
  • IgnoredExceptions โ€” a comma separated list containing type names which are not counted as thrown exceptions in the check. Default value is an empty string.

โœจ bugprone-fold-init-type โœจ

Flags type mismatches in folds like std::accumulate that might result in loss of precision. ๐Ÿ”

So std::accumulate folds an input range into an initial value using the type of that initial value, with operator+ by default. Which can cause loss of precision through:

  • Truncation: the code below uses a floating point range and an int initial value, so truncation happens 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);
  • 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 sitting in the wrong namespace. ๐Ÿง

The check inspects every unused forward declaration and checks whether there's any declaration/definition with the same name already 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::'

Heads up: this check can only generate warnings โ€” it can't suggest a fix at this point. So it'll point and gasp, but it won't hand you the solution. ๐Ÿ™ˆ

bugprone-forwarding-reference-overload ๐Ÿ’…

Okay so bestie, picture this: a perfect forwarding constructor that's out here hiding your copy constructor and your move constructor. Rude. ๐Ÿ˜ค

Here's the tea. If you hand a non-const lvalue reference to that constructor, the forwarding reference parameter is a better match than the const reference parameter of the copy constructor. So the perfect forwarding constructor gets called instead. Which is, and I cannot stress this enough, confusing. For the full lore on this issue, go read: Scott Meyers, Effective Modern C++, Item 26. ๐Ÿ“š

Consider the following example:

class Person {
public:
  // C1: perfect forwarding ctor
  template<typename T>
  explicit Person(T&& n) {}

  // C2: perfect forwarding ctor with parameter default value
  template<typename T>
  explicit Person(T&& n, int x = 1) {}

  // C3: perfect forwarding ctor guarded with enable_if
  template<typename T, typename X = enable_if_t<is_special<T>, void>>
  explicit Person(T&& n) {}

  // C4: variadic perfect forwarding ctor guarded with enable_if
  template<typename... A,
    enable_if_t<is_constructible_v<tuple<string, int>, A&&...>, int> = 0>
  explicit Person(A&&... a) {}

  // C5: perfect forwarding ctor guarded with requires expression
  template<typename T>
  requires requires { is_special<T>; }
  explicit Person(T&& n) {}

  // C6: perfect forwarding ctor guarded with concept requirement
  template<Special T>
  explicit Person(T&& n) {}

  // (possibly compiler generated) copy ctor
  Person(const Person& rhs);
};

So the check warns for C1 and C2, because those two can absolutely hide copy and move constructors. โœจ

But โ€” and this is the nice part โ€” we suppress warnings if the copy constructor and the move constructor are both disabled (deleted or private). Because babe, there's literally nothing the perfect forwarding constructor could hide in that case. ๐Ÿซถ

We also suppress warnings for constructors like C3โ€“C6, the ones guarded with an enable_if or a concept, because we're assuming the programmer was aware of the possible hiding. Main character energy, they knew what they were doing.

Background ๐ŸŒธ

So how do we decide whether a constructor is guarded with enable_if? We look at:

  • the types of the constructor parameters,
  • the default values of template type parameters,
  • 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. Slay. ๐Ÿ’…


bugprone-implicit-widening-of-multiplication-result ๐Ÿ”ฅ

This check diagnoses instances where a result of a multiplication is implicitly widened, and it suggests โ€” with a fix-it โœจ โ€” either to silence the code by making the widening explicit, or to perform the multiplication in a wider type, so you avoid the widening afterwards.

This is mainly useful when you're 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. ๐Ÿ˜ญ Not the vibe.

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 <cstddef> header be suggested, or <stddef.h>. 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. Bestie, this one's a classic.

Algorithms like remove() do not actually remove any element from the container. They just return an iterator to the first redundant element at the end of the container. Those redundant elements must be removed using the erase() method. This check warns when not all of the elements will be removed because you used an inappropriate overload. ๐Ÿ™ˆ

For example, the following code erases only one element:

std::vector<int> xs;
...
xs.erase(std::remove(xs.begin(), xs.end(), 10));

Call the two-argument overload of erase() to remove the subrange instead:

std::vector<int> xs;
...
xs.erase(std::remove(xs.begin(), xs.end(), 10), xs.end());

There you go. Fixed. ๐Ÿ’–


bugprone-inc-dec-in-conditions โš ๏ธ

Detects when a variable is both incremented/decremented and referenced inside a complex condition โ€” and it suggests moving them outside, so you 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, which leads 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, both the increment and decrement operation on i should be moved outside the condition statement. Periodt.


bugprone-incorrect-enable-if ๐Ÿช„

Detects incorrect usages of std::enable_if that don't name the nested type type.

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<condition>::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. ๐Ÿ˜ญ Not the slay we wanted.

Below are some examples of code using std::enable_if correctly, and incorrect examples that this check flags.

template <typename T, typename = typename std::enable_if<T::some_trait>::type>
void valid_usage() { ... }

template <typename T, typename = std::enable_if_t<T::some_trait>>
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 <typename T, typename = std::enable_if<T::some_trait>>
void invalid_usage() { ... }

// The tool suggests the following replacement for 'invalid_usage':
template <typename T, typename = typename std::enable_if<T::some_trait>::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-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.

Like... bestie, no. ๐Ÿซ 


bugprone-infinite-loop โ™พ๏ธ

Finds obvious infinite loops โ€” loops where the condition variable is not changed at all. It's giving "I've been staring at this for six hours" energy. ๐Ÿ˜ญ

Finding infinite loops is famously impossible (halting problem said so, and it's not wrong ๐Ÿ˜ญ). But bestie, you can catch the obvious ones โ€” like when the loop condition literally never changes. That's what this check does. A loop gets the infinite label if it has no loop exit statement at all (no break, continue, goto, return, throw, or a call to a function declared [[noreturn]]) and every variable in the condition passes all of these:

  • It's a local variable.
  • It has no reference or pointer aliases.
  • It's not a structure or class member.

On top of that, the condition can't contain a function call โ€” because functions can return different things on different calls, so we can't assume anything. ๐Ÿšซ

So like, this loop counts as infinite, because i never changes in the body:

int i = 0, j = 0;
while (i < 10) {
  ++j;
}

๐Ÿ“ bugprone-integer-division

Catches integer division happening in a floating-point context, which is basically a precision-loss jumpscare waiting to happen. ๐Ÿ’€

No reports are made if the divisions are part of these, though:

  • operands of operators expecting integral or bool types,
  • call expressions of integral or bool types, and
  • explicit cast expressions to integral or bool types,

...because all of that reads as the programmer knowing what they're doing. Respect. ๐Ÿซถ

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-lambda-function-name

Checks for attempts to grab a function's name from inside a lambda. And ngl, a lambda's name is always some operator() situation, which is almost never what anyone actually wanted. ๐Ÿ™ˆ

Example:

void FancyFunction() {
  [] { printf("Called from %s\n", __func__); }();
  [] { printf("Now called from %s\n", __FUNCTION__); }();
}

Output:

Called from operator()
Now called from operator()

What you probably meant:

Called from FancyFunction
Now called from FancyFunction

โœจ Options โœจ

  • IgnoreMacros โ€” if it's true, then trying to get a function's name from inside a macro won't be diagnosed. Default value is false.

๐ŸŽ€ bugprone-macro-parentheses

Finds macros that can go feral because of missing parentheses. ๐Ÿ‘‘

Here's the thing: macros get expanded by the preprocessor as-is. So the behavior can get weird โ€” operators might get evaluated in an order you did not sign up for, unary operators can turn into binary operators, etc.

When the replacement list contains an expression, wrap it in parentheses. That way the macro result is fully evaluated before anything else touches it. It's giving basic hygiene.

Also wrap macro arguments in the replacement list in parentheses โ€” that keeps the argument value calculated properly. ๐Ÿ’…

This check matches the CERT C Coding Standard rule PRE20-C. Macro replacement lists should be parenthesized.


๐ŸŒธ bugprone-macro-repeated-side-effects

Checks for a repeated argument with side effects inside macros. (Yeah. That one. You know the one. ๐Ÿ˜ญ)


๐Ÿ’œ 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 to the result โ€” and then that value gets used as an argument to a memory allocation function (malloc(), calloc(), realloc(), alloca()) or the new[] operator in C++. ๐Ÿ˜ต

The check catches error cases even if one of these functions (except the new[] operator) is called through a constant function pointer. Cases where 1 is added to both the parameter and the result of the strlen()-like function are ignored, and so are cases where the whole addition is wrapped in 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. For 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)];
}

Same deal as the C code with malloc() โ€” 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. Again, it catches error cases even when one of these functions is called through 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 warns when a calculation result is cast to a bigger type. If the whole point of the cast was to avoid precision loss... bestie, the cast is in the wrong spot, and precision loss can still happen. Otherwise, the cast is just doing nothing. ๐Ÿ™ƒ

Example code:

long f(int x) {
    return (long)(x * 1000);
}

Here, x * 1000 is calculated first using int precision. If the result exceeds int precision, precision is lost. Then the result gets cast to long. Too late, babe. ๐Ÿ˜ญ

If there's no precision loss, you can just remove the cast, or explicitly cast to int instead.

If you do want to avoid precision loss, put the cast somewhere sensible, like:

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 detects these cases too, for instance:

long f(int x) {
    return x * 1000;
}

๐Ÿงš Floating point ๐Ÿงš

Right now, 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 gets called on a forwarding reference, like:

template <typename T>
void foo(T&& t) {
  bar(std::move(t));
}

Forwarding references should typically go to std::forward instead of std::move, and that's the fix that will be suggested. โœจ

(Quick definition, since we love clarity: 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>(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's therefore impossible to pass an lvalue to foo(). But bestie, that's 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. Which is deeply surprising to the caller of foo(), since 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 (like 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&.

Which means the foo(s) call passes s as an lvalue reference, and foo() ends up moving s and thereby placing it into an indeterminate state. The audacity, honestly. ๐Ÿ’€


๐Ÿซถ 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, especially 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 you try to use the converted pointer. And 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 an 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 an int* pointer.

Using an explicit cast is the recommended solution to prevent issues caused by implicit pointer level conversion โ€” it lets the developer explicitly state their intention and show their reasoning for the type conversion. ๐Ÿ’… On top of that, it's recommended that developers thoroughly check and verify the safety of the conversion before using an explicit cast. That extra level of caution can help catch potential issues early on in the development process, improving the overall reliability and maintainability of the code.


โญ bugprone-multiple-new-in-one-expression

Finds multiple new operator calls in a single expression, where the memory allocated by the first new may leak if the second allocation fails and throws an exception. ๐Ÿ˜ญ

C++ does <em>not</em> often specify the exact order of evaluation of the operands of an operator, or the arguments of a function. So bestie, picture this: the first allocation succeeds, the second one fails, and then in your exception handler you're standing there like ๐Ÿ‘๏ธ๐Ÿ‘„๐Ÿ‘๏ธ because there is literally no way to tell which allocation failed and therefore no way to free the memory. And even <em>if</em> the order is fixed? The result of a first new may be stored in a temporary location that isn't reachable anymore by the time a second allocation fails. ๐Ÿ’€ So the move is: avoid any expression that contains more than one operator new call if you're using exception handling to check for allocation errors. Periodt.

Different rules apply for the short-circuit operators || and && and the , operator, where evaluation of one side <em>must</em> be completed before the other one starts. Expressions of a list-initialization (that's initialization or construction using { and } characters) are evaluated in fixed order. Similarly, the condition of a ? operator is evaluated before the branches are evaluated. Slay, order. โœจ

The check reports a 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 (which can also be an object construction with () syntax). These new calls can be nested at any level, no biggie. But for any warning to actually 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. And 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 being that in that 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

Detects multiple statement macros that are used in unbraced conditionals. ๐Ÿ˜ญ Only the first statement of the macro will be inside the conditional โ€” the other ones will be executed unconditionally. It's giving "why is my code running when the condition is false".

Example:

#define INCREMENT_TWO(x, y) (x)++; (y)++
if (do_increment)
  INCREMENT_TWO(a, b);  // (b)++ will be executed unconditionally.

๐Ÿฆ‹ bugprone-narrowing-conversions

The bugprone-narrowing-conversions check is an alias, please see cppcoreguidelines-narrowing-conversions for more information. That's it, that's the whole vibe, go read the other one bestie ๐Ÿ’…


๐ŸŒธ 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 <em>and</em> explicit casts of enum type into bool where the enum type doesn't have a zero-value enumerator. ๐Ÿšจ Here's the tea: if the enum is used only to hold values equal to its enumerators, then conversion to bool will <em>always</em> result in a true value. Which means unnecessary code that reduces readability and maintainability, and which can result in bugs. Not the serve you think it is. ๐Ÿ’€

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. ๐Ÿ™ˆ

Important note bestie: this check will <em>not</em> 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. Iconic behavior honestly.

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-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. And we do not want that, bestie.

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<char *>(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<char *>(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. Slay.

Rewrite based on the destination array

  • 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. โœจ
  • 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).
  • 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.
  • 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:

  • If the capacity of the destination array is unknown.
  • If the given length is equal to the destination array's capacity.

Rewrite based on the length of the source string

  • 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).
  • 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 the safe functions are around, the glow-up version is memmove_s, and she comes with a brand-new second argument: the length of the destination array. That one gets adjusted, and the length of the source string gets incremented by one. If the safe functions are not available, then the given length just gets incremented by one. That's the whole vibe. โœจ
  • memmove_s โ€” the given length is incremented by one. That's it, that's the tea. ๐Ÿ“

๐ŸŒธ String handler functions ๐ŸŒธ

  • strerror_s โ€” the given length is incremented by one.
  • strncmp โ€” if the third argument is the first argument's length + 1, or the second argument's length + 1, then it has to be truncated, no + 1 operation. iykyk. ๐Ÿ’…
  • strxfrm โ€” the given length is incremented by one.

โœจ Options โœจ

  • WantToUseSafeFunctions โ€” setting this to true means the target environment is considered to implement the _s-suffixed memory and string handler functions, which are safer than the older versions (think memcpy_s()). Default value is true. ๐Ÿ‘‘

๐Ÿ’œ bugprone-optional-value-conversion ๐Ÿ’œ

Okay bestie, this one catches conversions that are potentially unintentional and redundant โ€” the ones where a value gets extracted out of an optional-like type and then immediately used to build a brand-new instance of that same optional-like type. It's giving... why. ๐Ÿ˜ญ

These conversions might be leftovers from a refactor, plain developer oversight, or some other situation that could lead to unintended exceptions โ€” or to the resulting optional always being initialized, which honestly might be unexpected behavior.

To illustrate, look at this problematic snippet:

#include <optional>

void print(std::optional<int>);

int main()
{
  std::optional<int> opt;
  // ...

  // Unintentional conversion from std::optional<int> to int and back to
  // std::optional<int>:
  print(opt.value());

  // ...
}

A better approach would be to just pass opt straight to the print function without extracting its value first:

#include <optional>

void print(std::optional<int>);

int main()
{
  std::optional<int> opt;
  // ...

  // Proposed code: Directly pass the std::optional<int> to the print
  // function.
  print(opt);

  // ...
}

By passing opt directly to print, you dodge the unnecessary conversions, and potential unintended behavior or exceptions are minimized. Chef's kiss. ๐Ÿซถ

Value extraction using operator * is matched by default. Heads up though: support for non-standard optional types like boost::optional or absl::optional may be limited.

Options:

  • OptionalTypes โ€” a 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 โ€” a 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 the overridden parent's virtual methods. Not the ancestor, babes, the parent. ๐Ÿ™ˆ

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 differs from its dynamic type is undefined behavior, because the two types could have different sizes โ€” and then the vtable pointer could point to an invalid address. That's not a vibe, that's a crash waiting to happen.

Finding pointers whose static type contains a virtual member function is a good heuristic, since the pointer is likely to point to a different, derived object.

Example:

struct Base {
  virtual void ~Base();
};

struct Derived : public Base {};

void foo() {
  Base *b = new Derived[10];

  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

  delete[] static_cast<Derived*>(b);
}

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, since they make up the majority of false positives. Default: false.
void bar() {
  Base *b = new Base[10];
  b += 1; // warning, as Base declares a virtual destructor

  delete[] b;

  Derived *d = new Derived[10]; // Derived overrides the destructor, and
                                // declares no other virtual functions
  d += 1; // warning only if IgnoreVirtualDeclarationsOnly is set to false

  delete[] d;
}

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 โ€” and that failure value is positive only. So like, negative is never the answer. ๐Ÿ˜ญ

Example buggy usage looks like:

if (posix_fadvise(...) < 0) {

This will never happen, because the return value is always non-negative. A simple fix could be:

if (posix_fadvise(...) > 0) {

๐Ÿฆ‹ bugprone-redundant-branch-condition ๐Ÿฆ‹

Finds condition variables in nested if statements that were also checked in the outer if statement and were not changed in between. Double-checking the same thing? Not the move. ๐Ÿ’…

Simple example:

bool onFire = isBurning();
if (onFire) {
  if (onFire)
    scream();
}

Here onFire is checked both in the outer if and the inner if statement, with no possible change between the two checks. The check warns about this code and suggests removing that second check of the variable onFire.

The checker also detects redundant condition checks when 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 up 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" (||). And no cap: 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, so if the condition variable is not a local variable of the function, or it's volatile, or it has an alias (pointer or reference), then no warning is issued. Fair. ๐ŸŒธ

Known 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. Slay, aliases.

Checks for usages of identifiers reserved for use by the implementation.

The C and C++ standards both reserve the following names for such use:

  • identifiers that begin with an underscore followed by an uppercase letter;
  • 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, like, standard library implementors โ€” so they can make sure 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. Future update, bestie. ๐Ÿ™ˆ

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 a 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 a constant reference, then the returned reference can be used after the object it refers to has been destroyed. And that is not the main character energy you want, fr fr.

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

bugprone-shared-ptr-array-mismatch ๐Ÿซ 

okay bestie this one is so real. It finds initializations of C++ shared pointers to a non-array type that got initialized with an array. Iconic mistake, honestly.

Here's the tea: if a shared pointer std::shared_ptr<T> gets 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. But you actually need a delete[] call. A std::shared_ptr<T[]> calls the correct delete operator. slay ๐Ÿ’…

The check offers replacement of shared_ptr<T> to shared_ptr<T[]> if it is used at a single variable declaration (one variable in one statement).

Example:

std::shared_ptr<Foo> x(new Foo[10]); // -> std::shared_ptr<Foo[]> x(new Foo[10]);
//                     ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
std::shared_ptr<Foo> 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<Foo> x3(new Foo[10], [](const Foo *ptr) { delete[] ptr; }); // no warning

struct S {
  std::shared_ptr<Foo> 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. lowkey a limited edition cover, but we take it ๐ŸŽ€


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, so buckle up babes.

Checked signal handler rules for C:

  • Calls to non-asynchronous-safe functions are not allowed.

Checked signal handler rules for up to and including C++14:

  • Calls to non-asynchronous-safe functions are not allowed.
  • 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.
  • Calls to functions with non-C linkage are not allowed (including the signal handler itself).
The check is disabled on C++17 and later. bestie it literally clocks out ๐Ÿ’€

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. fr fr, it goes deep ๐Ÿงš

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:
    • minimal โ€” Selects a minimal set that is defined in the CERT SIG30-C rule. and includes functions abort(), _Exit(), quick_exit() and signal().
    • 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. literally the drama ๐ŸŽ€

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:

  • signed char is assigned to an integer variable
  • signed char and unsigned char are compared with equality/inequality operator
  • 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. slay ๐Ÿ’…

#define EOF (-1)

int read(void) {
  char CChar;
  int IChar = EOF;

  if (readChar(CChar)) {
    IChar = static_cast<unsigned char>(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<unsigned char>(SChar) == USChar)
    return true;
  return false;
}
  • CharTypdefsToIgnore โ€” 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.
  • 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. bestie... sizeof is giving "I read the container but not the vibe" ๐Ÿ˜ญ

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<int, 3> std_array;
int d = sizeof(std_array); // no warning, probably intended.

๐Ÿ’… bugprone-sizeof-expression

Okay so this one is like the friend who catches you about to text your ex โ€” it finds sizeof expressions that are most likely errors, bestie. ๐Ÿ˜ญ

The sizeof operator hands you the size (in bytes) of its operand, and that operand can be an expression or the parenthesized name of a type. Misusing this thing? Errors, sure, but also potential software vulnerabilities. Not cute. Not cute at all.


๐Ÿ‘€ Suspicious usage of sizeof(K)

Classic mistake #1: asking for the sizeof of an integer literal. That gives you the size of its type (probably int), not the number. What the programmer actually wanted was probably just the integer, not its size. ๐Ÿ™ˆ

#define BUFLEN 42
char buf[BUFLEN];
memset(buf, 0, sizeof(BUFLEN));  // sizeof(42) ==> sizeof(int)

๐Ÿ˜ฌ Suspicious usage of sizeof(expr)

Sometimes you've got an enum or an integer standing in for a type, and the vibe is wrong. The common mistake here is calling sizeof on the integer or enum that represents the type you actually wanted. So you get the size of the integer, not the size of the type it represents. It's giving "I asked for the cake and got a picture of the cake." ๐ŸŽ‚

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)

this is a pointer to an object of a given type, babes. So sizeof(this) gives you the size of a pointer. The programmer almost certainly wanted the size of the object itself, not the size of the pointer pointing at it. ๐Ÿ’€

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 as char* A = "" and as char A[] = "". In the first case, the type is char* โ€” not the aggregate type char[]. So when you sizeof an object declared with type char*, you get the size of a pointer, not the number of characters (bytes) in the string literal. Sneaky!! ๐ŸŒธ

const char* kMessage = "Hello World!";      // const char kMessage[] = "...";
void getMessage(char* buf) {
  memcpy(buf, kMessage, sizeof(kMessage));  // sizeof(char*)
}

๐Ÿ˜ต Suspicious usage of sizeof(A*)

Super common oopsie: computing the size of a pointer instead of its pointee. This can sneak in via an explicit cast or an implicit conversion. Lowkey everywhere. ๐Ÿฆ‹

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 the classic way to get the number of elements in an aggregate. But this check yells when the cases are incompatible or just plain suspicious.

In this example, the entity has 10 bytes and it's incompatible with the type int, which has 4 bytes. Math is not mathing. ๐Ÿ˜ญ

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]
}

And in this one, sizeof(Values) returns the size of a char*. The declaration fools you, but in a parameter declaration the size 10 is ignored and the function just receives a char*. The way that's legal and yet so rude. ๐Ÿ™ƒ

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 basically never makes sense and is probably a logic error. In the example below, the programmer used * instead of /. Sir. That's not the vibe. ๐Ÿ’…

const char kMessage[] = "Hello World!";
void getMessage(char* buf) {
  memcpy(buf, kMessage, sizeof(kMessage) * sizeof(char));  //  sizeof(kMessage) / sizeof(char)
}

Heads up though: this check can also trigger on code using the arraysize macro. The following code is actually working correctly, but it should be simplified by using only the sizeof operator. Not a bug, just extra. ๐ŸŽ€

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 zero sense and is typically an error hiding behind macros. Like a jump scare but in a #define. ๐Ÿ˜ญ

#define INT_SZ sizeof(int)
int buf[] = { 42 };
void getInt(int* dst) {
  memcpy(dst, buf, sizeof(INT_SZ));  // sizeof(sizeof(int)) is suspicious.
}

โš™๏ธ Options

  • WarnOnSizeOfConstant โ€” When true, the check warns on an expression like sizeof(CONSTANT). Default is true.
  • WarnOnSizeOfIntegerExpression โ€” When true, the check warns on an expression like sizeof(expr) where the expression results in an integer. Default is false.
  • WarnOnSizeOfThis โ€” When true, the check warns on an expression like sizeof(this). Default is true.
  • WarnOnSizeOfCompareToConstant โ€” When true, the check warns 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 warns 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 (which may be implicit or explicit, for example array + 2 or (int *)array). Default is true.
  • WarnOnSizeOfPointer โ€” When true, the check reports 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.

๐Ÿ˜ด bugprone-spuriously-wake-up-functions

This check 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 when the function doesn't have a condition parameter. Basically: don't let your thread get woken up for no reason. It's the 3am notification of concurrency. ๐Ÿ””

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's an existing member function. ๐Ÿ‘‘

The empty() method on several common ranges returns a Boolean indicating whether or not the range is empty, but it's often mistakenly read as a way to clear the contents of a range. Some ranges offer a clear() method for exactly that. This check warns when a call to empty returns a result that gets ignored, and suggests replacing it with a call to clear() if that's 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 โ€” except the result is ignored, so it does nothing. ๐Ÿ’€

std::vector<int> v;
...
v.empty();

A call to clear() would appropriately clear the contents of the range:

std::vector<int> v;
...
v.clear();

Limitations:

  • Doesn't warn if empty() is defined and used with the ignored result in the class template definition (for example in the library implementation). These error cases can be caught with the [[nodiscard]] attribute.

๐Ÿงต bugprone-string-constructor

Finds string constructors that are suspicious and probably errors. โœจ

A common mistake is swapping the parameters to the 'fill' string-constructor. You had ONE job and you flipped it, bestie.

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. Random characters?? In MY string?? ๐Ÿ™ˆ

Examples:

std::string("test", 200);   // Will include random characters after "test".
std::string_view("test", 200);

Creating an empty string from constructors with parameters is considered suspicious. The programmer should use the empty constructor instead. Periodt.

Examples:

std::string("test", 0);   // Creation of an empty string.
std::string_view("test", 0);

โš™๏ธ Options

  • WarnOnLargeLength โ€” When true, the check warns 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 it to e.g. ::std::basic_string;llvm::StringRef;QString to perform this check on custom classes.

๐Ÿ”ข bugprone-string-integer-assignment

This check finds assignments of an integer to std::basic_string<CharT> (std::string, std::wstring, etc.). The source of the problem is this assignment operator of std::basic_string<CharT>:

basic_string& operator=( CharT ch );

Because numeric types can be implicitly cast to character types. Implicitly!! Rude. ๐Ÿ˜ค

std::string s;
int x = 5965;
s = 6;
s = x;

Use the appropriate conversion functions or character literals instead:

std::string s;
int x = 5965;
s = '6';
s = std::to_string(x);

And in order to suppress false positives, use an explicit cast:

std::string s;
s = static_cast<char>(6);

๐Ÿ•ณ๏ธ bugprone-string-literal-with-embedded-nul

Finds occurrences of string literals with an embedded NUL character, and validates their usage. This is the "there's a \0 in there and nobody noticed" check. ๐Ÿซถ


๐Ÿงช Invalid escaping

Special characters can be escaped within a string literal by using their hexadecimal encoding, like \x42. A common mistake is escaping them like this โ€” \0x42 โ€” where the \0 stands for the NUL character. So close, and yet so far. ๐Ÿ˜ญ

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 play with strings that have NUL bytes tucked inside them, because they literally keep track of the bytes and the length. A plain char* (NUL-terminated) string does not do that. Bestie. It just doesn't. ๐Ÿ˜ญ

So the classic oopsie is passing a string literal with an embedded NUL into a string constructor that's expecting a NUL-terminated string. Everything after that first NUL gets chopped. Gone. Deleted. Not invited to the function. ๐Ÿ’€

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

Okay so this one checks for all the various ways the const CharT* constructor of std::basic_string_view can get handed a null argument, and it swaps them out for the default constructor in most cases. For the comparison operators, a braced initializer list just won't compile, so instead it uses a call to .empty() or the empty string literal, whichever fits. ๐ŸŒธ

The whole point is keeping your code from invoking behavior that is unconditionally undefined. Like, no cap, undefined. The single-argument const CharT* constructor does not check for the null case before dereferencing its input. And the standard is slated to add an explicitly-deleted overload to catch some of these cases: wg21.link/p2166.

If you also wanna catch the extra cases of NULL (which expands to __null) and 0, run the modernize-use-nullptr check first to convert your 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

gets 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 being misused (like, 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. We regard the enum as a (suspicious) bitmask if the three conditions below are true at the same time:

  • at most half of the elements of the enum are non pow-of-2 numbers (because of short enumerations)
  • there is another non pow-of-2 number than the enum constant representing all choices (the result "bitwise OR" operation of all enum elements)
  • 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. ๐Ÿ‘‘

  1. Investigating the right hand side of += and |= operator.
  2. Check only the enum value side of a | and + operator if one of them is not enum val.
  3. 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

This 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 <stdio.c>     // Warning, filename is suspicious.

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 char 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. Bestie, it happens to the best of us. ๐Ÿ˜ญ

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 ๐ŸŽ€

Ok bestie, this one is hunting for stray semicolons that sneakily change what your code actually does ๐Ÿ’€. Specifically, it's looking for if, while, for and for-range statements whose body is literally just a single semicolon, and then it reads the room โ€” context like indentation โ€” to figure out if you meant it or if you just fumbled the keyboard.

if (x < y);
{
  x++;
}

So here the body of the if statement is just that semicolon at the end of the first line, and x gets incremented no matter what the condition says. The if is giving nothing, honestly.

while ((line = readLine(file)) != NULL);
  processLine(line);

Because of this, processLine() gets called exactly once, when the while loop with the empty body exits with line == NULL. The indentation is basically the code telling on itself about what the programmer wanted.

if (x >= y);
x -= y;

Here the indentation doesn't suggest any nesting at all, and likeโ€ฆ there is genuinely no valid reason to have an if statement with an empty body (tho it can make sense for a loop). So the check raises a warning for the code above. Periodt.

To fix it, either yeet the stray semicolon, or โ€” if the empty body is on purpose โ€” show that with your code indentation or move the semicolon onto its own new line. Like:

while (readWhitespace());
  Token t = readNextToken();

Here the second line is indented in a way that implies it's supposed to be the body of the while loop โ€” except the body is actually empty, because of that semicolon chillin' at the end of the first line. ๐Ÿ™ˆ

Either remove the indentation from the second line:

while (readWhitespace());
Token t = readNextToken();

...or move the semicolon off the end of the first line and onto a new line:

while (readWhitespace())
  ;

  Token t = readNextToken();

In this case the check will assume you know what you're doing, and will not raise a warning. Slay. ๐Ÿ’…


๐Ÿฆ‹ bugprone-suspicious-string-compare ๐Ÿฆ‹

Finds suspicious usage of runtime string comparison functions. This check is valid in C and C++ โ€” bilingual icon behavior fr.

It checks for calls with an implicit comparator and suggests you add it explicitly:

if (strcmp(...))       // Implicitly compare to zero
if (!strcmp(...))      // Won't warn
if (strcmp(...) != 0)  // Won't warn

It also checks that compare function results (i.e., strcmp) are compared to a valid constant. The resulting value is

<  0    when lower than,
>  0    when greater than,
== 0    when equals.

A common mistake is comparing the result to 1 or -1.

if (strcmp(...) == -1)  // Incorrect usage of the returned value.

On top of that, the check warns if the result value is implicitly cast to a suspicious non-integer type. That happens 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. Not the vibe.

It warns when the result of data() is passed to a constructor or function without also passing the corresponding result of the size() or length() member function. Such usage can lead to unintended behavior, particularly when you assume 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. Bestie, it's a trap.

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's 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. Iconic restraint honestly. ๐Ÿ‘‘


๐Ÿ’œ 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 program will continue execution without any defined behavior or handling. ๐Ÿ˜ฌ

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-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. Like bestie, you built the exception and then justโ€ฆ didn't throw it? ๐Ÿซ 

Example:

void f(int i) {
  if (i < 0) {
    // Exception is created but is not thrown.
    std::runtime_error("Unexpected argument");
  }
}

๐Ÿ“ 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. Little variable, big range energy โ€” and not in a cute way. ๐Ÿ’€

          int main() {
            long size = 294967296l;
            for (short i = 0; i < size; ++i) {}
          }

okay so this for loop? it literally never ends. it's an infinite loop bestie ๐Ÿ’€ because the short type can't hold all the values in the [0..size] interval. it just. can't. it's not built for that range. periodt.

and in a real-world scenario, size is like a container's size that depends on user input, so:

          int doSomething(const std::vector& items) {
            for (short i = 0; i < items.size(); ++i) {}
          }

this works fine when there's a small number of objects, but with a bigger user input it will literally freeze. the way that a tiny type ruins everything ๐Ÿ˜ญ

also โ€” highly recommend turning on the compiler warning -Wtautological-constant-out-of-range-compare too, because this check does not inspect compile-time constant loop boundaries, specifically to avoid overlapping with that warning. no cap.

MagnitudeBitsUpperLimit this is the upper limit for the magnitude bits of the loop variable โœจ if you set it, the check filters out any catches where the loop variable's type has more magnitude bits than the upper limit you gave. the default value is 16. for example, if you set this option to 31 (bits), then a 32-bit unsigned int gets ignored by the check, but a 32-bit int does not (because a 32-bit signed int has 31 magnitude bits โ€” the sign bit ate one ๐Ÿ’…).

          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. so yeah, it may be more resource intensive (RAM, CPU) than your average clang-tidy check. she's thorough, she's hungry, she eats your memory. worth it tho ๐Ÿ’–

this check finds unsafe accesses to values contained in std::optional<T>, absl::optional<T>, base::Optional<T>, or folly::Optional<T> objects. and from here on out we're gonna call all of those collectively optional<T> โ€” group name, iconic ๐ŸŒˆ

so an access to the value of an optional<T> happens whenever one of its value, operator*, or operator-> member functions gets invoked. and to match up with common misconceptions, the check treats these member functions as equivalent โ€” even though there are subtle differences related to exceptions versus undefined behavior. (there's more on that in Additional notes below, iykyk ๐Ÿ‘€)

an access to the value of an optional<T> is considered safe if and only if code in the local scope (like, a function body) guarantees that the optional<T> has a value in all possible execution paths that can reach the access. that has to happen either through an explicit check using the optional<T>::has_value member function, or by constructing the optional<T> in a way that shows it unambiguously holds a value (e.g. using std::make_optional, which always returns a populated std::optional<T> โœจ).

below we list some examples โ€” unsafe optional access patterns first, then the safe ones. let's get into it ๐Ÿ’…

๐Ÿ’€ Unsafe access patterns

Access the value without checking if it exists

the check flags accesses to the value that aren't locally guarded by an existence check:

          void f(std::optional<int> 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<int> 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 knows that function results might not be stable. like, consecutive calls to the same function might return different values. for example:

          void f(Foo foo) {
            if (foo.opt().has_value()) {
              use(*foo.opt()); // unsafe: it is unclear whether `foo.opt()` has a value.
            }
          }

Rely on invariants of uncommon APIs

the check does not know about 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 the 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<int> opt) {
            use(*opt); // unsafe: it is unclear whether `opt` has a value.
          }

          void f(std::optional<int> opt) {
            if (opt.has_value()) {
              g(opt);
            }
          }

๐Ÿ’– Safe access patterns

Check if a value exists, then access the value

the check recognizes all the straightforward ways for checking if a value exists and accessing the value contained in an optional object. for example:

          void f(std::optional<int> opt) {
            if (opt.has_value()) {
              use(*opt);
            }
          }

Check if a value exists, then access the value from a copy

the criteria the check uses is semantic, not syntactic. she recognizes when a copy of the optional object being accessed is known to have a value. for example:

          void f(std::optional<int> opt1) {
            if (opt1.has_value()) {
              std::optional<int> 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<int> 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<int> opt) {
            bool safe = false;
            if (opt.has_value() && SomeOtherCondition()) {
              safe = true;
            }
            // ... more code...
            if (safe) {
              use(*opt);
            }
          }

Stabilize function results

since function results are not assumed to be stable across calls, it's 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.opt(); foo_opt.has_value()) {
              use(*foo_opt);
            }
          }

Do not rely on uncommon-API invariants

when uncommon APIs guarantee that an optional has contents, don't 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 that 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<int> opt) {
            if (opt.has_value()) {
              g(*opt);
            }
          }

and

          struct S {
            std::optional<int> 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<int>;

          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. best practice meanwhile: follow the same principles when using optionals in lambdas ๐Ÿซถ

Access with operator*() vs. value()

so like โ€” 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? and while we're at it, 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:

  • 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.
  • 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. slay โœจ

๐Ÿ’€ bugprone-undefined-memory-manipulation ๐Ÿ’€

finds calls of memory manipulation functions memset(), memcpy() and memmove() on non-TriviallyCopyable objects resulting in undefined behavior. yeah. that's the whole vibe and it's a bad one ๐Ÿ™ˆ

using memory manipulation functions on non-TriviallyCopyable objects can lead to a whole 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 a nightmare to identify and diagnose the root cause. additionally, misuse of memory manipulation functions can bypass essential object-specific operations โ€” like constructors and destructors โ€” leading to resource leaks or improper initialization. not cute ๐Ÿ’…

for example, when using memcpy to copy std::string, pointer data is being copied, and it can result in a double free issue.

          #include <cstring>
          #include <string>

          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 โ€” bestie, this one is so sneaky ๐Ÿ’… It finds places where a constructor accidentally creates a temporary object that looks like it's calling another constructor of the same class.

Like... the vibes say "I'm delegating" but the code is actually just building a whole separate temporary and yeeting it into the void. The user almost certainly meant to use a delegating constructor or a base class initializer. You meant to share the constructor, not make a lil baby copy of it, bestie. ๐Ÿ˜ญ


bugprone-unhandled-exception-at-new โ€” okay this one is about feelings AND memory ๐Ÿซถ It finds calls to new where there's no exception handler for std::bad_alloc.

Calls to new can throw exceptions of type std::bad_alloc and, like, those should be handled, bestie. Alternatively you can use the nonthrowing form of new. 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 โ€” you're off the hook โœจ

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 โ€” the self-sabotage check ๐Ÿ˜ญ cert-oop54-cpp redirects here as an alias for this check. For the CERT alias, the WarnOnlyIfThisHasSuspiciousField option is set to false.

It finds user-defined copy assignment operators that don't protect the code against self-assignment โ€” either by checking self-assignment explicitly, or by using the copy-and-swap or the copy-and-move method. Not you assigning yourself to yourself and breaking everything bestie ๐Ÿ™ˆ

By default, this check searches only those classes which have any pointer or C array field, to avoid false positives. Because with a pointer or a C array, it's likely that self-copy assignment breaks the object if the copy assignment operator wasn't written with care. iykyk

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;
  }
};

WarnOnlyIfThisHasSuspiciousField โ€” When true, the check will warn only if the container class of the copy assignment operator has any suspicious fields (pointer or C array). This option is set to true by default. Spooky fields only ๐Ÿ”


bugprone-unique-ptr-array-mismatch โ€” array girlies this is for you ๐ŸŒˆ It finds initializations of C++ unique pointers to a non-array type that are initialized with an array.

If a pointer std::unique_ptr<T> 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<T[]> uses the correct delete operator. The check does not emit a warning if a unique_ptr with a user-specified deleter type is used. ๐Ÿ’…

The check offers replacement of unique_ptr<T> to unique_ptr<T[]> if it is used at a single variable declaration (one variable in one statement).

Example:

std::unique_ptr<Foo> x(new Foo[10]); // -> std::unique_ptr<Foo[]> x(new Foo[10]);
//                     ^ warning: unique pointer to non-array is initialized with array
std::unique_ptr<Foo> x1(new Foo), x2(new Foo[10]); // no replacement
//                                   ^ warning: unique pointer to non-array is initialized with array

D d;
std::unique_ptr<Foo, D> x3(new Foo[10], d); // no warning (custom deleter used)

struct S {
  std::unique_ptr<Foo> 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. partial coverage, still iconic โœจ


bugprone-unsafe-functions โ€” ๐Ÿšจ BESTIE. RED ALERT. ๐Ÿšจ This 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:

  • Recommendation MSC24-C. Do not use deprecated or obsolescent functions.
  • 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. aliases, my beloved

Unsafe functions โœจ

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. that's a whole lotta function names bestie, drink some water ๐Ÿ“

If Annex K. is not available, replacements are suggested only for the following functions from the previous list:

  • asctime, asctime_r, suggested replacement: strftime
  • gets, suggested replacement: fgets

The following functions are always checked, regardless of Annex K availability:

  • rewind, suggested replacement: fseek
  • setbuf, suggested replacement: setvbuf

If ReportMoreUnsafeFunctions is enabled, the following functions are also checked:

  • bcmp, suggested replacement: memcmp
  • bcopy, suggested replacement: memcpy_s if Annex K is available, or memcpy
  • bzero, suggested replacement: memset_s if Annex K is available, or memset
  • getpw, suggested replacement: getpwuid
  • 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. they're on thin ice but they're fine, leave them alone

The availability of Annex K is determined based on the following macros:

  • __STDC_LIB_EXT1__: feature macro, which indicates the presence of Annex K. "Bounds-checking interfaces" in the library implementation
  • __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. order matters, don't @ me ๐Ÿ’…

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. more warnings? more fun ๐ŸŽ€

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 <string.h> // Defines functions from Annex K.
#include <stdio.h>

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; } }


---

## <pink>bugprone-unused-local-non-trivial-variable</pink> ๐Ÿ’ค

Okay bestie, this one yells at you when a **local non-trivial variable** is just... sitting there, unused, inside a function. Rude of it, honestly.

But wait โ€” some variables get a free pass and are **excluded** from this check:

- trivial and trivially copyable
- references and pointers
- exception variables in `catch` clauses
- `static` or thread local
- structured bindings
- variables with `[[maybe_unused]]` attribute

You can tweak this: set `IncludeTypes` to `.*` if you want it to warn on **all** non-trivial variables, then carve out the ones you don't care about with `ExcludeTypes`. Iconic behaviour tbh.

So in this example, `my_lock` would get a warning that it's unused. ๐Ÿ˜ญ

std::mutex my_lock; // my_lock local variable is never used


And in the next one, `future2` would get flagged as unused. ๐Ÿ‘€

std::future<MyObject> future1; std::future<MyObject> 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 these types are checked:

  - `::std::.*mutex`
  - `::std::future`
  - `::std::basic_string`
  - `::std::basic_regex`
  - `::std::basic_istringstream`
  - `::std::basic_stringstream`
  - `::std::bitset`
  - `::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.

---

## <purple>bugprone-unused-raii</purple> ๐Ÿชค

This one sniffs out **temporaries that look like RAII objects**. Sneaky little things.

The canonical example โ€” the moment everyone learns this lesson the hard way โ€” is a scoped lock:

{ scoped_lock(&global_mutex); critical_section(); }


> <red>Bestie. NO.</red> The destructor of the `scoped_lock` gets called **before** `critical_section` is even entered, which leaves it completely unprotected. The lock did nothing. It was for the vibes only. ๐Ÿ’€

So we apply a bunch of heuristics to keep the false positives down:

- Ignore code expanded from macros. Testing frameworks make heavy use of this.
- Ignore types with trivial destructors. They are very unlikely to be RAII objects and there's no difference when they are deleted.
- Ignore objects at the end of a compound statement (doesn't change behavior).
- Ignore objects returned from a call.

---

## <cyan>bugprone-unused-return-value</cyan> ๐Ÿ—‘๏ธ

Warns on **unused function return values**. Which functions? You get to configure them, bestie. ๐Ÿ’…

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$`

  - `std::async()`. Not using the return value makes the call synchronous.
  - `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.
  - `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.
  - `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()`.
  - `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.

---

## <hotpink>bugprone-use-after-move</hotpink> ๐Ÿ‘ป

Warns if an object is used **after it has been moved**. Classic. For example:

std::string str = "Hello, world!\n"; std::vector<std::string> messages; messages.emplace_back(std::move(str)); std::cout << str;


That last line will trigger a warning that `str` is used after it has been moved. ๐Ÿซ 

Now โ€” the check does **not** trigger a warning if the object is reinitialized after the move and before the use. So no warning for this code:

messages.emplace_back(std::move(str)); str = "Greetings, stranger!\n"; std::cout << str;


The 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**. Which means this code does not produce a warning:

if (condition) { messages.emplace_back(std::move(str)); } else { std::cout << str; }


On the other hand, this 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. Sneaky!)

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 <class T> 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. So 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<int> v); std::vector<int> v = { 1, 2, 3 }; f(v[1], std::move(v));


In this kind of situation, the check will note that the use and move are **unsequenced**.

The check will also take sequencing rules into account when reinitializations occur in the same statement as moves or uses. A reinitialization is only considered to reinitialize a variable if it is guaranteed to be evaluated after the move and before the use.

### Move

The check currently only considers calls of `std::move` on **local variables or function parameters**. It does not check moves of member variables or global variables.

Any call of `std::move` on a variable is considered to cause a move of that variable, even if the result of `std::move` is not passed to an rvalue reference parameter.

This means that the check will flag a use-after-move even on a type that does not define a move constructor or move assignment operator. This is intentional. Developers may use `std::move` on such a type in the expectation that the type will add move semantics in the future. If such a `std::move` has the potential to cause a use-after-move, we want to warn about it even if the type does not implement move semantics yet.

Furthermore, if the result of `std::move` is passed to an rvalue reference parameter, this will always be considered to cause a move, even if the function that consumes this parameter does not move from it, or if it does so only conditionally. For example, in the following situation, the check will assume that a move always takes place:

<pink>okay so back to the drama of moves and uses, bestie, we're still in the thick of it ๐Ÿ’…</pink>

std::vector<std::string> messages; void f(std::string &&str) { // Only remember the message if it isn't empty. if (!str.empty()) { messages.emplace_back(std::move(str)); } } std::string str = ""; f(std::move(str));


<cyan>The check is gonna assume that last line causes a move</cyan> โ€” even though in this exact case it literally does not. And yes, again: **this is intentional.** The check is doing its best, okay? ๐Ÿ˜ญ

There is <purple>one special case</purple>: a call to `std::move` inside a `try_emplace` call is conservatively assumed **not** to move. Why? To dodge spurious warnings, because the check has no way to reason about the `bool` returned by `try_emplace`. It simply cannot read that vibe. ๐Ÿซถ

When analyzing the order in which moves, uses and reinitializations happen (peep the section *Unsequenced moves, uses, and reinitializations*), the move is assumed to occur in **whichever function the result of the `std::move` is passed to**.

The check also handles perfect-forwarding with `std::forward`, so this lil snippet will also trigger a use-after-move warning. โœจ

void consume(int);

void f(int&& i) { consume(std::forward<int>(i)); consume(std::forward<int>(i)); // use-after-move }


---

### ๐ŸŒธ Use ๐ŸŒธ

Any occurrence of the moved variable that is **not** a reinitialization (see below) is considered to be a *use*. Simple as that.

Exception, because of course there's an exception: objects of type `std::unique_ptr`, `std::shared_ptr` and `std::weak_ptr` have **defined move behavior** โ€” objects of these classes are guaranteed to be empty after they have been moved from. So an object of these classes will only be considered used if it is <hotpink>dereferenced</hotpink>, i.e. if `operator*`, `operator->` or `operator[]` (in the case of `std::unique_ptr<T []>`) is called on it. ๐Ÿ‘‘

If multiple uses occur after a move, <yellow>only the first of these is flagged</yellow>. The check is not gonna spam you, it picks one and commits. Periodt.

---

### ๐Ÿ’œ Reinitialization ๐Ÿ’œ

The check considers a variable to be reinitialized in the following cases:

- The variable occurs on the <mint>left-hand side of an assignment</mint>.
- The variable is passed to a function as a <mint>non-const pointer or non-const lvalue reference</mint>. (It is assumed that the variable may be an out-parameter for the function.)
- `clear()` or `assign()` is called on the variable and the variable is of one of the standard container types `basic_string`, `vector`, `deque`, `forward_list`, `list`, `set`, `map`, `multiset`, `multimap`, `unordered_set`, `unordered_map`, `unordered_multiset`, `unordered_multimap`.
- `reset()` is called on the variable and the variable is of type `std::unique_ptr`, `std::shared_ptr` or `std::weak_ptr`.
- A member function marked with the `[[clang::reinitializes]]` attribute is called on the variable.

Now here's the spicy bit ๐Ÿซฃ โ€” if the variable in question is a struct and an individual member variable of that struct is written to, the check does **not** consider this to be a reinitialization. Even if, eventually, all member variables of the struct are written to. Like, all of them. Still no. Example:

struct S { std::string str; int i; }; S s = { "Hello, world!\n", 42 }; S s_other = std::move(s); s.str = "Lorem ipsum"; s.i = 99;


The check will **not** consider `s` to be reinitialized after the last line; instead, the line that assigns to `s.str` will be flagged as a use-after-move. This is intentional, bestie, because this pattern of reinitializing a struct is <red>error-prone</red>. For example, if an additional member variable is added to `S`, it is easy to forget to add the reinitialization for this additional member. Instead, it is safer to assign to the **entire struct in one go**, and this will also avoid the use-after-move warning. Clean, simple, iconic. ๐ŸŽ€

---

### ๐Ÿฆ‹ bugprone-virtual-near-miss ๐Ÿฆ‹

Warn if a function is a <hotpink>near miss</hotpink> (i.e. the name is very similar and the function signature is the same) to a virtual function from a base class. It's giving "you almost did it, but you didn't."

Example:

struct Base { virtual void func(); };

struct Derived : Base { virtual void funk(); // warning: 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? };


---

### โญ cert-con36-c โญ

The `cert-con36-c` check is an <purple>alias</purple>, please see `bugprone-spuriously-wake-up-functions` for more information.

### โญ cert-con54-cpp โญ

The `cert-con54-cpp` check is an <purple>alias</purple>, please see `bugprone-spuriously-wake-up-functions` for more information.

### โญ cert-ctr56-cpp โญ

The `cert-ctr56-cpp` check is an <purple>alias</purple>, please see `bugprone-pointer-arithmetic-on-polymorphic-object` for more information.

### โญ cert-dcl03-c โญ

The `cert-dcl03-c` check is an <purple>alias</purple>, please see `misc-static-assert` for more information.

### โญ cert-dcl16-c โญ

The `cert-dcl16-c` check is an <purple>alias</purple>, please see `readability-uppercase-literal-suffix` for more information.

### โญ cert-dcl37-c โญ

The `cert-dcl37-c` check is an <purple>alias</purple>, please see `bugprone-reserved-identifier` for more information.

### ๐Ÿ”ฅ cert-dcl50-cpp ๐Ÿ”ฅ

This check flags **all function definitions** (but not declarations) of C-style variadic functions. Yes, definitions only. Declarations get a pass.

This check corresponds to the CERT C++ Coding Standard rule <cyan>DCL50-CPP. Do not define a C-style variadic function.</cyan>

### โญ cert-dcl51-cpp โญ

The `cert-dcl51-cpp` check is an <purple>alias</purple>, please see `bugprone-reserved-identifier` for more information.

### โญ cert-dcl54-cpp โญ

The `cert-dcl54-cpp` check is an <purple>alias</purple>, please see `misc-new-delete-overloads` for more information.

### ๐ŸŒˆ cert-dcl58-cpp ๐ŸŒˆ

Modification of the `std` or `posix` namespace can result in <red>undefined behavior</red>. This check warns for such modifications. 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). So that one's fine, no cap.

The check detects the following (user provided) declarations in namespace `std` or `posix`:

- Anything that is not a template specialization.
- Explicit specializations of any standard library function template or class template, if it does not have any user-defined type as template argument.
- Explicit specializations of any member function of a standard library class template.
- Explicit specializations of any member function template of a standard library class or class template.
- 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 [cert-dcl58-cpp] }

namespace posix::a { // warning: modification of 'posix' namespace can result in undefined behavior }

template <> struct ::std::hash<long> { // 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<MyData> { // no warning: specialization with user-defined type unsigned long operator()(const MyData &K) const { return K.data; } };

namespace std { template <> void swap<bool>(bool &a, bool &b); // warning: modification of 'std' namespace can result in undefined behavior

template <> bool less<void>::operator()<MyData &&, MyData &&>(MyData &&, MyData &&) const { // warning: modification of 'std' namespace can result in undefined behavior return true; } }


This check corresponds to the CERT C++ Coding Standard rule <cyan>DCL58-CPP. Do not modify the standard namespaces.</cyan>

### โญ cert-dcl59-cpp โญ

The `cert-dcl59-cpp` check is an <purple>alias</purple>, please see `google-build-namespaces` for more information.

### ๐Ÿ“ cert-env33-c ๐Ÿ“

This check flags calls to `system()`, `popen()`, and `_popen()`, which execute a command processor. It does **not** flag calls to `system()` with a <hotpink>null pointer argument</hotpink>, as such a call checks for the presence of a command processor but does not actually attempt to execute a command. Sneaky lil exception, but okay. ๐Ÿ™ˆ

This check corresponds to the CERT C Coding Standard rule <cyan>ENV33-C. Do not call system().</cyan>

### ๐Ÿ’€ cert-err09-cpp ๐Ÿ’€

The `cert-err09-cpp` check is an <purple>alias</purple>, please see `misc-throw-by-value-catch-by-reference` for more information.

This check corresponds to the CERT C++ Coding Standard recommendation <cyan>ERR09-CPP. Throw anonymous temporaries.</cyan> However, all of the CERT recommendations have been removed from public view, and so their justification for the behavior of this check requires an account on their wiki to view. (not me having to make an account to read the lore ๐Ÿ˜ญ)

### ๐Ÿงš cert-err33-c ๐Ÿงš

Warns on <hotpink>unused function return values</hotpink>. Many of the standard library functions return a value that indicates if the call was successful. Ignoring the returned value can cause unexpected behavior if an error has occurred. The following functions are checked:

- `aligned_alloc()`
- `asctime_s()`
- `at_quick_exit()`
- `atexit()`
- `bsearch()`
- `bsearch_s()`
- `btowc()`
- `c16rtomb()`
- `c32rtomb()`
- `calloc()`
- `clock()`
- `cnd_broadcast()`
- `cnd_init()`
- `cnd_signal()`
- `cnd_timedwait()`
- `cnd_wait()`
- `ctime_s()`
- `fclose()`
- `fflush()`
- `fgetc()`
- `fgetpos()`
- `fgets()`
- `fgetwc()`
- `fopen()`
- `fopen_s()`
- `fprintf()`
- `fprintf_s()`
- `fputc()`
- `fputs()`
- `fputwc()`
- `fputws()`
- `fread()`
- `freopen()`
- `freopen_s()`
- `fscanf()`
- `fscanf_s()`
- `fseek()`
- `fsetpos()`
- `ftell()`
- `fwprintf()`
- `fwprintf_s()`
- `fwrite()`
- `fwscanf()`
- `fwscanf_s()`
- `getc()`
- `getchar()`
- `getenv()`
- `getenv_s()`
- `gets_s()`
- `getwc()`
- `getwchar()`
- `gmtime()`
- `gmtime_s()`
- `localtime()`
- `localtime_s()`
- `malloc()`
- `mbrtoc16()`
- `mbrtoc32()`
- `mbsrtowcs()`
- `mbsrtowcs_s()`
- `mbstowcs()`
- `mbstowcs_s()`
- `memchr()`
- `mktime()`
- `mtx_init()`
- `mtx_lock()`
- `mtx_timedlock()`
- `mtx_trylock()`
- `mtx_unlock()`
- `printf_s()`
- `putc()`
- `putwc()`
- `raise()`
- `realloc()`
- `remove()`
- `rename()`
- `setlocale()`
- `setvbuf()`
- `scanf()`
- `scanf_s()`
- `signal()`
- `snprintf()`
- `snprintf_s()`
- `sprintf()`
- `sprintf_s()`
- `sscanf()`
- `sscanf_s()`
- `strchr()`
- `strerror_s()`
- `strftime()`
- `strpbrk()`
- `strrchr()`
- `strstr()`
- `strtod()`
- `strtof()`
- `strtoimax()`
- `strtok()`
- `strtok_s()`
- `strtol()`
- `strtold()`
- `strtoll()`
- `strtoumax()`
- `strtoul()`
- `strtoull()`
- `strxfrm()`
- `swprintf()`
- `swprintf_s()`
- `swscanf()`

- `swscanf_s()`
- `thrd_create()`
- `thrd_detach()`
- `thrd_join()`
- `thrd_sleep()`
- `time()`
- `timespec_get()`
- `tmpfile()`
- `tmpfile_s()`
- `tmpnam()`
- `tmpnam_s()`
- `tss_create()`
- `tss_get()`
- `tss_set()`
- `ungetc()`
- `ungetwc()`
- `vfprintf()`
- `vfprintf_s()`
- `vfscanf()`
- `vfscanf_s()`
- `vfwprintf()`
- `vfwprintf_s()`
- `vfwscanf()`
- `vfwscanf_s()`
- `vprintf_s()`
- `vscanf()`
- `vscanf_s()`
- `vsnprintf()`
- `vsnprintf_s()`
- `vsprintf()`
- `vsprintf_s()`
- `vsscanf()`
- `vsscanf_s()`
- `vswprintf()`
- `vswprintf_s()`
- `vswscanf()`
- `vswscanf_s()`
- `vwprintf_s()`
- `vwscanf()`
- `vwscanf_s()`
- `wcrtomb()`
- `wcschr()`
- `wcsftime()`
- `wcspbrk()`
- `wcsrchr()`
- `wcsrtombs()`
- `wcsrtombs_s()`
- `wcsstr()`
- `wcstod()`
- `wcstof()`
- `wcstoimax()`
- `wcstok()`
- `wcstok_s()`
- `wcstol()`
- `wcstold()`
- `wcstoll()`
- `wcstombs()`
- `wcstombs_s()`
- `wcstoumax()`
- `wcstoul()`
- `wcstoull()`
- `wcsxfrm()`
- `wctob()`
- `wctrans()`
- `wctype()`
- `wmemchr()`
- `wprintf_s()`
- `wscanf()`
- `wscanf_s()`

<lavender>okay bestie so</lavender> this check is literally just an alias of `check bugprone-unused-return-value`, except it comes with a <pink>fixed set of functions</pink> pre-loaded. same energy, different wardrobe. ๐Ÿ’…

Suppressing issues by casting to `void` is enabled by default โ€” and you can turn that off by setting the `AllowCastToVoid` option to `false`. <yellow>slay, your call.</yellow>

This check corresponds to a part of CERT C Coding Standard rule <cyan>ERR33-C. Detect and handle standard library errors.</cyan> The list of checked functions is taken from the rule, with following exception:

- The check can not differentiate if a function is called with `NULL` argument. Therefore the following functions are <red>not</red> checked: `mblen`, `mbrlen`, `mbrtowc`, `mbtowc`, `wctomb`, `wctomb_s`

---

## cert-err34-c

<hotpink>bestie this one is coming for your lazy conversions</hotpink> โ€” it flags calls to string-to-number conversion functions that do <red>not</red> verify the validity of the conversion, like `atoi()` or `scanf()`. ๐Ÿšจ It does **not** flag calls to `strtol()`, or other related conversion functions that do actually do better error checking. <mint>those ones ate, we leave them alone.</mint>

#include <stdlib.h>

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 */ } }


This check corresponds to the CERT C Coding Standard rule <cyan>ERR34-C. Detect errors when converting a string to a number.</cyan>

---

## cert-err52-cpp

Flags **all** call expressions involving `setjmp()` and `longjmp()`. <red>all of them. every single one. no exceptions.</red> ๐Ÿ˜ญ

This check corresponds to the CERT C++ Coding Standard rule <cyan>ERR52-CPP. Do not use setjmp() or longjmp().</cyan>

---

## cert-err58-cpp

Flags all `static` or `thread_local` variable declarations where the initializer for the object <purple>may throw an exception</purple>. imagine your program exploding before `main()` even gets to say hi ๐Ÿ’€

This check corresponds to the CERT C++ Coding Standard rule <cyan>ERR58-CPP. Handle all exceptions thrown before main() begins executing.</cyan>

---

## cert-err60-cpp

Flags all `throw` expressions where the exception object is not <lavender>nothrow copy constructible</lavender>.

This check corresponds to the CERT C++ Coding Standard rule <cyan>ERR60-CPP. Exception objects must be nothrow copy constructible.</cyan>

---

## cert-err61-cpp

The `cert-err61-cpp` check is an alias, please see `misc-throw-by-value-catch-by-reference` for more information. <peach>(yes girl, go read that one, it's the main character here)</peach>

---

## cert-exp42-c

The `cert-exp42-c` check is an alias, please see `bugprone-suspicious-memory-comparison` for more information.

---

## cert-fio38-c

The `cert-fio38-c` check is an alias, please see `misc-non-copyable-objects` for more information.

---

## cert-flp30-c

Flags `for` loops where the induction expression has a <yellow>floating-point type</yellow>. ๐Ÿฆ‹ floats as loop counters? in THIS economy? no ma'am.

This check corresponds to the CERT C Coding Standard rule <cyan>FLP30-C. Do not use floating-point variables as loop counters.</cyan>

---

## cert-flp37-c

The `cert-flp37-c` check is an alias, please see `bugprone-suspicious-memory-comparison` for more information.

---

## cert-int09-c

The `cert-int09-c` check is an alias, please see `readability-enum-initial-value` for more information.

---

## cert-mem57-cpp

This one flags uses of <purple>default `operator new`</purple> where the type has **extended alignment** โ€” that's 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.) <lavender>ikyk what I mean, over-aligned types need their own handler.</lavender>

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. <mint>so if you brought your own, you're off the hook fr fr.</mint>

This check corresponds to the CERT C++ Coding Standard rule <cyan>MEM57-CPP. Avoid using default operator new for over-aligned types.</cyan>

---

## cert-msc24-c

The `cert-msc24-c` check is an alias, please see `bugprone-unsafe-functions` for more information.

---

## cert-msc30-c

The `cert-msc30-c` check is an alias, please see `cert-msc50-cpp` for more information. <cyan>(yes, it's pointing at the cpp one, don't @ me, that's just what it says ๐Ÿ’…)</cyan>

---

## cert-msc32-c

The `cert-msc32-c` check is an alias, please see `cert-msc51-cpp` for more information.

---

## cert-msc33-c

The `cert-msc33-c` check is an alias, please see `bugprone-unsafe-functions` for more information.

---

## cert-msc50-cpp

<rainbow>okay this is the RNG tea โ˜•</rainbow> Pseudorandom number generators use mathematical algorithms to produce a sequence of numbers with good statistical properties, but the numbers produced are <red>not genuinely random</red>. The `std::rand()` function takes a seed (number), runs a mathematical operation on it and returns the result. By manipulating the seed the result can be **predictable**. ๐Ÿ˜ฑ This check warns for the usage of `std::rand()`.

---

## cert-msc51-cpp

This check flags all pseudo-random number engines, engine adaptor instantiations and `srand()` when initialized or seeded with a <red>default argument, constant expression or any user-configurable type</red>. Pseudo-random number engines seeded with a predictable value may cause vulnerabilities โ€” e.g. in security protocols. ๐Ÿ”ฅ This is a CERT security rule, see <cyan>MSC51-CPP. Ensure your random number generator is properly seeded</cyan> and <cyan>MSC32-C. Properly seed pseudorandom number generators.</cyan>

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 values are `time_t`, `std::time_t`.

---

## cert-msc54-cpp

The `cert-msc54-cpp` check is an alias, please see `bugprone-signal-handler` for more information.

---

## cert-oop11-cpp

The `cert-oop11-cpp` check is an alias, please see `performance-move-constructor-init` for more information.

This check corresponds to the CERT C++ Coding Standard recommendation <cyan>OOP11-CPP. Do not copy-initialize members or base classes from a move constructor.</cyan> However, all of the CERT recommendations have been <purple>removed from public view</purple>, and so their justification for the behavior of this check requires an account on their wiki to view. ๐Ÿ™ˆ <peach>(gatekeeping, honestly)</peach>

---

## cert-oop54-cpp

The `cert-oop54-cpp` check is an alias, please see `bugprone-unhandled-self-assignment` for more information.

---

## cert-oop57-cpp

Flags use of the C standard library functions `memset`, `memcpy` and `memcmp` and similar derivatives on <red>non-trivial types</red>. ๐Ÿ’€ don't do that to your objects bestie.

### Options

- **`MemSetNames`** โ€” Specify extra functions to flag that act similarly to `memset`. Specify names in a <yellow>semicolon delimited</yellow> list. Default is an empty string. The check will detect the following functions: `memset`, `std::memset`.
- **`MemCpyNames`** โ€” Specify extra functions to flag that act similarly to `memcpy`. Specify names in a semicolon delimited list. Default is an empty string. The check will detect the following functions: `std::memcpy`, `memcpy`, `std::memmove`, `memmove`, `std::strcpy`, `strcpy`, `memccpy`, `stpncpy`, `strncpy`.
- **`MemCmpNames`** โ€” Specify extra functions to flag that act similarly to `memcmp`. Specify names in a semicolon delimited list. Default is an empty string. The check will detect the following functions: `std::memcmp`, `memcmp`, `std::strcmp`, `strcmp`, `strncmp`.

This check corresponds to the CERT C++ Coding Standard rule <cyan>OOP57-CPP. Prefer special member functions and overloaded operators to C Standard Library functions.</cyan>

---

## cert-oop58-cpp

Finds assignments to the copied object and its direct or indirect members in copy constructors and copy assignment operators. <lavender>the copy is copying onto itself... that's not the vibe.</lavender>

This check corresponds to the CERT C Coding Standard rule <cyan>OOP58-CPP. Copy operations must not mutate the source object.</cyan>

---

## cert-pos44-c

The `cert-pos44-c` check is an alias, please see `bugprone-bad-signal-to-kill-thread` for more information.

---

## cert-pos47-c

The `cert-pos47-c` check is an alias, please see `concurrency-thread-canceltype-asynchronous` for more information.

---

## cert-sig30-c

The `cert-sig30-c` check is an alias, please see `bugprone-signal-handler` for more information.

---

## cert-str34-c

The `cert-str34-c` check is an alias, please see `bugprone-signed-char-misuse` for more information.

---

## clang-analyzer-core.BitwiseShift

Finds cases where bitwise shift operation causes <red>undefined behaviour</red>. ๐Ÿ˜ญ shifting into the void, not the move.

The `clang-analyzer-core.BitwiseShift` check is an alias, please see <cyan>Clang Static Analyzer Available Checkers</cyan> for more information.

---

## clang-analyzer-core.CallAndMessage

Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers). <pink>the group chat of bugs fr fr.</pink>

The `clang-analyzer-core.CallAndMessage` check is an alias, please see <cyan>Clang Static Analyzer Available Checkers</cyan> for more information.

---

## clang-analyzer-core.DivideZero

Check for division by zero. <yellow>math said no.</yellow> ๐Ÿšซ

The `clang-analyzer-core.DivideZero` check is an alias, please see <cyan>Clang Static Analyzer Available Checkers</cyan> for more information.

---

## clang-analyzer-core.NonNullParamChecker

Check for null pointers passed as arguments to a function whose arguments are references or marked with the `'nonnull'` attribute. <mint>respect the attribute bestie, it's literally right there.</mint>

The `clang-analyzer-core.NonNullParamChecker` check is an alias, please see <cyan>Clang Static Analyzer Available Checkers</cyan> for more information.

---

## clang-analyzer-core.NullDereference

Check for dereferences of null pointers. ๐Ÿ’€ <hotpink>the classic. the icon. the moment.</hotpink>

The `clang-analyzer-core.NullDereference` check is an alias, please see <cyan>Clang Static Analyzer Available Checkers</cyan> for more information.

### ๐Ÿ’œ `clang-analyzer-core.StackAddressEscape`

Making sure stack memory addresses **don't** go running off and escaping the function ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ like bestie sneaking out the window.

The `clang-analyzer-core.StackAddressEscape` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿฆ‹ `clang-analyzer-core.UndefinedBinaryOperatorResult`

Catches undefined results coming out of binary operators ๐Ÿ‘€ no cap.

The `clang-analyzer-core.UndefinedBinaryOperatorResult` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒธ `clang-analyzer-core.VLASize`

Checks when you declare a VLA with an undefined or zero size ๐Ÿ˜ญ (girl, zero is not a size).

The `clang-analyzer-core.VLASize` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### โœจ `clang-analyzer-core.uninitialized.ArraySubscript`

Watching for uninitialized values being used as array subscripts ๐Ÿ™ˆ absolute chaos energy.

The `clang-analyzer-core.uninitialized.ArraySubscript` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŽ€ `clang-analyzer-core.uninitialized.Assign`

Catches you assigning uninitialized values ๐Ÿ’€ the audacity.

The `clang-analyzer-core.uninitialized.Assign` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿซถ `clang-analyzer-core.uninitialized.Branch`

Uninitialized values getting used as branch conditions?? <red>ma'am.</red> Not on this analyzer's watch ๐Ÿ”ฅ.

The `clang-analyzer-core.uninitialized.Branch` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒˆ `clang-analyzer-core.uninitialized.CapturedBlockVariable`

Check for blocks that capture uninitialized values ๐Ÿ“ฆ๐Ÿ’– โ€” the block is holding onto something it literally never got.

The `clang-analyzer-core.uninitialized.CapturedBlockVariable` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### โญ `clang-analyzer-core.uninitialized.NewArraySize`

Checks if the size of the array in a `new[]` expression is undefined ๐Ÿซ  because that's a whole mess.

The `clang-analyzer-core.uninitialized.NewArraySize` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ’œ `clang-analyzer-core.uninitialized.UndefReturn`

Catches uninitialized values getting returned to the caller ๐Ÿ˜ณ sending out a whole mystery box, no thanks.

The `clang-analyzer-core.uninitialized.UndefReturn` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ”ฅ `clang-analyzer-cplusplus.ArrayDelete`

Reports destructions of arrays of polymorphic objects that are destructed as their base class ๐Ÿ’… classic C++ betrayal.

The `clang-analyzer-cplusplus.ArrayDelete` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿฆ‹ `clang-analyzer-cplusplus.InnerPointer`

Checks for inner pointers of C++ containers used after re/deallocation ๐Ÿ‘‘ the "you freed me and I'm still here" situation.

The `clang-analyzer-cplusplus.InnerPointer` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒธ `clang-analyzer-cplusplus.Move`

Finds use-after-move bugs in C++ ๐Ÿƒโ€โ™€๏ธโœจ bestie moved out, why are you still texting her.

The `clang-analyzer-cplusplus.Move` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŽ€ `clang-analyzer-cplusplus.NewDelete`

Checks for double-free and use-after-free problems, and traces memory managed by `new`/`delete` ๐Ÿ’€ twice is not twice as nice.

The `clang-analyzer-cplusplus.NewDelete` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿซถ `clang-analyzer-cplusplus.NewDeleteLeaks`

Checks for memory leaks, and traces memory managed by `new`/`delete` ๐Ÿ˜ญ your memory went on a little walk and never came back.

The `clang-analyzer-cplusplus.NewDeleteLeaks` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒˆ `clang-analyzer-cplusplus.PlacementNew`

Checks if default placement `new` is provided with pointers to sufficient storage capacity ๐Ÿ“ no cap, you gotta have the space before you move in.

The `clang-analyzer-cplusplus.PlacementNew` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### โญ `clang-analyzer-cplusplus.PureVirtualCall`

Checks pure virtual function calls during construction/destruction ๐Ÿ˜ตโ€๐Ÿ’ซ iconic nightmare fuel, fr fr.

The `clang-analyzer-cplusplus.PureVirtualCall` check is an alias of *Clang Static Analyzer* `cplusplus.PureVirtualCall`.

---

### ๐Ÿ’œ `clang-analyzer-cplusplus.StringChecker`

Checks C++ `std::string` bugs ๐Ÿ“ your string is going through it.

The `clang-analyzer-cplusplus.StringChecker` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ”ฅ `clang-analyzer-deadcode.DeadStores`

Checks for values stored to variables that are never read afterwards ๐Ÿ™ˆ the variable said nothing and left.

The `clang-analyzer-deadcode.DeadStores` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿฆ‹ `clang-analyzer-fuchsia.HandleChecker`

A checker that detects leaks related to Fuchsia handles ๐Ÿ‘‘ keeping tabs on those handles so they don't wander off.

The `clang-analyzer-fuchsia.HandleChecker` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒธ `clang-analyzer-nullability.NullPassedToNonnull`

Warns when a null pointer is passed to a pointer which has a `_Nonnull` type ๐Ÿ’€ you said nonnull and then sent null, be so serious.

The `clang-analyzer-nullability.NullPassedToNonnull` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŽ€ `clang-analyzer-nullability.NullReturnedFromNonnull`

Warns when a null pointer is returned from a function that has a `_Nonnull` return type ๐Ÿ˜ญ the promise was right there.

The `clang-analyzer-nullability.NullReturnedFromNonnull` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿซถ `clang-analyzer-nullability.NullableDereferenced`

Warns when a nullable pointer is dereferenced ๐Ÿ™ˆ girl you were told she might not show up.

The `clang-analyzer-nullability.NullableDereferenced` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒˆ `clang-analyzer-nullability.NullablePassedToNonnull`

Warns when a nullable pointer is passed to a pointer which has a `_Nonnull` type ๐Ÿ˜ณ mismatch of the century.

The `clang-analyzer-nullability.NullablePassedToNonnull` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### โญ `clang-analyzer-nullability.NullableReturnedFromNonnull`

Warns when a nullable pointer is returned from a function that has a `_Nonnull` return type ๐Ÿ’… the vibes are inconsistent and the analyzer noticed.

The `clang-analyzer-nullability.NullableReturnedFromNonnull` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ’œ `clang-analyzer-optin.core.EnumCastOutOfRange`

Checks integer to enumeration casts for out of range values ๐Ÿ” that number does not belong in that enum, periodt.

The `clang-analyzer-optin.core.EnumCastOutOfRange` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ”ฅ `clang-analyzer-optin.cplusplus.UninitializedObject`

Reports uninitialized fields after object construction ๐Ÿซ  the object got built and some of its rooms are still empty.

The `clang-analyzer-optin.cplusplus.UninitializedObject` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿฆ‹ `clang-analyzer-optin.cplusplus.VirtualCall`

Checks virtual function calls during construction/destruction โœจ same iconic chaos, different flag.

The `clang-analyzer-optin.cplusplus.VirtualCall` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒธ `clang-analyzer-optin.mpi.MPI-Checker`

Checks MPI code ๐Ÿ‘‘ yes, that MPI.

The `clang-analyzer-optin.mpi.MPI-Checker` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŽ€ `clang-analyzer-optin.osx.OSObjectCStyleCast`

Checker for C-style casts of `OSObject`s ๐Ÿ’€ old-school cast energy, and not in the cute way.

The `clang-analyzer-optin.osx.OSObjectCStyleCast` check is an alias of *Clang Static Analyzer* `optin.osx.OSObjectCStyleCast`.

---

### ๐Ÿซถ `clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker`

Checks that `NSLocalizedString` macros include a comment for context ๐Ÿ“ bestie the translator needs context, give them something.

The `clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒˆ `clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker`

Warns about uses of non-localized `NSString`s passed to UI methods expecting localized `NSString`s ๐ŸŒ that string did not get the localization treatment.

The `clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### โญ `clang-analyzer-optin.performance.GCDAntipattern`

Checks for performance anti-patterns when using Grand Central Dispatch โšก the anti-pattern is anti-ing.

The `clang-analyzer-optin.performance.GCDAntipattern` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ’œ `clang-analyzer-optin.performance.Padding`

Checks for excessively padded structs ๐Ÿ›‹๏ธ so much padding, so little data.

The `clang-analyzer-optin.performance.Padding` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ”ฅ `clang-analyzer-optin.portability.UnixAPI`

Finds implementation-defined behavior in UNIX/Posix functions ๐Ÿง classic portability drama.

The `clang-analyzer-optin.portability.UnixAPI` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿฆ‹ `clang-analyzer-optin.taint.TaintedAlloc`

Checks for memory allocations where the size parameter might be a tainted (attacker controlled) value ๐Ÿ˜ฑ bestie the attacker is picking your allocation size, that's not a vibe.

The `clang-analyzer-optin.taint.TaintedAlloc` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒธ `clang-analyzer-osx.API`

Checks for proper uses of various Apple APIs ๐ŸŽ use them right, please.

The `clang-analyzer-osx.API` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŽ€ `clang-analyzer-osx.MIG`

Finds violations of the Mach Interface Generator calling convention ๐Ÿ“ž follow the convention, it's not optional.

The `clang-analyzer-osx.MIG` check is an alias of *Clang Static Analyzer* `osx.MIG`.

---

### ๐Ÿซถ `clang-analyzer-osx.NumberObjectConversion`

Checks for erroneous conversions of objects representing numbers into numbers ๐Ÿ”ข object to number, but make it wrong.

The `clang-analyzer-osx.NumberObjectConversion` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒˆ `clang-analyzer-osx.OSObjectRetainCount`

Checks for leaks and improper reference count management for `OSObject` ๐Ÿซ  the retain count is doing its own thing again.

The `clang-analyzer-osx.OSObjectRetainCount` check is an alias of *Clang Static Analyzer* `osx.OSObjectRetainCount`.

---

### โญ `clang-analyzer-osx.ObjCProperty`

Checks for proper uses of Objective-C properties ๐Ÿ’… property etiquette matters.

The `clang-analyzer-osx.ObjCProperty` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ’œ `clang-analyzer-osx.SecKeychainAPI`

Checks for proper uses of Secure Keychain APIs ๐Ÿ” keys stay secure, bestie.

The `clang-analyzer-osx.SecKeychainAPI` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿ”ฅ `clang-analyzer-osx.cocoa.AtSync`

Checks for nil pointers used as mutexes for `@synchronized` ๐Ÿ’€ locking on nil, iconic mistake.

The `clang-analyzer-osx.cocoa.AtSync` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐Ÿฆ‹ `clang-analyzer-osx.cocoa.AutoreleaseWrite`

Warns about potentially crashing writes to autoreleasing objects from different autoreleasing pools in Objective-C ๐Ÿ˜ญ pool crossover drama, and it crashes.

The `clang-analyzer-osx.cocoa.AutoreleaseWrite` check is an alias, please see *Clang Static Analyzer Available Checkers* for more information.

---

### ๐ŸŒธ `clang-analyzer-osx.cocoa.ClassRelease`

Checks for sending `'retain'`, `'release'`, or `'autorelease'` directly to a Class ๐Ÿซฃ you cannot just do that to a Class.

### `clang-analyzer-osx.cocoa.ClassRelease`

Screams at you when Objective-C classes aren't being released correctly. ๐Ÿ’…

The `clang-analyzer-osx.cocoa.ClassRelease` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.Dealloc`

Warns about Objective-C classes that don't have a correct implementation of `-dealloc`. like bestie where is your cleanup ๐Ÿ˜ญ

The `clang-analyzer-osx.cocoa.Dealloc` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.IncompatibleMethodTypes`

Warns about Objective-C method signatures where the types just don't match up. it's giving mismatched energy ๐Ÿ’€

The `clang-analyzer-osx.cocoa.IncompatibleMethodTypes` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.Loops`

Better modeling of loops that use Cocoa collection types. โœจ

The `clang-analyzer-osx.cocoa.Loops` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.MissingSuperCall`

Warns about Objective-C methods that forgot the call to `super` that they literally cannot skip. ๐Ÿ™ˆ

The `clang-analyzer-osx.cocoa.MissingSuperCall` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.NSAutoreleasePool`

Warns about suboptimal uses of `NSAutoreleasePool` when you're in Objective-C GC mode.

The `clang-analyzer-osx.cocoa.NSAutoreleasePool` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.NSError`

Checks how you're using `NSError**` parameters. ๐Ÿ‘‘

The `clang-analyzer-osx.cocoa.NSError` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.NilArg`

Checks for `nil` arguments being passed to ObjC method calls where `nil` is a big no-no. ๐Ÿšซ

The `clang-analyzer-osx.cocoa.NilArg` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.NonNilReturnValue`

Models the APIs that are guaranteed to hand you back a non-`nil` value. no cap ๐Ÿซถ

The `clang-analyzer-osx.cocoa.NonNilReturnValue` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.ObjCGenerics`

Checks for type errors when you're out here using Objective-C generics.

The `clang-analyzer-osx.cocoa.ObjCGenerics` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.RetainCount`

Checks for leaks and messy reference count management. the way that retain count will humble you ๐Ÿ˜ญ

The `clang-analyzer-osx.cocoa.RetainCount` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak`

Checks for leaked memory hanging out in autorelease pools that are never, ever gonna be drained. ๐Ÿ’€

The `clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.SelfInit`

Checks that `'self'` is properly initialized inside an initializer method. main character energy only โœจ

The `clang-analyzer-osx.cocoa.SelfInit` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.SuperDealloc`

Warns about improper use of `'[super dealloc]'` in Objective-C.

The `clang-analyzer-osx.cocoa.SuperDealloc` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.UnusedIvars`

Warns about private ivars that nobody ever uses. ghosted fr ๐Ÿ‘ป

The `clang-analyzer-osx.cocoa.UnusedIvars` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.cocoa.VariadicMethodTypes`

Checks for passing non-Objective-C types into variadic collection initialization methods that only want Objective-C types. periodt ๐Ÿ’…

The `clang-analyzer-osx.cocoa.VariadicMethodTypes` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.coreFoundation.CFError`

Checks how you're using `CFErrorRef*` parameters.

The `clang-analyzer-osx.coreFoundation.CFError` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.coreFoundation.CFNumber`

Checks for proper uses of the `CFNumber` APIs. ๐ŸŒธ

The `clang-analyzer-osx.coreFoundation.CFNumber` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.coreFoundation.CFRetainRelease`

Checks for `null` arguments going into `CFRetain`/`CFRelease`/`CFMakeCollectable`. ๐Ÿšจ

The `clang-analyzer-osx.coreFoundation.CFRetainRelease` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.coreFoundation.containers.OutOfBounds`

Checks for index out-of-bounds situations when you're using the `'CFArray'` API.

The `clang-analyzer-osx.coreFoundation.containers.OutOfBounds` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-osx.coreFoundation.containers.PointerSizedValues`

Warns if `'CFArray'`, `'CFDictionary'`, or `'CFSet'` get created with values that aren't pointer-sized.

The `clang-analyzer-osx.coreFoundation.containers.PointerSizedValues` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.FloatLoopCounter`

Warns when you're using a floating point value as a loop counter (CERT: `FLP30-C`, `FLP30-CPP`). bestie no ๐Ÿ˜ญ

The `clang-analyzer-security.FloatLoopCounter` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.PutenvStackArray`

Finds calls to the function `'putenv'` that pass a pointer to an automatic (stack-allocated) array as the argument. that's a yikes ๐Ÿ”ฅ

The `clang-analyzer-security.PutenvStackArray` check is an alias of Clang Static Analyzer `security.PutenvStackArray`.

### `clang-analyzer-security.SetgidSetuidOrder`

Warns about a possibly reversed order of `'setgid(getgid()))'` and `'setuid(getuid())'` (CERT: `POS36-C`). order matters, queen ๐Ÿ‘‘

The `clang-analyzer-security.SetgidSetuidOrder` check is an alias of Clang Static Analyzer `security.SetgidSetuidOrder`.

### `clang-analyzer-security.cert.env.InvalidPtr`

Finds usages of pointers that might be invalidated. ๐Ÿ•ต๏ธโ€โ™€๏ธ

The `clang-analyzer-security.cert.env.InvalidPtr` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling`

Warns on uses of buffer-manipulating functions that are unsecure or deprecated.

The `clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.UncheckedReturn`

Warns on uses of functions whose return values must always be checked. yes ALL of them ๐Ÿ’…

The `clang-analyzer-security.insecureAPI.UncheckedReturn` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.bcmp`

Warns on uses of the `'bcmp'` function.

The `clang-analyzer-security.insecureAPI.bcmp` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.bcopy`

Warns on uses of the `'bcopy'` function.

The `clang-analyzer-security.insecureAPI.bcopy` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.bzero`

Warns on uses of the `'bzero'` function.

The `clang-analyzer-security.insecureAPI.bzero` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.decodeValueOfObjCType`

Warns on uses of the `'-decodeValueOfObjCType:at:'` method. ๐Ÿ“

The `clang-analyzer-security.insecureAPI.decodeValueOfObjCType` check is an alias of Clang Static Analyzer `security.insecureAPI.decodeValueOfObjCType`.

### `clang-analyzer-security.insecureAPI.getpw`

Warns on uses of the `'getpw'` function.

The `clang-analyzer-security.insecureAPI.getpw` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.gets`

Warns on uses of the `'gets'` function. yeah literally never use `gets` ๐Ÿ˜ญ

The `clang-analyzer-security.insecureAPI.gets` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.mkstemp`

Warns when `'mkstemp'` gets fewer than 6 `X`'s in the format string. count them bestie ๐Ÿ”ข

The `clang-analyzer-security.insecureAPI.mkstemp` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.mktemp`

Warns on uses of the `'mktemp'` function.

The `clang-analyzer-security.insecureAPI.mktemp` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.rand`

Warns on uses of `'rand'`, `'random'`, and all their related little friends. ๐ŸŽฒ

The `clang-analyzer-security.insecureAPI.rand` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.strcpy`

Warns on uses of the `'strcpy'` and `'strcat'` functions.

The `clang-analyzer-security.insecureAPI.strcpy` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-security.insecureAPI.vfork`

Warns on uses of the `'vfork'` function. ๐Ÿ’€

The `clang-analyzer-security.insecureAPI.vfork` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.API`

Checks calls to a whole bunch of UNIX/Posix functions. ๐ŸŒˆ

The `clang-analyzer-unix.API` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.BlockInCriticalSection`

Checks for calls to blocking functions happening inside a critical section. not the vibe ๐Ÿšซ

The `clang-analyzer-unix.BlockInCriticalSection` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.Errno`

Checks for improper use of `'errno'`.

The `clang-analyzer-unix.Errno` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.Malloc`

Checks for memory leaks, double free, and use-after-free problems. Traces memory that's managed by `malloc()`/`free()`. iconic detective work ๐Ÿ”

The `clang-analyzer-unix.Malloc` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.MallocSizeof`

Checks for dubious `malloc` arguments involving `sizeof`. ๐Ÿคจ

The `clang-analyzer-unix.MallocSizeof` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.MismatchedDeallocator`

Checks for mismatched deallocators. they gotta match, bestie ๐Ÿ’…

The `clang-analyzer-unix.MismatchedDeallocator` check is an alias, please see Clang Static Analyzer Available Checkers for more information.

### `clang-analyzer-unix.StdCLibraryFunctions`

Checks for invalid arguments of C standard library functions, and applies relations between arguments and return value. ๐Ÿงš

### clang-analyzer-unix.StdCLibraryFunctions

okay so this one is an <pink>alias</pink> bestie ๐Ÿ’– โ€” for the actual tea, go see *Clang Static Analyzer Available Checkers*.

### clang-analyzer-unix.Stream

Checks your stream handling functions ๐Ÿšฐ

Also just an alias, so peek at *Clang Static Analyzer Available Checkers* for the real info.

### clang-analyzer-unix.Vfork

Checks that you're using `vfork` properly ๐Ÿ‘€

Alias alert โœจ โ€” *Clang Static Analyzer Available Checkers* has the deets.

### clang-analyzer-unix.cstring.BadSizeArg

Checks the size argument you're passing into C string functions for common erroneous patterns ๐Ÿง

Yep, it's an alias โ€” *Clang Static Analyzer Available Checkers*, go look.

### clang-analyzer-unix.cstring.NullArg

Checks for null pointers being passed as arguments to C string functions ๐Ÿšซ

Alias vibes โ€” please see *Clang Static Analyzer Available Checkers* for more.

### clang-analyzer-valist.CopyToSelf

Checks for `va_list`s which are copied onto itself ๐Ÿ™ˆ

The `clang-analyzer-valist.CopyToSelf` check is an alias of Clang Static Analyzer `valist.CopyToSelf`.

### clang-analyzer-valist.Uninitialized

Checks for usages of uninitialized (or already released) `va_list`s ๐Ÿ˜ญ

The `clang-analyzer-valist.Uninitialized` check is an alias of Clang Static Analyzer `valist.Uninitialized`.

### clang-analyzer-valist.Unterminated

Checks for `va_list`s which are not released by a `va_end` call ๐Ÿ‘‘

The `clang-analyzer-valist.Unterminated` check is an alias of Clang Static Analyzer `valist.Unterminated`.

### clang-analyzer-webkit.NoUncountedMemberChecker

Checks for no uncounted member variables ๐ŸŒธ

This one's an alias โ€” *Clang Static Analyzer Available Checkers* is where you wanna be.

### clang-analyzer-webkit.RefCntblBaseVirtualDtor

Checks for any ref-countable base class having a virtual destructor ๐Ÿฆ‹

Alias, bestie โ€” see *Clang Static Analyzer Available Checkers* for the full story.

### clang-analyzer-webkit.UncountedLambdaCapturesChecker

Checks uncounted lambda captures ๐ŸŽ€

Alias energy โ€” *Clang Static Analyzer Available Checkers*, go get your info.

---

### concurrency-mt-unsafe

Checks for some thread-unsafe functions against a black list of known-to-be-unsafe functions ๐Ÿ’€ Usually they access static variables without synchronization (like `gmtime(3)`) or utilize signals in a racy way. The set of functions to check is specified with the `FunctionSet` option.

> ngl, using some thread-unsafe functions may still be valid in concurrent programming if only a single thread is used (like `setenv(3)`), but some functions may track a state in global variables which would get clobbered by subsequent (non-parallel, but concurrent) calls to a related function. Like, look at this code suffering from unprotected accesses to a global state:

// getnetent(3) maintains global state with DB connection, etc. // If a concurrent green thread calls getnetent(3), the global state is corrupted. netent = getnetent(); yield(); netent = getnetent();


Examples:

tm = gmtime(timep); // uses a global buffer

sleep(1); // implementation may use SIGALRM


- **`FunctionSet`** โ€” Specifies which functions in libc should be considered thread-safe. Possible values are `posix`, `glibc`, or `any`.

  - `posix` means POSIX defined thread-unsafe functions. POSIX.1-2001 in "2.9.1 Thread-Safety" defines that all functions specified in the standard are thread-safe except a predefined list of thread-unsafe functions.
  - `Glibc` defines some of them as thread-safe (like `dirname(3)`), but adds non-POSIX thread-unsafe ones (like `getopt_long(3)`). Glibc's list is compiled from GNU web documentation with a search for MT-Safe tag: `https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html`
  - If you want to identify thread-unsafe API for at least one libc or you're unsure which libc will be used, use `any` (default). ๐Ÿ’…

### concurrency-thread-canceltype-asynchronous

Finds `pthread_setcanceltype` function calls where a thread's cancellation type is set to asynchronous ๐Ÿšจ Asynchronous cancellation type (`PTHREAD_CANCEL_ASYNCHRONOUS`) is generally unsafe โ€” use type `PTHREAD_CANCEL_DEFERRED` instead, which is the default. Even with deferred cancellation, a cancellation point in an asynchronous signal handler may still be acted upon and the effect is as if it was an asynchronous cancellation.

pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);


This check corresponds to the CERT C Coding Standard rule POS47-C. Do not use threads that can be canceled asynchronously.

`cert-pos47-c` redirects here as an alias of this check โœจ

### cppcoreguidelines-avoid-c-arrays

The `cppcoreguidelines-avoid-c-arrays` check is an alias, please see `modernize-avoid-c-arrays` for more information.

### cppcoreguidelines-avoid-capturing-lambda-coroutines

Flags C++20 coroutine lambdas with non-empty capture lists that may cause use-after-free errors ๐Ÿ˜ฑ and suggests avoiding captures or ensuring the lambda closure object has a guaranteed lifetime.

This check implements CP.51 from the C++ Core Guidelines.

Using coroutine lambdas with non-empty capture lists can be risky, because capturing variables can lead to accessing freed memory after the first suspension point. This issue can occur even with refcounted smart pointers and copyable types. When a lambda expression creates a coroutine, it results in a closure object with storage, which is often on the stack and will eventually go out of scope. When the closure object goes out of scope, its captures also go out of scope. While normal lambdas finish executing before this happens, coroutine lambdas may resume from suspension after the closure object has been destructed, resulting in use-after-free memory access for all captures ๐Ÿ’€

Consider the following example:

int value = get_value(); std::shared_ptr<Foo> sharedFoo = get_foo(); { const auto lambda = [value, sharedFoo]() -> std::future<void> { co_await something(); // "sharedFoo" and "value" have already been destroyed // the "shared" pointer didn't accomplish anything }; lambda(); } // the lambda closure object has now gone out of scope


In this example, the lambda object is defined with two captures: `value` and `sharedFoo`. When `lambda()` is called, the lambda object is created on the stack, and the captures are copied into the closure object. When the coroutine is suspended, the lambda object goes out of scope, and the closure object is destroyed. When the coroutine is resumed, the captured variables may have been destroyed, resulting in use-after-free bugs ๐Ÿ˜ญ

In conclusion, the use of coroutine lambdas with non-empty capture lists can lead to use-after-free errors when resuming the coroutine after the closure object has been destroyed. This check helps prevent such errors by flagging C++20 coroutine lambdas with non-empty capture lists and suggesting avoiding captures or ensuring the lambda closure object has a guaranteed lifetime.

Following these guidelines can help ensure the safe and reliable use of coroutine lambdas in C++ code ๐Ÿซถ

### cppcoreguidelines-avoid-const-or-ref-data-members

This check warns when structs or classes that are copyable or movable have `const`-qualified or reference (lvalue or rvalue) data members ๐Ÿ‘€ Having such members is rarely useful, and makes the class only copy-constructible but not copy-assignable.

Examples:

// Bad, const-qualified member struct Const { const int x; }

// Good: class Foo { public: int get() const { return x; } private: int x; };

// Bad, lvalue reference member struct Ref { int& x; };

// Good: struct Foo { int* x; std::unique_ptr<int> x; std::shared_ptr<int> x; gsl::not_null<int*> x; };

// Bad, rvalue reference member struct RefRef { int&& x; };


This check implements C.12 from the C++ Core Guidelines.

Further reading: *Data members: Never const*.

### cppcoreguidelines-avoid-do-while

Warns when you're using `do-while` loops ๐Ÿ™… They're less readable than plain `while` loops, since the termination condition is at the end and the condition is not checked prior to the first iteration. This can lead to subtle bugs.

This check implements ES.75 from the C++ Core Guidelines.

Examples:

int x; do { std::cin >> x; // ... } while (x < 0);


**Options**

- **`IgnoreMacros`** โ€” Ignore the check when analyzing macros. This is useful for safely defining function-like macros:

#define FOO_BAR(x) \ do { \ foo(x); \ bar(x); \ } while(0)


  Defaults to `false`.

### cppcoreguidelines-avoid-goto

The usage of `goto` for control flow is error prone and should be replaced with looping constructs ๐Ÿ˜ค Only forward jumps in nested loops are accepted.

This check implements ES.76 from the C++ Core Guidelines and 6.3.1 from High Integrity C++ Coding Standard.

For more information on why to avoid programming with `goto` you can read the famous paper *A Case against the GO TO Statement.*.

The check diagnoses `goto` for backward jumps in every language mode. These should be replaced with C/C++ looping constructs.

// Bad, handwritten for loop. int i = 0; // Jump label for the loop loop_start: do_some_operation();

if (i < 100) { ++i; goto loop_start; }

// Better for(int i = 0; i < 100; ++i) do_some_operation();


Modern C++ needs `goto` only to jump out of nested loops.

for(int i = 0; i < 100; ++i) { for(int j = 0; j < 100; ++j) { if (i * j > 500) goto early_exit; } }

early_exit: some_operation();


All other uses of `goto` are diagnosed in C++ โœจ

### cppcoreguidelines-avoid-magic-numbers

The `cppcoreguidelines-avoid-magic-numbers` check is an alias, please see `readability-magic-numbers` for more information.

### cppcoreguidelines-avoid-non-const-global-variables

Finds non-`const` global variables as described in I.2 of C++ Core Guidelines ๐Ÿ” As R.6 of C++ Core Guidelines is a duplicate of rule I.2 it also covers that rule.

char a; // Warns! const char b = 0;

namespace some_namespace { char c; // Warns! const char d = 0; }

char * c_ptr1 = &some_namespace::c; // Warns! char *const c_const_ptr = &some_namespace::c; // Warns! char & c_reference = some_namespace::c; // Warns!

class Foo // No Warnings inside Foo, only namespace scope is covered { public: char e = 0; const char f = 0; protected: char g = 0; private: char h = 0; };


So the variables `a`, `c`, `c_ptr1`, `c_const_ptr` and `c_reference` are all gonna get flagged ๐Ÿšจ โ€” because they're either a <hotpink>non-const globally accessible variable</hotpink>, or a pointer / reference that hands out global access to non-const data, or honestly both at once. Overachievers fr.

**Options**

- **`AllowInternalLinkage`** โ€” when you set this to `true`, static non-const variables and variables sitting in anonymous namespaces will <green>not</green> generate a warning. Default value is `false`. (so like, by default it's off, bestie ๐Ÿ’…)

---

### `cppcoreguidelines-avoid-reference-coroutine-parameters`

Warns when a coroutine accepts reference parameters. Lowkey terrifying because after a coroutine suspend point, references could be **dangling** and no longer valid ๐Ÿ˜ญ. Instead, pass parameters as values. No cap.

Examples:

std::future<int> someCoroutine(int& val) { co_await ...; // When the coroutine is resumed, 'val' might no longer be valid. if (val) ... }


This check implements `CP.53` from the C++ Core Guidelines.

---

### `cppcoreguidelines-c-copy-assignment-signature`

The `cppcoreguidelines-c-copy-assignment-signature` check is an alias, please see `misc-unconventional-assign-operator` for more information. (yes it's giving middle-manager, it just points you somewhere else ๐Ÿซถ)

---

### `cppcoreguidelines-explicit-virtual-functions`

The `cppcoreguidelines-explicit-virtual-functions` check is an alias, please see `modernize-use-override` for more information. Same energy, different name ๐Ÿ’–

---

### `cppcoreguidelines-init-variables`

Checks whether there are local variables that are declared <red>without an initial value</red>. These may lead to unexpected behavior if there is a code path that reads the variable before assigning to it. And bestie, that's exactly the kind of bug that shows up at 3am ๐ŸŒ™

This rule is part of the Type safety (`Type.5`) profile and `ES.20` from the C++ Core Guidelines.

Only integers, booleans, floats, doubles and pointers are checked. The fix option initializes all detected values with the value of zero. An exception is `float` and `double` types, which are initialized to `NaN`.

As an example a function that looks like this:

void function() { int x; char *txt; double d;

// Rest of the function. }


Would be rewritten to look like this:

#include <math.h>

void function() { int x = 0; char *txt = nullptr; double d = NAN;

// Rest of the function. }


It warns for the uninitialized enum case, but **without a FixIt** (rude, but okay ๐Ÿ™ˆ):

enum A {A1, A2, A3}; enum A_c : char { A_c1, A_c2, A_c3 }; enum class B { B1, B2, B3 }; enum class B_i : int { B_i1, B_i2, B_i3 }; void function() { A a; // Warning: variable 'a' is not initialized A_c a_c; // Warning: variable 'a_c' is not initialized B b; // Warning: variable 'b' is not initialized B_i b_i; // Warning: variable 'b_i' is not initialized }


**Options**

- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.
- **`MathHeader`** โ€” a string specifying the header to include to get the definition of `NAN`. Default is `<math.h>`.

---

### `cppcoreguidelines-interfaces-global-init`

This check flags initializers of globals that access `extern` objects, and therefore can lead to <red>order-of-initialization problems</red>. The classic "why is my global a garbage value" saga ๐Ÿ’€

This check implements `I.22` from the C++ Core Guidelines.

Note that currently this does not flag calls to non-`constexpr` functions, and therefore globals could still be accessed from functions themselves. (iykyk, it's a whole thing)

---

### `cppcoreguidelines-macro-to-enum`

The `cppcoreguidelines-macro-to-enum` check is an alias, please see `modernize-macro-to-enum` for more information. โœจ

---

### `cppcoreguidelines-macro-usage`

Finds macro usage that is considered problematic because <purple>better language constructs exist for the task</purple>. Like bestie, we have `constexpr` now, it's 2024 ๐ŸŒˆ

The relevant sections in the C++ Core Guidelines are `ES.31`, and `ES.32`.

Examples:

#define C 0 #define F1(x, y) ((a) > (b) ? (a) : (b)) #define F2(...) (__VA_ARGS__) #define F3(x, y) x##y #define COMMA , #define NORETURN [[noreturn]] #define DEPRECATED attribute((deprecated)) #if LIB_EXPORTS #define DLLEXPORTS __declspec(dllexport) #else #define DLLEXPORTS __declspec(dllimport) #endif


results in the following warnings:

4 warnings generated. test.cpp:1:9: warning: macro 'C' used to declare a constant; consider using a 'constexpr' constant [cppcoreguidelines-macro-usage] #define C 0 ^ test.cpp:2:9: warning: function-like macro 'F1' used; consider a 'constexpr' template function [cppcoreguidelines-macro-usage] #define F1(x, y) ((a) > (b) ? (a) : (b)) ^ test.cpp:3:9: warning: variadic macro 'F2' used; consider using a 'constexpr' variadic template function [cppcoreguidelines-macro-usage] #define F2(...) (__VA_ARGS__) ^


**Options**

- **`AllowedRegexp`** โ€” a regular expression to filter allowed macros. For example `DEBUG*|LIBTORRENT*|TORRENT*|UNI*` could be applied to filter libtorrent. Default value is `^DEBUG_*`.
- **`CheckCapsOnly`** โ€” boolean flag to warn on all macros except those with `CAPS_ONLY` names. This option is intended to ease introduction of this check into older code bases. Default value is `false`.
- **`IgnoreCommandLineMacros`** โ€” boolean flag to toggle ignoring command-line-defined macros. Default value is `true`.

---

### `cppcoreguidelines-misleading-capture-default-by-value`

Warns when lambda specify a by-value capture default **and** capture `this`. Ma'am that is a jumpscare ๐Ÿซฃ

By-value capture defaults in member functions can be misleading about whether data members are captured by value or reference. This occurs because specifying the capture default `[=]` actually captures the `this` pointer <hotpink>by value</hotpink>, not the data members themselves. As a result, data members are still indirectly accessed via the captured `this` pointer, which essentially means they are being accessed by reference. Therefore, even when using `[=]`, data members are effectively captured by reference, which might not align with the user's expectations. Not me thinking `[=]` was safe ๐Ÿ˜ญ

Examples:

struct AClass { int member; void misleadingLogic() { int local = 0; member = 0; auto f = [=]() mutable { local += 1; member += 1; }; f(); // Here, local is 0 but member is 1 }

void clearLogic() { int local = 0; member = 0; auto f = [this, local]() mutable { local += 1; member += 1; }; f(); // Here, local is 0 but member is 1 } };


This check implements `F.54` from the C++ Core Guidelines.

---

### `cppcoreguidelines-missing-std-forward`

Warns when a forwarding reference parameter is **not** forwarded inside the function body. Bestie, you had one job and it was `std::forward` ๐Ÿ’€

Example:

template <class T> void wrapper(T&& t) { impl(std::forward<T>(t), 1, 2); // Correct }

template <class T> void wrapper2(T&& t) { impl(t, 1, 2); // Oops - should use std::forward<T>(t) }

template <class T> void wrapper3(T&& t) { impl(std::move(t), 1, 2); // Also buggy - should use std::forward<T>(t) }

template <class F> void wrapper_function(F&& f) { std::forward<F>(f)(1, 2); // Correct }

template <class F> void wrapper_function2(F&& f) { f(1, 2); // Incorrect - may not invoke the desired qualified function operator }


This check implements `F.19` from the C++ Core Guidelines.

---

### `cppcoreguidelines-narrowing-conversions`

Checks for <yellow>silent narrowing conversions</yellow>, 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; }`. That second one is giving sneaky little gremlin ๐Ÿ“

This check implements `ES.46` from the C++ Core Guidelines.

We enforce only part of the guideline, more specifically, we flag narrowing conversions from:

- an integer to a narrower integer (e.g. `char` to `unsigned char`) if `WarnOnIntegerNarrowingConversion` Option is set,
- an integer to a narrower floating-point (e.g. `uint64_t` to `float`) if `WarnOnIntegerToFloatingPointNarrowingConversion` Option is set,
- a floating-point to an integer (e.g. `double` to `int`),
- a floating-point to a narrower floating-point (e.g. `double` to `float`) if `WarnOnFloatingPointNarrowingConversion` Option is set.

This check will flag:

- 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);`,
- All applications of binary operators with a narrowing conversions. For example: `int i; i+= 0.1;`.

**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<char>().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 it's `true`, this check gets *extra* picky and will warn you about assigning a floating point constant to an integer value <pink>even if the float is exactly representable</pink> in the destination type (like, hello, `int i = 1.0;`). It's `false` by default, so chill by default. โœจ

---

## ๐ŸŒธ FAQ ๐ŸŒธ

- <hotpink>**What does "narrowing conversion from 'int' to 'float'" mean?**</hotpink>

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]`. <cyan>so yeah, the float just cannot hold every int, that's the whole tea ๐Ÿซ–</cyan>

- <hotpink>**What does "implementation-defined" mean?**</hotpink>

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. <purple>iykyk ๐Ÿ’…</purple>

---

### ๐ŸŒˆ `cppcoreguidelines-no-malloc` ๐ŸŒˆ

This check handles C-Style memory management using `malloc()`, `realloc()`, `calloc()` and `free()`. It warns about its use and tries to suggest the use of an appropriate RAII object. Furthermore, it can be configured to check against a user-specified list of functions that are used for memory management (e.g. `posix_memalign()`). <yellow>bestie, raw malloc is giving 1994 โœ‹</yellow>

This check implements **R.10** from the C++ Core Guidelines.

There is no attempt made to provide fix-it hints, since manual resource management isn't easily transformed automatically into RAII. <mint>no auto-glowup available, sad</mint>

// Warns each of the following lines. // Containers like std::vector or std::string should be used. char* some_string = (char*) malloc(sizeof(char) * 20); char* some_string = (char*) realloc(sizeof(char) * 30); free(some_string);

int* int_array = (int*) calloc(30, sizeof(int));

// Rather use a smartpointer or stack variable. struct some_struct* s = (struct some_struct*) malloc(sizeof(struct some_struct));


#### โœจ Options โœจ

- **`Allocations`** โ€” semicolon-separated list of fully qualified names of memory allocation functions. Defaults to `::malloc;::calloc`. ๐ŸŽ€
- **`Deallocations`** โ€” semicolon-separated list of fully qualified names of memory allocation functions. Defaults to `::free`. ๐ŸŽ€
- **`Reallocations`** โ€” semicolon-separated list of fully qualified names of memory allocation functions. Defaults to `::realloc`. ๐ŸŽ€

---

### ๐Ÿ’€ `cppcoreguidelines-no-suspend-with-lock` ๐Ÿ’€

Flags coroutines that suspend while a lock guard is in scope at the suspension point. <red>this one is a genuine yikes, not a cute yikes</red>

When a coroutine suspends, any mutexes held by the coroutine will remain locked until the coroutine resumes and eventually destructs the lock guard. This can lead to long periods with a mutex held and runs the risk of deadlock. <red>deadlock is NOT the vibe ๐Ÿ”ฅ</red>

Instead, locks should be released before suspending a coroutine.

This check only checks suspending coroutines while a `lock_guard` is in scope; it does not consider manual locking or unlocking of mutexes, e.g., through calls to `std::mutex::lock()`.

Examples:

future bad_coro() { std::lock_guard lock{mtx}; ++some_counter; co_await something(); // Suspending while holding a mutex }

future good_coro() { { std::lock_guard lock{mtx}; ++some_counter; } // Destroy the lock_guard to release the mutex before suspending the coroutine co_await something(); // Suspending while holding a mutex }


This check implements **CP.52** from the C++ Core Guidelines. ๐Ÿ‘‘

---

### ๐Ÿฆ‹ `cppcoreguidelines-noexcept-destructor` ๐Ÿฆ‹

This check implements **C.37** from the C++ Core Guidelines.

The `cppcoreguidelines-noexcept-destructor` check is an alias, please see `performance-noexcept-destructor` for more information. <lavender>alias moment, go read that one bestie ๐Ÿ’œ</lavender>

---

### ๐Ÿฆ‹ `cppcoreguidelines-noexcept-move-operations` ๐Ÿฆ‹

This check implements **C.66** from the C++ Core Guidelines.

The `cppcoreguidelines-noexcept-move-operations` check is an alias, please see `performance-noexcept-move-constructor` for more information.

---

### ๐Ÿฆ‹ `cppcoreguidelines-noexcept-swap` ๐Ÿฆ‹

This check implements **C.83** , **C.84** and **C.85** from the C++ Core Guidelines.

The `cppcoreguidelines-noexcept-swap` check is an alias, please see `performance-noexcept-swap` for more information.

---

### ๐Ÿฆ‹ `cppcoreguidelines-non-private-member-variables-in-classes` ๐Ÿฆ‹

The `cppcoreguidelines-non-private-member-variables-in-classes` check is an alias, please see `misc-non-private-member-variables-in-classes` for more information.

---

### ๐Ÿ‘‘ `cppcoreguidelines-owning-memory` ๐Ÿ‘‘

This check implements the type-based semantics of `gsl::owner<T*>`, which allows static analysis on code, that uses raw pointers to handle resources like dynamic memory, but won't introduce RAII concepts. <rainbow>the main character of ownership checks ๐Ÿ’–</rainbow>

This check implements **I.11**, **C.33**, **R.3** and **GSL.Views** from the C++ Core Guidelines. The definition of a `gsl::owner<T*>` is straight forward

namespace gsl { template <typename T> owner = T; }


It is therefore simple to introduce the owner even without using an implementation of the Guideline Support Library.

All checks are purely type based and not (yet) flow sensitive. <cyan>no flow analysis yet, she's doing her best ๐Ÿซถ</cyan>

The following examples will demonstrate the correct and incorrect initializations of owners, assignment is handled the same way. Note that both `new` and `malloc()`-like resource functions are considered to produce resources.

// Creating an owner with factory functions is checked. gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }

// Dynamic memory must be assigned to an owner int* Something = new int(42); // BAD, will be caught gsl::owner<int*> Owner = new int(42); // Good gsl::owner<int*> Owner = new int[42]; // Good as well

// Returned owner must be assigned to an owner int* Something = function_that_returns_owner(); // Bad, factory function gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner

// Something not a resource or owner should not be assigned to owners int Stack = 42; gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned


In the case of dynamic memory as resource, only `gsl::owner<T*>` variables are allowed to be deleted.

// Example Bad, non-owner as resource handle, will be caught. int* NonOwner = new int(42); // First warning here, since new must land in an owner delete NonOwner; // Second warning here, since only owners are allowed to be deleted

// Example Good, Ownership correctly stated gsl::owner<int*> Owner = new int(42); // Good delete Owner; // Good as well, statically enforced, that only owners get deleted


The check will furthermore ensure, that functions, that expect a `gsl::owner<T*>` as argument get called with either a `gsl::owner<T*>` or a newly created resource.

void expects_owner(gsl::owner<int*> o) { delete o; }

// Bad Code int NonOwner = 42; expects_owner(&NonOwner); // Bad, will get caught

// Good Code gsl::owner<int*> Owner = new int(42); expects_owner(Owner); // Good expects_owner(new int(42)); // Good as well, recognized created resource

// Port legacy code for better resource-safety gsl::owner<FILE*> File = fopen("my_file.txt", "rw+"); FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned

// ... use the file

fclose(File); // Ok, File is annotated as 'owner<>' fclose(BadFile); // BadFile is not an 'owner<>', will be warned


#### โœจ Options โœจ

- **`LegacyResourceProducers`** โ€” semicolon-separated list of fully qualified names of legacy functions that create resources but cannot introduce `gsl::owner<>`. Defaults to `::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile`. ๐ŸŽ€
- **`LegacyResourceConsumers`** โ€” semicolon-separated list of fully qualified names of legacy functions expecting resource owners as pointer arguments but cannot introduce `gsl::owner<>`. Defaults to `::free;::realloc;::freopen;::fclose`. ๐ŸŽ€

#### ๐Ÿ˜ญ Limitations ๐Ÿ˜ญ

Using `gsl::owner<T*>` in a `typedef` or alias is not handled correctly.

using heap_int = gsl::owner<int*>; heap_int allocated = new int(42); // False positive!


The `gsl::owner<T*>` is declared as a templated type alias. In template functions and classes, like in the example below, the information of the type aliases gets lost. Therefore using `gsl::owner<T*>` in a heavy templated code base might lead to false positives. <red>false positives are the absolute enemy fr fr ๐Ÿ˜ค</red>

Known code constructs that do not get diagnosed correctly are:

- `std::exchange`
- `std::vector<gsl::owner<T*>>`

// This template function works as expected. Type information doesn't get lost. template <typename T> void delete_owner(gsl::owner<T*> owned_object) { delete owned_object; // Everything alright }

gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }

// Type deduction does not work for auto variables. // This is caught by the check and will be noted accordingly. auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int*

// Problematic function template that looses the typeinformation on owner template <typename T> void bad_template_function(T some_object) { // This line will trigger the warning, that a non-owner is assigned to an owner gsl::owner<T*> new_owner = some_object; }

// Calling the function with an owner still yields a false positive. bad_template_function(gsl::owner<int*>(new int(42)));

// The same issue occurs with templated classes like the following. template <typename T> class OwnedValue { public: const T getValue() const { return _val; } private: T _val; };

// Code, that yields a false positive. OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int * // False positive, getValue returns int* and not gsl::owner<int*> gsl::owner<int*> OwnedInt = Owner.getValue();


Another limitation of the current implementation is only the type based checking. Suppose you have code like the following:

// Two owners with assigned resources gsl::owner<int*> Owner1 = new int(42); gsl::owner<int*> Owner2 = new int(42);

Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2! Owner1 = nullptr;


The semantic of a `gsl::owner<T*>` is mostly like a `std::unique_ptr<T>`, therefore assignment of two `gsl::owner<T*>` is considered a move, which requires that the resource `Owner2` must have been released before the assignment. This kind of condition could be caught in later improvements of this check with flowsensitive analysis. Currently, the Clang Static Analyzer catches this bug for dynamic memory, but not for general types of resources. <purple>so likeโ€ฆ someday. but not today. ๐Ÿ™ˆ</purple>

### `cppcoreguidelines-prefer-member-initializer` ๐Ÿ’…

Okay bestie, so this check is out here finding member initializations sitting in the constructor *body* that could totally have been member initializers in the constructor instead. And like โ€” moving them isn't just โœจaestheticโœจ (tho it is), it lowkey also positively affects performance. Two birds, one slay.

Real talk tho: class-member assignments that live inside a control statement, or that come *after* the first control statement, get ignored. This check is C.49 from the C++ Core Guidelines, fr fr.

> Careful! This check does **not** enforce rule C.48 from the C++ Core Guidelines. If that's the vibe you're after, go look at the check `modernize-use-default-member-init` instead. Different check, different job, no cap.

#### Example 1 ๐ŸŽ€

class C { int n; int m; public: C() { n = 1; // Literal in default constructor if (dice()) return; m = 1; } };


So here, `n` can absolutely be initialized in the constructor initializer list. `m`? Not so much, because `m`'s initialization comes after a control statement (`if`). Sibling behavior, honestly.

class C { int n; int m; public: C(): n(1) { if (dice()) return; m = 1; } };


#### Example 2 ๐ŸŒธ

class C { int n; int m; public: C(int nn, int mm) { n = nn; // Neither default constructor nor literal if (dice()) return; m = mm; } };


Same energy as before: `n` can go in the constructor initializer list, `m` cannot, because `m`'s initialization follows that control statement (`if`). The check is not fooled.

C(int nn, int mm) : n(nn) { if (dice()) return; m = mm; }


---

### `cppcoreguidelines-pro-bounds-array-to-pointer-decay` ๐Ÿ’€

This check flags **all** array to pointer decays. Every single one. It is not playing.

Pointers should not be used as arrays, bestie. Like, ever. `<span>`<T> is the bounds-checked, safe alternative to using pointers to access arrays. This rule is part of the <cyan>Bounds safety (Bounds 3)</cyan> profile from the C++ Core Guidelines.

Wait โ€” careful, that's literally written as `span<T>` in the source. So:

Pointers should not be used as arrays. `span<T>` is a bounds-checked, safe alternative to using pointers to access arrays.

---

### `cppcoreguidelines-pro-bounds-constant-array-index` ๐Ÿฆ‹

This check flags all array subscript expressions on static arrays and `std::array`s that either:

- don't have a constant integer expression index, **or**
- are out of bounds (this one's for `std::array`).

For out-of-bounds checking of static arrays, see the `-Warray-bounds` Clang diagnostic โ€” different tool, go say hi.

This rule is part of the <purple>Bounds safety (Bounds 2)</purple> profile from the C++ Core Guidelines.

Optionally, this check can generate fixes using `gsl::at` for indexing. Iconic of it.

#### Options โœจ

- **`GslHeader`** โ€” the check can generate fixes after this option has been set to the name of the include file that contains `gsl::at()`, e.g. `"gsl/gsl.h"`.
- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.

---

### `cppcoreguidelines-pro-bounds-pointer-arithmetic` ๐Ÿ”ฅ

This check flags **all** usage of pointer arithmetic, because it could lead to an invalid pointer. The subtraction of two pointers is **not** flagged by this check โ€” that one gets a pass.

Pointers should only refer to single objects, and pointer arithmetic is fragile and easy to get wrong. `span<T>` is a bounds-checked, safe type for accessing arrays of data. Use it, babe.

This rule is part of the <mint>Bounds safety (Bounds 1)</mint> profile from the C++ Core Guidelines.

---

### `cppcoreguidelines-pro-type-const-cast` ๐Ÿ‘‘

This check imposes limitations on the use of `const_cast` within C++ code. Whether it flags **all** instances of `const_cast` or only those that remove either a `const` or a `volatile` qualifier depends entirely on the `StrictMode` option setting. Choose your own adventure, basically.

Modifying a variable that has been declared as `const` in C++ is generally considered **undefined behavior**, and this remains true even when using `const_cast`. In C++, the `const` qualifier indicates that a variable is intended to be read-only, and the compiler enforces this by disallowing any attempts to change the value of that variable. So likeโ€ฆ don't.

Removing the `volatile` qualifier in C++ can have serious consequences. This qualifier indicates that a variable's value can change unpredictably, and removing it may lead to undefined behavior, optimization problems, and debugging challenges. It's essential to retain the `volatile` qualifier in situations where the variable's volatility is a crucial aspect of program correctness and reliability. Periodt.

This rule is part of the <hotpink>Type safety (Type 3)</hotpink> profile and `ES.50: Don't cast away const` rule from the C++ Core Guidelines.

#### Options โœจ

- **`StrictMode`** โ€” when this setting is set to `true`, it means that any usage of `const_cast` is not allowed. On the other hand, when it's set to `false`, it permits casting to `const` or `volatile` types. Default value is `false`.

---

### `cppcoreguidelines-pro-type-cstyle-cast` ๐Ÿ“

This check flags all use of C-style casts that perform a `static_cast` downcast, `const_cast`, or `reinterpret_cast`.

Use of these casts can violate type safety and cause the program to access a variable that is actually of type `X` to be accessed as if it were of an unrelated type `Z`. And note this, bestie: a C-style `(T)expression` cast means to perform the **first** of the following that is possible:

1. a `const_cast`
2. a `static_cast`
3. a `static_cast` followed by a `const_cast`
4. a `reinterpret_cast`
5. a `reinterpret_cast` followed by a `const_cast`

This rule bans `(T)expression` only when used to perform an unsafe cast. So not all of them, just the ones that are giving red flag.

This rule is part of the <cyan>Type safety (Type.4)</cyan> profile from the C++ Core Guidelines.

---

### `cppcoreguidelines-pro-type-member-init` ๐ŸŒˆ

The check flags user-provided constructor definitions that do not initialize all fields that would be left in an undefined state by default construction โ€” think builtins, pointers, and record types without user-provided default constructors containing at least one such type. If these fields aren't initialized, the constructor will leave some of the memory in an undefined state. Not the vibe.

For C++11 it suggests fixes to add in-class field initializers. For older versions it inserts the field initializers into the constructor initializer list. It will also initialize any direct base classes that need to be zeroed in the constructor initializer list. So thorough of it.

The check takes assignment of fields in the constructor body into account, but generates **false positives** for fields initialized in methods invoked in the constructor body. Heads up.

The check also flags variables with automatic storage duration that have record types without a user-provided constructor and are not initialized. The suggested fix is to zero initialize the variable via `{}` for C++11 and beyond, or `= {}` for older language versions.

#### Options โœจ

- **`IgnoreArrays`** โ€” if set to `true`, the check will not warn about array members that are not zero-initialized during construction. For performance critical code, it may be important to not initialize fixed-size array members. Default is `false`.
- **`UseAssignment`** โ€” if set to `true`, the check will provide fix-its with literal initializers (`int i = 0;`) instead of curly braces (`int i{};`).

This rule is part of the <purple>Type safety (Type.6)</purple> profile from the C++ Core Guidelines.

---

### `cppcoreguidelines-pro-type-reinterpret-cast` ๐ŸŽ€

This check flags all uses of `reinterpret_cast` in C++ code. All of them. No exceptions, no mercy.

Use of these casts can violate type safety and cause the program to access a variable that is actually of type `X` to be accessed as if it were of an unrelated type `Z`. Yikes!

This rule is part of the <yellow>Type safety (Type.1.1)</yellow> profile from the C++ Core Guidelines.

---

### `cppcoreguidelines-pro-type-static-cast-downcast` โญ

This check flags all usages of `static_cast`, where a base class is casted to a derived class. In those cases, a fix-it is provided to convert the cast to a `dynamic_cast`. Free glow-up, courtesy of the check.

Use of these casts can violate type safety and cause the program to access a variable that is actually of type `X` to be accessed as if it were of an unrelated type `Z`.

This rule is part of the <mint>Type safety (Type.2)</mint> profile from the C++ Core Guidelines.

#### Options โœจ

- **`StrictMode`** โ€” when set to `false`, no warnings are emitted for casts on non-polymorphic types. Default is `true`.

---

### `cppcoreguidelines-pro-type-union-access` ๐Ÿ™ˆ

This check flags all access to members of unions. Passing unions **as a whole** is not flagged, so that one's fine, you're safe.

Reading from a union member assumes that member was the last one written, and writing to a union member assumes another member with a nontrivial destructor had its destructor called. This is fragile because it cannot generally be enforced to be safe in the language and so relies on programmer discipline to get it right. Which isโ€ฆ a lot of pressure on the programmer, ngl.

This rule is part of the <blue>Type safety (Type.7)</blue> profile from the C++ Core Guidelines.

---

### `cppcoreguidelines-pro-type-vararg` ๐Ÿ’œ

This check flags all calls to c-style vararg functions and all use of `va_arg`.

To allow for SFINAE use of vararg functions, a call is **not** flagged if a literal `0` is passed as the only vararg argument, or if the function is used in an unevaluated context. Little loopholes, but they're legal ones.

Passing to varargs assumes the correct type will be read. This is fragile because it cannot generally be enforced to be safe in the language and so relies on programmer discipline to get it right.

This rule is part of the <hotpink>Type safety (Type.8)</hotpink> profile from the C++ Core Guidelines.

---

### `cppcoreguidelines-rvalue-reference-param-not-moved` ๐Ÿงš

Warns when an rvalue reference function parameter is never moved within the function body. Likeโ€ฆ bestie, it's *right there*, move it.

Rvalue reference parameters indicate a parameter that should be moved with `std::move` from within the function body. Any such parameter that is never moved is confusing and potentially indicative of a buggy program. So this one's actually important.

Example:

void logic(std::string&& Input) { std::string Copy(Input); // Oops - forgot to std::move }


Note that parameters that are unused and marked as such will **not** be diagnosed. The check can read the room.

Example:

void conditional_use([[maybe_unused]] std::string&& Input) { // No diagnostic here since Input is unused and marked as such }


#### Options โœจ

- **`AllowPartialMove`** โ€” if set to `true`, the check accepts `std::move` calls containing any subexpression containing the parameter. CppCoreGuideline `F.18` officially mandates that the parameter itself must be moved. Default is `false`.

// 'p' is flagged by this check if and only if AllowPartialMove is false void move_members_of(pair<Obj, Obj>&& p) { pair<Obj, Obj> other; other.first = std::move(p.first); other.second = std::move(p.second); }

// 'p' is never flagged by this check void move_whole_pair(pair<Obj, Obj>&& p) { pair<Obj, Obj> other = std::move(p); }


- **`IgnoreUnnamedParams`** โ€” if set to `true`, the check ignores unnamed rvalue reference parameters. Default is `false`.
- **`IgnoreNonDeducedTemplateTypes`** โ€” if set to `true`, the check ignores non-deduced template type rvalue reference parameters. Default is `false`.

template <class T> struct SomeClass { // Below, 'T' is not deduced and 'T&&' is an rvalue reference type. // This will be flagged if and only if IgnoreNonDeducedTemplateTypes is // false. One suggested fix would be to specialize the class for 'T' and // 'T&' separately (e.g., see std::future), or allow only one of 'T' or // 'T&' instantiations of SomeClass (e.g., see std::optional). SomeClass(T&& t) { } };

// Never flagged, since 'T' is a forwarding reference in a deduced context template <class T> void forwarding_ref(T&& t) { T other = std::forward<T>(t); }


This check implements <purple>F.18</purple> from the C++ Core Guidelines. ๐Ÿ’œ

---

### `cppcoreguidelines-slicing` ๐ŸŒธ

Okay so bestie, this one catches <hotpink>slicing</hotpink> of member variables or vtable. Slicing happens when you copy a derived object into a base object: the members of the derived object (both member variables **and** virtual member functions) will be discarded. ๐Ÿ’€

This can be misleading especially for member function slicing, for example:

struct B { int a; virtual int f(); }; struct D : B { int b; int f() override; };

void use(B b) { // Missing reference, intended? b.f(); // Calls B::f. }

D d; use(d); // Slice.


This check implements <purple>ES.63</purple> and <purple>C.145</purple> from the C++ Core Guidelines. โœจ

---

### `cppcoreguidelines-special-member-functions` ๐ŸŽ€

The check finds classes where <yellow>some but not all</yellow> of the special member functions are defined. ๐Ÿ‘‘

By default the compiler defines a copy constructor, copy assignment operator, move constructor, move assignment operator and destructor. The default can be suppressed by explicit user-definitions. The relationship between which functions will be suppressed by definitions of other functions is *complicated* and it is advised that all five are defaulted or explicitly defined. fr fr, just do all five.

> Note that defining a function with `= delete` is considered to be a **definition**.

This check implements <purple>C.21</purple> from the C++ Core Guidelines. ๐Ÿ’…

#### Options

- **`AllowSoleDefaultDtor`** โ€” When set to `true` (default is `false`), this check will only trigger on destructors if they are defined and not defaulted.

struct A { // This is fine. virtual ~A() = default; };

struct B { // This is not fine. ~B() {} };

struct C { // This is not checked, because the destructor might be defaulted in // another translation unit. ~C(); };


- **`AllowMissingMoveFunctions`** โ€” When set to `true` (default is `false`), this check doesn't flag classes which define **no** move operations at all. It still flags classes which define only one of either move constructor or move assignment operator. With this option enabled, the following class won't be flagged:

struct A { A(const A&); A& operator=(const A&); ~A(); };


- **`AllowMissingMoveFunctionsWhenCopyIsDeleted`** โ€” When set to `true` (default is `false`), this check doesn't flag classes which define deleted copy operations but don't define move operations. This flag is related to Google C++ Style Guide โ€ Copyable and Movable Types. With this option enabled, the following class won't be flagged:

struct A { A(const A&) = delete; A& operator=(const A&) = delete; ~A(); };


- **`AllowImplicitlyDeletedCopyOrMove`** โ€” When set to `true` (default is `false`), this check doesn't flag classes which implicitly delete copy or move operations. With this option enabled, the following class won't be flagged:

struct A : boost::noncopyable { ~A() { std::cout << "dtor\n"; } };


---

### `cppcoreguidelines-use-default-member-init` ๐Ÿฆ‹

This check implements <purple>C.48</purple> from the C++ Core Guidelines.

The `cppcoreguidelines-use-default-member-init` check is an **alias**, please see `modernize-use-default-member-init` for more information. ๐Ÿ‘€

---

### `cppcoreguidelines-virtual-class-destructor` ๐Ÿ’…

Finds virtual classes whose destructor is neither *public and virtual* nor *protected and non-virtual*. A virtual class's destructor should be specified in one of these ways to prevent <red>undefined behavior</red>. ๐Ÿ˜ญ

This check implements <purple>C.35</purple> from the C++ Core Guidelines.

> Note that this check will diagnose a class with a virtual method **regardless** of whether the class is used as a base class or not.

Fixes are available for user-declared and implicit destructors that are either public and non-virtual or protected and virtual. No fixes are offered for **private** destructors. There, the decision whether to make them private and virtual or protected and non-virtual depends on the use case and is thus left to the user. ๐Ÿซถ

#### Example

For example, the following classes/structs get flagged by the check since they violate guideline C.35:

struct Foo { // NOK, protected destructor should not be virtual virtual void f(); protected: virtual ~Foo(){} };

class Bar { // NOK, public destructor should be virtual virtual void f(); public: ~Bar(){} };


This would be rewritten to look like this:

struct Foo { // OK, destructor is not virtual anymore virtual void f(); protected: ~Foo(){} };

class Bar { // OK, destructor is now virtual virtual void f(); public: virtual ~Bar(){} };


---

### `darwin-avoid-spinlock` ๐ŸŒˆ

Finds usages of `OSSpinlock`, which is **deprecated** due to potential livelock problems. ๐Ÿ™ˆ

This check will detect following function invocations:

- `OSSpinlockLock`

- `OSSpinlockTry`

- `OSSpinlockUnlock`

The corresponding information about the problem of OSSpinlock: โ€ `https://blog.postmates.com/why-spinlocks-are-bad-on-ios-b69fc5221058`

---

### `darwin-dispatch-once-nonstatic` โญ

Finds declarations of `dispatch_once_t` variables without `static` or global storage. The behavior of using `dispatch_once_t` predicates with automatic or dynamic storage is <red>undefined by libdispatch</red>, and should be avoided. no cap.

It is a common pattern to have functions initialize internal static or global data once when the function runs, but programmers have been known to miss the `static` on the `dispatch_once_t` predicate, leading to an uninitialized flag value at the mercy of the stack. ๐Ÿ’€

Programmers have also been known to make `dispatch_once_t` variables be members of structs or classes, with the intent to lazily perform some expensive struct or class member initialization only once; however, this violates the libdispatch requirements.

See the discussion section of Apple's `dispatch_once` documentation for more information.

---

### `fuchsia-default-arguments-calls` ๐ŸŒธ

Warns if a function or method is called with default arguments.

For example, given the declaration:

int foo(int value = 5) { return value; }


A function call expression that uses a default argument will be diagnosed. Calling it without defaults will not cause a warning:

foo(); // warning foo(0); // no warning


See the features disallowed in Fuchsia at โ€ `https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en`

---

### `fuchsia-default-arguments-declarations` โœจ

Warns if a function or method is declared with default parameters.

For example, the declaration:

int foo(int value = 5) { return value; }


will cause a warning.

See the features disallowed in Fuchsia at โ€ `https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en`

---

### `fuchsia-header-anon-namespaces` ๐ŸŽ€

The `fuchsia-header-anon-namespaces` check is an **alias**, please see `google-build-namespace` for more information.

---

### `fuchsia-multiple-inheritance` ๐Ÿ‘‘

Warns if a class inherits from multiple classes that are not pure virtual.

For example, declaring a class that inherits from multiple concrete classes is disallowed:

class Base_A { public: virtual int foo() { return 0; } };

class Base_B { public: virtual int bar() { return 0; } };

// Warning class Bad_Child1 : public Base_A, Base_B {};


A class that inherits from a pure virtual is allowed:

class Interface_A { public: virtual int foo() = 0; };

class Interface_B { public: virtual int bar() = 0; };

// No warning class Good_Child1 : public Interface_A, Interface_B { virtual int foo() override { return 0; } virtual int bar() override { return 0; } };


See the features disallowed in Fuchsia at โ€ `https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en`

---

### `fuchsia-overloaded-operator` ๐Ÿฆ‹

Warns if an operator is overloaded, **except** for the assignment (copy and move) operators. ๐Ÿ’…

For example:

int operator+(int); // Warning

B &operator=(const B &Other); // No warning B &operator=(B &&Other) // No warning


See the features disallowed in Fuchsia at โ€ `https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en`

---

### `fuchsia-statically-constructed-objects` ๐Ÿ’œ

Warns if global, non-trivial objects with static storage are constructed, **unless** the object is statically initialized with a `constexpr` constructor or has no explicit constructor.

For example:

class A {};

class B { public: B(int Val) : Val(Val) {} private: int Val; };

class C { public: constexpr C(int Val) : Val(Val) {} C(int Val1, int Val2) : Val(Val1+Val2) {}

private: int Val; };

static A a; // No warning, as there is no explicit constructor static C c(0); // No warning, as constructor is constexpr

static B b(0); // Warning, as constructor is not constexpr static C c2(0, 1); // Warning, as constructor is not constexpr

static int i; // No warning, as it is trivial

extern int get_i(); static C c3(get_i());// Warning, as the constructor is dynamically initialized


See the features disallowed in Fuchsia at โ€ `https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en`

---

### `fuchsia-trailing-return` ๐Ÿ”ฅ

Functions that have trailing returns are disallowed, except for those using `decltype` specifiers and lambda with otherwise unutterable return types.

For example:

// No warning int add_one(const int arg) { return arg; }

// Warning auto get_add_one() -> int (*)(const int) { return add_one; }


Exceptions are made for lambdas and `decltype` specifiers:

// No warning auto lambda = [](double x, double y) -> double {return x + y;};

// No warning template <typename T1, typename T2> auto fn(const T1 &lhs, const T2 &rhs) -> decltype(lhs + rhs) { return lhs + rhs; }


See the features disallowed in Fuchsia at โ€ <cyan>https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en</cyan>

---

## <purple>fuchsia-virtual-inheritance</purple>

Warns if classes are defined with virtual inheritance. ๐Ÿšซ

For example, classes should <hotpink>not</hotpink> be defined with virtual inheritance:

class B : public virtual A {}; // warning


See the features disallowed in Fuchsia at โ€ <cyan>https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en</cyan>

## <purple>google-build-explicit-make-pair</purple>

Check that `make_pair`'s template arguments are deduced. ๐Ÿ‘€

G++ 4.6 in C++11 mode fails <red>badly</red> if `make_pair`'s template arguments are specified explicitly, and such use isn't intended in any case, bestie. So likeโ€ฆ don't.

Corresponding `cpplint.py` check name: `build/explicit_make_pair`.

## <purple>google-build-namespaces</purple>

`cert-dcl59-cpp` redirects here as an alias for this check.
`fuchsia-header-anon-namespaces` redirects here as an alias for this check.

Finds anonymous namespaces in headers. ๐Ÿ’€

<cyan>https://google.github.io/styleguide/cppguide.html#Namespaces</cyan>

Corresponding `cpplint.py` check name: `build/namespaces`.

## <purple>google-build-using-namespace</purple>

Finds `using namespace` directives. ๐Ÿ•ต๏ธ

The check implements the following rule of the Google C++ Style Guide:

> You may not use a using-directive to make all names from a namespace available.

// Forbidden -- This pollutes the namespace. using namespace foo;


Corresponding `cpplint.py` check name: `build/namespaces`.

## <purple>google-default-arguments</purple>

Checks that default arguments are <hotpink>not</hotpink> given for virtual methods. ๐Ÿ’…

See <cyan>https://google.github.io/styleguide/cppguide.html#Default_Arguments</cyan>

## <purple>google-explicit-constructor</purple>

Checks that constructors callable with a single argument and conversion operators are marked `explicit` to avoid the risk of unintentional implicit conversions. ๐Ÿ˜ญ

Consider this example:

struct S { int x; operator bool() const { return true; } };

bool f() { S a{1}; S b{2}; return a == b; }


The function will return `true`, since the objects are implicitly converted to `bool` before comparison, which is unlikely to be the intent. Not me getting owned by implicit conversionโ€ฆ ๐Ÿ™ˆ

The check will suggest inserting `explicit` before the constructor or conversion operator declaration. <yellow>However</yellow>, copy and move constructors should <hotpink>not</hotpink> be `explicit`, as well as constructors taking a single `initializer_list` argument.

This code:

struct S { S(int a); explicit S(const S&); operator bool() const; ...


will become

struct S { explicit S(int a); S(const S&); explicit operator bool() const; ...


See โ€ <cyan>https://google.github.io/styleguide/cppguide.html#Explicit_Constructors</cyan>

## <purple>google-global-names-in-headers</purple>

Flag global namespace pollution in header files. Right now it only triggers on `using` declarations and directives. ๐ŸŽฏ

The relevant style guide section is โ€ <cyan>https://google.github.io/styleguide/cppguide.html#Namespaces</cyan>.

## <purple>google-objc-avoid-nsobject-new</purple>

Finds calls to `+new` or overrides of it, which are prohibited by the Google Objective-C style guide. โœจ

The Google Objective-C style guide forbids calling `+new` or overriding it in class implementations, preferring `+alloc` and `-init` methods to instantiate objects.

An example:

NSDate *now = [NSDate new]; Foo *bar = [Foo new];


Instead, code should use `+alloc`/`-init` or class factory methods.

NSDate *now = [NSDate date]; Foo *bar = [[Foo alloc] init];


This check corresponds to the Google Objective-C Style Guide rule <purple>Do Not Use +new</purple>.

## <purple>google-objc-avoid-throwing-exception</purple>

Finds uses of throwing exceptions usages in Objective-C files. ๐Ÿšซ

For the same reason as the Google C++ style guide, we prefer <hotpink>not</hotpink> throwing exceptions from Objective-C code.

The corresponding C++ style guide rule: โ€ <cyan>https://google.github.io/styleguide/cppguide.html#Exceptions</cyan>

Instead, prefer passing in `NSError **` and return `BOOL` to indicate success or failure. ๐Ÿ‘‘

A counterexample:
  • (void)readFile { if ([self isError]) { @throw [NSException exceptionWithName:...]; } }

Instead, returning an error via `NSError **` is preferred:
  • (BOOL)readFileWithError:(NSError **)error { if ([self isError]) { *error = [NSError errorWithDomain:...]; return NO; } return YES; }

The corresponding style guide rule: โ€ <cyan>https://google.github.io/styleguide/objcguide.html#avoid-throwing-exceptions</cyan>

## <purple>google-objc-function-naming</purple>

Finds function declarations in Objective-C files that do not follow the pattern described in the Google Objective-C Style Guide. ๐Ÿ”

The corresponding style guide rule can be found here: โ€ <cyan>https://google.github.io/styleguide/objcguide.html#function-names</cyan>

All function names should be in <purple>Pascal case</purple>. Functions whose storage class is not `static` should have an appropriate prefix.

The following code sample does not follow this pattern:

static bool is_positive(int i) { return i > 0; } bool IsNegative(int i) { return i < 0; }


The sample above might be corrected to the following code:

static bool IsPositive(int i) { return i > 0; } bool *ABCIsNegative(int i) { return i < 0; }


## <purple>google-objc-global-variable-declaration</purple>

Finds global variable declarations in Objective-C files that do not follow the pattern of variable names in Google's Objective-C Style Guide. ๐ŸŒธ

The corresponding style guide rule: โ€ <cyan>https://google.github.io/styleguide/objcguide.html#variable-names</cyan>

All the global variables should follow the pattern of `g[A-Z].*` (variables) or `k[A-Z].*` (constants). The check will suggest a variable name that follows the pattern if it can be inferred from the original name.

For code:

static NSString* myString = @"hello";


The fix will be:

static NSString* gMyString = @"hello";


Another example of constant:

static NSString* const myConstString = @"hello";


The fix will be:

static NSString* const kMyConstString = @"hello";


<yellow>However</yellow> for code that prefixed with non-alphabetical characters like:

static NSString* __anotherString = @"world";


The check will give a warning message but will <hotpink>not</hotpink> be able to suggest a fix. The user needs to fix it on their own, bestie. ๐Ÿ˜ญ

## <purple>google-readability-avoid-underscore-in-googletest-name</purple>

Checks whether there are underscores in googletest test suite names and test names in test macros: ๐Ÿ”Ž

- `TEST`

- `TEST_F`

- `TEST_P`

- `TYPED_TEST`

- `TYPED_TEST_P`

The `FRIEND_TEST` macro is <hotpink>not</hotpink> included.

For example:

TEST(TestSuiteName, Illegal_TestName) {} TEST(Illegal_TestSuiteName, TestName) {}


would trigger the check. Underscores are not allowed in test suite name nor test names. ๐Ÿšซ

The `DISABLED_` prefix, which may be used to disable test suites and individual tests, is removed from the test suite name and test name before checking for underscores.

This check does not propose any fixes. ๐Ÿ’…

## <purple>google-readability-braces-around-statements</purple>

The `google-readability-braces-around-statements` check is an alias, please see `readability-braces-around-statements` for more information. ๐Ÿ”

## <purple>google-readability-casting</purple>

Finds usages of C-style casts. ๐Ÿ‘€

<cyan>https://google.github.io/styleguide/cppguide.html#Casting</cyan>

Corresponding `cpplint.py` check name: `readability/casting`.

This check is similar to `-Wold-style-cast`, but it suggests automated fixes in some cases. The reported locations should <hotpink>not</hotpink> be different from the ones generated by `-Wold-style-cast`. โœจ

## <purple>google-readability-function-size</purple>

The `google-readability-function-size` check is an alias, please see `readability-function-size` for more information. ๐Ÿ”

## <purple>google-readability-namespace-comments</purple>

The `google-readability-namespace-comments` check is an alias, please see `llvm-namespace-comment` for more information. ๐Ÿ”

## <purple>google-readability-todo</purple>

Finds `TODO` comments without a username or bug number. ๐Ÿ“

The relevant style guide section is โ€ <cyan>https://google.github.io/styleguide/cppguide.html#TODO_Comments</cyan>.

Corresponding `cpplint.py` check: `readability/todo`

## <purple>google-runtime-int</purple>

Finds uses of `short`, `long` and `long long` and suggest replacing them with `u?intXX(_t)?`. ๐Ÿ”ข

The corresponding style guide rule: โ€ <cyan>https://google.github.io/styleguide/cppguide.html#Integer_Types</cyan>.

Corresponding `cpplint.py` check: `runtime/int`.

### <lavender>Options</lavender>

- **`UnsignedTypePrefix`** โ€” A string specifying the unsigned type prefix. Default is `uint`.
- **`SignedTypePrefix`** โ€” A string specifying the signed type prefix. Default is `int`.
- **`TypeSuffix`** โ€” A string specifying the type suffix. Default is an empty string.

## <purple>google-runtime-operator</purple>

Finds overloads of unary `operator &`. โš ๏ธ

<cyan>https://google.github.io/styleguide/cppguide.html#Operator_Overloading</cyan>

Corresponding `cpplint.py` check name: `runtime/operator`.

## <purple>google-upgrade-googletest-case</purple>

Finds uses of deprecated Google Test version 1.9 APIs with names containing `case` and replaces them with equivalent APIs with `suite`. ๐Ÿงš

All names containing `case` are being replaced to be consistent with the meanings of "test case" and "test suite" as used by the International Software Testing Qualifications Board and ISO 29119. Fancy, right? ๐Ÿ’…

The new names are a part of Google Test version 1.9 (release pending). It is recommended that users update their dependency to version 1.9 and then use this check to remove deprecated names.

The affected APIs are:

- Member functions of `testing::Test`, `testing::TestInfo`, `testing::TestEventListener`, `testing::UnitTest`, and any type inheriting from these types

- The macros `TYPED_TEST_CASE`, `TYPED_TEST_CASE_P`, `REGISTER_TYPED_TEST_CASE_P`, and `INSTANTIATE_TYPED_TEST_CASE_P`

- The type alias `testing::TestCase`

Examples of fixes created by this check:

class FooTest : public testing::Test { public: static void SetUpTestCase(); static void TearDownTestCase(); };

TYPED_TEST_CASE(BarTest, BarTypes);


becomes

class FooTest : public testing::Test { public: static void SetUpTestSuite(); static void TearDownTestSuite(); };

TYPED_TEST_SUITE(BarTest, BarTypes);


For better consistency of user code, the check renames both virtual and non-virtual member functions with matching names in derived types. The check tries to provide only a warning when a fix cannot be made safely, as is the case with some template and macro uses. ๐Ÿซถ

### hicpp-avoid-c-arrays

bestie this one is literally just an alias ๐Ÿ’… please go look at `modernize-avoid-c-arrays` for the actual tea. It *partly* enforces the rule 4.1.1, okurrr.

---

### hicpp-avoid-goto

another alias bestie ๐Ÿ™ˆ go read `cppcoreguidelines-avoid-goto` instead. It enforces the rule 6.3.1.

---

### hicpp-braces-around-statements

alias alert ๐Ÿšจ please see `readability-braces-around-statements`. It enforces the rule 6.1.1.

---

### hicpp-deprecated-headers

yep, another alias ๐ŸŒธ check out `modernize-deprecated-headers`. It enforces the rule 1.3.3.

---

### hicpp-exception-baseclass

okay so this one's not an alias, she's her own girl ๐Ÿ’– What she does: makes sure that every single value you throw in a `throw` expression is an instance of `std::exception`. Iconic behavior honestly.

This enforces rule 15.1 of the High Integrity C++ Coding Standard.

class custom_exception {};

void throwing() noexcept(false) { // Problematic throw expressions. throw int(42); throw custom_exception(); }

class mathematical_error : public std::exception {};

void throwing2() noexcept(false) { // These kind of throws are ok. throw mathematical_error(); throw std::runtime_error(); throw std::exception(); }


---

### hicpp-explicit-conversions

This one is an alias for `google-explicit-constructor` โœจ She's used to enforce parts of rule 5.4.1. What she does: makes sure your constructors and conversion operators are marked `explicit`. The other flavors of casting checks live in other places โ€” the following checks are the ones to hit up if you wanna check for more forms of casting:

- `cppcoreguidelines-pro-type-static-cast-downcast`
- `cppcoreguidelines-pro-type-reinterpret-cast`
- `cppcoreguidelines-pro-type-const-cast`
- `cppcoreguidelines-pro-type-cstyle-cast`

---

### hicpp-function-size

An alias for `readability-function-size`. Super useful if you wanna enforce multiple sections on function complexity ๐Ÿซถ

- rule 8.2.2
- rule 8.3.1
- rule 8.3.2

---

### hicpp-ignored-remove-result

no cap, this one makes sure you don't ignore the result of `std::remove`, `std::remove_if` and `std::unique` โ€” that's rule 17.5.1, periodt.

So here's the lore: the mutating algorithms `std::remove`, `std::remove_if` and both overloads of `std::unique` work by swapping or moving elements of the range they're operating over. When they're done, they hand you back an iterator to the last valid element. In the majority of cases the correct behavior is to use this result as the first operand in a call to `std::erase` ๐Ÿ’…

Also FYI: this check is a subset of `bugprone-unused-return-value`, and depending on used options it can be superfluous to enable both checks. So like, don't double up unless you know what you're doing bestie ๐Ÿ˜ญ

#### Options

- **`AllowCastToVoid`** โ€” Controls whether casting return values to `void` is permitted. Default: `true`.

---

### hicpp-invalid-access-moved

This check is an alias for `bugprone-use-after-move`.

She implements parts of the rule 8.4.1 to check if moved-from objects are being accessed ๐Ÿ‘€ (which you should NOT be doing).

---

### hicpp-member-init

An alias for `cppcoreguidelines-pro-type-member-init`. Implements the check for rule 12.4.2 โ€” initialize class members in the right order, bestie, the ORDER matters ๐ŸŽ€

---

### hicpp-move-const-arg

The `hicpp-move-const-arg` check is an alias, please see `performance-move-const-arg` for more information. It enforces the rule 17.3.1.

---

### hicpp-multiway-paths-covered

Okay this one is lowkey a baddie ๐Ÿ‘‘ She discovers situations where code paths are not fully-covered. She furthermore suggests using `if` instead of `switch` if the code will be more clear. The rule 6.1.2 and rule 6.1.4 of the High Integrity C++ Coding Standard are enforced.

So `if`-`else if` chains that miss a final `else` branch might lead to unexpected program execution and be the result of a logical error ๐Ÿ˜ญ If the missing `else` branch is intended you can leave it empty with a clarifying comment. Heads up though: this warning can be noisy on some code bases, so it is <red>disabled by default</red>.

void f1() { int i = determineTheNumber();

if(i > 0) { // Some Calculation } else if (i < 0) { // Precondition violated or something else. } // ... }


Similar arguments hold for `switch` statements which do not cover all possible code paths.

// The missing default branch might be a logical error. It can be kept empty // if there is nothing to do, making it explicit. void f2(int i) { switch (i) { case 0: // something break; case 1: // something else break; } // All other numbers? }

// Violates this rule as well, but already emits a compiler warning (-Wswitch). enum Color { Red, Green, Blue, Yellow }; void f3(enum Color c) { switch (c) { case Red: // We can't drive for now. break; case Green: // We are allowed to drive. break; } // Other cases missing }


The rule 6.1.4 requires every `switch` statement to have at least two `case` labels other than a `default` label. Otherwise, the `switch` could be better expressed with an `if` statement. Degenerated `switch` statements without any labels are caught as well ๐Ÿซฃ

// Degenerated switch that could be better written as if int i = 42; switch(i) { case 1: // do something here default: // do something else here }

// Should rather be the following: if (i == 1) { // do something here } else { // do something here }

// A completely degenerated switch will be diagnosed. int i = 42; switch(i) {}


#### Options

- **`WarnOnMissingElse`** โ€” Boolean flag that activates a warning for missing `else` branches. Default is `false`.

---

### hicpp-named-parameter

An alias for `readability-named-parameter` โœจ

She implements rule 8.2.1.

---

### hicpp-new-delete-operators

An alias for `misc-new-delete-overloads`. Implements rule 12.3.1 to make sure the `new` and `delete` operators have the correct signature, fr fr.

---

### hicpp-no-array-decay

The `hicpp-no-array-decay` check is an alias, please see `cppcoreguidelines-pro-bounds-array-to-pointer-decay` for more information. It enforces the rule 4.1.1.

---

### hicpp-no-assembler

Checks for assembler statements ๐Ÿšซ Use of inline assembly should be avoided since it restricts the portability of the code. Like, your code could just not work on other machines, and that's not the vibe.

This enforces rule 7.5.1 of the High Integrity C++ Coding Standard.

---

### hicpp-no-malloc

The `hicpp-no-malloc` check is an alias, please see `cppcoreguidelines-no-malloc` for more information. It enforces the rule 5.3.2.

---

### hicpp-noexcept-move

An alias for `performance-noexcept-move-constructor`. Checks rule 12.5.4 to mark move assignment and move construction `noexcept` ๐Ÿ’…

---

### hicpp-signed-bitwise

Finds uses of bitwise operations on signed integer types, which may lead to <red>undefined or implementation defined behavior</red> ๐Ÿ˜ญ yeah that's the scary stuff.

The according rule is defined in the High Integrity C++ Standard, Section 5.6.1.

#### Options

- **`IgnorePositiveIntegerLiterals`** โ€” If this option is set to `true`, the check will not warn on bitwise operations with positive integer literals, e.g. `~0`, `2 << 1`, etc. Default value is `false`.

---

### hicpp-special-member-functions

An alias for `cppcoreguidelines-special-member-functions`. She checks that special member functions have the correct signature, according to rule 12.5.7 โœจ

---

### hicpp-static-assert

The `hicpp-static-assert` check is an alias, please see `misc-static-assert` for more information. It enforces the rule 7.1.10.

---

### hicpp-undelegated-constructor

An alias for `bugprone-undelegated-constructor`. She partially implements rule 12.4.5 to find misplaced constructor calls inside a constructor ๐Ÿ‘€ sneaky little things.

struct Ctor { Ctor(); Ctor(int); Ctor(int, int); Ctor(Ctor *i) { // All Ctor() calls result in a temporary object Ctor(); // did you intend to call a delegated constructor? Ctor(0); // did you intend to call a delegated constructor? Ctor(1, 2); // did you intend to call a delegated constructor? foo(); } };


---

### hicpp-uppercase-literal-suffix

The `hicpp-uppercase-literal-suffix` check is an alias, please see `readability-uppercase-literal-suffix` for more information.

She partially implements rule 4.2.1 to ensure that the `U` suffix is writeln properly ๐ŸŽ€

---

### hicpp-use-auto

The `hicpp-use-auto` check is an alias, please see `modernize-use-auto` for more information. It enforces the rule 7.1.8.

---

### hicpp-use-emplace

The `hicpp-use-emplace` check is an alias, please see `modernize-use-emplace` for more information. It enforces the rule 17.4.2.

---

### hicpp-use-equals-default

This check is an alias for `modernize-use-equals-default`. Implements rule 12.5.1 to explicitly `default` special member functions ๐Ÿ’…

---

### hicpp-use-equals-delete

This check is an alias for `modernize-use-equals-delete`. Implements rule 12.5.1 to explicitly `default` or `delete` special member functions.

---

### hicpp-use-noexcept

The `hicpp-use-noexcept` check is an alias, please see `modernize-use-noexcept` for more information. It enforces the rule 1.3.5.

---

### hicpp-use-nullptr

The `hicpp-use-nullptr` check is an alias, please see `modernize-use-nullptr` for more information. It enforces the rule 2.5.3.

---

### hicpp-use-override

This check is an alias for `modernize-use-override`. Implements rule 10.2.1 to declare a virtual function `override` when overriding ๐Ÿ‘‘

---

### hicpp-vararg

The `hicpp-vararg` check is an alias, please see `cppcoreguidelines-pro-type-vararg` for more information. It enforces the rule 14.1.1.

---

### linuxkernel-must-check-errs

Okay this one is specifically for Linux kernel girlies ๐Ÿง She checks Linux kernel code to see if it uses the results from the functions in `linux/err.h`. Also checks to see if code uses the results from functions that directly return a value from one of these error functions.

Why does this matter? Because in the Linux kernel, `ERR_PTR`, `PTR_ERR`, `IS_ERR`, `IS_ERR_OR_NULL`, `ERR_CAST`, and `PTR_ERR_OR_ZERO` return values <red>must</red> be checked, since positive pointers and negative error codes are being used in the same context ๐Ÿ˜ญ These functions are marked with `__attribute__((warn_unused_result))`, but some kernel versions do not have this warning enabled for clang. So like, don't rely on the compiler to catch it for you.

Examples:

/* Trivial unused call to an ERR function */ PTR_ERR_OR_ZERO(some_function_call());

/* A function that returns ERR_PTR. */ void *fn() { ERR_PTR(-EINVAL); }

/* An invalid use of fn. */ fn();


---

### llvm-else-after-return

The `llvm-else-after-return` check is an alias, please see `readability-else-after-return` for more information.

---

### llvm-header-guard

Finds and fixes header guards that do not adhere to LLVM style ๐Ÿ›ก๏ธ she's out here tidying up your `#ifndef`s, bestie.

---

### llvm-include-order

Checks the correct order of `#includes` โœจ yes, the order is a whole thing, it's giving main character energy.

See <cyan>https://llvm.org/docs/CodingStandards.html#include-style</cyan>

## llvm-namespace-comment

<pink>okay so bestie, first thing:</pink> <code>google-readability-namespace-comments</code> redirects here as an alias for this check. ๐Ÿ’…

This one checks that long namespaces have a closing comment. That's it. That's the whole vibe. ๐ŸŒธ

<purple>https://llvm.org/docs/CodingStandards.html#namespace-indentation</purple>

<purple>https://google.github.io/styleguide/cppguide.html#Namespaces</purple>

namespace n1 { void f(); }

// becomes

namespace n1 { void f(); } // namespace n1


### Options

- **`ShortNamespaceLines`** โ€” wants the closing brace of a namespace definition to be followed by a closing comment *if* the body of the namespace has more than `ShortNamespaceLines` lines of code. The value is an unsigned integer and it defaults to `1U`. ๐ŸŽ€
- **`SpacesBeforeComments`** โ€” an unsigned integer saying how many spaces go before the comment that closes a namespace definition. Default is `1U`. โœจ

---

## llvm-prefer-isa-or-dyn-cast-in-conditionals

<pink>Not me catching every conditional that's doing too much.</pink> This check looks at conditionals and finds and replaces:

- cases of `cast<>` โ€” which will **assert** rather than return a null pointer ๐Ÿ˜ญ
- `dyn_cast<>` where the return value is not captured
- and it also finds and replaces cases matching the pattern `var && isa<X>(var)`, where `var` is evaluated **twice** (rude, honestly)

// Finds these: if (auto x = cast<X>(y)) {} // is replaced by: if (auto x = dyn_cast<X>(y)) {}

if (cast<X>(y)) {} // is replaced by: if (isa<X>(y)) {}

if (dyn_cast<X>(y)) {} // is replaced by: if (isa<X>(y)) {}

if (var && isa<T>(var)) {} // is replaced by: if (isa_and_nonnull<T>(var.foo())) {}

// Other cases are ignored, e.g.: if (auto f = cast<Z>(y)->foo()) {} if (cast<Z>(y)->foo()) {} if (X.cast(y)) {}


---

## llvm-prefer-register-over-unsigned

Finds historical use of `unsigned` to hold vregs and physregs and rewrites them to use `Register`. <green>glow-up moment.</green> โœจ

Right now it works by finding all variables of unsigned integer type whose initializer begins with an implicit cast from `Register` to `unsigned`.

void example(MachineOperand &MO) { unsigned Reg = MO.getReg(); ... }


becomes:

void example(MachineOperand &MO) { Register Reg = MO.getReg(); ... }


---

## llvm-qualified-auto

The `llvm-qualified-auto` check is an **alias**, bestie โ€” please see `readability-qualified-auto` for more information. ๐Ÿฆ‹

---

## llvm-twine-local

Looks for local `Twine` variables which are prone to use after frees and should be generally avoided. <red>lowkey a menace.</red> ๐Ÿ’€

static Twine Moo = Twine("bark") + "bah";

// becomes

static std::string Moo = (Twine("bark") + "bah").str();


---

## llvmlibc-callee-namespace

Checks all calls resolve to functions within the correct namespace. <purple>stay in your lane, function calls.</purple> ๐Ÿ‘‘

// Implementation inside the LIBC_NAMESPACE namespace. // Correct if: // - LIBC_NAMESPACE is a macro // - LIBC_NAMESPACE expansion starts with __llvm_libc namespace LIBC_NAMESPACE {

// Allow calls with the fully qualified name. LIBC_NAMESPACE::strlen("hello");

// Allow calls to compiler provided functions. (void)__builtin_abs(-1);

// Bare calls are allowed as long as they resolve to the correct namespace. strlen("world");

// Disallow calling into functions in the global namespace. ::strlen("!");

} // namespace LIBC_NAMESPACE


---

## llvmlibc-implementation-in-namespace

Checks that all declarations in the llvm-libc implementation are within the correct namespace. ๐Ÿซถ

// Implementation inside the LIBC_NAMESPACE_DECL namespace. // Correct if: // - LIBC_NAMESPACE_DECL is a macro // - LIBC_NAMESPACE_DECL expansion starts with [[gnu::visibility("hidden")]] __llvm_libc namespace LIBC_NAMESPACE_DECL { void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} // Namespaces within LIBC_NAMESPACE_DECL namespace are allowed. namespace inner { int localVar = 0; } // Functions with C linkage are allowed. extern "C" void str_fuzz() {} }

// Incorrect: implementation not in the LIBC_NAMESPACE_DECL namespace. void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}

// Incorrect: outer most namespace is not the LIBC_NAMESPACE_DECL macro. namespace something_else { void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} }

// Incorrect: outer most namespace expansion does not start with [[gnu::visibility("hidden")]] __llvm_libc. #define LIBC_NAMESPACE_DECL custom_namespace namespace LIBC_NAMESPACE_DECL { void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} }


---

## llvmlibc-inline-function-decl

Checks that all implicitly and explicitly inline functions in header files are tagged with the `LIBC_INLINE` macro โ€” **except** for functions implicit to classes or deleted functions. ๐ŸŽ€ See the libc style guide for more information about this macro.

---

## llvmlibc-restrict-system-libc-headers

Finds includes of system libc headers not provided by the compiler within llvm-libc implementations. <mint>no sneaky system headers allowed in here.</mint> ๐Ÿšซ

#include <stdio.h> // Not allowed because it is part of system libc. #include <stddef.h> // Allowed because it is provided by the compiler. #include "internal/stdio.h" // Allowed because it is NOT part of system libc.


This check is necessary because accidentally including system libc headers can lead to subtle and hard to detect bugs. For example consider a system libc whose `dirent` struct has slightly different field ordering than llvm-libc. While this will compile successfully, this can cause issues during runtime because they are ABI incompatible. <red>fr fr, runtime bugs are the worst kind of jumpscare.</red> ๐Ÿ˜ญ

### Options

- **`Includes`** โ€” a string containing a comma separated glob list of allowed include filenames. Similar to the `-checks` glob list for running clang-tidy itself, the two wildcard characters are `*` and `-`, to include and exclude globs, respectively. The default is `-*`, which disallows **all** includes.

  This can be used to allow known safe includes such as Linux development headers. See `portability-restrict-system-includes` for more details. โœจ

---

## misc-confusable-identifiers

Warn about confusable identifiers, i.e. identifiers that are visually close to each other, but use different Unicode characters. This detects a potential attack described in **CVE-2021-42574**. <hotpink>security girlies stay winning.</hotpink> ๐Ÿ”ฅ

Example:

int fo; // Initial character is U+0066 (LATIN SMALL LETTER F). int ๐Ÿo; // Initial character is U+1D41F (MATHEMATICAL BOLD SMALL F) not U+0066 (LATIN SMALL LETTER F).


---

## misc-const-correctness

This check implements detection of local variables which could be declared as `const` but are not. Declaring variables as `const` is required or recommended by many coding guidelines, such as: **ES.25** from the C++ Core Guidelines and **AUTOSAR C++14 Rule A7-1-1 (6.7.1 Specifiers)**. ๐Ÿ“š

Please note that this check's analysis is **type-based only**. Variables that are not modified but used to create a non-`const` handle that might escape the scope are not diagnosed as potential `const`. <lavender>iykyk.</lavender>

// Declare a variable, which is not const ... int i = 42; // but use it as read-only. This means that i can be declared const. int result = i * i; // Before transformation int const result = i * i; // After transformation


The check can analyze values, pointers and references but **not (yet)** pointees:

// Normal values like built-ins or objects. int potential_const_int = 42; // Before transformation int const potential_const_int = 42; // After transformation int copy_of_value = potential_const_int;

MyClass could_be_const; // Before transformation MyClass const could_be_const; // After transformation could_be_const.const_qualified_method();

// References can be declared const as well. int &reference_value = potential_const_int; // Before transformation int const& reference_value = potential_const_int; // After transformation int another_copy = reference_value;

// The similar semantics of pointers are not (yet) analyzed. int *pointer_variable = &potential_const_int; // _NO_ 'const int *pointer_variable' suggestion. int last_copy = *pointer_variable;


The automatic code transformation is only applied to variables that are declared in **single declarations**. You may want to prepare your code base with `readability-isolate-declaration` first. ๐Ÿ’…

Note that there is the check `cppcoreguidelines-avoid-non-const-global-variables` to enforce const correctness on all globals.

### Known Limitations

- The check **does not run on C code**.
- The check will not analyze templated variables or variables that are instantiation dependent. Different instantiations can result in different const correctness properties and in general it is not possible to find all instantiations of a template. The template might be used differently in an independent translation unit. ๐Ÿฆ‹
- Pointees can not be analyzed for constness yet. The following code shows this limitation.

// Declare a variable that will not be modified. int constant_value = 42;

// Declare a pointer to that variable, that does not modify either, but misses 'const'. // Could be 'const int *pointer_to_constant = &constant_value;' int *pointer_to_constant = &constant_value;

// Usage: int result = 520 * 120 * (*pointer_to_constant);


This limitation affects the capability to add `const` to methods which is not possible, too. ๐Ÿ˜ญ

### Options

- **`AnalyzeValues`** (default = `true`) โ€” enable or disable the analysis of ordinary value variables, like `int i = 42;`

// Warning int i = 42; // No warning int const i = 42;

// Warning int a[] = {42, 42, 42}; // No warning int const a[] = {42, 42, 42};


- **`AnalyzeReferences`** (default = `true`) โ€” enable or disable the analysis of reference variables, like `int &ref = i;`

int i = 42; // Warning int& ref = i; // No warning int const& ref = i;


- **`WarnPointersAsValues`** (default = `false`) โ€” this option enables the suggestion for `const` of the pointer itself. Pointer values have two possibilities to be `const`: the pointer and the value pointing to. ๐ŸŽฏ

int value = 42;

// Warning const int * pointer_variable = &value; // No warning const int *const pointer_variable = &value;


- **`TransformValues`** (default = `true`) โ€” provides fixit-hints for value types that automatically add `const` if its a single declaration. โœจ

// Before int value = 42; // After int const value = 42;


- **`TransformReferences`** (default = <green>`true`</green>) โ€” gives you fixit-hints for reference types, automatically slapping `const` on when it's a single declaration. ๐Ÿ’…

// This variable could still be a constant. But because there is a non-const reference to // it, it can not be transformed (yet). int value = 42; // The reference 'ref_value' is not modified and can be made 'const int &ref_value = value;' // Before int &ref_value = value; // After int const &ref_value = value;

// Result is modified later in its life-time. No diagnostic and fixit hint will be emitted. int result = ref_value * 3; result -= 10;


- **`TransformPointersAsValues`** (default = <red>`false`</red>) โ€” gives you fixit-hints for pointers when their pointee isn't changed. <yellow>Heads up bestie:</yellow> this does **not** analyze whether the value-pointed-to is unchanged! ๐Ÿ™ˆ

  Needs `WarnPointersAsValues` to be `true`, no cap.

int value = 42;

// Before const int * pointer_variable = &value; // After const int *const pointer_variable = &value;

// Before const int * a[] = {&value, &value}; // After const int *const a[] = {&value, &value};

// Before int *ptr_value = &value; // After int *const ptr_value = &value;

int result = 100 * (*ptr_value); // Does not modify the pointer itself. // This modification of the pointee is still allowed and not diagnosed. *ptr_value = 0;

// The following pointer may not become a 'int *const'. int *changing_pointee = &value; changing_pointee = &result;


---

### ๐Ÿ’€ misc-coroutine-hostile-raii

Detects when objects of certain <red>hostile</red> RAII types persist across suspension points inside a coroutine. Those hostile types include scoped-lockable types plus whatever's chillin' on a configurable denylist.

Some objects are like, "I *must* be destroyed on the same thread that created me." Traditionally people phrased that as "must be a local variable", assuming locals always behave that way. <hotpink>But that's just not true with C++20 coroutines</hotpink>, because an intervening `co_await` can make the coroutine suspend and then get resumed on a whole different thread. ๐Ÿ˜ญ

The lifetime of an object that needs to be destroyed on the same thread must **not** encompass a `co_await` or `co_yield` point. If you create/destroy an object, you gotta do it without letting the coroutine suspend in between. Periodt.

Following types are considered hostile:

  - <red>Scoped-lockable types:</red> a scoped-lockable object persisting across a suspension point is a problem because the lock it's holding could get unlocked by a different thread. That would be undefined behaviour. This includes all types annotated with the `scoped_lockable` attribute.
  - <red>Types belonging to a configurable denylist.</red>

// Call some async API while holding a lock. task coro() { const std::lock_guard l(&mu_);

// Oops! The async Bar function may finish on a different // thread from the one that created the lock_guard (and called // Mutex::Lock). After suspension, Mutex::Unlock will be called on the wrong thread. co_await Bar(); }


### Options ๐ŸŽ€

- **`RAIITypesList`** โ€” a semicolon-separated list of qualified types that should **not** be allowed to persist across suspension points. Like: `my::lockable; a::b;::my::other::lockable;` The default value of this option is `"std::lock_guard;std::scoped_lock"`. โœจ

- **`AllowedAwaitablesList`** โ€” a semicolon-separated list of qualified types of awaitable types which can be safely awaited while you've got hostile RAII objects in scope.

  `co_await`-ing an expression of awaitable type is considered safe if the awaitable type is part of this list. RAII objects persisting across such a `co_await` expression are considered safe, so they don't get flagged. ๐Ÿซถ

  Example usage:

// Consider option AllowedAwaitablesList = "safe_awaitable" struct safe_awaitable { bool await_ready() noexcept { return false; } void await_suspend(std::coroutine_handle<>) noexcept {} void await_resume() noexcept {} }; auto wait() { return safe_awaitable{}; }

task coro() { // This persists across both the co_await's but is not flagged // because the awaitable is considered safe to await on. const std::lock_guard l(&mu_); co_await safe_awaitable{}; co_await wait(); }


  Eg: `my::safe::awaitable;other::awaitable` The default value of this option is empty string `""`. (iconic, honestly โ€” empty by default ๐Ÿ’…)

---

### ๐Ÿ“š misc-definitions-in-headers

Finds non-`extern`, non-`inline` function and variable definitions in header files, which can totally lead to potential ODR violations if those headers get included from multiple translation units. ๐Ÿ’€

// Foo.h int a = 1; // Warning: variable definition. extern int d; // OK: extern variable.

namespace N { int e = 2; // Warning: variable definition. }

// Warning: variable definition. const char* str = "foo";

// OK: internal linkage variable definitions are ignored for now. // Although these might also cause ODR violations, we can be less certain and // should try to keep the false-positive rate down. static int b = 1; const int c = 1; const char* const str2 = "foo"; constexpr int k = 1; namespace { int x = 1; }

// Warning: function definition. int g() { return 1; }

// OK: inline function definition is allowed to be defined multiple times. inline int e() { return 1; }

class A { public: int f1() { return 1; } // OK: implicitly inline member function definition is allowed. int f2();

static int d; };

// Warning: not an inline member function definition. int A::f2() { return 1; }

// OK: class static data member declaration is allowed. int A::d = 1;

// OK: function template is allowed. template<typename T> T f3() { T a = 1; return a; }

// Warning: full specialization of a function template is not allowed. template <> int f3() { int a = 1; return a; }

template <typename T> struct B { void f1(); };

// OK: member function definition of a class template is allowed. template <typename T> void B<T>::f1() {}

class CE { constexpr static int i = 5; // OK: inline variable definition. };

inline int i = 5; // OK: inline variable definition.

constexpr int f10() { return 0; } // OK: constexpr function implies inline.

// OK: C++14 variable templates are inline. template <class T> constexpr T pi = T(3.1415926L);


When `clang-tidy` is invoked with the `--fix-notes` option, this check provides fixes that automatically add the `inline` keyword to discovered functions. <yellow>But fr fr, note this:</yellow> adding the `inline` keyword to **variables** is not currently supported by this check. ๐Ÿ™ˆ

---

### ๐Ÿ” misc-header-include-cycle

Check detects cyclic `#include` dependencies between user-defined headers. It's giving... spaghetti. ๐Ÿ

// Header A.hpp #pragma once #include "B.hpp"

// Header B.hpp #pragma once #include "C.hpp"

// Header C.hpp #pragma once #include "A.hpp"

// Include chain: A->B->C->A


Header files are a crucial part of many C++ programs, since they provide a way to organize declarations and definitions shared across multiple source files. <purple>But</purple> header files can also create problems when they get entangled in complex dependency cycles. Such cycles can cause issues with compilation times, unnecessary rebuilds, and make it harder to understand the overall structure of the code. ๐Ÿ˜ญ

To address these issues, a check has been developed to detect cyclic dependencies between header files, also known as <rainbow>"include cycles"</rainbow>. An include cycle happens when a header file A includes header file B, and B (or any subsequent included header file) includes back header file A, resulting in a circular dependency cycle. ๐Ÿ”„

This check operates at the preprocessor level and specifically analyzes user-defined headers and their dependencies. It focuses solely on detecting include cycles while disregarding other types or function dependencies. This specialized analysis helps identify and prevent issues related to header file organization. ๐Ÿ’…

By detecting include cycles early in the development process, developers can identify and resolve these issues before they become more difficult and time-consuming to fix. This can lead to faster compile times, improved code quality, and a more maintainable codebase overall. Additionally, by ensuring that header files are organized in a way that avoids cyclic dependencies, developers can make their code easier to understand and modify over time. โœจ

It's worth noting that only user-defined headers their dependencies are analyzed โ€” system includes such as standard library headers and third-party library headers are excluded. System includes are usually well-designed and free of include cycles, and ignoring them helps to focus on potential issues within the project's own codebase. This limitation doesn't diminish the ability to detect `#include` cycles within the analyzed code. ๐Ÿฆ‹

Developers should carefully review any warnings or feedback provided by this solution. While the analysis aims to identify and prevent include cycles, there may be situations where exceptions or modifications are necessary. It's important to exercise judgment and consider the specific context of the codebase when making adjustments. ๐Ÿซถ

### Options ๐ŸŒธ

- **`IgnoredFilesList`** โ€” provides a way to exclude specific files/headers from the warnings raised by a check. You do this by specifying a semicolon-separated list of regular expressions or filenames. This option can be used as an alternative to `//NOLINT` when using it is not possible. The default value of this option is an empty string, indicating that no files are ignored by default. ๐Ÿ’–

## ๐ŸŒˆ misc-include-cleaner ๐ŸŒˆ

Okay bestie, this is the <pink>include police</pink> and they are <hotpink>thorough</hotpink> ๐Ÿ’… It checks for unused and missing includes. Big caveat tho: it generates findings <purple>only for the main file of a translation unit</purple>, so don't come for it about your headers. The findings correspond to <cyan>https://clangd.llvm.org/design/include-cleaner</cyan>.

Example, because a picture is worth a thousand warnings:

// foo.h class Foo{}; // bar.h #include "baz.h" class Bar{}; // baz.h class Baz{}; // main.cc #include "bar.h" // OK: uses class Bar from bar.h #include "foo.h" // warning: unused include "foo.h" Bar bar; Baz baz; // warning: missing include "baz.h"


### โœจ Options โœจ

- **`IgnoreHeaders`** โ€” a semicolon-separated list of regexes that disables insertion/removal of header files matching that regex as a suffix. E.g., `foo/.*` disables insertion/removal for all headers under the directory `foo`. By default, <yellow>no headers will be ignored</yellow>.

- **`DeduplicateFindings`** โ€” a boolean that controls whether the check should deduplicate findings for the same symbol. Defaults to `true`.

---

## ๐Ÿ’€ misc-misleading-bidirectional ๐Ÿ’€

Warns about <red>unterminated bidirectional unicode sequence</red>, detecting a potential attack as described in the <rainbow>Trojan Source attack</rainbow>. Yes this is as scary as it sounds. The invisible characters are literally doing crimes.

Example:

#include <iostream>

int main() { bool isAdmin = false; /โ€ฎ } if (isAdmin)โฉ โฆ begin admins only */ std::cout << "You are an admin.\n"; / end admins only โ€ฎ { */ return 0; }


> Okay the way that block <hotpink>looks</hotpink> normal but isn't โ€” that's the whole point bestie ๐Ÿ˜ญ

---

## ๐Ÿฆ‹ misc-misleading-identifier ๐Ÿฆ‹

Finds identifiers that contain Unicode characters with <purple>right-to-left direction</purple>, which can be confusing as they may change the understanding of a whole statement line, as described in <rainbow>Trojan Source</rainbow>. It's giving "the code is lying to your face" energy ๐Ÿ™ˆ

An example of such misleading code follows:

#include <stdio.h>

short int ื = (short int)0; short int ื’ = (short int)12345;

int main() { int ื = ื’; // a local variable, set to zero? printf("ื’ is %d\n", ื’); printf("ื is %d\n", ื); }


---

## ๐ŸŽ€ misc-misplaced-const ๐ŸŽ€

This check diagnoses when a `const` qualifier is applied to a `typedef`/`using` to a <yellow>pointer type</yellow> rather than to the pointee, because such constructs are often misleading to developers โ€” the `const` applies to the pointer, not the pointee, and that is <red>not</red> what anyone reading it thinks. Sneaky.

For instance, in the following code, the resulting type is `int * const` rather than `const int *`:

typedef int *int_ptr; void f(const int_ptr ptr) { *ptr = 0; // potentially quite unexpectedly the int can be modified here ptr = 0; // does not compile }


The check does **not** diagnose when the underlying `typedef`/`using` type is a pointer to a `const` type or a function pointer type. Why? Because the `const` qualifier is less likely to be mistaken there, since it would be redundant (or disallowed) on the underlying pointee type. Iconic reasoning tbh ๐Ÿ‘‘

---

## ๐Ÿ”ฅ misc-new-delete-overloads ๐Ÿ”ฅ

`cert-dcl54-cpp` redirects here as an alias for this check.

The check flags overloaded `operator new()` and `operator delete()` functions that don't have a corresponding free store function defined within the <pink>same scope</pink>. For instance, the check will flag a class implementation of a non-placement `operator new()` when the class doesn't also define a non-placement `operator delete()` function as well. They're a package deal bestie, you can't just take one ๐Ÿ’…

The check does **not** flag implicitly-defined operators, deleted or private operators, or placement operators.

This check corresponds to <cyan>CERT C++ Coding Standard rule DCL54-CPP</cyan>. Overload allocation and deallocation functions as a pair in the same scope.

---

## ๐ŸŒธ misc-no-recursion ๐ŸŒธ

Finds strongly connected functions (by analyzing the call graph for SCC's โ€” <purple>Strongly Connected Components</purple> โ€” that are loops), diagnoses <hotpink>each function in the cycle</hotpink>, and displays one example of a possible call graph loop (recursion). It's giving detective energy ๐Ÿ”

References:

- CERT C++ Coding Standard rule <cyan>DCL56-CPP</cyan>. Avoid cycles during initialization of static objects.
- JPL Institutional Coding Standard for the C Programming Language (JPL DOCID D-60411) rule <cyan>2.4</cyan> Do not use direct or indirect recursion.
- OpenCL Specification, Version 1.2 rule <cyan>6.9</cyan> Restrictions: i. Recursion is not supported..

Limitations:

- The check does **not** handle calls done through function pointers.
- The check does **not** handle C++ destructors.

---

## ๐Ÿ“ misc-non-copyable-objects ๐Ÿ“

`cert-fio38-c` redirects here as an alias for this check.

The check flags dereferences and non-pointer declarations of objects that are <yellow>not meant to be passed by value</yellow>, such as C `FILE` objects or POSIX `pthread_mutex_t` objects. Do not copy the `FILE`, it does not want that ๐Ÿ™…

This check corresponds to <cyan>CERT C++ Coding Standard rule FIO38-C</cyan>. Do not copy a `FILE` object.

---

## ๐Ÿ‘‘ misc-non-private-member-variables-in-classes ๐Ÿ‘‘

`cppcoreguidelines-non-private-member-variables-in-classes` redirects here as an alias for this check.

Finds classes that contain non-static data members <purple>in addition to</purple> user-declared non-static member functions, and diagnoses all data members declared with a non-public access specifier. The data members should be declared as `private` and accessed through member functions instead of exposed to derived classes or class consumers. Protect your data, bestie, it's giving main character boundaries โœจ

### Options

- **`IgnoreClassesWithAllMemberVariablesBeingPublic`** โ€” allows you to completely ignore classes if <hotpink>all</hotpink> the member variables in that class are declared with a `public` access specifier.

- **`IgnorePublicMemberVariables`** โ€” allows you to ignore (not diagnose) all the member variables declared with a `public` access specifier.

---

## ๐ŸŒˆ misc-redundant-expression ๐ŸŒˆ

Detects redundant expressions which are <yellow>typically errors due to copy-paste</yellow>. We've all done it. The check has not.

Depending on the operator, expressions may be:

- redundant,
- always true,
- always false,
- always a constant (zero or one).

Examples:

((x+1) | (x+1)) // (x+1) is redundant (p->x == p->x) // always true (p->x < p->x) // always false (speed - speed + 1 == 12) // speed - speed is always zero


---

## ๐Ÿฆ‹ misc-static-assert ๐Ÿฆ‹

`cert-dcl03-c` redirects here as an alias for this check.

Replaces `assert()` with `static_assert()` if the condition is evaluable at <purple>compile time</purple>.

The condition of `static_assert()` is evaluated at compile time, which is <hotpink>safer and more efficient</hotpink>. No cap ๐Ÿ’…

---

## ๐Ÿ’– misc-throw-by-value-catch-by-reference ๐Ÿ’–

`cert-err09-cpp` and `cert-err61-cpp` redirect here as aliases of this check.

Finds violations of the rule <rainbow>"Throw by value, catch by reference"</rainbow> presented for example in "C++ Coding Standards" by H. Sutter and A. Alexandrescu, as well as the <cyan>CERT C++ Coding Standard rule ERR61-CPP</cyan>. Catch exceptions by lvalue reference.

Exceptions โ€” and yes, that's a pun, we're keeping it:

- Throwing string literals will **not** be flagged despite being a pointer. They are not susceptible to slicing and the usage of string literals is idiomatic.
- Catching character pointers (`char`, `wchar_t`, unicode character types) will **not** be flagged, to allow catching string literals.
- Moved named values will **not** be flagged as not throwing an anonymous temporary. In this case we can be sure that the user knows that the object can't be accessed outside catch blocks handling the error.
- Throwing function parameters will **not** be flagged as not throwing an anonymous temporary. This allows helper functions for throwing.
- Re-throwing caught exception variables will **not** be flagged as not throwing an anonymous temporary. Although this can usually be done by just writing `throw;`, it happens often enough in real code.

### Options

- **`CheckThrowTemporaries`** โ€” triggers detection of violations of the CERT recommendation <cyan>ERR09-CPP</cyan>. Throw anonymous temporaries. Default is `true`.

- **`WarnOnLargeObject`** โ€” also warns for any large, trivial object caught by value. Catching a large object by value is <yellow>not dangerous</yellow> but affects the performance negatively. The maximum size of an object allowed to be caught without warning can be set using the `MaxSize` option. Default is `false`.

- **`MaxSize`** โ€” determines the maximum size of an object allowed to be caught without warning. Only applicable if `WarnOnLargeObject` is set to `true`. If the option is set by the user to `std::numeric_limits<uint64_t>::max()` then it reverts to the default value. Default is the size of `size_t`.

---

## ๐ŸŽ€ misc-unconventional-assign-operator ๐ŸŽ€

Finds declarations of assign operators with the <red>wrong return and/or argument types</red>, and definitions with good return type but wrong return statements. The assignment operator has RULES, bestie:

- The return type must be `Class&`.
- The assignment may be from the class type by value, const lvalue reference, non-const rvalue reference, or from a completely different type (e.g. `int`).
- Private and deleted operators are ignored.
- The operator must always return `*this`.

This check implements <cyan>AUTOSAR C++14 Rule A13-2-1</cyan>.

---

## โœจ misc-uniqueptr-reset-release โœจ

Find and replace `unique_ptr::reset(release())` with `std::move()`.

Example:

std::unique_ptr<Foo> x, y; x.reset(y.release()); -> x = std::move(y);


If `y` is already rvalue, `std::move()` is not added. `x` and `y` can also be `std::unique_ptr<Foo>*`. Slay, go off, cleaner code ๐Ÿซถ

### Options

- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.

---

## ๐ŸŒธ misc-unused-alias-decls ๐ŸŒธ

Finds unused namespace alias declarations. Like, why did you even name it if you're not gonna use it ๐Ÿ˜ญ

namespace my_namespace { class C {}; } namespace unused_alias = ::my_namespace;


---

## ๐Ÿ’œ misc-unused-parameters ๐Ÿ’œ

Finds unused function parameters. Unused parameters may signify a <red>bug</red> in the code (e.g. when a different parameter is used instead). The suggested fixes either comment the parameter name out or remove the parameter completely, if all callers of the function are in the same translation unit and can be updated.

The check is similar to the `-Wunused-parameter` compiler diagnostic and can be used to prepare a codebase for enabling of that diagnostic. By default the check is <pink>more permissive</pink> (see `StrictMode`).

void a(int i) { /some code that doesn't use i/ }

// becomes

void a(int /i/) { /some code that doesn't use i/ }

static void staticFunctionA(int i); static void staticFunctionA(int i) { /some code that doesn't use i/ }

// becomes

static void staticFunctionA() static void staticFunctionA() { /some code that doesn't use i/ }


### Options

- **`StrictMode`** โ€” when `false` (the default value), the check will ignore <purple>trivially unused parameters</purple>, i.e. when the corresponding function has an empty body (and in case of constructors โ€” no constructor initializers). When the function body is empty, an unused parameter is unlikely to be unnoticed by a human reader, and there's basically no place for a bug to hide.

- **`IgnoreVirtual`** โ€” decides whether virtual method parameters get inspected at all. Set it to `true` and they're left alone. Default is `false`. <purple>(leave the virtuals alone bestie, they're going through enough ๐Ÿ’…)</purple>

---

### <pink>misc-unused-using-decls</pink> ๐ŸŽ€

Catches `using` declarations that nobody ever actually uses. It's giving "you brought a plus-one and they never showed up" energy.

<rainbow>Important</rainbow>: unused `using` declarations in **header files** will <red>NOT</red> be diagnosed, because those `using` declarations are part of the header's public API โ€” other people's code might be leaning on them. The allowed header file extensions are configurable via the global option `HeaderFileExtensions`.

Example, bestie:

// main.cpp namespace n { class C; } using n::C; // Never actually used.


---

### <pink>misc-use-anonymous-namespace</pink> ๐Ÿฆ‹

Finds `static` functions or variables declared at global scope that could be yeeted into an anonymous namespace instead.

According to the C++ Standard, anonymous namespaces are the *"superior alternative"*. <hotpink>`static`</hotpink> was proposed for deprecation, but then later un-deprecated to keep C compatibility <purple>[1]</purple>. The thing is, `static` is a wildly overloaded term โ€” it means different things in different contexts โ€” so it creates confusion. <yellow>vibe check: failed, honestly.</yellow>

The following uses of `static` will **not** be diagnosed:

- Functions or variables in header files, since anonymous namespaces in headers are considered an antipattern. Allowed header file extensions can be configured via the global option `HeaderFileExtensions`.
- `const` or `constexpr` variables, since they already have implicit internal linkage in C++.

Examples:

// Bad static void foo(); static int x;

// Good namespace { void foo(); int x; } // namespace


<purple>[1] Undeprecating static</purple>

---

### <pink>misc-use-internal-linkage</pink> ๐Ÿ’–

Detects variables and functions that could be marked `static` or moved into an anonymous namespace to enforce internal linkage. Basically: <mint>"you're only used in this one file, own it, babe"</mint>.

Static functions and variables are scoped to a single file. Marking functions and variables as `static` helps you better remove dead code. On top of that, it hands the compiler more information, which allows for more aggressive optimizations. <cyan>compiler slay, fr fr โœจ</cyan>

Example:

int v1; // can be marked as static

void fn1(); // can be marked as static

namespace { // already in anonymous namespace int v2; void fn2(); } // already declared as extern extern int v2;


### <pink>Options</pink> โœจ

- **`FixMode`** โ€” selects what kind of a fix the check should provide. The default is `UseStatic`.
  - `None` โ€” don't fix automatically.
  - `UseStatic` โ€” add `static` for internal linkage variable and function.

---

### <pink>modernize-avoid-bind</pink> ๐ŸŒธ

This check finds uses of `std::bind` and `boost::bind` and replaces them with lambdas. <rainbow>the glow-up โœจ</rainbow> Lambdas will use value-capture unless reference capture is explicitly requested with `std::ref` or `boost::ref`.

It supports arbitrary callables, including member functions, function objects, and free functions, and all variations thereof. Anything that you can pass to the first argument of `bind` should be diagnosable. Currently, the only known case where a fix-it is unsupported is when the same placeholder is specified multiple times in the parameter list. <yellow>(yeah, that one's cursed, we don't touch it)</yellow>

Given:

int add(int x, int y) { return x + y; }


Then:

void f() { int x = 2; auto clj = std::bind(add, x, _1); }


is replaced by:

void f() { int x = 2; auto clj = [=](auto && arg1) { return add(x, arg1); }; }


Why do we care? Because `std::bind` can be hard to read <purple>(ngl it's giving alphabet soup)</purple> and can result in larger object files and binaries, due to type information that will not be produced by equivalent lambdas.

### <pink>Options</pink> โœจ

- **`PermissiveParameterList`** โ€” if the option is set to `true`, the check will append `auto&&...` to the end of every placeholder parameter list. Without this, it is possible for a fix-it to perform an **incorrect transformation** in the case where the result of the `bind` is used in the context of a type erased functor such as `std::function`, which allows mismatched arguments. For example:

int add(int x, int y) { return x + y; } int foo() { std::function<int(int,int)> ignore_args = std::bind(add, 2, 2); return ignore_args(3, 3); }


is valid code, and returns 4. The actual values passed to `ignore_args` are simply ignored. <mint>they said "not my problem" and meant it</mint>. Without `PermissiveParameterList`, this would be transformed into

int add(int x, int y) { return x + y; } int foo() { std::function<int(int,int)> ignore_args = [] { return add(2, 2); } return ignore_args(3, 3); }


which will <red>NOT</red> compile, since the lambda does not contain an `operator()` that accepts 2 arguments. <red>๐Ÿ’€ broken build alert ๐Ÿ’€</red> With permissive parameter list, it instead generates

int add(int x, int y) { return x + y; } int foo() { std::function<int(int,int)> ignore_args = [](auto&&...) { return add(2, 2); } return ignore_args(3, 3); }


which is correct. <green>we love a functioning fix-it ๐Ÿซถ</green>

> This check requires using C++14 or higher to run.

---

### <pink>modernize-avoid-c-arrays</pink> ๐Ÿ‘‘

`cppcoreguidelines-avoid-c-arrays` redirects here as an alias for this check.

`hicpp-avoid-c-arrays` redirects here as an alias for this check.

Finds C-style array types and recommends using `std::array<>` / `std::vector<>` instead. All types of C arrays are diagnosed. <hotpink>all of them. every single one. we see you ๐Ÿ‘€</hotpink>

However, fix-it are potentially **dangerous** in header files and are therefore not emitted right now.

int a[] = {1, 2}; // warning: do not declare C-style arrays, use std::array<> instead

int b[1]; // warning: do not declare C-style arrays, use std::array<> instead

void foo() { int c[b[0]]; // warning: do not declare C VLA arrays, use std::vector<> instead }

template <typename T, int Size> class array { T d[Size]; // warning: do not declare C-style arrays, use std::array<> instead

int e[1]; // warning: do not declare C-style arrays, use std::array<> instead };

array<int[4], 2> d; // warning: do not declare C-style arrays, use std::array<> instead

using k = int[4]; // warning: do not declare C-style arrays, use std::array<> instead


However, `extern "C"` code is ignored, since it is common to share such headers between C code and C++ code. <cyan>respecting the C girlies' turf ๐Ÿ™</cyan>

// Some header extern "C" {

int f[] = {1, 2}; // not diagnosed

int j[1]; // not diagnosed

inline void bar() { { int j[j[0]]; // not diagnosed } }

}


Similarly, the `main()` function is ignored. Its second and third parameters can be either `char* argv[]` or `char** argv`, but cannot be `std::array<>`. <purple>main is the main character, it gets a pass โœจ</purple>

- **`AllowStringArrays`** โ€” when set to `true` (default is `false`), variables of character array type with deduced length, initialized directly from string literals, will be ignored. This option doesn't affect cases where length can't be deduced, resembling pointers, as seen in class members and parameters. Example:

const char name[] = "Some name";


---

### <pink>modernize-concat-nested-namespaces</pink> ๐Ÿงš

Checks for use of nested namespaces such as `namespace a { namespace b { ... } }` and suggests changing to the more concise syntax introduced in C++17: `namespace a::b { ... }`. Inline namespaces are **not** modified. <yellow>leave the inline ones be, they're doing their best ๐Ÿ’…</yellow>

For example:

namespace n1 { namespace n2 { void t(); } }

namespace n3 { namespace n4 { namespace n5 { void t(); } } namespace n6 { namespace n7 { void t(); } } }

// in c++20 namespace n8 { inline namespace n9 { void t(); } }


Will be modified to:

namespace n1::n2 { void t(); }

namespace n3 { namespace n4::n5 { void t(); } namespace n6::n7 { void t(); } }

// in c++20 namespace n8::inline n9 { void t(); }


---

### <pink>modernize-deprecated-headers</pink> ๐Ÿ“

Some headers from the C library were deprecated in C++ and are no longer welcome in C++ codebases. <red>they're on the list ๐Ÿ“‹</red> Some have no effect in C++ at all. For more details refer to the C++14 Standard `[depr.c.headers]` section.

This check replaces C standard library headers with their C++ alternatives and removes redundant ones.

// C++ source file... #include <assert.h> #include <stdbool.h>

// becomes

#include <cassert> // No 'stdbool.h' here.


> **Important note:** the Standard doesn't guarantee that the C++ headers declare all the same functions in the global namespace. The check in its current form can <red>break</red> the code that uses library symbols from the global namespace.

- `<assert.h>`
- `<complex.h>`
- `<ctype.h>`
- `<errno.h>`
- `<fenv.h>`     // deprecated since C++11
- `<float.h>`
- `<inttypes.h>`
- `<limits.h>`
- `<locale.h>`
- `<math.h>`
- `<setjmp.h>`
- `<signal.h>`
- `<stdarg.h>`
- `<stddef.h>`
- `<stdint.h>`
- `<stdio.h>`
- `<stdlib.h>`
- `<string.h>`
- `<tgmath.h>`   // deprecated since C++11
- `<time.h>`
- `<uchar.h>`    // deprecated since C++11
- `<wchar.h>`
- `<wctype.h>`

If the specified standard is older than C++11 the check will only replace headers deprecated **before** C++11, otherwise โ€” every header that appeared in the previous list. <purple>iykyk</purple>

These headers don't have effect in C++:

- `<iso646.h>`
- `<stdalign.h>`
- `<stdbool.h>`

The checker ignores include directives within `extern "C" { ... }` blocks, since a library might want to expose some API for C and C++ libraries.

// C++ source file... extern "C" { #include <assert.h> // Left intact. #include <stdbool.h> // Left intact. }


### <pink>Options</pink> โœจ

- **`CheckHeaderFile`** โ€” `clang-tidy` cannot know if the header file included by the currently analyzed C++ source file is not included by any other C source files. Hence, to omit false-positives and wrong fixit-hints, we ignore emitting reports into header files. One can set this option to `true` if they know that the header files in the project are only used by C++ source file. Default is `false`.

### <purple>`modernize-deprecated-ios-base-aliases`</purple> ๐Ÿงš

Okay bestie, this one is a lil archaeological dig โœจ โ€” it spots when you're out here using the <red>deprecated</red> member types of `std::ios_base`, and it swaps in the modern glow-up for every one that actually has a non-deprecated equivalent. No cap.

- **`std::ios_base::io_state`** โ†’ gets replaced with `std::ios_base::iostate` ๐Ÿ’…
- **`std::ios_base::open_mode`** โ†’ gets replaced with `std::ios_base::openmode` ๐Ÿ’…
- **`std::ios_base::seek_dir`** โ†’ gets replaced with `std::ios_base::seekdir` ๐Ÿ’…
- **`std::ios_base::streamoff`** โ†’ no replacement, she's just deprecated, that's it ๐Ÿ˜ญ
- **`std::ios_base::streampos`** โ†’ also no replacement, just deprecated ๐Ÿ™ˆ

---

### <purple>`modernize-loop-convert`</purple> ๐Ÿ”โœจ

This check takes your crusty old `for(...; ...; ...)` loops and converts them to use the shiny new range-based loops from C++11. It's giving glow-up, fr fr.

Three kinds of loops can be converted:

- Loops over <cyan>statically allocated arrays</cyan> ๐Ÿงบ
- Loops over <cyan>containers, using iterators</cyan> ๐Ÿฆ‹
- Loops over <cyan>array-like containers, using `operator[]` and `at()`</cyan> ๐Ÿ‘‘

---

**<hotpink>MinConfidence option</hotpink>** ๐ŸŽš๏ธ

- **`risky`** โ€” In loops where the container expression is more complex than just a reference to a declared expression (a variable, function, enum, etc.), and some part of it appears elsewhere in the loop, we lower our confidence in the transformation because there's a bigger risk of changing semantics. Transformations for these loops get marked as **risky**, and so they will only be converted if the minimum required confidence level is set to `risky`. ๐Ÿ‘€

int arr[10][20]; int l = 5;

for (int j = 0; j < 20; ++j) int k = arr[l][j] + l; // using l outside arr[l] is considered risky

for (int i = 0; i < obj.getVector().size(); ++i) obj.foo(10); // using 'obj' is considered risky


  See <cyan>Range-based loops evaluate end() only once</cyan> for an example of an incorrect transformation when the minimum required confidence level is set to `risky`. ๐Ÿ˜ฌ

- **`reasonable` (Default)** โ€” If a loop calls `.end()` or `.size()` after each iteration, the transformation for that loop gets marked as **reasonable**, and so it will be converted if the required confidence level is set to `reasonable` (that's the default, bestie) or lower. ๐Ÿซถ

// using size() is considered reasonable for (int i = 0; i < container.size(); ++i) cout << container[i];


- **`safe`** โ€” Any other loops that don't match the criteria above for being marked `risky` or `reasonable` get marked **safe**, and so they will be converted if the required confidence level is set to `safe` or lower. ๐ŸŒˆ

int arr[] = {1,2,3};

for (int i = 0; i < 3; ++i) cout << arr[i];


---

**<hotpink>Example</hotpink>** ๐ŸŒธ

Original:

const int N = 5; int arr[] = {1,2,3,4,5}; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3);

// safe conversion for (int i = 0; i < N; ++i) cout << arr[i];

// reasonable conversion for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) cout << *it;

// reasonable conversion for (vector<int>::iterator it = begin(v); it != end(v); ++it) cout << *it;

// reasonable conversion for (vector<int>::iterator it = std::begin(v); it != std::end(v); ++it) cout << *it;

// reasonable conversion for (int i = 0; i < v.size(); ++i) cout << v[i];

// reasonable conversion for (int i = 0; i < size(v); ++i) cout << v[i];


After applying the check with minimum confidence level set to `reasonable` (the default, iykyk):

const int N = 5; int arr[] = {1,2,3,4,5}; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3);

// safe conversion for (auto & elem : arr) cout << elem;

// reasonable conversion for (auto & elem : v) cout << elem;

// reasonable conversion for (auto & elem : v) cout << elem;


---

**<hotpink>Reverse Iterator Support</hotpink>** ๐Ÿ”™๐Ÿ”

The converter can also transform iterator loops that use `rbegin` and `rend` to loop <purple>backwards</purple> over a container. Out of the box this automatically happens in C++20 mode using the ranges library โ€” iconic. But the check can be configured to work without C++20 too, by specifying a function to reverse a range and, optionally, the header file where that function lives. ๐ŸŽ€

- **`UseCxx20ReverseRanges`** โ€” When set to `true`, convert loops when in C++20 or later mode using `std::ranges::reverse_view`. Default value is `true`. โœ…
- **`MakeReverseRangeFunction`** โ€” Specify the function used to reverse an iterator pair. The function should accept a class with `rbegin` and `rend` methods and return a class with `begin` and `end` methods that call the `rbegin` and `rend` methods respectively. Common examples are `ranges::reverse_view` and `llvm::reverse`. Default value is an empty string. ๐Ÿซ™
- **`MakeReverseRangeHeader`** โ€” Specifies the header file where `MakeReverseRangeFunction` is declared. For the previous examples this option would be set to `range/v3/view/reverse.hpp` and `llvm/ADT/STLExtras.h` respectively. If this is an empty string and `MakeReverseRangeFunction` is set, the check will proceed on the assumption that the function is already available in the translation unit. This can be wrapped in angle brackets to signify to add the include as a system include. Default value is an empty string. ๐Ÿ“„
- **`IncludeStyle`** โ€” A string specifying which include-style is used, `llvm` or `google`. Default is `llvm`. ๐Ÿ’œ

---

**<hotpink>Limitations</hotpink>** ๐Ÿ’€

There are certain situations where the tool may *erroneously* perform transformations that remove information and change semantics. Users of the tool should be aware of the behavior and limitations of the check outlined by the cases below. Read this part, bestie, it's important. ๐Ÿ˜ญ

---

**<hotpink>Comments inside loop headers</hotpink>** ๐Ÿ™ˆ

Comments inside the original loop header are ignored and deleted when transformed. Gone. Poof. โœจ

for (int i = 0; i < N; /* This will be deleted */ ++i) { }


---

**<hotpink>Range-based loops evaluate end() only once</hotpink>** โš ๏ธ

The C++11 range-based `for` loop calls `.end()` only **once**, during the initialization of the loop. If in the original loop `.end()` is called after each iteration, the semantics of the transformed loop may differ. ๐Ÿšจ

// The following is semantically equivalent to the C++11 range-based for loop, // therefore the semantics of the header will not change. for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }

// Instead of calling .end() after each iteration, this loop will be // transformed to call .end() only once during the initialization of the loop, // which may affect semantics. for (iterator it = container.begin(); it != container.end(); ++it) { }


As explained above, calling member functions of the container in the body of the loop is considered <red>risky</red>. If the called member function modifies the container, the semantics of the converted loop will differ, because `.end()` is called only once. ๐Ÿ’…

bool flag = false; for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) { // Add a copy of the first element to the end of the vector. if (!flag) { // This line makes this transformation 'risky'. vec.push_back(*it); flag = true; } cout << *it; }


The original code above prints out the contents of the container **including the newly added element**, while the converted loop shown below will only print the original contents and not the newly added element. ๐Ÿ˜ญ

bool flag = false; for (auto & elem : vec) { // Add a copy of the first element to the end of the vector. if (!flag) { // This line makes this transformation 'risky' vec.push_back(elem); flag = true; } cout << elem; }


Semantics will also be affected if `.end()` has side effects. For example, in the case where calls to `.end()` are logged, the semantics will change in the transformed loop if `.end()` was originally called after each iteration. ๐Ÿ“

iterator end() { num_of_end_calls++; return container.end(); }


---

**<hotpink>Overloaded operator->() with side effects</hotpink>** โš ๏ธ

Similarly, if `operator->()` was overloaded to have side effects, such as logging, the semantics will change. If the iterator's `operator->()` was used in the original loop, it will be replaced with `<container element>.<member>` instead, due to the implicit dereference as part of the range-based `for` loop. Therefore any side effect of the overloaded `operator->()` will no longer be performed. ๐Ÿ’€

for (iterator it = c.begin(); it != c.end(); ++it) { it->func(); // Using operator->() } // Will be transformed to: for (auto & elem : c) { elem.func(); // No longer using operator->() }


---

**<hotpink>Pointers and references to containers</hotpink>** ๐Ÿซฃ

While most of the check's risk analysis is dedicated to determining whether the iterator or container was modified within the loop, it's possible to circumvent the analysis by accessing and modifying the container through a <purple>pointer</purple> or <purple>reference</purple>. Sneaky sneaky.

If the container were directly used instead of using the pointer or reference, the following transformation would have only been applied at the `risky` level, since calling a member function of the container is considered risky. The check cannot identify expressions associated with the container that are different than the one used in the loop header, therefore the transformation below ends up being performed at the `safe` level. ๐Ÿ˜ฌ

vector<int> vec;

vector<int> *ptr = &vec; vector<int> &ref = vec;

for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) { if (!flag) { // Accessing and modifying the container is considered risky, but the risk // level is not raised here. ptr->push_back(*it); ref.push_back(*it); flag = true; } }


### ๐ŸŒ€ OpenMP

okay so <yellow>range-based for loops only exist since OpenMP 5</yellow>, which means you should **not** be running this check on code that needs to stay compatible with OpenMP older than version 5. And like, it's <pink>intentional</pink> โ€” the check does *not* even try to filter out wrong diagnostics on OpenMP `for` loops from before OpenMP 5. It just doesn't. That's the design, bestie. ๐Ÿ’€

So if you wanna stop this check from touching (and breaking) your OpenMP `for` loops while still letting it do its thing on non-OpenMP `for` loops? The recommended move is to slap a `NOLINT` on those specific `for` loops (peep *Suppressing Undesired Diagnostics* for the deets). ๐Ÿซถ

### ๐ŸŽ€ modernize-macro-to-enum

This one takes groups of adjacent macros and swaps them out for an **unscoped anonymous enum**. And the slay part? Because it's an unscoped anonymous enum, everywhere that macro token used to live, the enumerator name can now be safely used. โœจ

Use it to enforce the C++ core guideline **Enum.1: Prefer enumerations over macros** โ€” within the constraints listed below, obviously. ๐Ÿ‘‘

So like, which macros are even eligible for replacement? They gotta pass ALL of this:

- Macros must expand **only** to integral literal tokens or expressions of literal tokens. That expression can have any of the unary operators `-`, `+`, `~` or `!`; any of the binary operators `,`, `-`, `+`, `*`, `/`, `%`, `&`, `|`, `^`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `||`, `&&`, `<<`, `>>` or `<=>`; the ternary operator `?:` and its GNU extension. Parenthesized expressions are recognized too. This catches most valid expressions โ€” but heads up, expressions using the `sizeof` operator are <red>not</red> recognized. ๐Ÿ™ˆ
- Macros must be defined on **sequential source file lines**, or with only comment lines between the macro definitions.
- Macros must all be defined in the **same source file**.
- Macros must **not** be defined inside a conditional compilation block. (Conditional include guards are exempt from this one, don't worry.)
- Macros must **not** be defined adjacent to other preprocessor directives.
- Macros must **not** be used in any conditional preprocessing directive.
- Macros must **not** be used as arguments to other macros.
- Macros must **not** be undefined.
- Macros must be defined at the **top-level** โ€” not inside any declaration or definition.

Every cluster of macros that survives all that is assumed to be a set of values you could swap for an anonymous enum. From there you (the developer, the main character ๐Ÿ’…) can name that anonymous enum and keep refactoring toward a scoped enum if that's the vibe. Comments on the same line as a macro definition, or between subsequent macro definitions, get preserved in the output. No formatting is assumed in the provided replacements, though `clang-tidy` can optionally format all the fixes. ๐Ÿฆ‹

> **WARNING:** Initializing expressions are assumed to be valid initializers for an enum. C requires that enum values fit into an `int`, but that may not be the case for some accepted constant expressions. For instance `1 << 40` will not fit into an `int` when the size of an `int` is 32 bits. ๐Ÿ˜ญ

Examples, bestie:

#define RED 0xFF0000 #define GREEN 0x00FF00 #define BLUE 0x0000FF

#define TM_NONE (-1) // No method selected. #define TM_ONE 1 // Use tailored method one. #define TM_TWO 2 // Use tailored method two. Method two // is preferable to method one. #define TM_THREE 3 // Use tailored method three.


becomes

enum { RED = 0xFF0000, GREEN = 0x00FF00, BLUE = 0x0000FF };

enum { TM_NONE = (-1), // No method selected. TM_ONE = 1, // Use tailored method one. TM_TWO = 2, // Use tailored method two. Method two // is preferable to method one. TM_THREE = 3 // Use tailored method three. };


### ๐Ÿ’– modernize-make-shared

This check catches you creating `std::shared_ptr` objects by explicitly calling the constructor with a `new` expression, and rewrites that into a call to `std::make_shared`. So much cleaner, fr fr. โœจ

auto my_ptr = std::shared_ptr<MyPair>(new MyPair(1, 2));

// becomes

auto my_ptr = std::make_shared<MyPair>(1, 2);


It also finds calls to `std::shared_ptr::reset()` that pass a `new` expression, and swaps those for a call to `std::make_shared` too. ๐Ÿ‘‘

my_ptr.reset(new MyPair(1, 2));

// becomes

my_ptr = std::make_shared<MyPair>(1, 2);


#### Options

- **`MakeSmartPtrFunction`** โ€” a string specifying the name of the make-shared-ptr function. Default is `std::make_shared`.
- **`MakeSmartPtrFunctionHeader`** โ€” a string specifying the corresponding header of the make-shared-ptr function. Default is `memory`.
- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.
- **`IgnoreMacros`** โ€” if set to `true`, the check will not give warnings inside macros. Default is `true`.
- **`IgnoreDefaultInitialization`** โ€” if set to non-zero, the check does not suggest edits that will transform default initialization into value initialization, since that can cause performance regressions. Default is `1`.

### ๐ŸŒธ modernize-make-unique

Same energy, different smart pointer: this check finds `std::unique_ptr` objects being created by explicitly calling the constructor plus a `new` expression, and replaces it with a call to `std::make_unique` โ€” which, important, was introduced in **C++14**. ๐Ÿ’œ

auto my_ptr = std::unique_ptr<MyPair>(new MyPair(1, 2));

// becomes

auto my_ptr = std::make_unique<MyPair>(1, 2);


And it also finds `std::unique_ptr::reset()` calls with a `new` expression, replacing those with a call to `std::make_unique`. ๐ŸŽ€

my_ptr.reset(new MyPair(1, 2));

// becomes

my_ptr = std::make_unique<MyPair>(1, 2);


#### Options

- **`MakeSmartPtrFunction`** โ€” a string specifying the name of the make-unique-ptr function. Default is `std::make_unique`.
- **`MakeSmartPtrFunctionHeader`** โ€” a string specifying the corresponding header of the make-unique-ptr function. Default is `<memory>`.
- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.
- **`IgnoreMacros`** โ€” if set to `true`, the check will not give warnings inside macros. Default is `true`.
- **`IgnoreDefaultInitialization`** โ€” if set to non-zero, the check does not suggest edits that will transform default initialization into value initialization, since that can cause performance regressions. Default is `1`.

### ๐Ÿฆ‹ modernize-min-max-use-initializer-list

Nested `std::min` and `std::max` calls? Yeah, this check collapses them into an initializer list wherever that applies. Gorgeous. โœจ

Take this code for example:

int a = std::max(std::max(i, j), k);


The check transforms it into:

int a = std::max({i, j, k});


#### Performance Considerations

So like, yes, this simplifies the code and makes it way more readable โ€” but it **can** cause performance degradation for non-trivial types, because objects have to get copied into the initializer list. ๐Ÿ˜ญ

To dodge that, the recommended move is `std::ref` or `std::cref` for non-trivial types:

std::string b = std::max({std::ref(i), std::ref(j), std::ref(k)});


#### Options

- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.
- **`IgnoreNonTrivialTypes`** โ€” a boolean specifying whether to ignore non-trivial types. Default is `true`.
- **`IgnoreTrivialTypesOfSizeAbove`** โ€” an integer specifying the size (in bytes) above which trivial types are ignored. Default is `32`.

### ๐Ÿ‘‘ modernize-pass-by-value

okay so with move semantics in the language now, and the standard library having picked up move constructors for a ton of types, it's genuinely interesting to take an argument **directly by value** instead of by const-reference and then copy. This check lets the compiler handle picking the best way to construct the copy. ๐Ÿ’…

The transformation is usually a win when the calling code passes an rvalue and assumes move construction is cheap. This lil example shows how the value's construction goes down:

void foo(std::string s); std::string get_str();

void f(const std::string &str) { foo(str); // lvalue -> copy construction foo(get_str()); // prvalue -> move construction }


> **NOTE:** Currently, only constructors are transformed to make use of pass-by-value. Contributions that handle other situations are welcome! ๐Ÿ™ˆ

#### Pass-by-value in constructors

This replaces the uses of const-reference constructor parameters that get copied into class fields. The parameter then gets moved with `std::move()`. ๐ŸŒ€

And since `std::move()` is a library function declared in `<utility>`, you might need to add that include. The check will add the include directive when necessary. ๐Ÿซถ

#include <string>

class Foo { public:

  • Foo(const std::string &Copied, const std::string &ReadOnly)
  • : Copied(Copied), ReadOnly(ReadOnly)
  • Foo(std::string Copied, const std::string &ReadOnly)
  • : Copied(std::move(Copied)), ReadOnly(ReadOnly) {}

private: std::string Copied; const std::string &ReadOnly; };

std::string get_cwd();

void f(const std::string &Path) { // The parameter corresponding to 'get_cwd()' is move-constructed. By // using pass-by-value in the Foo constructor we managed to avoid a // copy-construction. Foo foo(get_cwd(), Path); }


But if the parameter is used more than once, no transformation happens โ€” because moved objects have an undefined state, bestie. Which means this code stays exactly as it is:

#include <string>

void pass(const std::string &S);

struct Foo { Foo(const std::string &S) : Str(S) { pass(S); }

std::string Str; };


#### Known limitations

There's a situation where the generated code can be **wrong**: when the object being referenced gets modified before the assignment in the init-list, through a "hidden" reference. ๐Ÿ˜ญ

Example:

std::string s("foo");

struct Base { Base() { s = "bar"; } };

struct Derived : Base {

  • Derived(const std::string &S) : Field(S)
  • Derived(std::string S) : Field(std::move(S)) { }

std::string Field; };

void f() {

  • Derived d(s); // d.Field holds "bar"
  • Derived d(s); // d.Field holds "foo" }

### Note about delayed template parsing

okay so <pink>delayed template parsing</pink> is on and you're like "why is my constructor not getting the glow-up" โ€” bestie, it's because when that's enabled, constructors living in templated contexts **do not get transformed**. That means templated constructors, constructors inside class templates, constructors of inner classes of template classes, etc. โ€” all of them just sit there, untouched. ๐Ÿ’€

Delayed template parsing is **enabled by default on Windows** as a Microsoft extension (peep the *Clang Compiler User's Manual - Microsoft extensions* if you want the lore).

You can turn it on with `-fdelayed-template-parsing` and turn it off with `-fno-delayed-template-parsing`. โœจ

Example, because we love a visual:

template <typename T> class C { std::string S;

public: = // using -fdelayed-template-parsing (default on Windows) = C(const std::string &S) : S(S) {}

  • // using -fno-delayed-template-parsing (default on non-Windows systems)
  • C(std::string S) : S(std::move(S)) {} };

**SEE ALSO:**

For more information about the pass-by-value idiom, read: *Want Speed? Pass by Value*. ๐Ÿซถ

---

### Options

- **`IncludeStyle`** โ€” a string telling the check which include-style is being used, either `llvm` or `google`. Default is `llvm`. ๐Ÿ’…
- **`ValuesOnly`** โ€” when `true`, the check only warns about copied parameters that are *already* passed by value. Default is `false`.

---

## ๐ŸŒˆ modernize-raw-string-literal ๐ŸŒˆ

This check <hotpink>selectively</hotpink> replaces string literals containing escaped characters with raw string literals. Selective is the key word, she's not doing a full makeover, just the glow-ups that make sense. โœจ

Example:

const char *const Quotes{"embedded "quotes""}; const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"}; const char *const SingleLine{"Single line.\n"}; const char *const TrailingSpace{"Look here -> \n"}; const char *const Tab{"One\tTwo\n"}; const char *const Bell{"Hello!\a And welcome!"}; const char *const Path{"C:\Program Files\Vendor\Application.exe"}; const char *const RegEx{"\w\([a-z]\)"};


becomes

const char *const Quotes{R"(embedded "quotes")"}; const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"}; const char *const SingleLine{"Single line.\n"}; const char *const TrailingSpace{"Look here -> \n"}; const char *const Tab{"One\tTwo\n"}; const char *const Bell{"Hello!\a And welcome!"}; const char *const Path{R"(C:\Program Files\Vendor\Application.exe)"}; const char *const RegEx{R"(\w([a-z]))"};


The presence of **any** of the following escapes can cause the string to be converted to a raw string literal: `\\`, `\'`, `\"`, `\?`, and octal or hexadecimal escapes for printable ASCII characters. ๐Ÿ‘‘

A string literal containing **only** escaped newlines is a common way of writing lines of text output. Introducing physical newlines with raw string literals in this case is likely to impede readability, so these string literals are **left unchanged**. periodt. ๐ŸŽ€

An escaped horizontal tab, form feed, or vertical tab **prevents** the string literal from being converted. Why? Because the presence of a horizontal tab, form feed or vertical tab in source code is not visually obvious. iykyk. ๐Ÿฆ‹

### Options

- **`DelimiterStem`** โ€” custom delimiter to escape characters in raw string literals. It is used in the following construction: `R"stem_delimiter(contents)stem_delimiter"`. The default value is `lit`.
- **`ReplaceShorterLiterals`** โ€” controls replacing shorter non-raw string literals with *longer* raw string literals. Setting this option to `true` enables the replacement. The default value is `false` (shorter literals are not replaced). ๐Ÿ™ˆ

---

## ๐Ÿ’… modernize-redundant-void-arg ๐Ÿ’…

Find and remove redundant void argument lists. she's doing a little decluttering, as a treat. ๐Ÿงนโœจ

Examples:

- Initial code: `int f(void);` โ†’ code with applied fixes: `int f();`
- Initial code: `int (*f(void))(void);` โ†’ code with applied fixes: `int (*f())();`
- Initial code: `typedef int (*f_t(void))(void);` โ†’ code with applied fixes: `typedef int (*f_t())();`
- Initial code: `void (C::*p)(void);` โ†’ code with applied fixes: `void (C::*p)();`
- Initial code: `C::C(void) {}` โ†’ code with applied fixes: `C::C() {}`
- Initial code: `C::~C(void) {}` โ†’ code with applied fixes: `C::~C() {}`

---

## ๐Ÿฆ‹ modernize-replace-auto-ptr ๐Ÿฆ‹

This check replaces the uses of the **deprecated** class `std::auto_ptr` by `std::unique_ptr` (introduced in C++11). The transfer of ownership โ€” done by the copy-constructor and the assignment operator โ€” is changed to match `std::unique_ptr` usage by using explicit calls to `std::move()`. out with the old, in with the slay. ๐Ÿ’€โžก๏ธ๐Ÿ‘‘

Migration example:

-void take_ownership_fn(std::auto_ptr<int> int_ptr); +void take_ownership_fn(std::unique_ptr<int> int_ptr);

void f(int x) {

  • std::auto_ptr<int> a(new int(x));
  • std::auto_ptr<int> b;
  • std::unique_ptr<int> a(new int(x));
  • std::unique_ptr<int> b;
  • b = a;
  • take_ownership_fn(b);
  • b = std::move(a);
  • take_ownership_fn(std::move(b)); }

Since `std::move()` is a library function declared in `<utility>`, it may be necessary to add this include. The check will add the include directive when necessary. ๐Ÿซถ

### Known Limitations

- If headers modification is not activated, or if a header is not allowed to be changed, this check will produce **broken code (compilation error)**, where the headers' code will stay unchanged while the code using them will be changed. ๐Ÿ˜ญ
- Client code that declares a reference to an `std::auto_ptr` coming from code that can't be migrated (such as a header coming from a 3rd party library) will produce a compilation error after migration. This is because the type of the reference will be changed to `std::unique_ptr` but the type returned by the library won't change โ€” binding a reference to `std::unique_ptr` from an `std::auto_ptr`. This pattern doesn't make much sense, and usually `std::auto_ptr` are stored by value (otherwise, what is the point in using them instead of a reference or a pointer?).

// <3rd-party header...> std::auto_ptr<int> get_value(); const std::auto_ptr<int> & get_ref();

// <calling code (with migration)...> -std::auto_ptr<int> a(get_value()); +std::unique_ptr<int> a(get_value()); // ok, unique_ptr constructed from auto_ptr

-const std::auto_ptr<int> & p = get_ptr(); +const std::unique_ptr<int> & p = get_ptr(); // won't compile


- Non-instantiated templates aren't modified. ๐Ÿšซ

template <typename X> void f() { std::auto_ptr<X> p; }

// only 'f<int>()' (or similar) will trigger the replacement.


### Options

- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.

---

## ๐ŸŽ€ modernize-replace-disallow-copy-and-assign-macro ๐ŸŽ€

Finds macro expansions of `DISALLOW_COPY_AND_ASSIGN(Type)` and replaces them with a deleted copy constructor and a deleted assignment operator. iconic behaviour honestly. โœจ

Before the `delete` keyword was introduced in C++11, it was common practice to declare a copy constructor and an assignment operator as **private** members. This effectively makes them unusable to the public API of a class.

With the advent of the `delete` keyword in C++11, we can abandon the private access of the copy constructor and the assignment operator and delete the methods entirely. glow-up complete. ๐Ÿ’…

When running this check on code like this:

class Foo { private: DISALLOW_COPY_AND_ASSIGN(Foo); };


It will be transformed to this:

class Foo { private: Foo(const Foo &) = delete; const Foo &operator=(const Foo &) = delete; };


### Known Limitations

- Notice that the migration example above **leaves the private access specification untouched**. You might want to run the check `modernize-use-equals-delete` to get warnings for deleted functions in private sections. ๐Ÿ‘€

### Options

- **`MacroName`** โ€” a string specifying the macro name whose expansion will be replaced. Default is `DISALLOW_COPY_AND_ASSIGN`.

See: `https://en.cppreference.com/w/cpp/language/function#Deleted_functions`

---

## ๐Ÿ”ฅ modernize-replace-random-shuffle ๐Ÿ”ฅ

This check will find occurrences of `std::random_shuffle` and replace it with `std::shuffle`. In C++17, `std::random_shuffle` will no longer be available, and thus we need to replace it. it's giving *expired*, bestie. ๐Ÿ•ฐ๏ธ๐Ÿ’€

Below are two examples of what kind of occurrences will be found and two examples of what it will be replaced with.

std::vector<int> v;

// First example std::random_shuffle(vec.begin(), vec.end());

// Second example std::random_shuffle(vec.begin(), vec.end(), randomFunc);


Both of these examples will be replaced with:

std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));


The second example will also receive a **warning** that `randomFunc` is no longer supported in the same way as before โ€” so if the user wants the same functionality, the user will need to change the implementation of the `randomFunc`. ๐Ÿ˜ญ

One thing to be aware of here is that `std::random_device` is quite expensive to initialize. So if you are using the code in a performance critical place, you probably want to initialize it elsewhere. Another thing is that the seeding quality of the suggested fix is quite poor: `std::mt19937` has an internal state of 624 32-bit integers, but is only seeded with a single integer. So if you require higher quality randomness, you should consider seeding better, for example:

std::shuffle(v.begin(), v.end(), []() { std::mt19937::result_type seeds[std::mt19937::state_size]; std::random_device device; std::uniform_int_distribution<typename std::mt19937::result_type> dist; std::generate(std::begin(seeds), std::end(seeds), [&] { return dist(device); }); std::seed_seq seq(std::begin(seeds), std::end(seeds)); return std::mt19937(seq); }());


---

## ๐ŸŽ€ modernize-return-braced-init-list ๐ŸŽ€

Bestie, this one takes those explicit constructor calls hiding inside a `return` and swaps them for a braced initializer list. The glow-up here? Your return type doesn't get needlessly duplicated in the function definition *and* the return statement anymore. Obsessed.

Foo bar() { Baz baz; return Foo(baz); }

// transforms to:

Foo bar() { Baz baz; return {baz}; }


---

## ๐Ÿฆ‹ modernize-shrink-to-fit ๐Ÿฆ‹

Say bye to the copy-and-swap trick on shrinkable containers, because this check replaces it with a plain `shrink_to_fit()` method call. ๐Ÿ’…

Why? Because `shrink_to_fit()` is <pink>more readable</pink> and <pink>more effective</pink> than the copy-and-swap trick when you're trying to reduce the capacity of a shrinkable container. Lowkey iconic.

> Heads up though: the `shrink_to_fit()` method is **only available in C++11 and up**. So if you're on older vibes, this one's not for you. ๐Ÿซถ

---

## โœจ modernize-type-traits โœจ

This check converts standard library type traits that look like `traits<...>::type` and `traits<...>::value` into the cute little shorthand `traits_t<...>` and `traits_v<...>` respectively. Glow-up central.

For example:

std::is_integral<T>::value std::is_same<int, float>::value typename std::add_const<T>::type std::make_signed<unsigned>::type


Would be converted into:

std::is_integral_v<T> std::is_same_v<int, float> std::add_const_t<T> std::make_signed_t<unsigned>


### Options

- **`IgnoreMacros`** โ€” if true, don't diagnose traits defined in macros.

  > Note: fixes will **never** be emitted for code inside of macros. Fr fr.

#define IS_SIGNED(T) std::is_signed<T>::value


Defaults to `false`.

---

## ๐ŸŒธ modernize-unary-static-assert ๐ŸŒธ

This check diagnoses any `static_assert` declaration with an empty string literal and hands you a fix-it that replaces it with a single-argument `static_assert` declaration. Cleaning up the vibe check one assert at a time.

> Only applicable for **C++17 and later** code, bestie. Don't try it on anything older. ๐Ÿ˜ญ

The following code:

void f_textless(int a) { static_assert(sizeof(a) <= 10, ""); }


is replaced by:

void f_textless(int a) { static_assert(sizeof(a) <= 10); }


---

## ๐Ÿ‘‘ modernize-use-auto ๐Ÿ‘‘

Okay so this check is the main character of variable declarations: it's responsible for using the `auto` type specifier on variable declarations to make your code more readable and more maintainable. For example:

std::vector<int>::iterator I = my_container.begin();

// transforms to:

auto I = my_container.begin();


Now here's the fine print, because it's giving nuance. The `auto` type specifier will **only** be introduced in situations where the variable type matches the type of the initializer expression. In other words, `auto` should deduce the exact same type that was originally spelled out in the source. However โ€” not every situation should be transformed:

int val = 42; InfoStruct &I = SomeObject.getInfo();

// Should not become:

auto val = 42; auto &I = SomeObject.getInfo();


In this example, using `auto` for builtins doesn't improve readability. And in other situations it makes the code less self-documenting, which impairs readability and maintainability. So as a result, `auto` is only introduced in the specific situations described below. No cap. ๐Ÿงš

### Iterators

Iterator type specifiers tend to be *long* and used *frequently*, especially in loop constructs. And since the functions generating iterators have a common format, the type specifier can be replaced without obscuring the meaning of the code, while improving readability and maintainability. Slay.

for (std::vector<int>::iterator I = my_container.begin(), E = my_container.end(); I != E; ++I) { }

// becomes

for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) { }


The check will only replace iterator type-specifiers when **all** of the following conditions are satisfied:

- The iterator is for one of the standard containers in the `std` namespace:

  - `array`

  - `deque`

  - `forward_list`

  - `list`

  - `vector`

  - `map`

  - `multimap`

  - `set`

  - `multiset`

  - `unordered_map`

  - `unordered_multimap`

  - `unordered_set`

  - `unordered_multiset`

  - `queue`

  - `priority_queue`

  - `stack`

- The iterator is one of the possible iterator types for standard containers:

  - `iterator`

  - `reverse_iterator`

  - `const_iterator`

  - `const_reverse_iterator`

- In addition to using iterator types directly, typedefs or other ways of referring to those types are also allowed. However, implementation-specific types for which a type like `std::vector<int>::iterator` is itself a typedef will **not** be transformed. Consider the following examples:

// The following direct uses of iterator types will be transformed. std::vector<int>::iterator I = MyVec.begin(); { using namespace std; list<int>::iterator I = MyList.begin(); }

// The type specifier for J would transform to auto since it's a typedef // to a standard iterator type. typedef std::map<int, std::string>::const_iterator map_iterator; map_iterator J = MyMap.begin();

// The following implementation-specific iterator type for which // std::vector<int>::iterator could be a typedef would not be transformed. __gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();


- The initializer for the variable being declared is **not** a braced initializer list. Otherwise, use of `auto` would cause the type of the variable to be deduced as `std::initializer_list`.

### New expressions

Frequently, when a pointer is declared and initialized with `new`, the pointee type gets written **twice**: once in the declaration type and once in the `new` expression. In this case, the declaration type can be replaced with `auto`, improving readability and maintainability. Two birds, one stone. ๐Ÿ“

TypeName *my_pointer = new TypeName(my_param);

// becomes

auto *my_pointer = new TypeName(my_param);


The check will also replace the declaration type in multiple declarations, if the following conditions are satisfied:

- All declared variables have the same type (i.e. all of them are pointers to the same type).

- All declared variables are initialized with a `new` expression.

- The types of all the `new` expressions are the same than the pointee of the declaration type.

TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;

// becomes

auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;


### Cast expressions

Frequently, when a variable is declared and initialized with a cast, the variable type gets written **twice**: in the declaration type and in the cast expression. In this case, the declaration type can be replaced with `auto`, improving readability and maintainability. Same energy as before, different outfit. ๐Ÿ’œ

TypeName *my_pointer = static_cast<TypeName>(my_param);

// becomes

auto *my_pointer = static_cast<TypeName>(my_param);


The check handles `static_cast`, `dynamic_cast`, `const_cast`, `reinterpret_cast`, functional casts, C-style casts and function templates that behave as casts, such as `llvm::dyn_cast`, `boost::lexical_cast` and `gsl::narrow_cast`. Calls to function templates are considered to behave as casts if the first template argument is explicit and is a type, and the function returns that type, or a pointer or reference to it.

### Known Limitations

- If the initializer is an explicit conversion constructor, the check will not replace the type specifier even though it would be safe to do so. ๐Ÿ™ˆ

- User-defined iterators are **not** handled at this time.

### Options

- **`MinTypeNameLength`** โ€” if the option is set to non-zero (default `5`), the check will ignore type names having a length less than the option value. The option affects expressions only, **not** iterators. Spaces between multi-lexeme type names (`long int`) are considered as one. If the `RemoveStars` option (see below) is set to true, then `*`s in the type are also counted as a part of the type name.

// MinTypeNameLength = 0, RemoveStars=0

int a = static_cast<int>(foo()); // ---> auto a = ... // length(bool *) = 4 bool *b = new bool; // ---> auto *b = ... unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ...

// MinTypeNameLength = 5, RemoveStars=0

int a = static_cast<int>(foo()); // ---> int a = ... bool b = static_cast<bool>(foo()); // ---> bool b = ... bool pb = static_cast<bool>(foo()); // ---> bool *pb = ... unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ... // length(long <on-or-more-spaces> int) = 8 long int d = static_cast<long int>(foo()); // ---> auto d = ...

// MinTypeNameLength = 5, RemoveStars=1

int a = static_cast<int>(foo()); // ---> int a = ... // length(int * * ) = 5 int pa = static_cast<int>(foo()); // ---> auto pa = ... bool b = static_cast<bool>(foo()); // ---> bool b = ... bool pb = static_cast<bool>(foo()); // ---> auto pb = ... unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ... long int d = static_cast<long int>(foo()); // ---> auto d = ...


- **`RemoveStars`** โ€” if the option is set to `true` (default is `false`), the check will remove stars from the non-typedef pointer types when replacing type names with `auto`. Otherwise, the check will leave stars. For example:

TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;

// RemoveStars = 0

auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;

// RemoveStars = 1

auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;


---

## ๐ŸŒˆ modernize-use-bool-literals ๐ŸŒˆ

Finds integer literals that are cast to `bool`. Sir, this is a Wendy's, we use `true` and `false` here. โญ

bool p = 1; bool f = static_cast<bool>(1); std::ios_base::sync_with_stdio(0); bool x = p ? 1 : 0;

// transforms to

bool p = true; bool f = true; std::ios_base::sync_with_stdio(false); bool x = p ? true : false;


### Options

- **`IgnoreMacros`** โ€” if set to `true`, the check will not give warnings inside macros. Default is `true`.

---

## ๐Ÿ’ซ modernize-use-constraints ๐Ÿ’ซ

Replace `std::enable_if` with C++20 requires clauses. That's it, that's the whole vibe. Upgrading you to the modern era, bestie. ๐Ÿ”ฅ

so <pink>`std::enable_if`</pink> is basically the O.G. SFINAE hack โ€” it's a mechanism for picking which function or class template you actually want, based on type traits or whatever other requirements you're vibing with. <purple>But.</purple> <hotpink>`enable_if`</hotpink> also changes the *meta-arity* of the template and drags a whole pile of adverse side effects into your code. Meanwhile C++20 walked in with **concepts and constraints**, which is the clean, language-provided glow-up that gets you the exact same outcome. โœจ

This check sniffs out some of the common `std::enable_if` patterns that can be swapped for C++20 `requires` clauses. Some of them it rewrites for you automatically โ€” the rest, it just drops a diagnostic and leaves the code alone. Here's the tea on what it can detect:

1. `std::enable_if` in the return type of a function
2. `std::enable_if` as the trailing template parameter for function templates

Other uses โ€” say, in class templates, or for function parameters โ€” are **not currently supported** by this tool. And other variants like <cyan>`boost::enable_if`</cyan>? Also not supported, bestie. ๐Ÿ™ˆ

Below are some examples of code using `std::enable_if`.

// enable_if in function return type template <typename T> std::enable_if_t<T::some_trait, int> only_if_t_has_the_trait() { ... }

// enable_if in the trailing template parameter template <typename T, std::enable_if_t<T::some_trait, int> = 0> void another_version() { ... }

template <typename T> typename std::enable_if<T::some_value, Obj>::type existing_constraint() requires (T::another_value) { return Obj{}; }

template <typename T, std::enable_if_t<T::some_trait, int> = 0> struct my_class {};


The tool will replace the above code with,

// warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] template <typename T> int only_if_t_has_the_trait() requires T::some_trait { ... }

// warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] template <typename T> void another_version() requires T::some_trait { ... }

// The tool will emit a diagnostic for the following, but will // not attempt to replace the code. // warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints] template <typename T> typename std::enable_if<T::some_value, Obj>::type existing_constraint() requires (T::another_value) { return Obj{}; }

// The tool will not emit a diagnostic or attempt to replace the code. template <typename T, std::enable_if_t<T::some_trait, int> = 0> struct my_class {};


> **NOTE:** ๐Ÿšจ System headers are *not* analyzed by this check. Not even a little bit.

---

## ๐ŸŽ€ modernize-use-default ๐ŸŽ€

Plot twist: this check got **renamed** to <lavender>`modernize-use-equals-default`</lavender>. That's it, that's the whole entry. ๐Ÿ’…

---

## ๐ŸŒธ modernize-use-default-member-init ๐ŸŒธ

This check takes your constructors' member initializers and converts them into the shiny new C++11 default member initializers. Any *other* member initializers that happen to match the default one get yeeted too. The result? Less repeated code, and sometimes you can even use `= default`. Iconic behavior, honestly.

struct A { A() : i(5), j(10.0) {} A(int i) : i(i), j(10.0) {} int i; double j; };

// becomes

struct A { A() {} A(int i) : i(i) {} int i{5}; double j{10.0}; };


> **NOTE:** โš ๏ธ It only converts member initializers for **built-in types, enums, and pointers**. For classes, the <mint>`readability-redundant-member-init`</mint> check is the one that removes redundant member initializers. Different check, different job, no overlap. ๐Ÿซถ

### โœจ Options โœจ

- **`UseAssignment`** โ€” if you set this to `true` (default is `false`), the check initializes members with an **assignment** instead. Like so:

struct A { A() {} A(int i) : i(i) {} int i = 5; double j = 10.0; };


- **`IgnoreMacros`** โ€” if this is `true` (default is `true`), the check will not warn about members that are declared inside macros. ๐Ÿ™ˆ

---

## ๐Ÿฆ‹ modernize-use-designated-initializers ๐Ÿฆ‹

This one finds initializer lists for aggregate types that could be written as **designated initializers** instead. Lowkey a lifesaver.

Here's the problem: with plain initializer lists, it is *very* easy to introduce bugs when you add new fields in the middle of a struct or class type. And the same chaos can strike when you change the order of fields. ๐Ÿ˜ญ

C++20 supports the designated initializer syntax for aggregate types. Apply it and you can always be sure your aggregates are constructed correctly, because every variable being initialized is referenced **by its name**. Chef's kiss. ๐Ÿ‘‘

Example:

struct S { int i, j; };


is an aggregate type that should be initialized as

S s{.i = 1, .j = 2};


instead of

S s{1, 2};


which could easily become an issue when `i` and `j` are swapped in the declaration of `S`. <red>Swap energy = bug energy.</red>

Even when you're compiling in a language version older than C++20, depending on your compiler, designated initializers are *potentially* supported. So by default the check restricts itself to **C99/C++20 and above**. Wanna go further? Peep the options <cyan>`-Wc99-designator`</cyan> (for support of mixed designators in initializer list in C) and <cyan>`-Wc++20-designator`</cyan> (for support of designated initializers in older C++ language modes). ๐ŸŒˆ

### โœจ Options โœจ

- **`IgnoreMacros`** โ€” the value `false` specifies that components of initializer lists expanded from macros are **not** checked. The default value is `true`.
- **`IgnoreSingleElementAggregates`** โ€” the value `false` specifies that even initializers for aggregate types with only a **single element** should be checked. The default value is `true`.
- **`RestrictToPODTypes`** โ€” the value `true` specifies that only **Plain Old Data (POD)** types shall be checked. This makes the check applicable to even older C++ standards. The default value is `false`.
- **`StrictCStandardCompliance`** โ€” when set to `false`, the check will **not** restrict itself to C99 and above. The default value is `true`.
- **`StrictCppStandardCompliance`** โ€” when set to `false`, the check will **not** restrict itself to C++20 and above. The default value is `true`.

---

## ๐Ÿ”ฅ modernize-use-emplace ๐Ÿ”ฅ

This check flags insertions into an STL-style container where you call `push_back`, `push`, or `push_front` with an **explicitly-constructed temporary** of the container's element type. In those cases, the matching `emplace` methods give you code that's less verbose and potentially more efficient. Ate. ๐Ÿ’…

Right now the check doesn't support `insert`. It also doesn't support `insert` functions for associative containers โ€” because replacing `insert` with `emplace` may cause a **speed regression** โ€” though it might get support with some extra flag in the future. ๐Ÿ‘€

The `ContainersWithPushBack`, `ContainersWithPush`, and `ContainersWithPushFront` options specify which container types support the `push_back`, `push`, and `push_front` operations respectively. Their default values are as follows:

- `ContainersWithPushBack`: `std::vector`, `std::deque`, and `std::list`.
- `ContainersWithPush`: `std::stack`, `std::queue`, and `std::priority_queue`.
- `ContainersWithPushFront`: `std::forward_list`, `std::list`, and `std::deque`.

This check also reports when an emplace-like method is used **improperly** โ€” for example, calling `emplace_back` while also calling a constructor. That creates a temporary which requires at best a move and at worst a copy. ๐Ÿ˜ฌ Almost all emplace-like functions in the STL are covered by this, with `try_emplace` on `std::map` and `std::unordered_map` being the exception, since it behaves slightly differently than all the others. More containers can be added with the `EmplacyFunctions` option, so long as the container defines a `value_type` type, and the emplace-like functions construct a `value_type` object.

**Before:**

std::vector<MyClass> v; v.push_back(MyClass(21, 37)); v.emplace_back(MyClass(21, 37));

std::vector<std::pair<int, int>> w;

w.push_back(std::pair<int, int>(21, 37)); w.push_back(std::make_pair(21L, 37L)); w.emplace_back(std::make_pair(21L, 37L));


**After:**

std::vector<MyClass> v; v.emplace_back(21, 37); v.emplace_back(21, 37);

std::vector<std::pair<int, int>> w; w.emplace_back(21, 37); w.emplace_back(21L, 37L); w.emplace_back(21L, 37L);


By default, the check is able to remove unnecessary `std::make_pair` and `std::make_tuple` calls from `push_back` calls on containers of `std::pair` and `std::tuple`. Custom tuple-like types can be modified with the `TupleTypes` option; custom make functions can be modified with the `TupleMakeFunctions` option.

The other situation is when we pass arguments that will be **converted** to a type inside a container.

**Before:**

std::vector<boost::optional<std::string> > v; v.push_back("abc");


**After:**

std::vector<boost::optional<std::string> > v; v.emplace_back("abc");


In some cases the transformation would be valid, but the code **wouldn't be exception safe**. In that case, the calls to `push_back` won't be replaced.

std::vector<std::unique_ptr<int>> v; v.push_back(std::unique_ptr<int>(new int(0))); auto *ptr = new int(1); v.push_back(std::unique_ptr<int>(ptr));


That's because replacing it with `emplace_back` could cause a **leak** of this pointer if `emplace_back` would throw an exception before emplacement (e.g. not enough memory to add a new element). ๐Ÿ’€

For more info, read item 42 - "Consider emplacement instead of insertion." of Scott Meyers "Effective Modern C++".

The default smart pointers that are considered are `std::unique_ptr`, `std::shared_ptr`, `std::auto_ptr`. To specify other smart pointers or other classes, use the `SmartPointers` option.

The check also doesn't fire if any argument of the constructor call would be:

- a **bit-field** (bit-fields can't bind to rvalue/universal reference)
- a **new expression** (to avoid leak)
- if the argument would be converted via **derived-to-base cast**.

> This check requires **C++11 or higher** to run. โญ

### โœจ Options โœจ

- **`ContainersWithPushBack`** โ€” semicolon-separated list of class names of custom containers that support `push_back`.
- **`ContainersWithPush`** โ€” semicolon-separated list of class names of custom containers that support `push`.
- **`ContainersWithPushFront`** โ€” semicolon-separated list of class names of custom containers that support `push_front`.
- **`IgnoreImplicitConstructors`** โ€” when `true`, the check will ignore implicitly constructed arguments of `push_back`, e.g.

std::vector<std::string> v; v.push_back("a"); // Ignored when IgnoreImplicitConstructors is true.


  Default is `false`.

- **`SmartPointers`** โ€” semicolon-separated list of class names of custom smart pointers.
- **`TupleTypes`** โ€” semicolon-separated list of `std::tuple`-like class names.

- **`TupleMakeFunctions`** โ€” a semicolon-separated list of `std::make_tuple`-flavoured function names ๐Ÿ’ซ any call to one of these gets yoinked out of `push_back` and turned into an `emplace_back` instead.

- **`EmplacyFunctions`** โ€” semicolon-separated list of containers *without* their template params, plus some emplace-like method on that container. Example: `vector::emplace_back`. These methods get vibe-checked for improper use, and the check will call it out when a temporary is getting created for absolutely no reason ๐Ÿ™ˆ.

**Example**

std::vector<MyTuple<int, bool, char>> x; x.push_back(MakeMyTuple(1, false, 'x')); x.emplace_back(MakeMyTuple(1, false, 'x'));


transforms to:

std::vector<MyTuple<int, bool, char>> x; x.emplace_back(1, false, 'x'); x.emplace_back(1, false, 'x');


when `TupleTypes` is set to `MyTuple`, `TupleMakeFunctions` is set to `MakeMyTuple`, and `EmplacyFunctions` is set to `vector::emplace_back`. bestie that's a glow-up fr ๐Ÿ’…

---

### modernize-use-equals-default

This check swaps the default bodies of special member functions out for `= default;`. Why do we care? Because explicitly defaulted function declarations open up way more optimization opportunities โ€” the compiler is allowed to treat an explicitly defaulted function as trivial ๐Ÿ‘‘

struct A { A() {} ~A(); }; A::~A() {}

// becomes

struct A { A() = default; ~A(); }; A::~A() = default;


> **NOTE:** Move-constructor and move-assignment operator are **not supported yet**. ๐Ÿ˜ญ patience, bestie.

**Options**

- **`IgnoreMacros`** โ€” if this is `true`, the check will not give warnings inside macros, and it'll ignore special members whose bodies contain macros or preprocessor directives. Default is `true`.

---

### modernize-use-equals-delete

Spots private special member functions that were never implemented and recommends using `= delete` on them instead. On top of that, it also recommends relocating any deleted member function from the `private` section into the `public` section โœจ

Before C++11 even existed, the whole move to effectively "erase" a particular function was to declare it `private` and just... never define it. That approach would get you either a compiler error (if you tried to call a private function) or a linker error (undefined reference, classic ๐Ÿ’€).

But post-C++11? A much more iconic approach showed up: flag the function as `= delete` and keep it sitting in the `public` section of the class.

To avoid false positives, this check is only active inside a translation unit where **all** other member functions have already been implemented. The check will generate partial fixes by introducing `= delete`, but *you*, the user, are responsible for manually relocating functions into the public section ๐Ÿซถ

// Example: bad class A { private: A(const A&); A& operator=(const A&); };

// Example: good class A { public: A(const A&) = delete; A& operator=(const A&) = delete; };


**Options**

- **`IgnoreMacros`** โ€” if this option is set to `true` (default is `true`), the check will not warn about functions declared inside macros.

---

### modernize-use-nodiscard

Slaps `[[nodiscard]]` attributes (introduced in C++17, very demure) onto member functions so that at compile time it's super obvious which return values you should absolutely not be ignoring ๐Ÿ”ฅ

For a member function to even be considered by this check, it has to satisfy all of these:

- no `[[nodiscard]]`, `[[noreturn]]`, `__attribute__((warn_unused_result))`, `[[clang::warn_unused_result]]` nor `[[gcc::warn_unused_result]]` attribute,
- non-void return type,
- non-template return types,
- `const` member function,
- non-variadic functions,
- no non-const reference parameters,
- no pointer parameters,
- no template parameters,
- no template function parameters,
- not be a member of a class with mutable member variables,
- no Lambdas,
- no conversion functions.

Such functions have no way of altering any state or passing values around other than through the return type. Unless, of course, the member functions are altering state via some external call (like I/O ๐Ÿ‘€).

**Example**

bool empty() const; bool empty(int i) const;


transforms to:

[[nodiscard]] bool empty() const; [[nodiscard]] bool empty(int i) const;


**Options**

- **`ReplacementString`** โ€” specifies a macro to use instead of `[[nodiscard]]`. Super handy when you're maintaining source code that also needs to compile with a pre-C++17 compiler.

**Example**

bool empty() const; bool empty(int i) const;


transforms to:

NO_DISCARD bool empty() const; NO_DISCARD bool empty(int i) const;


if the `ReplacementString` option is set to `NO_DISCARD`.

> **NOTE:** If the `ReplacementString` is not a C++ attribute but instead a macro, then that macro **must** be defined in scope or the fix-it will not be applied. no cap.

> **NOTE:** For alternative `__attribute__` syntax options to mark functions as `[[nodiscard]]` in non-c++17 source code, see <blue>https://clang.llvm.org/docs/AttributeReference.html#nodiscard-warn-unused-result</blue>

---

### modernize-use-noexcept

This check replaces deprecated dynamic exception specifications with the appropriate `noexcept` specification (introduced in C++11, we love her ๐ŸŒธ). By default this check will replace `throw()` with `noexcept`, and `throw(<exception>[,...])` or `throw(...)` with `noexcept(false)`.

**Example**

void foo() throw(); void bar() throw(int) {}


transforms to:

void foo() noexcept; void bar() noexcept(false) {}


**Options**

- **`ReplacementString`** โ€” you can use `ReplacementString` to specify a macro to use instead of `noexcept`. Useful when maintaining source code that uses custom exception specification marking other than `noexcept`. Fix-it hints will **only** be generated for non-throwing specifications.

**Example**

void bar() throw(int); void foo() throw();


transforms to:

void bar() throw(int); // No fix-it generated. void foo() NOEXCEPT;


if the `ReplacementString` option is set to `NOEXCEPT`.

- **`UseNoexceptFalse`** โ€” enabled by default. Disabling it will generate fix-it hints that remove throwing dynamic exception specs, e.g., `throw(<something>)`, completely, without providing any replacement text โ€” except for destructors and delete operators that are `noexcept(true)` by default.

**Example**

void foo() throw(int) {}

struct bar { void foobar() throw(int); void operator delete(void *ptr) throw(int); void operator delete[](void *ptr) throw(int); ~bar() throw(int); }


transforms to:

void foo() {}

struct bar { void foobar(); void operator delete(void *ptr) noexcept(false); void operator delete[](void *ptr) noexcept(false); ~bar() noexcept(false); }


if the `UseNoexceptFalse` option is set to `false`.

---

### modernize-use-nullptr

This check converts the usage of null pointer constants (e.g. `NULL`, `0`) over to the new C++11 and C23 `nullptr` keyword ๐ŸŽ€

**Example**

void assignment() { char *a = NULL; char *b = 0; char c = 0; }

int *ret_ptr() { return 0; }


transforms to:

void assignment() { char *a = nullptr; char *b = nullptr; char c = 0; }

int *ret_ptr() { return nullptr; }


**Options**

- **`IgnoredTypes`** โ€” semicolon-separated list of regular expressions to match pointer types for which implicit casts will be ignored. Default value: `std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec`.

- **`NullMacros`** โ€” comma-separated list of macro names that will be transformed along with `NULL`. By default this check will only replace the `NULL` macro and will skip any similar user-defined macros ๐Ÿ™ˆ

**Example**

#define MY_NULL (void*)0 void assignment() { void *p = MY_NULL; }


transforms to:

#define MY_NULL NULL void assignment() { int *p = nullptr; }


if the `NullMacros` option is set to `MY_NULL`.

---

### modernize-use-override

Adds `override` (introduced in C++11) to overridden virtual functions, and removes `virtual` from those functions since it's not actually required ๐Ÿ’ซ

Back in the day, `virtual` on non base class implementations was used to help signal to the user that a function was virtual. C++ compilers did not use the presence of this to signify an overridden function, though.

In C++11, the `override` and `final` keywords were introduced so overridden functions could be marked appropriately. Their presence lets compilers verify that an overridden function correctly overrides a base class implementation ๐Ÿ‘‘

This is genuinely useful, because compilers can generate a compile time error when:

- The base class implementation function signature changes.
- The user has not created the override with the correct signature.

**Options**

- **`IgnoreDestructors`** โ€” if set to `true`, this check will not diagnose destructors. Default is `false`.

- **`IgnoreTemplateInstantiations`** โ€” if set to `true`, instructs this check to ignore virtual function overrides that are part of template instantiations. Default is `false`.

- **`AllowOverrideAndFinal`** โ€” if set to `true`, this check will not diagnose `override` as redundant with `final`. Handy when code will be compiled by a compiler with warning/error checking flags requiring `override` explicitly on overridden members, such as `gcc -Wsuggest-override`/`gcc -Werror=suggest-override`. Default is `false`.

- **`OverrideSpelling`** โ€” specifies a macro to use instead of `override`. Useful when maintaining source code that also needs to compile with a pre-C++11 compiler.

- **`FinalSpelling`** โ€” specifies a macro to use instead of `final`. Useful when maintaining source code that also needs to compile with a pre-C++11 compiler.

> **NOTE:** For more information on the use of `override` see <blue>https://en.cppreference.com/w/cpp/language/override</blue>

---

### modernize-use-ranges

Detects calls to standard library iterator algorithms that could totally be replaced with a 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 = std::ranges::find(Items, 0); auto AreSame = std::ranges::equal(Items1, Items2);


**Supported algorithms**

Calls to the following std library algorithms are checked:

- `std::adjacent_find`, `std::all_of`, `std::any_of`, `std::binary_search`, `std::copy_backward`, `std::copy_if`, `std::copy`, `std::destroy`, `std::equal_range`, `std::equal`, `std::fill`, `std::find_end`, `std::find_if_not`, `std::find_if`, `std::find`, `std::for_each`, `std::generate`, `std::includes`, `std::inplace_merge`, `std::iota`, `std::is_heap_until`, `std::is_heap`, `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::minmax_element`, `std::mismatch`, `std::move_backward`, `std::move`, `std::next_permutation`, `std::none_of`, `std::partial_sort_copy`, `std::partition_copy`, `std::partition_point`, `std::partition`, `std::pop_heap`, `std::prev_permutation`, `std::push_heap`, `std::remove_copy_if`, `std::remove_copy`, `std::remove`, `std::remove_if`, `std::replace_if`, `std::replace`, `std::reverse_copy`, `std::reverse`, `std::rotate`, `std::rotate_copy`, `std::sample`, `std::search`, `std::set_difference`, `std::set_intersection`, `std::set_symmetric_difference`, `std::set_union`, `std::shift_left`, `std::shift_right`, `std::sort_heap`, `std::sort`, `std::stable_partition`, `std::stable_sort`, `std::transform`, `std::uninitialized_copy`, `std::uninitialized_default_construct`, `std::uninitialized_fill`, `std::uninitialized_move`, `std::uninitialized_value_construct`, `std::unique_copy`, `std::unique`, `std::upper_bound`.

Okay bestie, that's the whole roster ๐Ÿ’… every single one of these gets the ranges glow-up.

---

### <pink>Reverse Iteration</pink> ๐Ÿ”„

So if someone's out here calling these with reverse iterators on containers, the fix is to use the `std::views::reverse` adaptor. No cap.

auto AreSame = std::equal(Items1.rbegin(), Items1.rend(), std::crbegin(Items2), std::crend(Items2));


Transforms to:

auto AreSame = std::ranges::equal(std::ranges::reverse_view(Items1), std::ranges::reverse_view(Items2));


---

### <pink>Options</pink> โœจ

- **`IncludeStyle`** โ€” a string that says which include style you're on: `llvm` or `google`. Default is `llvm`.
- **`UseReversePipe`** โ€” when it's `true` (and default is `false` btw), fixes that involve reverse ranges will use the pipe adaptor syntax instead of the function syntax. It's giving โœจaestheticโœจ.

std::find(Items.rbegin(), Items.rend(), 0);


Transforms to:

std::ranges::find(Items | std::views::reverse, 0);


---

## <rainbow>modernize-use-starts-ends-with</rainbow> ๐ŸŽ€

This one is literally catching all the roundabout ways people express `starts_with` and `ends_with` and being like "bestie, just say it directly" โ€” it suggests swapping in `starts_with` when the method is actually available. And iconic detail: this works with `std::string` and `std::string_view`. Periodt.

std::string s = "..."; if (s.find("prefix") == 0) { /* do something / } if (s.rfind("prefix", 0) == 0) { / do something / } if (s.compare(0, strlen("prefix"), "prefix") == 0) { / do something */ }


becomes

std::string s = "..."; if (s.starts_with("prefix")) { /* do something / } if (s.starts_with("prefix")) { / do something / } if (s.starts_with("prefix")) { / do something */ }


See how clean?? Obsessed. ๐Ÿซถ

---

## <rainbow>modernize-use-std-format</rainbow> ๐Ÿ’–

This check converts calls to `absl::StrFormat` โ€” or, like, other functions if you configure it that way โ€” into C++20's `std::format`, or another function of your choosing via a configuration option. It also modifies the format string appropriately and yeets the now-unnecessary calls to `std::string::c_str()` and `std::string::data()`. Bye bye redundant calls ๐Ÿ‘‹

For example, it turns lines like

return absl::StrFormat("The %s is %3d", description.c_str(), value);


into:

return std::format("The {} is {:3}", description, value);


And heads up: this check uses the *exact same* format-string-conversion algorithm as `modernize-use-std-print`, and all its little shortcomings are described over in that check's documentation. So if something looks weird here, go read that one, iykyk.

### <pink>Options</pink> โœจ

- **`StrictMode`** โ€” when this is `true`, the check will add casts when it's converting from variadic functions and printing signed or unsigned integer types (that includes the fixed-width integer types from `<cstdint>`, plus `ptrdiff_t`, `size_t` and `ssize_t`) as the opposite signedness, to make sure the output matches what a simple wrapper for `std::sprintf` that accepted a C-style variable argument list would have produced. Like, for example, with `StrictMode` enabled:

extern std::string strprintf(const char *format, ...); int i = -42; unsigned int u = 0xffffffff; return strprintf("%d %u\n", i, u);


would be converted to

return std::format("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));


to make sure the output keeps on being the unsigned representation of `-42` and the signed representation of `0xffffffff` (which is often `4294967254` and `-1` respectively). When it's `false` โ€” and `false` *is* the default โ€” those casts do **not** get added, which may cause a change in the output. โš ๏ธ And note that this option makes zero difference for the default value of `StrFormatLikeFunctions`, because `absl::StrFormat` takes a function parameter pack and is not a variadic function. Not me knowing the difference, it's just facts.

- **`StrFormatLikeFunctions`** โ€” a semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. The default value for this option is `absl::StrFormat`.

- **`ReplacementFormatFunction`** โ€” the function that will be used to replace the function set by the `StrFormatLikeFunctions` option, instead of the default `std::format`. It's expected that the function provides an interface that's compatible with `std::format`. A suitable candidate would be `fmt::format`. Slay.

- **`FormatHeader`** โ€” the header that must be included for the declaration of `ReplacementFormatFunction`, so that a `#include` directive can be added if it's required. If `ReplacementFormatFunction` is `std::format` then this option defaults to `<format>`, otherwise this option defaults to nothing and no `#include` directive gets added.

---

## <rainbow>modernize-use-std-numbers</rainbow> ๐Ÿ”ข

This one finds constants and function calls to math functions that can be swapped out for C++20's mathematical constants from the numbers header, and it offers fix-it hints. ๐Ÿ’…

Important deets: it does **not** match the use of variables with that value โ€” instead, it offers a replacement for the *definition* of those variables. Function calls that match the pattern of how the constant is calculated get matched and replaced with the `std::numbers` constant. And the use of macros gets replaced with the corresponding `std::numbers` constant, instead of changing the macro definition. Okay so it's smart like that.

The following list of constants from the numbers header are supported:

- `e`
- `log2e`
- `log10e`
- `pi`
- `inv_pi`
- `inv_sqrtpi`
- `ln2`
- `ln10`
- `sqrt2`
- `sqrt3`
- `inv_sqrt3`
- `egamma`
- `phi`

The list currently includes all constants as of C++20. That's the whole squad ๐Ÿ‘‘

The replacements use the type of the matched constant and can remove explicit casts โ€” i.e., switching between `std::numbers::e`, `std::numbers::e_v<float>` and `std::numbers::e_v<long double>` where appropriate.

double sqrt(double); double log2(double); void sink(auto&&) {} void floatSink(float);

#define MY_PI 3.1415926

void foo() { const double Pi = 3.141592653589; // const double Pi = std::numbers::pi const auto Use = Pi / 2; // no match for Pi static constexpr double Euler = 2.7182818; // static constexpr double Euler = std::numbers::e;

log2(exp(1)); // std::numbers::log2e; log2(Euler); // std::numbers::log2e; 1 / sqrt(MY_PI); // std::numbers::inv_sqrtpi; sink(MY_PI); // sink(std::numbers::pi); floatSink(MY_PI); // floatSink(std::numbers::pi); floatSink(static_cast<float>(MY_PI)); // floatSink(std::numbers::pi_v<float>); }


Look at all those lil comments doing the most. Chef's kiss ๐Ÿง‘โ€๐Ÿณ๐Ÿ’‹

### <pink>Options</pink> โœจ

- **`DiffThreshold`** โ€” a floating point value that sets the detection threshold for when literals match a constant. A literal matches a constant if `abs(literal - constant) < DiffThreshold` evaluates to `true`. Default is `0.001`.
- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.

---

## <rainbow>modernize-use-std-print</rainbow> ๐Ÿ–จ๏ธ

This one converts calls to `printf`, `fprintf`, `absl::PrintF` and `absl::FPrintf` into equivalent calls to C++23's `std::print` or `std::println` as appropriate, modifying the format string appropriately. And the replaced and replacement functions can both be customised with configuration options, obviously. ๐Ÿ’…

Every argument that is the result of a call to `std::string::c_str()` and `std::string::data()` will have that now-unnecessary call removed, in a similar manner to the `readability-redundant-string-cstr` check. Redundant? We don't know her.

In other words, it turns lines like:

fprintf(stderr, "The %s is %3d\n", description.c_str(), value);


into:

std::println(stderr, "The {} is {:3}", description, value);


If the `ReplacementPrintFunction` or `ReplacementPrintlnFunction` options are left, or assigned to their default values, then this check is only enabled with `-std=c++23` or later. Just so you know.

The check doesn't do a bad job, but it's not perfect. In particular:

- It assumes that the format string is correct for the arguments. If you get any warnings when compiling with `-Wformat` then misbehaviour is possible. ๐Ÿ˜ญ
- At the point that the check runs, the AST contains a single `StringLiteral` for the format string and any macro expansion, token pasting, adjacent string literal concatenation and escaping has been handled. Although it's possible for the check to automatically put the escapes back, they may not be exactly as they were written (e.g. `"\x0a"` will become `"\n"` and `"ab" "cd"` will become `"abcd"`.) This is helpful since it means that the `PRIx` macros from `<inttypes.h>` are removed correctly.
- It supports field widths, precision, positional arguments, leading zeros, leading `+`, alignment and alternative forms. Iconic range honestly.
- Use of any unsupported flags or specifiers will cause the entire statement to be left alone and a warning to be emitted. Particular unsupported features are:
  - The `%'` flag for thousands separators.
  - The glibc extension `%m`.
- `printf` and similar functions return the number of characters printed. `std::print` does not. This means that any invocations that use the return value will not be converted. Unfortunately this currently includes explicitly-casting to `void`. Deficiencies in this check mean that any invocations inside GCC compound statements cannot be converted even if the resulting value is not used.

If conversion would be incomplete or unsafe then the entire invocation will be left unchanged. Safety first bestie ๐Ÿ›ก๏ธ

If the call is deemed suitable for conversion then:

- `printf`, `fprintf`, `absl::PrintF`, `absl::FPrintF` and literally any function you listed in the <pink>`PrintfLikeFunctions`</pink> or <pink>`FprintfLikeFunctions`</pink> options get swapped out for whatever <pink>`ReplacementPrintlnFunction`</pink> says โ€” but **only** if the format string ends in `\n`. If it doesn't, they get swapped for <pink>`ReplacementPrintFunction`</pink> instead. okay bestie, noted ๐Ÿ’–

- the format string itself gets rewritten into the `std::formatter` language โœจ and heads up: if a `\n` is chillin' at the end of the format string and it's **not** preceded by `r`, it gets removed and `ReplacementPrintlnFunction` is used instead of `ReplacementPrintFunction`.

- any argument that matched a `%p` specifier that `std::formatter` wouldn't accept gets wrapped in a `static_cast` to `const void *` ๐Ÿซถ

- any argument that matched a `%s` specifier where the argument is of `signed char` or `unsigned char` type gets wrapped in a `reinterpret_cast<const char *>` ๐Ÿ’…

- any argument where the format string and the parameter differ in signedness gets wrapped in an appropriate `static_cast` **if** <pink>`StrictMode`</pink> is enabled ๐Ÿ‘‘

- any argument that ends in a call to `std::string::c_str()` or `std::string::data()` gets that call removed. bye bestie ๐Ÿ™ˆ

---

## โœจ Options โœจ

- **`StrictMode`** โ€” when `true`, the check adds casts when it's converting from variadic functions like `printf` and printing signed or unsigned integer types (including fixed-width integer types from `<cstdint>`, `ptrdiff_t`, `size_t` and `ssize_t`) as the opposite signedness, so the output still matches what `printf` would've done. This does **not** apply when converting from non-variadic functions like `absl::PrintF` and `fmt::printf`. For example, with `StrictMode` enabled:

int i = -42; unsigned int u = 0xffffffff; printf("%d %u\n", i, u);


would be converted to:

std::print("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));


to make sure the output stays the unsigned representation of `-42` and the signed representation of `0xffffffff` (often `4294967254` and `-1` respectively, fr fr ๐Ÿ˜ญ). When `false` (**which is the default**), those casts are not added, which may cause a change in the output. lowkey important, bestie.

- **`PrintfLikeFunctions`** โ€” a semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. If neither this option nor `FprintfLikeFunctions` is set, then the default value for this option is `printf; absl::PrintF` โ€” otherwise it's empty.

- **`FprintfLikeFunctions`** โ€” a semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter is retained, the second parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. If neither this option nor `PrintfLikeFunctions` is set, then the default value for this option is `fprintf; absl::FPrintF` โ€” otherwise it's empty.

- **`ReplacementPrintFunction`** โ€” the function used to replace `printf`, `fprintf` etc. during conversion, instead of the default `std::print`, whenever the original format string does **not** end with `\n`. It's expected that the function provides an interface compatible with `std::print`. A suitable candidate would be `fmt::print` โœจ

- **`ReplacementPrintlnFunction`** โ€” the function used to replace `printf`, `fprintf` etc. during conversion, instead of the default `std::println`, whenever the original format string **does** end with `\n`. It's expected that the function provides an interface compatible with `std::println`. A suitable candidate would be `fmt::println` ๐ŸŽ€

- **`PrintHeader`** โ€” the header that must be included for the declaration of `ReplacementPrintFunction`, so that a `#include` directive can be added if required. If `ReplacementPrintFunction` is `std::print` then this option defaults to `<print>`, otherwise this option defaults to nothing and no `#include` directive gets added. periodt ๐Ÿ’…

---

## <rainbow>modernize-use-trailing-return-type</rainbow>

Rewrites function signatures to use a trailing return type (introduced in C++11). This transformation is **purely stylistic** โ€” it's giving glow-up, not behaviour change ๐Ÿ’– The return type before the function name is replaced by `auto` and inserted after the function parameter list (and qualifiers).

### Example

int f1(); inline int f2(int arg) noexcept; virtual float f3() const && = delete;


transforms to:

auto f1() -> int; inline auto f2(int arg) -> int noexcept; virtual auto f3() const && -> float = delete;


### Known Limitations

The following categories of return types **cannot** be rewritten currently ๐Ÿ˜ญ:

- function pointers

- member function pointers

- member pointers

Unqualified names in the return type might **erroneously** refer to different entities after the rewrite. Preventing such errors requires a full lookup of all unqualified names present in the return type in the scope of the trailing return type location. That location includes, like, function parameter names and members of the enclosing class (including all inherited classes). Such a lookup is currently **not implemented**. ๐Ÿ’€

Given the following piece of code

struct S { long long value; }; S f(unsigned S) { return {S * 2}; } class CC { int S; struct S m(); }; S CC::m() { return {0}; }


a careless rewrite would produce the following output:

struct S { long long value; }; auto f(unsigned S) -> S { return {S * 2}; } // error class CC { int S; auto m() -> struct S; }; auto CC::m() -> S { return {0}; } // error


This code fails to compile because the `S` in the context of `f` refers to the equally named function parameter. Similarly, the `S` in the context of `m` refers to the equally named class member. The check can currently only detect and avoid a clash with a **function parameter name**. ๐Ÿ™ˆ

---

## <rainbow>modernize-use-transparent-functors</rainbow>

Prefer transparent functors to non-transparent ones. When you use transparent functors, the type doesn't need to be repeated, the code is easier to read, easier to maintain and less prone to errors โ€” and it's *not possible* to introduce unwanted conversions. obsessed โœจ

// Non-transparent functor std::map<int, std::string, std::greater<int>> s;

// Transparent functor. std::map<int, std::string, std::greater<>> s;

// Non-transparent functor using MyFunctor = std::less<MyType>;


It is **not always** a safe transformation though. The following case will be left untouched to preserve the semantics:

// Non-transparent functor std::map<const char *, std::string, std::greater<std::string>> s;


### Options

- **`SafeMode`** โ€” if this option is set to `true`, the check will not diagnose cases where using a transparent functor cannot be **guaranteed** to produce identical results as the original code. The default value for this option is `false`.

> This check requires using C++14 or higher to run. no cap.

---

## <rainbow>modernize-use-uncaught-exceptions</rainbow>

This check warns on calls to `std::uncaught_exception` and replaces them with calls to `std::uncaught_exceptions`, since `std::uncaught_exception` was deprecated in C++17. (note the extra `s`, bestie โ€” that's the whole plot ๐Ÿ˜ญ)

Below are a few examples of what kind of occurrences will be found and what they will be replaced with.

#define MACRO1 std::uncaught_exception #define MACRO2 std::uncaught_exception

int uncaught_exception() { return 0; }

int main() { int res;

res = uncaught_exception(); // No warning, since it is not the deprecated function from namespace std

res = MACRO2(); // Warning, but will not be replaced

res = std::uncaught_exception(); // Warning and replaced

using std::uncaught_exception; // Warning and replaced

res = uncaught_exception(); // Warning and replaced }


After applying the fixes the code will look like the following:

#define MACRO1 std::uncaught_exception #define MACRO2 std::uncaught_exception

int uncaught_exception() { return 0; }

int main() { int res;

res = uncaught_exception();

res = MACRO2();

res = std::uncaught_exceptions();

using std::uncaught_exceptions;

res = uncaught_exceptions(); }


---

## <rainbow>modernize-use-using</rainbow>

The check converts the usage of `typedef` with the `using` keyword. glow up ๐Ÿ’…

Before:

typedef int variable;

class Class{}; typedef void (Class::* MyPtrType)() const;

typedef struct { int a; } R_t, *R_p;


After:

using variable = int;

class Class{}; using MyPtrType = void (Class::*)() const;

using R_t = struct { int a; }; using R_p = R_t*;


The checker **ignores** `typedef` within `extern "C" { ... }` blocks.

extern "C" { typedef int InExternC; // Left intact. }


> This check requires using C++11 or higher to run.

### Options

- **`IgnoreMacros`** โ€” if set to `true`, the check will not give warnings inside macros. Default is `true`.

- **`IgnoreExternC`** โ€” if set to `true`, the check will not give warning inside `extern "C"` scope. Default is `false`.

---

## <rainbow>mpi-buffer-deref</rainbow>

This check verifies if a buffer passed to an MPI (Message Passing Interface) function is **sufficiently dereferenced**. Buffers should be passed as a single pointer or array. Since MPI function signatures specify `void *` for their buffer types, insufficiently dereferenced buffers can get passed in โ€” like, say, double pointers or multidimensional arrays โ€” **without a compiler warning** being emitted. sneaky ๐Ÿ‘€

Examples:

// A double pointer is passed to the MPI function. char *buf; MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);

// A multidimensional array is passed to the MPI function. short buf[1][1]; MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);

// A pointer to an array is passed to the MPI function. short *buf[1]; MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);


---

## <rainbow>mpi-type-mismatch</rainbow>

This check verifies if buffer type and MPI (Message Passing Interface) datatype pairs **match** for used MPI functions. All MPI datatypes defined by the MPI standard (3.1) are verified by this check. User defined typedefs, custom MPI datatypes and null pointer constants are **skipped**, in the course of verification. ๐Ÿฆ‹

Example:

// In this case, the buffer type matches MPI datatype. char buf; MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);

// In the following case, the buffer type does not match MPI datatype. int buf; MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);


---

## ๐ŸŽ€ objc-assert-equals

okay bestie, this one is hunting for <pink>improper usages</pink> of `XCTAssertEqual` and `XCTAssertNotEqual` and swapping them out for `XCTAssertEqualObjects` or `XCTAssertNotEqualObjects`.

why do we care?? because it makes your tests <green>less fragile</green> โ€” a LOT of them are lowkey relying on pointer equality for strings that just happen to have equal *values*, and the language never promised you that. it's giving false confidence ๐Ÿ’€

## ๐ŸŒธ objc-avoid-nserror-init

catches improper initialization of `NSError` objects. per Apple's developer docs, you should always create new `NSError` objects with the factory method `errorWithDomain:code:userInfo:` instead of `[NSError alloc] init]`. otherwise you get a warning message at runtime, and nobody wants that on their conscience ๐Ÿซถ

the corresponding info about `NSError` creation: `https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/CreateCustomizeNSError/CreateCustomizeNSError.html`

## ๐Ÿฆ‹ objc-dealloc-in-category

finds implementations of `-dealloc` inside Objective-C **categories**. the category implementation will override any `-dealloc` in the class implementation, which can cause <red>issues</red>, periodt.

classes implement `-dealloc` to do important things while deallocating an object. if a category on that class also implements `-dealloc`, it overrides the class's implementation and you can get unexpected deallocation behavior. not the vibe โœจ

## ๐Ÿ’… objc-forbidden-subclassing

finds Objective-C classes that subclass classes which were *not designed to be subclassed*.

by default it includes a list of Objective-C classes publicly documented as not supporting subclassing.

> **NOTE:** instead of using this check, for code under your control you should add `__attribute__((objc_subclassing_restricted))` before your `@interface` declarations so the compiler itself prevents others from subclassing your Objective-C classes. see: `https://clang.llvm.org/docs/AttributeReference.html#objc-subclassing-restricted`

### โœจ Options

- **`ForbiddenSuperClassNames`** โ€” semicolon-separated list of names of Objective-C classes which do not support subclassing. defaults to `ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView`.

## ๐Ÿ“ objc-missing-hash

finds Objective-C implementations that implement `-isEqual:` without also *appropriately* implementing `-hash`.

Apple's documentation is very clear that objects which are equal **must** have the same hash value: `https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418795-isequal?language=objc`

heads up tho: this check only verifies the *presence* of `-hash` in scenarios where omitting it could cause unexpected behavior. verifying the actual implementation of `-hash` is on the developer, bestie โ€” e.g. by adding unit tests to check it ๐Ÿงš

## ๐ŸŒˆ objc-nsdate-formatter

when `NSDateFormatter` converts an `NSDate` type to a `String` type, you can specify a custom format string. some format specifiers are undesirable even though they're *legal*. for all legal date patterns see: `http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns`

this checker reports as warnings the following string patterns in a date format specifier:

1. `yyyy` + `ww` : calendar year specified with week of a week year (unless `YYYY` is also specified).
   - Example 1: Input Date: `29 December 2014` ; Format String: `yyyy-ww`; Output string: `2014-01` (Wrong because it's not the first week of 2014)
   - Example 2: Input Date: `29 December 2014` ; Format String: `dd-MM-yyyy (ww-YYYY)`; Output string: `29-12-2014 (01-2015)` (This is correct)

2. `F` without `ee`/`EE` : numeric day of week in a month without actual day.
   - Example: Input Date: `29 December 2014` ; Format String: `F-MM`; Output string: `5-12` (Wrong because it reads as 5th ___ of Dec in English)

3. `F` without `MM` : numeric day of week in a month without month.
   - Example: Input Date: `29 December 2014` ; Format String: `F-EE` Output string: `5-Mon` (Wrong because it reads as 5th Mon of ___ in English)

4. `WW` without `MM` : week of the month without the month.
   - Example: Input Date: `29 December 2014` ; Format String: `WW-yyyy` Output string: `05-2014` (Wrong because it reads as 5th Week of ___ in English)

5. `YYYY` + `QQ` : week year specified with quarter of normal year (unless `yyyy` is also specified).
   - Example 1: Input Date: `29 December 2014` ; Format String: `YYYY-QQ` Output string: `2015-04` (Wrong because it's not the 4th quarter of 2015)
   - Example 2: Input Date: `29 December 2014` ; Format String: `ww-YYYY (QQ-yyyy)` Output string: `01-2015 (04-2014)` (This is correct)

6. `YYYY` + `MM` : week year specified with Month of a calendar year (unless `yyyy` is also specified).
   - Example 1: Input Date: `29 December 2014` ; Format String: `YYYY-MM` Output string: `2015-12` (Wrong because it's not the 12th month of 2015)
   - Example 2: Input Date: `29 December 2014` ; Format String: `ww-YYYY (MM-yyyy)` Output string: `01-2015 (12-2014)` (This is correct)

7. `YYYY` + `DD` : week year with day of a calendar year (unless `yyyy` is also specified).
   - Example 1: Input Date: `29 December 2014` ; Format String: `YYYY-DD` Output string: `2015-363` (Wrong because it's not the 363rd day of 2015)
   - Example 2: Input Date: `29 December 2014` ; Format String: `ww-YYYY (DD-yyyy)` Output string: `01-2015 (363-2014)` (This is correct)

8. `YYYY` + `WW` : week year with week of a calendar year (unless `yyyy` is also specified).
   - Example 1: Input Date: `29 December 2014` ; Format String: `YYYY-WW` Output string: `2015-05` (Wrong because it's not the 5th week of 2015)
   - Example 2: Input Date: `29 December 2014` ; Format String: `ww-YYYY (WW-MM-yyyy)` Output string: `01-2015 (05-12-2014)` (This is correct)

9. `YYYY` + `F` : week year with day of week in a calendar month (unless `yyyy` is also specified).
   - Example 1: Input Date: `29 December 2014` ; Format String: `YYYY-ww-F-EE` Output string: `2015-01-5-Mon` (Wrong because it's not the 5th Monday of January in 2015)
   - Example 2: Input Date: `29 December 2014` ; Format String: `ww-YYYY (F-EE-MM-yyyy)` Output string: `01-2015 (5-Mon-12-2014)` (This is correct)

## ๐Ÿ’œ objc-nsinvocation-argument-lifetime

finds calls to `NSInvocation` methods under ARC that don't have proper argument object lifetimes. when you pass Objective-C objects as parameters to the `NSInvocation` methods `getArgument:atIndex:` and `getReturnValue:`, the values are copied **by value** into the argument pointer โ€” which leads to incorrect releasing behavior if the object pointers aren't declared `__unsafe_unretained`.

for code:

id arg; [invocation getArgument:&arg atIndex:2];

__strong id returnValue; [invocation getReturnValue:&returnValue];


the fix will be:

__unsafe_unretained id arg; [invocation getArgument:&arg atIndex:2];

__unsafe_unretained id returnValue; [invocation getReturnValue:&returnValue];


the check will warn on being passed instance variable references that have lifetimes other than `__unsafe_unretained`, but it does **not** propose a fix:

// "id _returnValue" is declaration of instance variable of class. [invocation getReturnValue:&self->_returnValue];


## ๐ŸŽ€ objc-property-declaration

finds property declarations in Objective-C files that don't follow the pattern of property names in Apple's programming guide. property names should be in **Lower Camel Case**, no exceptions bestie.

for code:

@property(nonatomic, assign) int LowerCamelCase;


the fix will be:

@property(nonatomic, assign) int lowerCamelCase;


the check will only fix `'CamelCase'` to `'camelCase'`. in some other cases it only gives you warning messages, because the property name could be complicated. you're gonna have to come up with a proper name on your own, sorry ๐Ÿซถ

this check also accepts special acronyms as prefixes or suffixes. such prefixes or suffixes suppress the Lower Camel Case check according to the guide: `https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-1002931-BBCFHEAB`

for a full list of well-known acronyms: `https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE`

the corresponding style rule: `https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-1001757`

the check will also accept a property declared in a category with a prefix of lowercase letters followed by a `'_'`, to avoid naming conflicts. for example:

@property(nonatomic, assign) int abc_lowerCamelCase;


the corresponding style rule: `https://developer.apple.com/library/content/qa/qa1908/_index.html`

## ๐ŸŒŸ objc-super-self

finds invocations of `-self` on super instances in initializers of subclasses of `NSObject`, and recommends calling a superclass initializer instead.

invoking `-self` on super instances in initializers is a super common programmer error, because what the programmer *actually* meant was to call a superclass initializer. failing to call a superclass initializer breaks initializer chaining and can result in <red>invalid object initialization</red>. not iconic, not slay, fix it ๐Ÿ’…

## ๐Ÿ”ฅ openmp-exception-escape

analyzes OpenMP Structured Blocks and checks that no exception escapes out of the Structured Block it was thrown in.

as per the OpenMP specification, a structured block is an executable statement โ€” possibly compound โ€” with a single entry at the top and a single exit at the bottom. which means `throw` may **not** be used to "exit" out of the structured block. if an exception is not caught in the same structured block it was thrown in, the behavior is <red>undefined</red> ๐Ÿ˜ญ

FIXME: this check does not model SEH, `setjmp`/`longjmp`.

> **WARNING!** this check may be <hotpink>expensive</hotpink> on large source files. your compile times are gonna feel it.

### โœจ Options

- **`IgnoredExceptions`** โ€” comma-separated list containing type names which are not counted as thrown exceptions in the check. default value is an empty string.

## ๐Ÿ‘‘ openmp-use-default-none

finds OpenMP directives that are allowed to contain a `default` clause, but either don't specify it, or specify it with a kind other than `none` โ€” and suggests you use the `default(none)` clause instead. go off, be explicit โญ

Okay so using `default(none)` forces developers to <pink>explicitly</pink> specify the data sharing attributes for every single variable referenced in the construct, which means it's <hotpink>obvious</hotpink> which variables are referenced and what their data sharing attribute is โ€” so readability goes up and errors become way easier to spot. <rainbow>iconic behavior tbh</rainbow> ๐Ÿ’…

### โœจ Example โœจ

// for directive cannot have default clause, no diagnostics. void n0(const int a) { #pragma omp for for (int b = 0; b < a; b++) ; }

// parallel directive.

// parallel directive can have default clause, but said clause is not // specified, diagnosed. void p0_0() { #pragma omp parallel ; // WARNING: OpenMP directive parallel does not specify default // clause. Consider specifying default(none) clause. }

// parallel directive can have default clause, and said clause is // specified, with none kind, all good. void p0_1() { #pragma omp parallel default(none) ; }

// parallel directive can have default clause, and said clause is // specified, but with shared kind, which is not none, diagnose. void p0_2() { #pragma omp parallel default(shared) ; // WARNING: OpenMP directive parallel specifies default(shared) // clause. Consider using default(none) clause instead. }

// parallel directive can have default clause, and said clause is // specified, but with firstprivate kind, which is not none, diagnose. void p0_3() { #pragma omp parallel default(firstprivate) ; // WARNING: OpenMP directive parallel specifies default(firstprivate) // clause. Consider using default(none) clause instead. }


---

### `performance-avoid-endl`

Bestie, this one sniffs out uses of `std::endl` on streams and is like "girlโ€ฆ just use the newline character `'\n'` instead." ๐ŸŒธ

**Rationale:** using `std::endl` on streams can be *less efficient* than using the newline character `'\n'`, because `std::endl` does <yellow>two</yellow> whole operations: it writes a newline character to the output stream and then flushes the stream buffer. Writing a single newline character with `'\n'` does **not** trigger a flush, which can improve performance. Plus, flushing the stream buffer can add extra overhead when you're working with streams that are buffered. <purple>the way that it's just doing extra work for no reason ๐Ÿ˜ญ</purple>

**Example:**

Consider this code:

#include <iostream>

int main() { std::cout << "Hello" << std::endl; }


Which gets transformed into:

#include <iostream>

int main() { std::cout << "Hello" << '\n'; }


This code writes a single newline character to the `std::cout` stream without flushing the stream buffer. <mint>slay, less work, same output โœจ</mint>

Also, super important to note: the standard C++ streams (like `std::cerr`, `std::wcerr`, `std::clog` and `std::wclog`) <hotpink>always flush after a write operation</hotpink>, unless `std::ios_base::sync_with_stdio` is set to `false`. regardless of whether `std::endl` or `'\n'` is used. So using `'\n'` with these streams won't actually give you any performance gain โ€” but it's *still* recommended to use `'\n'` for consistency and readability. ๐ŸŽ€

If you genuinely do need to flush the stream buffer, you can use `std::flush` explicitly, like this:

#include <iostream>

int main() { std::cout << "Hello\n" << std::flush; }


---

### `performance-enum-size`

This one recommends the <green>smallest possible underlying type</green> for an enum or enum class, based on the range of its enumerators. It analyzes the values of the enumerators in an enum or enum class โ€” signed values included โ€” to recommend the smallest possible underlying type that can represent <pink>all</pink> the values of the enum. The suggested underlying types are the integral types `std::uint8_t`, `std::uint16_t`, and `std::uint32_t` for unsigned types, and `std::int8_t`, `std::int16_t`, and `std::int32_t` for signed types. Using the suggested underlying types can help reduce the memory footprint of the program and improve performance in some cases. <cyan>tiny enum, big gains, we love to see it</cyan> ๐Ÿ’–

For example:

// BEFORE enum Color { RED = -1, GREEN = 0, BLUE = 1 };

std::optional<Color> color_opt;


The `Color` enum uses the default underlying type, which is `int` in this case, and its enumerators have values of `-1`, `0`, and `1`. Additionally, the `std::optional<Color>` object uses 8 bytes due to padding (platform dependent). ๐Ÿ˜ญ

// AFTER enum Color : std:int8_t { RED = -1, GREEN = 0, BLUE = 1 }

std::optional<Color> color_opt;


In the revised version of the `Color` enum, the underlying type has been changed to `std::int8_t`. The enumerator `RED` has a value of `-1`, which can be represented by a signed 8-bit integer. <mint>no cap, it fits perfectly</mint>

By using a smaller underlying type, the memory footprint of the `Color` enum is reduced from <yellow>4 bytes to 1 byte</yellow>. The revised version of the `std::optional<Color>` object would only require <yellow>2 bytes</yellow> (due to lack of padding), since it contains a single byte for the `Color` enum and a single byte for the `bool` flag that indicates whether the optional value is set. ๐Ÿ”ฅ

Reducing the memory footprint of an enum can have significant benefits in terms of memory usage and cache performance. *However*, it's important to consider the trade-offs and potential impact on code readability and maintainability. ๐Ÿ‘‘

Enums without enumerators (empty) are excluded from analysis.

Requires C++11 or above. Does not provide auto-fixes.

### โœจ Options โœจ

- **`EnumIgnoreList`** โ€” this option is used to ignore certain enum types. 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.

---

### `performance-faster-string-find`

Optimize calls to `std::string::find()` and friends when the needle passed is a <pink>single character string literal</pink>. The character literal overload is more efficient. ๐Ÿฆ‹

Examples:

str.find("A");

// becomes

str.find('A');


### โœจ Options โœจ

- **`StringLikeClasses`** โ€” semicolon-separated list of names of string-like classes. By default only `::std::basic_string` and `::std::basic_string_view` are considered. The check will only consider member functions named `find`, `rfind`, `find_first_of`, `find_first_not_of`, `find_last_of`, or `find_last_not_of` within these classes.

---

### `performance-for-range-copy`

Finds C++11 `for` ranges where the loop variable is copied in each iteration but it would totally suffice to obtain it by `const` reference. ๐Ÿ’…

The check is only applied to loop variables of types that are <hotpink>expensive to copy</hotpink>, which means they are not trivially copyable or have a non-trivial copy constructor or destructor.

To make sure it's safe to replace the copy with a `const` reference, the following heuristic is employed:

1. The loop variable is `const` qualified.
2. The loop variable is not `const`, but only `const` methods or operators are invoked on it, or it is used as a `const` reference or value argument in constructors or function calls.

### โœจ Options โœจ

- **`WarnOnAllAutoCopies`** โ€” when `true`, warns on any use of `auto` as the type of the range-based for loop variable. Default is `false`.
- **`AllowedTypes`** โ€” a semicolon-separated list of names of types allowed to be copied in each iteration. 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 typename (i.e. `namespace::Type`, otherwise it is matched against only the type name (i.e. `Type`).

---

### `performance-implicit-cast-in-loop`

This check has been renamed to `performance-implicit-conversion-in-loop`. <purple>she got a glow-up and a new name, periodt</purple>

---

### `performance-implicit-conversion-in-loop`

This warning shows up in a range-based loop with a loop variable of `const` ref type where the type of the variable <yellow>does not match</yellow> the one returned by the iterator. That means an implicit conversion is happening, which can for example result in expensive deep copies. ๐Ÿ˜ญ

Example:

map<int, vector<string>> my_map; for (const pair<int, vector<string>>& p : my_map) {} // The iterator type is in fact pair<const int, vector<string>>, which means // that the compiler added a conversion, resulting in a copy of the vectors.


The easiest solution is usually to use `const auto&` instead of writing the type manually. <mint>just let the compiler do her thing ๐Ÿ’–</mint>

---

### `performance-inefficient-algorithm`

Warns on inefficient use of STL algorithms on associative containers. ๐ŸŽ€

Associative containers implement some of the algorithms as methods, and those methods should be preferred over the algorithms in the `algorithm` header. The methods can take advantage of the order of the elements. <cyan>iykyk</cyan>

std::set<int> s; auto it = std::find(s.begin(), s.end(), 43);

// becomes

auto it = s.find(43);

std::set<int> s; auto c = std::count(s.begin(), s.end(), 43);

// becomes

auto c = s.count(43);


---

### `performance-inefficient-string-concatenation`

This check warns about the performance overhead arising from concatenating strings using the `operator+`, for instance:

std::string a("Foo"), b("Bar"); a = a + b;


Instead of this structure you should use `operator+=` or `std::string`'s (`std::basic_string`) class member function `append()`. For instance:

std::string a("Foo"), b("Baz"); for (int i = 0; i < 20000; ++i) { a = a + "Bar" + b; }


Could be rewritten in a greatly more efficient way like:

std::string a("Foo"), b("Baz"); for (int i = 0; i < 20000; ++i) { a.append("Bar").append(b); }


And this can be rewritten too:

void f(const std::string&) {} std::string a("Foo"), b("Baz"); void g() { f(a + "Bar" + b); }


In a slightly more efficient way like:

void f(const std::string&) {} std::string a("Foo"), b("Baz"); void g() { f(std::string(a).append("Bar").append(b)); }


### โœจ Options โœจ

- **`StrictMode`** โ€” when `false`, the check will only check the string usage in `while`, `for` and for-range statements. Default is `false`.

---

### `performance-inefficient-vector-operation`

Finds possible inefficient `std::vector` operations (e.g. `push_back`, `emplace_back`) that may cause <red>unnecessary memory reallocations</red>. ๐Ÿ’€

It can also find calls that add an element to a protobuf repeated field in a loop without calling `Reserve()` before the loop. Calling `Reserve()` first can avoid unnecessary memory reallocations. ๐ŸŒธ

Currently, the check only detects following kinds of loops with a single statement body:

- Counter-based for loops start with `0`:

- For-range loops like `for (range-declaration : range_expression)`, the type of `range_expression` can be `std::vector`, `std::array`, `std::deque`, `std::set`, `std::unordered_set`, `std::map`, `std::unordered_set`:

std::vector<int> data; std::vector<int> v;

for (auto element : data) { v.push_back(element); // This will trigger the warning since the 'push_back' may cause multiple // memory reallocations in v. This can be avoid by inserting a // 'reserve(data.size())' statement before the for statement. }


## โœจ Options โœจ

- **`VectorLikeClasses`** โ€” a semicolon-separated list of names of vector-like classes. By default only `::std::vector` is considered. ๐Ÿฆ‹
- **`EnableProto`** โ€” when `true`, the check will also warn on inefficient operations for proto repeated fields. Otherwise, the check only warns on inefficient vector operations. Default is `false`.

---

### ๐ŸŒธ performance-move-const-arg ๐ŸŒธ

The check warns

- if `std::move()` is called with a constant argument,
- if `std::move()` is called with an argument of a trivially-copyable type,
- if the result of `std::move()` is passed as a const reference argument.

In all three cases, the check will suggest a fix that removes the `std::move()`. ๐Ÿ’…

Here are examples of each of the three cases:

const string s; return std::move(s); // Warning: std::move of the const variable has no effect

int x; return std::move(x); // Warning: std::move of the variable of a trivially-copyable type has no effect

void f(const string &s); string s; f(std::move(s)); // Warning: passing result of std::move as a const reference argument; no move will actually happen


## ๐ŸŽ€ Options ๐ŸŽ€

- **`CheckTriviallyCopyableMove`** โ€” if `true`, enables detection of trivially copyable types that do not have a move constructor. Default is `true`.
- **`CheckMoveToConstRef`** โ€” if `true`, enables detection of `std::move()` passed as a const reference argument. Default is `true`.

---

### ๐Ÿ’œ performance-move-constructor-init ๐Ÿ’œ

<purple>"cert-oop11-cpp"</purple> redirects here as an alias for this check.

The check flags user-defined move constructors that have a ctor-initializer initializing a member or base class through a copy constructor instead of a move constructor.

---

### ๐ŸŒˆ performance-no-automatic-move ๐ŸŒˆ

Finds local variables that cannot be automatically moved due to constness.

Under certain conditions, local values are automatically moved out when returning from a function. A common mistake is to declare local lvalue variables `const`, which prevents the move. ๐Ÿ˜ญ

Example [1]:

StatusOr<std::vector<int>> Cool() { std::vector<int> obj = ...; return obj; // calls StatusOr::StatusOr(std::vector<int>&&) }

StatusOr<std::vector<int>> NotCool() { const std::vector<int> obj = ...; return obj; // calls StatusOr::StatusOr(const std::vector<int>&) }


The former version (<green>Cool</green>) should be preferred over the latter (<red>NotCool</red>) as it will avoid allocations and potentially large memory copies. โœจ

### ๐ŸŒŸ Semantics ๐ŸŒŸ

In the example above, `StatusOr::StatusOr(T&&)` have the same semantics as long as the copy and move constructors for `T` have the same semantics. Note that there is no guarantee that `S::S(T&&)` and `S::S(const T&)` have the same semantics for any single `S`, so we're not providing automated fixes for this check, and judgement should be exerted when making the suggested changes. ๐Ÿ‘‘

### ๐ŸŒธ -Wreturn-std-move ๐ŸŒธ

Another case where the move cannot happen is the following:

StatusOr<std::vector<int>> Uncool() { std::vector<int>&& obj = ...; return obj; // calls StatusOr::StatusOr(const std::vector<int>&) }


In that case the fix is more consensual: just `return std::move(obj)`. This is handled by the `-Wreturn-std-move` warning.

---

### ๐Ÿ”ฅ performance-no-int-to-ptr ๐Ÿ”ฅ

Diagnoses every integer to pointer cast.

While casting an (integral) pointer to an integer is obvious โ€” you just get the integral value of the pointer โ€” casting an integer to an (integral) pointer is <yellow>deceivingly</yellow> different. While you will get a pointer with that integral value, if you got that integral value via a pointer-to-integer cast originally, the new pointer will lack the provenance information from the original pointer.

So while (integral) pointer to integer casts are effectively no-ops, and are transparent to the optimizer, integer to (integral) pointer casts are <red>NOT</red> transparent, and may conceal information from optimizer.

While that may be the intention, it is not always so. For example, let's take a look at a routine to align the pointer up to the multiple of 16: The obvious, naive implementation for that is:

char* src(char* maybe_underbiased_ptr) { uintptr_t maybe_underbiased_intptr = (uintptr_t)maybe_underbiased_ptr; uintptr_t aligned_biased_intptr = maybe_underbiased_intptr + 15; uintptr_t aligned_intptr = aligned_biased_intptr & (~15); return (char*)aligned_intptr; // warning: avoid integer to pointer casts [performance-no-int-to-ptr] }


The check will rightfully diagnose that cast. ๐Ÿ’…

But when provenance concealment is not the goal of the code, but an accident, this example can be rewritten as follows, without using integer to pointer cast:

char* tgt(char* maybe_underbiased_ptr) { uintptr_t maybe_underbiased_intptr = (uintptr_t)maybe_underbiased_ptr; uintptr_t aligned_biased_intptr = maybe_underbiased_intptr + 15; uintptr_t aligned_intptr = aligned_biased_intptr & (~15); uintptr_t bias = aligned_intptr - maybe_underbiased_intptr; return maybe_underbiased_ptr + bias; }


---

### ๐ŸŽ€ performance-noexcept-destructor ๐ŸŽ€

The check flags user-defined destructors marked with `noexcept(expr)` where `expr` evaluates to `false` (but is not a `false` literal itself).

When a destructor is marked as `noexcept`, it assures the compiler that no exceptions will be thrown during the destruction of an object, which allows the compiler to perform certain optimizations such as omitting exception handling code. โœจ

---

### ๐Ÿ’– performance-noexcept-move-constructor ๐Ÿ’–

The check flags user-defined move constructors and assignment operators not marked with `noexcept` or marked with `noexcept(expr)` where `expr` evaluates to `false` (but is not a `false` literal itself).

Move constructors of all the types used with STL containers, for example, need to be declared `noexcept`. Otherwise STL will choose copy constructors instead. The same is valid for move assignment operations. ๐Ÿซถ

---

### ๐Ÿฆ‹ performance-noexcept-swap ๐Ÿฆ‹

The check flags user-defined `swap` and `iter_swap` functions not marked with `noexcept` or marked with `noexcept(expr)` where `expr` evaluates to `false` (but is not a `false` literal itself).

When a `swap` or `iter_swap` function is marked as `noexcept`, it assures the compiler that no exceptions will be thrown during the swapping of two objects, which allows the compiler to perform certain optimizations such as omitting exception handling code. ๐ŸŒˆ

---

### ๐ŸŒธ performance-trivially-destructible ๐ŸŒธ

Finds types that could be made trivially-destructible by removing out-of-line defaulted destructor declarations.

struct A: TrivialType { ~A(); // Makes A non-trivially-destructible. TrivialType trivial_fields; }; A::~A() = default;


---

### โญ performance-type-promotion-in-math-fn โญ

Finds calls to C math library functions (from `math.h` or, in C++, `cmath`) with implicit `float` to `double` promotions.

For example, warns on `::sin(0.f)`, because this function's parameter is a `double`. You probably meant to call `std::sin(0.f)` (in C++), or `sinf(0.f)` (in C).

float a; asin(a);

// becomes

float a; std::asin(a);


---

### ๐Ÿ’œ performance-unnecessary-copy-initialization ๐Ÿ’œ

Finds local variable declarations that are initialized using the copy constructor of a non-trivially-copyable type but it would suffice to obtain a `const` reference.

The check is only applied if it is safe to replace the copy by a `const` reference. This is the case when the variable is `const` qualified or when it is only used as a `const`, i.e. only `const` methods or operators are invoked on it, or it is used as `const` reference or value argument in constructors or function calls. ๐Ÿ‘‘

Example:

const string& constReference(); void Function() { // The warning will suggest making this a const reference. const string UnnecessaryCopy = constReference(); }

struct Foo { const string& name() const; }; void Function(const Foo& foo) { // The warning will suggest making this a const reference. string UnnecessaryCopy1 = foo.name(); UnnecessaryCopy1.find("bar");

// The warning will suggest making this a const reference. string UnnecessaryCopy2 = UnnecessaryCopy1; UnnecessaryCopy2.find("bar"); }


## ๐ŸŽ€ Options ๐ŸŽ€

- **`AllowedTypes`** โ€” a semicolon-separated list of names of types allowed to be initialized by copying. 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 typename (i.e. `namespace::Type`, otherwise it is matched against only the type name (i.e. `Type`).
- **`ExcludedContainerTypes`** โ€” a semicolon-separated list of names of types whose methods are allowed to return the const reference the variable is copied from. When an expensive to copy variable is copy initialized by the return value from a type on this list the check does not trigger. This can be used to exclude types known to be const incorrect or where the lifetime or immutability of returned references is not tied to mutations of the container. An example are view types that don't own the underlying data. Like for `AllowedTypes` above, regular expressions are accepted and the inclusion of `::` determines whether the qualified typename is matched or not.

### ๐ŸŽ€ `performance-unnecessary-value-param` ๐ŸŽ€

okay bestie so this one is <pink>sniffing out</pink> value parameter declarations where the type is expensive to copy and it gets copied on <hotpink>every single invocation</hotpink>โ€ฆ when honestly it would've sufficed to just pass it by `const` reference. ๐Ÿ’…

lowkey important tho: the check is ONLY applied to parameters of types that are expensive to copy, which means they are <yellow>not trivially copyable</yellow>, or they have a non-trivial copy constructor, or a non-trivial destructor.

and to make sure it's actually safe to swap that value parameter out for a `const` reference, it uses this heuristic:

1. the parameter is `const` qualified;
2. the parameter is not `const`, but only `const` methods or operators are invoked on it, or it is used as `const` reference or value argument in constructors or function calls.

Example:

void f(const string Value) { // The warning will suggest making Value a reference. }

void g(ExpensiveToCopy Value) { // The warning will suggest making Value a const reference. Value.ConstMethd(); ExpensiveToCopy Copy(Value); }


and if the parameter is **not** `const`, is only copied or assigned <purple>once</purple>, and has a non-trivial move-constructor or move-assignment operator respectively โ€” the check will suggest to <cyan>move</cyan> it. iconic behavior tbh.

Example:

void setValue(string Value) { Field = Value; }


Will become:

#include <utility>

void setValue(string Value) { Field = std::move(Value); }


#### โœจ Options โœจ

- **`IncludeStyle`** โ€” a string specifying which include-style is used, `llvm` or `google`. Default is `llvm`.
- **`AllowedTypes`** โ€” a semicolon-separated list of names of types allowed to be passed by value. 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 typename (i.e. `namespace::Type`), otherwise it is matched against only the type name (i.e. `Type`).

---

### ๐ŸŒˆ `portability-restrict-system-includes` ๐ŸŒˆ

Checks to selectively allow or disallow a configurable list of system headers. <lavender>gatekeeping, but for includes</lavender>. ๐Ÿซถ

For example:

In order to only allow `zlib.h` from the system you would set the options to `-*,zlib.h`.

#include <curses.h> // Bad: disallowed system header. #include <openssl/ssl.h> // Bad: disallowed system header. #include <zlib.h> // Good: allowed system header. #include "src/myfile.h" // Good: non-system header always allowed.


In order to allow everything except `zlib.h` from the system you would set the options to `*,-zlib.h`.

#include <curses.h> // Good: allowed system header. #include <openssl/ssl.h> // Good: allowed system header. #include <zlib.h> // Bad: disallowed system header. #include "src/myfile.h" // Good: non-system header always allowed.


Since the options support globbing you can use wildcarding to allow groups of headers. โœจ

`-*,openssl/*.h` will allow all openssl headers but disallow any others.

#include <curses.h> // Bad: disallowed system header. #include <openssl/ssl.h> // Good: allowed system header. #include <openssl/rsa.h> // Good: allowed system header. #include <zlib.h> // Bad: disallowed system header. #include "src/myfile.h" // Good: non-system header always allowed.


#### โœจ Options โœจ

- **`Includes`** โ€” a string containing a comma separated glob list of allowed include filenames. Similar to the `-checks` glob list for running `clang-tidy` itself, the two wildcard characters are `*` and `-`, to include and exclude globs, respectively. The default is `*`, which allows all includes.

---

### ๐Ÿฆ‹ `portability-simd-intrinsics` ๐Ÿฆ‹

Finds SIMD intrinsics calls and suggests `std::experimental::simd` (P0214) alternatives. <cyan>manifesting portable SIMD</cyan> fr fr.

If the option `Suggest` is set to `true`, for

_mm_add_epi32(a, b); // x86 vec_add(a, b); // Power


the check suggests an alternative: `operator+` on `std::experimental::simd` objects.

Otherwise, it just complains the intrinsics are non-portable (and there are P0214 alternatives). ๐Ÿ˜ญ

Many architectures provide SIMD operations (e.g. x86 SSE/AVX, Power AltiVec/VSX, ARM NEON). It is common that SIMD code implementing the same algorithm, is written in multiple target-dispatching pieces to optimize for different architectures or micro-architectures.

The C++ standard proposal P0214 and its extensions cover many common SIMD operations. By migrating from target-dependent intrinsics to P0214 operations, the SIMD code can be simplified and pieces for different targets can be unified. ๐Ÿ‘‘

Refer to P0214 for introduction and motivation for the data-parallel standard library.

#### โœจ Options โœจ

- **`Suggest`** โ€” if this option is set to `true` (default is `false`), the check will suggest P0214 alternatives, otherwise it only points out the intrinsic function is non-portable.
- **`Std`** โ€” the namespace used to suggest P0214 alternatives. If not specified, `std::` for `-std=c++20` and `std::experimental::` for `-std=c++11`.

---

### ๐Ÿ’€ `portability-std-allocator-const` ๐Ÿ’€

Report use of `std::vector<const T>` (and similar containers of `const` elements). These are <red>not allowed</red> in standard C++, and should usually be `std::vector<T>` instead."

Per C++ `[allocator.requirements.general]`: "T is any cv-unqualified object type", `std::allocator<const T>` is undefined. Many standard containers use `std::allocator` by default and therefore their `const T` instantiations are undefined.

`libc++` defines `std::allocator<const T>` as an extension which will be removed in the future. ๐Ÿ™ˆ

`libstdc++` and MSVC do not support `std::allocator<const T>`:

// libstdc++ has a better diagnostic since https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48101 std::deque<const int> deque; // error: static assertion failed: std::deque must have a non-const, non-volatile value_type std::set<const int> set; // error: static assertion failed: std::set must have a non-const, non-volatile value_type std::vector<int* const> vector; // error: static assertion failed: std::vector must have a non-const, non-volatile value_type

// MSVC // error C2338: static_assert failed: 'The C++ Standard forbids containers of const elements because allocator<const T> is ill-formed.'


Code bases only compiled with `libc++` may accrue such undefined usage. This check finds such code and prevents backsliding while clean-up is ongoing. <mint>caught in 4k</mint>. ๐ŸŽ€

---

### ๐ŸŒธ `readability-avoid-const-params-in-decls` ๐ŸŒธ

Checks whether a function declaration has parameters that are <yellow>top level `const`</yellow>.

`const` values in declarations do not affect the signature of a function, so they should not be put there. like why would u even. ๐Ÿ’…

Examples:

void f(const string); // Bad: const is top level. void f(const string&); // Good: const is not top level.


#### โœจ Options โœจ

- **`IgnoreMacros`** โ€” if set to `true`, the check will not give warnings inside macros. Default is `true`.

---

### ๐Ÿงš `readability-avoid-nested-conditional-operator` ๐Ÿงš

Identifies instances of nested conditional operators in the code.

Nested conditional operators, also known as ternary operators, can contribute to reduced code readability and comprehension. So they should be split as several statements and stored the intermediate results in temporary variable. <purple>un-nest it bestie</purple>.

Examples:

int NestInConditional = (condition1 ? true1 : false1) ? true2 : false2; int NestInTrue = condition1 ? (condition2 ? true1 : false1) : false2; int NestInFalse = condition1 ? true1 : condition2 ? true2 : false1;


This check implements part of <blue>AUTOSAR C++14 Rule A5-16-1</blue>.

---

### ๐Ÿ“ `readability-avoid-return-with-void-value` ๐Ÿ“

Finds `return` statements with `void` values used within functions with `void` result types.

A function with a `void` return type is intended to perform a task without producing a return value. Return statements with expressions could lead to confusion and may miscommunicate the function's intended behavior. ๐Ÿ˜ญ

Example:

void g(); void f() { // ... return g(); }


In a long function body, the `return` statement suggests that the function returns a value. However, `return g();` is a combination of two statements that should be written as

g(); return;


to make clear that `g()` is called and immediately afterwards the function returns (nothing).

In C, the same issue is detected by the compiler if the `-Wpedantic` mode is enabled.

#### โœจ Options โœจ

- **`IgnoreMacros`** โ€” the value `false` specifies that `return` statements expanded from macros are not checked. The default value is `true`.
- **`StrictMode`** โ€” the value `false` specifies that a direct `return` statement shall be excluded from the analysis if it is the only statement not contained in a block, like `if (cond) return g();`. The default value is `true`.

---

### ๐ŸŒˆ `readability-avoid-unconditional-preprocessor-if` ๐ŸŒˆ

Finds code blocks that are constantly enabled or disabled in preprocessor directives by analyzing `#if` conditions, such as `#if 0` and `#if 1`, etc.

#if 0 // some disabled code #endif

#if 1 // some enabled code that can be disabled manually #endif


Unconditional preprocessor directives, such as `#if 0` for disabled code and `#if 1` for enabled code, can lead to <red>dead code</red> and always enabled code, respectively. Dead code can make understanding the codebase more difficult, hinder readability, and may be a sign of unfinished functionality or abandoned features. This can cause maintenance issues, confusion for future developers, and potential compilation problems.

As a solution for both cases, consider using preprocessor macros or defines, like `#ifdef DEBUGGING_ENABLED`, to control code enabling or disabling. This approach provides better coordination and flexibility when working with different parts of the codebase. Alternatively, you can comment out the entire code using `/* */` block comments and add a hint, such as `@todo`, to indicate future actions. <mint>future you will thank present you</mint>. ๐Ÿซถ

---

### โœจ `readability-braces-around-statements` โœจ

`google-readability-braces-around-statements` redirects here as an alias for this check.

Checks that bodies of `if` statements and loops (`for`, `do while`, and `while`) are inside braces. no more naked one-liners bestie, we're <pink>wrapping it up</pink>. ๐Ÿ’…

Before:

if (condition) statement;


After:

if (condition) { statement; }


#### โœจ Options โœจ

- **`ShortStatementLines`** โ€” defines the minimal number of lines that the statement should have in order to trigger this check.

  The number of lines is counted from the end of condition or initial keyword (`do`/`else`) until the last line of the inner statement. Default value `0` means that braces will be added to all statements (not having them already).

## readability-const-return-type ๐Ÿ’…

Okay bestie, this one is SO petty and SO correct. It checks for functions that return a **const-qualified** type and says "girl, take that `const` off." Using `const` like that is usually superfluous, and โ€” plot twist โ€” it can actually block valuable compiler optimizations. <pink>Does not (yet) fix trailing return types.</pink> ๐Ÿ™ˆ

Examples:

const int foo(); const Clazz foo(); Clazz *const foo();


Now here's the โœจ important nuance โœจ โ€” this applies **strictly to top-level qualification**, which means it does *not* apply to pointers or references to const values. For example, these are totally fine:

const int* foo(); const int& foo(); const Clazz* foo();


### Options

- **`IgnoreMacros`** โ€” if set to `true`, the check will not give warnings inside macros. Default is `true`.

---

## readability-container-contains ๐Ÿซถ

This one finds usages of `container.count()` and `container.find() == container.end()` that should honestly just be a call to `container.contains()` โ€” the method that landed in **C++20**. ๐ŸŒธ

Why do we care? Because whether an element is inside a container should be checked with `contains` instead of `count`/`find`, since `contains` conveys the intent way more clearly. And bonus: for containers that permit multiple entries per key (`multimap`, `multiset`, ...), `contains` is **more efficient** than `count`, because `count` has to do unnecessary additional work. No cap. ๐Ÿ’…

Examples:

- `myMap.find(x) == myMap.end()` โ†’ `!myMap.contains(x)`
- `myMap.find(x) != myMap.end()` โ†’ `myMap.contains(x)`
- `if (myMap.count(x))` โ†’ `if (myMap.contains(x))`
- `bool exists = myMap.count(x)` โ†’ `bool exists = myMap.contains(x)`
- `bool exists = myMap.count(x) > 0` โ†’ `bool exists = myMap.contains(x)`
- `bool exists = myMap.count(x) >= 1` โ†’ `bool exists = myMap.contains(x)`
- `bool missing = myMap.count(x) == 0` โ†’ `bool missing = !myMap.contains(x)`

This check applies to `std::set`, `std::unordered_set`, `std::map`, `std::unordered_map` and the corresponding multi-key variants. <yellow>It is only active for C++20 and later</yellow>, as the `contains` method was only added in C++20. โญ

---

## readability-container-data-pointer ๐Ÿ‘‘

Finds cases where code could use `data()` rather than the address of the element at index `0` in a container. This pattern is commonly used to materialize a pointer to the backing data of a container. `std::vector` and `std::string` provide a `data()` accessor to retrieve the data pointer, and that's the one you should be using, bestie. ๐Ÿ’–

This also ensures that in the case that the container is **empty**, the data pointer access does not perform an errant memory access. So it's not just aesthetic, it's safety. ๐Ÿ˜Œ

### Options

- **`IgnoredContainers`** โ€” semicolon-separated list of containers regexp for which this check won't be enforced. Default is empty.

---

## readability-container-size-empty ๐ŸŽ€

Checks whether a call to the `size()`/`length()` method can be replaced with a call to `empty()`.

The emptiness of a container should be checked using the `empty()` method instead of the `size()`/`length()` method. It is not guaranteed that `size()`/`length()` is a constant-time function, and it is generally more efficient and also shows clearer intent to use `empty()`. Furthermore, some containers may implement the `empty()` method but **not** implement the `size()` or `length()` method. Using `empty()` whenever possible makes it easier to switch to another container in the future. ๐ŸŒˆ

The check issues a warning if a container has `empty()` and `size()` or `length()` methods matching the following signatures:

size_type size() const; size_type length() const; bool empty() const;


`size_type` can be any kind of integer type.

- **`ExcludedComparisonTypes`** โ€” a semicolon-separated list of class names for which the check will ignore comparisons of objects with default-constructed objects of the same type. If a class is listed here, the check will not suggest using `empty()` instead of such comparisons for objects of that class. Default value is: `::std::array`.

---

## readability-convert-member-functions-to-static โœจ

Finds non-static member functions that can be made `static` because the functions don't use `this`. That's it. That's the whole vibe check. ๐Ÿฆ‹

After applying modifications as suggested by the check, running the check **again** might find more opportunities to mark member functions `static`. (It's giving cascade. ๐Ÿ’€)

After making a member function `static`, you might want to run the check `readability-static-accessed-through-instance` to replace calls like `Instance.method()` by `Class::method()`.

---

## readability-delete-null-pointer ๐Ÿงš

Checks the `if` statements where a pointer's existence is checked and then deletes the pointer. The check is **unnecessary**, because deleting a null pointer has no effect. Literally nothing happens. So we can drop the whole `if`. ๐Ÿ˜ญ

int *p; if (p) delete p;


---

## readability-duplicate-include ๐Ÿ’œ

Looks for duplicate includes and removes them. The check maintains a list of included files and looks for duplicates. If a macro is defined or undefined, then the list of included files is **cleared**. ๐Ÿ‘€

Examples:

#include <memory> #include <vector> #include <memory>


becomes

#include <memory> #include <vector>


Because of the intervening macro definitions, this code remains unchanged:

#undef NDEBUG #include "assertion.h" // ...code with assertions enabled

#define NDEBUG #include "assertion.h" // ...code with assertions disabled


---

## readability-else-after-return ๐ŸŒธ

The LLVM Coding Standards advise reducing indentation where possible and where it makes understanding code easier. Early exit is one of the suggested enforcements of that. So please, bestie, do **not** use `else` or `else if` after something that interrupts control flow โ€” like `return`, `break`, `continue`, `throw`. ๐Ÿšซ

The following piece of code illustrates how the check works. This piece of code:

void foo(int Value) { int Local = 0; for (int i = 0; i < 42; i++) { if (Value == 1) { return; } else { Local++; }

if (Value == 2) continue; else Local++;

if (Value == 3) { throw 42; } else { Local++; } } }


Would be transformed into:

void foo(int Value) { int Local = 0; for (int i = 0; i < 42; i++) { if (Value == 1) { return; } Local++;

if (Value == 2) continue; Local++;

if (Value == 3) { throw 42; } Local++; } }


### Options

- **`WarnOnUnfixable`** โ€” when `true`, emit a warning for cases where the check can't output a Fix-It. These can occur with declarations inside the `else` branch that would have an extended lifetime if the `else` branch was removed. Default value is `true`.

- **`WarnOnConditionVariables`** โ€” when `true`, the check will attempt to refactor a variable defined inside the condition of the `if` statement that is used in the `else` branch, defining them just before the `if` statement. This can only be done if the `if` statement is the last statement in its parent's scope. Default value is `true`.

### LLVM alias

There is an alias of this check called `llvm-else-after-return`. In that version the options `WarnOnUnfixable` and `WarnOnConditionVariables` are both set to `false` by default.

This check helps to enforce this LLVM Coding Standards recommendation.

---

## readability-enum-initial-value ๐Ÿ’–

Enforces consistent style for enumerators' initialization, covering three styles: **none**, **first only**, or **all** initialized explicitly.

An inconsistent style and strictness to defining the initializing value of enumerators may cause issues if the enumeration is extended with new enumerators that obtain their integer representation implicitly. ๐Ÿ™ˆ

The following three cases are accepted:

1. No enumerators are explicit initialized.
2. Exactly the first enumerator is explicit initialized.
3. All enumerators are explicit initialized.

enum A { // (1) Valid, none of enumerators are initialized. a0, a1, a2, };

enum B { // (2) Valid, the first enumerator is initialized. b0 = 0, b1, b2, };

enum C { // (3) Valid, all of enumerators are initialized. c0 = 0, c1 = 1, c2 = 2, };

enum D { // Invalid, d1 is not explicitly initialized! d0 = 0, d1, d2 = 2, };

enum E { // Invalid, e1, e3, and e5 are not explicitly initialized. e0 = 0, e1, e2 = 2, e3, // Dangerous, as the numeric values of e3 and e5 are both 3, and this is not explicitly visible in the code! e4 = 2, e5, };


> <red>Watch out for `enum E` โ€” `e3` and `e5` both secretly equal `3` and the code does NOT make that visible. That's the whole point of this check.</red> ๐Ÿ’€

This check corresponds to the CERT C Coding Standard recommendation โ€ **INT09-C. Ensure enumeration constants map to unique values.**

`cert-int09-c` redirects here as an alias of this check.

### Options

- **`AllowExplicitZeroFirstInitialValue`** โ€” if set to `false`, the first enumerator must **not** be explicitly initialized to a literal `0`. Default is `true`.

enum F { f0 = 0, // Not allowed if AllowExplicitZeroFirstInitialValue is false. f1, f2, };


- **`AllowExplicitSequentialInitialValues`** โ€” flip this to `false` and you're not allowed to explicitly init things to sequential values, bestie. Default is `true`.

enum G { g0 = 1, // Not allowed if AllowExplicitSequentialInitialValues is false. g1 = 2, g2 = 3,


---

## readability-function-cognitive-complexity

Okay so this one checks the <pink>function Cognitive Complexity metric</pink>. ๐Ÿ’…

The metric is implemented as per the **COGNITIVE COMPLEXITY** by SonarSource specification version **1.2 (19 April 2017)**.

### Options

- **`Threshold`** โ€” flags any function whose Cognitive Complexity goes <red>over</red> this number. Default is `25`.
- **`DescribeBasicIncrements`** โ€” set it to `true` and, for every function that blows past the complexity threshold, the check drops extra diagnostics on every single piece of code (loop, if statement, etc.) that's contributing to that complexity. Peep the examples below too. Default is `true`.
- **`IgnoreMacros`** โ€” set it to `true` and the check will straight-up ignore code inside macros. Heads up though: any macro arguments get ignored too, even if they *should* count toward the complexity. And since this might change in the future, this option isn't guaranteed to be forward-compatible. Default is `false`.

### Building blocks

There are three basic building blocks of a Cognitive Complexity metric:

### Increment

These structures bump the function's Cognitive Complexity metric up (by `1`):

- Conditional operators:
  - `if()`
  - `else if()`
  - `else`
  - `cond ? true : false`
- `switch()`
- Loops:
  - `for()`
  - C++11 range-based `for()`
  - `while()`
  - `do while()`
- `catch ()`
- `goto LABEL, goto *(&&LABEL)),`
- sequences of binary logical operators:
  - `boolean1 || boolean2`
  - `boolean1 && boolean2`

### Nesting level

So by itself the nesting level does **not** change the function's Cognitive Complexity metric โ€” it's just tracked, and then the next building block (the third one) uses it. ๐Ÿ’– These structures bump the nesting level up (by `1`):

- Conditional operators:
  - `if()`
  - `else if()`
  - `else`
  - `cond ? true : false`
- `switch()`
- Loops:
  - `for()`
  - C++11 range-based `for()`
  - `while()`
  - `do while()`
- `catch ()`
- Nested functions:
  - C++11 Lambda
  - Nested class
  - Nested struct
- GNU statement expression
- Apple Block Declaration

### Nesting increment

This is where that previous building block, Nesting level, actually matters. These structures bump the function's Cognitive Complexity metric by the **current** Nesting level:

- Conditional operators:
  - `if()`
  - `cond ? true : false`
- `switch()`
- Loops:
  - `for()`
  - C++11 range-based `for()`
  - `while()`
  - `do while()`
- `catch ()`

### Examples

The simplest case. This function has Cognitive Complexity of `0`.

void function0() {}


Slightly better example. This function has Cognitive Complexity of `1`.

int function1(bool var) { if(var) // +1, nesting level +1 return 42; return 0; }


Full example. This function has Cognitive Complexity of `3`.

int function3(bool var1, bool var2) { if(var1) { // +1, nesting level +1 if(var2) // +2 (1 + current nesting level of 1), nesting level +1 return 42; }

return 0; }


In that last example, the check will flag `function3` if the option `Threshold` is set to `2` or smaller. And if the option `DescribeBasicIncrements` is set to `true`, it'll *additionally* flag the two `if` statements with the amounts by which they increase the complexity of the function and the current nesting level. ๐ŸŒธ

### Limitations

The metric is implemented with two notable exceptions:

- preprocessor conditionals (`#ifdef`, `#if`, `#elif`, `#else`, `#endif`) are not accounted for.
- each method in a recursion cycle is not accounted for. It can't be fully implemented, because cross-translational-unit analysis would be needed, which is currently not possible in clang-tidy.

---

## readability-function-size

`google-readability-function-size` redirects here as an alias for this check.

Checks for large functions based on various metrics. ๐Ÿ‘‘

### Options

- **`LineThreshold`** โ€” flags functions exceeding this number of lines. Default is none (ignore the number of lines).
- **`StatementThreshold`** โ€” flags functions exceeding this number of statements. Ngl, this may differ significantly from the number of lines for macro-heavy code. Default is `800`.
- **`BranchThreshold`** โ€” flags functions exceeding this number of control statements. Default is none (ignore the number of branches).
- **`ParameterThreshold`** โ€” flags functions that exceed a specified number of parameters. Default is none (ignore the number of parameters).
- **`NestingThreshold`** โ€” flags compound statements which create the next nesting level after `NestingThreshold`. This may differ significantly from the expected value for macro-heavy code. Default is none (ignore the nesting level).
- **`VariableThreshold`** โ€” flags functions exceeding this number of variables declared in the body. Default is none (ignore the number of variables). โš ๏ธ Please note that function parameters and variables declared in lambdas, GNU Statement Expressions, and nested class inline functions are **not** counted.

---

## readability-identifier-length

This check finds variables and function parameters whose length is too short. ๐Ÿ“ The desired name length is configurable.

Special cases are supported for loop counters and for exception variable names.

### Options

The following options are described below:

- `MinimumVariableNameLength`, `IgnoredVariableNames`
- `MinimumParameterNameLength`, `IgnoredParameterNames`
- `MinimumLoopCounterNameLength`, `IgnoredLoopCounterNames`
- `MinimumExceptionNameLength`, `IgnoredExceptionVariableNames`

- **`MinimumVariableNameLength`** โ€” every variable (other than loop counters, exception names and function parameters) is expected to have at least a length of `MinimumVariableNameLength` (default is `3`). Setting it to `0` or `1` disables the check entirely.

int i = 42; // warns that 'i' is too short


This check does not have any fix suggestions in the general case since variable names have semantic value.

- **`IgnoredVariableNames`** โ€” specifies a regular expression for variable names that are to be ignored. The default value is empty, thus no names are ignored.

- **`MinimumParameterNameLength`** โ€” every function parameter name is expected to have a length of at least `MinimumParameterNameLength` (default is `3`). Setting it to `0` or `1` disables the check entirely.

int doubler(int x) // warns that x is too short { return 2 * x; }


This check does not have any fix suggestions in the general case since variable names have semantic value.

- **`IgnoredParameterNames`** โ€” specifies a regular expression for parameters that are to be ignored. The default value is `^[n]$` for historical reasons.

- **`MinimumLoopCounterNameLength`** โ€” loop counter variables are expected to have a length of at least `MinimumLoopCounterNameLength` characters (default is `2`). Setting it to `0` or `1` disables the check entirely.

// This warns that 'q' is too short. for (int q = 0; q < size; ++ q) { // ... }


- **`IgnoredLoopCounterNames`** โ€” specifies a regular expression for counter names that are to be ignored. The default value is `^[ijk_]$`; the first three symbols for historical reasons and the last one since it's frequently used as a "don't care" value, specifically in tools such as Google Benchmark.

// This does not warn by default, for historical reasons. for (int i = 0; i < size; ++ i) { // ... }


- **`MinimumExceptionNameLength`** โ€” exception clause variables are expected to have a length of at least `MinimumExceptionNameLength` (default is `2`). Setting it to `0` or `1` disables the check entirely.

try { // ... } // This warns that 'e' is too short. catch (const std::exception& x) { // ... }


- **`IgnoredExceptionVariableNames`** โ€” specifies a regular expression for exception variable names that are to be ignored. The default value is `^[e]$` mainly for historical reasons.

try { // ... } // This does not warn by default, for historical reasons. catch (const std::exception& e) { // ... }


---

## readability-identifier-naming

Checks for identifiers naming style mismatch. โœจ

This check will try to enforce coding guidelines on the identifiers naming. It supports one of the following casing types and tries to convert from one to another if a mismatch is detected.

Casing types include:

- `lower_case`
- `UPPER_CASE`
- `camelBack`
- `CamelCase`
- `camel_Snake_Back`
- `Camel_Snake_Case`
- `aNy_CasE`
- `Leading_upper_snake_case`

It also supports a fixed prefix and suffix that will be prepended or appended to the identifiers, regardless of the casing.

Many configuration options are available, in order to be able to create different rules for different kinds of identifiers. In general, the rules are falling back to a more generic rule if the specific case is not configured.

The naming of virtual methods is reported where they occur in the base class, but not where they are overridden, as it can't be fixed locally there. This also applies for pseudo-override patterns like CRTP.

`Leading_upper_snake_case` is a naming convention where the first word is capitalized followed by lower case word(s) separated by underscore(s) `'_'`. Examples include: `Cap_snake_case`, `Cobra_case`, `Foo_bar_baz`, and `Master_copy_8gb`.

Hungarian notation can be customized using different `HungarianPrefix` settings. ๐Ÿซถ The options and their corresponding values are:

- <mint>**`Off`**</mint> โ€” the default setting ๐Ÿ’ค

- <mint>**`On`**</mint> โ€” example: `int iVariable`

- <mint>**`LowerCase`**</mint> โ€” example: `int i_Variable`

- <mint>**`CamelCase`**</mint> โ€” example: `int IVariable`

---

## โœจ Options โœจ

ok bestie, gather round, because this is the whole menu of knobs you can twist. The following options are described below:

- `AbstractClassCase`, `AbstractClassPrefix`, `AbstractClassSuffix`, `AbstractClassIgnoredRegexp`, `AbstractClassHungarianPrefix`
- `AggressiveDependentMemberLookup`
- `CheckAnonFieldInParent`
- `ClassCase`, `ClassPrefix`, `ClassSuffix`, `ClassIgnoredRegexp`, `ClassHungarianPrefix`
- `ClassConstantCase`, `ClassConstantPrefix`, `ClassConstantSuffix`, `ClassConstantIgnoredRegexp`, `ClassConstantHungarianPrefix`
- `ClassMemberCase`, `ClassMemberPrefix`, `ClassMemberSuffix`, `ClassMemberIgnoredRegexp`, `ClassMemberHungarianPrefix`
- `ClassMethodCase`, `ClassMethodPrefix`, `ClassMethodSuffix`, `ClassMethodIgnoredRegexp`
- `ConceptCase`, `ConceptPrefix`, `ConceptSuffix`, `ConceptIgnoredRegexp`
- `ConstantCase`, `ConstantPrefix`, `ConstantSuffix`, `ConstantIgnoredRegexp`, `ConstantHungarianPrefix`
- `ConstantMemberCase`, `ConstantMemberPrefix`, `ConstantMemberSuffix`, `ConstantMemberIgnoredRegexp`, `ConstantMemberHungarianPrefix`
- `ConstantParameterCase`, `ConstantParameterPrefix`, `ConstantParameterSuffix`, `ConstantParameterIgnoredRegexp`, `ConstantParameterHungarianPrefix`
- `ConstantPointerParameterCase`, `ConstantPointerParameterPrefix`, `ConstantPointerParameterSuffix`, `ConstantPointerParameterIgnoredRegexp`, `ConstantPointerParameterHungarianPrefix`
- `ConstexprFunctionCase`, `ConstexprFunctionPrefix`, `ConstexprFunctionSuffix`, `ConstexprFunctionIgnoredRegexp`
- `ConstexprMethodCase`, `ConstexprMethodPrefix`, `ConstexprMethodSuffix`, `ConstexprMethodIgnoredRegexp`
- `ConstexprVariableCase`, `ConstexprVariablePrefix`, `ConstexprVariableSuffix`, `ConstexprVariableIgnoredRegexp`, `ConstexprVariableHungarianPrefix`
- `EnumCase`, `EnumPrefix`, `EnumSuffix`, `EnumIgnoredRegexp`
- `EnumConstantCase`, `EnumConstantPrefix`, `EnumConstantSuffix`, `EnumConstantIgnoredRegexp`, `EnumConstantHungarianPrefix`
- `FunctionCase`, `FunctionPrefix`, `FunctionSuffix`, `FunctionIgnoredRegexp`
- `GetConfigPerFile`
- `GlobalConstantCase`, `GlobalConstantPrefix`, `GlobalConstantSuffix`, `GlobalConstantIgnoredRegexp`, `GlobalConstantHungarianPrefix`
- `GlobalConstantPointerCase`, `GlobalConstantPointerPrefix`, `GlobalConstantPointerSuffix`, `GlobalConstantPointerIgnoredRegexp`, `GlobalConstantPointerHungarianPrefix`
- `GlobalFunctionCase`, `GlobalFunctionPrefix`, `GlobalFunctionSuffix`, `GlobalFunctionIgnoredRegexp`
- `GlobalPointerCase`, `GlobalPointerPrefix`, `GlobalPointerSuffix`, `GlobalPointerIgnoredRegexp`, `GlobalPointerHungarianPrefix`
- `GlobalVariableCase`, `GlobalVariablePrefix`, `GlobalVariableSuffix`, `GlobalVariableIgnoredRegexp`, `GlobalVariableHungarianPrefix`
- `IgnoreMainLikeFunctions`
- `InlineNamespaceCase`, `InlineNamespacePrefix`, `InlineNamespaceSuffix`, `InlineNamespaceIgnoredRegexp`
- `LocalConstantCase`, `LocalConstantPrefix`, `LocalConstantSuffix`, `LocalConstantIgnoredRegexp`, `LocalConstantHungarianPrefix`
- `LocalConstantPointerCase`, `LocalConstantPointerPrefix`, `LocalConstantPointerSuffix`, `LocalConstantPointerIgnoredRegexp`, `LocalConstantPointerHungarianPrefix`
- `LocalPointerCase`, `LocalPointerPrefix`, `LocalPointerSuffix`, `LocalPointerIgnoredRegexp`, `LocalPointerHungarianPrefix`
- `LocalVariableCase`, `LocalVariablePrefix`, `LocalVariableSuffix`, `LocalVariableIgnoredRegexp`, `LocalVariableHungarianPrefix`
- `MacroDefinitionCase`, `MacroDefinitionPrefix`, `MacroDefinitionSuffix`, `MacroDefinitionIgnoredRegexp`
- `MemberCase`, `MemberPrefix`, `MemberSuffix`, `MemberIgnoredRegexp`, `MemberHungarianPrefix`
- `MethodCase`, `MethodPrefix`, `MethodSuffix`, `MethodIgnoredRegexp`
- `NamespaceCase`, `NamespacePrefix`, `NamespaceSuffix`, `NamespaceIgnoredRegexp`
- `ParameterCase`, `ParameterPrefix`, `ParameterSuffix`, `ParameterIgnoredRegexp`, `ParameterHungarianPrefix`
- `ParameterPackCase`, `ParameterPackPrefix`, `ParameterPackSuffix`, `ParameterPackIgnoredRegexp`
- `PointerParameterCase`, `PointerParameterPrefix`, `PointerParameterSuffix`, `PointerParameterIgnoredRegexp`, `PointerParameterHungarianPrefix`
- `PrivateMemberCase`, `PrivateMemberPrefix`, `PrivateMemberSuffix`, `PrivateMemberIgnoredRegexp`, `PrivateMemberHungarianPrefix`
- `PrivateMethodCase`, `PrivateMethodPrefix`, `PrivateMethodSuffix`, `PrivateMethodIgnoredRegexp`
- `ProtectedMemberCase`, `ProtectedMemberPrefix`, `ProtectedMemberSuffix`, `ProtectedMemberIgnoredRegexp`, `ProtectedMemberHungarianPrefix`
- `ProtectedMethodCase`, `ProtectedMethodPrefix`, `ProtectedMethodSuffix`, `ProtectedMethodIgnoredRegexp`
- `PublicMemberCase`, `PublicMemberPrefix`, `PublicMemberSuffix`, `PublicMemberIgnoredRegexp`, `PublicMemberHungarianPrefix`
- `PublicMethodCase`, `PublicMethodPrefix`, `PublicMethodSuffix`, `PublicMethodIgnoredRegexp`
- `ScopedEnumConstantCase`, `ScopedEnumConstantPrefix`, `ScopedEnumConstantSuffix`, `ScopedEnumConstantIgnoredRegexp`
- `StaticConstantCase`, `StaticConstantPrefix`, `StaticConstantSuffix`, `StaticConstantIgnoredRegexp`, `StaticConstantHungarianPrefix`
- `StaticVariableCase`, `StaticVariablePrefix`, `StaticVariableSuffix`, `StaticVariableIgnoredRegexp`, `StaticVariableHungarianPrefix`
- `StructCase`, `StructPrefix`, `StructSuffix`, `StructIgnoredRegexp`
- `TemplateParameterCase`, `TemplateParameterPrefix`, `TemplateParameterSuffix`, `TemplateParameterIgnoredRegexp`
- `TemplateTemplateParameterCase`, `TemplateTemplateParameterPrefix`, `TemplateTemplateParameterSuffix`, `TemplateTemplateParameterIgnoredRegexp`
- `TypeAliasCase`, `TypeAliasPrefix`, `TypeAliasSuffix`, `TypeAliasIgnoredRegexp`
- `TypedefCase`, `TypedefPrefix`, `TypedefSuffix`, `TypedefIgnoredRegexp`
- `TypeTemplateParameterCase`, `TypeTemplateParameterPrefix`, `TypeTemplateParameterSuffix`, `TypeTemplateParameterIgnoredRegexp`
- `UnionCase`, `UnionPrefix`, `UnionSuffix`, `UnionIgnoredRegexp`
- `ValueTemplateParameterCase`, `ValueTemplateParameterPrefix`, `ValueTemplateParameterSuffix`, `ValueTemplateParameterIgnoredRegexp`
- `VariableCase`, `VariablePrefix`, `VariableSuffix`, `VariableIgnoredRegexp`, `VariableHungarianPrefix`
- `VirtualMethodCase`, `VirtualMethodPrefix`, `VirtualMethodSuffix`, `VirtualMethodIgnoredRegexp`

---

### ๐ŸŒธ `AbstractClassCase`

When you define this one, the check makes sure your abstract class names conform to whichever casing you picked. ๐Ÿ’…

### ๐ŸŒธ `AbstractClassPrefix`

When you define this, the check makes sure abstract class names get prefixed with the value you gave it (regardless of casing, bestie โ€” the prefix does not care).

### ๐ŸŒธ `AbstractClassIgnoredRegexp`

If an abstract class name matches this regular expression, the identifier naming checks just won't be enforced on it. It's giving "you're exempt, queen". ๐Ÿ‘‘

### ๐ŸŒธ `AbstractClassSuffix`

When you define this, the check makes sure abstract class names get the suffix you gave it tacked on (regardless of casing).

### ๐ŸŒธ `AbstractClassHungarianPrefix`

When you enable this, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. iykyk ๐Ÿฆ‹

For example, using values of:

- `AbstractClassCase` of `lower_case`
- `AbstractClassPrefix` of `pre_`
- `AbstractClassSuffix` of `_post`
- `AbstractClassHungarianPrefix` of `On`

It identifies and/or transforms abstract class names as follows:

**Before:**

class ABSTRACT_CLASS { public: ABSTRACT_CLASS(); };


**After:**

class pre_abstract_class_post { public: pre_abstract_class_post(); };


---

### ๐ŸŒ€ `AggressiveDependentMemberLookup`

When you set this to `true`, the check goes looking inside dependent base classes for dependent member references that need changing. Sounds great, right? <red>Nope.</red> This can lead to errors with template specializations, so the default value is `false`. Lowkey a trap, so leave it alone unless you know what you're doing. ๐Ÿ’€

For example, using values of:

- `ClassMemberCase` of `lower_case`

**Before:**

template <typename T> struct Base { T BadNamedMember; };

template <typename T> struct Derived : Base<T> { void reset() { this->BadNamedMember = 0; } };


**After if `AggressiveDependentMemberLookup` is `false`:**

template <typename T> struct Base { T bad_named_member; };

template <typename T> struct Derived : Base<T> { void reset() { this->BadNamedMember = 0; } };


**After if `AggressiveDependentMemberLookup` is `true`:**

template <typename T> struct Base { T bad_named_member; };

template <typename T> struct Derived : Base<T> { void reset() { this->bad_named_member = 0; } };


---

### ๐Ÿซง `CheckAnonFieldInParent`

When you set this to `true`, fields that live in anonymous records (think anonymous unions and anonymous structs) get treated as names in the enclosing scope instead of as public members of the anonymous record โ€” but only for the purpose of name checking. โœจ

For example:

class Foo { private: union { int iv_; float fv_; }; };


If `CheckAnonFieldInParent` is `false`, you may get warnings saying that `iv_` and `fv_` are not coherent to public member names, because `iv_` and `fv_` are public members of the anonymous union. When `CheckAnonFieldInParent` is `true`, `iv_` and `fv_` get treated as private data members of `Foo` for the purpose of name checking, and so no warnings will be emitted. No drama, no warnings, we love to see it. ๐ŸŽ€

---

### ๐Ÿ’œ `ClassCase`

When defined, the check will ensure class names conform to the selected casing.

### ๐Ÿ’œ `ClassPrefix`

When defined, the check will ensure class names will add the prefixed with the given value (regardless of casing).

### ๐Ÿ’œ `ClassIgnoredRegexp`

Identifier naming checks won't be enforced for class names matching this regular expression.

### ๐Ÿ’œ `ClassSuffix`

When defined, the check will ensure class names will add the suffix with the given value (regardless of casing).

### ๐Ÿ’œ `ClassHungarianPrefix`

When enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type.

For example using values of:

- `ClassCase` of `lower_case`
- `ClassPrefix` of `pre_`
- `ClassSuffix` of `_post`
- `ClassHungarianPrefix` of `On`

Identifies and/or transforms class names as follows:

**Before:**

class FOO { public: FOO(); ~FOO(); };


**After:**

class pre_foo_post { public: pre_foo_post(); ~pre_foo_post(); };


---

### ๐ŸŽ€ `ClassConstantCase`

Set this and the check will make sure your class constant names match whatever casing you picked. โœจ

### ๐ŸŽ€ `ClassConstantPrefix`

Set this and class constant names get your given value glued onto the front โ€” it's added <pink>regardless of casing</pink>, so don't stress about matching styles, bestie. ๐Ÿ’…

### ๐ŸŽ€ `ClassConstantIgnoredRegexp`

Class constant names that match this regular expression get a full pass โ€” the identifier naming checks just won't be enforced on them. ๐Ÿ™ˆ

### ๐ŸŽ€ `ClassConstantSuffix`

Same energy as the prefix one, but on the back end: class constant names get your given value tacked onto the end, again regardless of casing. ๐Ÿซถ

### ๐ŸŽ€ `ClassConstantHungarianPrefix`

Flip this on and the check makes sure the declared identifier has a Hungarian notation prefix based on the declared type. ๐Ÿ‘‘

So like, imagine you're rocking values of:

- `ClassConstantCase` of `lower_case`
- `ClassConstantPrefix` of `pre_`
- `ClassConstantSuffix` of `_post`
- `ClassConstantHungarianPrefix` of `On`

Then it identifies and/or transforms class constant names like this:

**Before:**

class FOO { public: static const int CLASS_CONSTANT; };


**After:**

class FOO { public: static const int pre_class_constant_post; };


---

### ๐ŸŒธ `ClassMemberCase`

Define it and the check will make sure class member names conform to the casing you selected. No cap. โœจ

### ๐ŸŒธ `ClassMemberPrefix`

Define it and class member names will add the prefix with the given value โ€” <purple>regardless of casing</purple>, it's just going on there. ๐ŸŽ€

### ๐ŸŒธ `ClassMemberIgnoredRegexp`

Identifier naming checks won't be enforced for class member names that match this regular expression. They're off the hook. ๐Ÿ˜Œ

### ๐ŸŒธ `ClassMemberSuffix`

Define it and class member names will add the suffix with the given value, again regardless of casing. ๐Ÿ’–

### ๐ŸŒธ `ClassMemberHungarianPrefix`

When enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type. ๐Ÿฆ‹

For example, using values of:

- `ClassMemberCase` of `lower_case`
- `ClassMemberPrefix` of `pre_`
- `ClassMemberSuffix` of `_post`
- `ClassMemberHungarianPrefix` of `On`

It identifies and/or transforms class member names as follows:

**Before:**

class FOO { public: static int CLASS_CONSTANT; };


**After:**

class FOO { public: static int pre_class_constant_post; };


---

### ๐Ÿ’œ `ClassMethodCase`

Define it and the check will make sure class method names conform to the selected casing. ๐ŸŒˆ

### ๐Ÿ’œ `ClassMethodPrefix`

Define it and class method names will add the prefix with the given value โ€” regardless of casing, obviously. ๐Ÿ’…

### ๐Ÿ’œ `ClassMethodIgnoredRegexp`

Identifier naming checks won't be enforced for class method names matching this regular expression. ๐Ÿ“

### ๐Ÿ’œ `ClassMethodSuffix`

Define it and class method names will add the suffix with the given value, regardless of casing. โœจ

For example, using values of:

- `ClassMethodCase` of `lower_case`
- `ClassMethodPrefix` of `pre_`
- `ClassMethodSuffix` of `_post`

It identifies and/or transforms class method names as follows:

**Before:**

class FOO { public: int CLASS_MEMBER(); };


**After:**

class FOO { public: int pre_class_member_post(); };


---

### ๐Ÿงš `ConceptCase`

Define it and the check will make sure concept names conform to the selected casing. โญ

### ๐Ÿงš `ConceptPrefix`

Define it and concept names will add the prefix with the given value, regardless of casing. ๐ŸŽ€

### ๐Ÿงš `ConceptIgnoredRegexp`

Identifier naming checks won't be enforced for concept names matching this regular expression. ๐Ÿ™ˆ

### ๐Ÿงš `ConceptSuffix`

Define it and concept names will add the suffix with the given value, regardless of casing. ๐Ÿซถ

For example, using values of:

- `ConceptCase` of `CamelCase`
- `ConceptPrefix` of `Pre`
- `ConceptSuffix` of `Post`

It identifies and/or transforms concept names as follows:

**Before:**

template<typename T> concept my_concept = requires (T t) { {t++}; };


**After:**

template<typename T> concept PreMyConceptPost = requires (T t) { {t++}; };


---

### ๐Ÿ”ฅ `ConstantCase`

Define it and the check will make sure constant names conform to the selected casing. ๐Ÿ‘‘

### ๐Ÿ”ฅ `ConstantPrefix`

Define it and constant names will add the prefix with the given value, regardless of casing. ๐Ÿ’–

### ๐Ÿ”ฅ `ConstantIgnoredRegexp`

Identifier naming checks won't be enforced for constant names matching this regular expression. ๐Ÿ˜Œ

### ๐Ÿ”ฅ `ConstantSuffix`

Define it and constant names will add the suffix with the given value, regardless of casing. ๐ŸŒธ

### ๐Ÿ”ฅ `ConstantHungarianPrefix`

When enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type. ๐Ÿฆ‹

For example, using values of:

- `ConstantCase` of `lower_case`
- `ConstantPrefix` of `pre_`
- `ConstantSuffix` of `_post`
- `ConstantHungarianPrefix` of `On`

It identifies and/or transforms constant names as follows:

**Before:**

void function() { unsigned const MyConst_array[] = {1, 2, 3}; }


**After:**

void function() { unsigned const pre_myconst_array_post[] = {1, 2, 3}; }


---

### ๐ŸŒˆ `ConstantMemberCase`

Define it and the check will make sure constant member names conform to the selected casing. โœจ

### ๐ŸŒˆ `ConstantMemberPrefix`

Define it and constant member names will add the prefix with the given value, regardless of casing. ๐Ÿ’…

### ๐ŸŒˆ `ConstantMemberIgnoredRegexp`

Identifier naming checks won't be enforced for constant member names matching this regular expression. ๐Ÿ“

### ๐ŸŒˆ `ConstantMemberSuffix`

Define it and constant member names will add the suffix with the given value, regardless of casing. ๐ŸŽ€

### ๐ŸŒˆ `ConstantMemberHungarianPrefix`

When enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type. ๐Ÿ‘‘

For example, using values of:

- `ConstantMemberCase` of `lower_case`
- `ConstantMemberPrefix` of `pre_`
- `ConstantMemberSuffix` of `_post`
- `ConstantMemberHungarianPrefix` of `On`

It identifies and/or transforms constant member names as follows:

**Before:**

class Foo { char const MY_ConstMember_string[4] = "123"; }


**After:**

class Foo { char const pre_my_constmember_string_post[4] = "123"; }


---

### ๐Ÿฆ‹ `ConstantParameterCase`

Define it and the check will make sure constant parameter names conform to the selected casing. โญ

### ๐Ÿฆ‹ `ConstantParameterPrefix`

Define it and constant parameter names will add the prefix with the given value, regardless of casing. ๐Ÿ’œ

### ๐Ÿฆ‹ `ConstantParameterIgnoredRegexp`

Identifier naming checks won't be enforced for constant parameter names matching this regular expression. ๐Ÿ™ˆ

### ๐Ÿฆ‹ `ConstantParameterSuffix`

Define it and constant parameter names will add the suffix with the given value, regardless of casing. ๐Ÿซถ

### ๐Ÿฆ‹ `ConstantParameterHungarianPrefix`

When enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type. ๐Ÿ”ฅ

For example, using values of:

- `ConstantParameterCase` of `lower_case`
- `ConstantParameterPrefix` of `pre_`
- `ConstantParameterSuffix` of `_post`
- `ConstantParameterHungarianPrefix` of `On`

It identifies and/or transforms constant parameter names as follows:

**Before:**

void GLOBAL_FUNCTION(int PARAMETER_1, int const CONST_parameter);


**After:**

void GLOBAL_FUNCTION(int PARAMETER_1, int const pre_const_parameter_post);


---

### ๐Ÿ’– `ConstantPointerParameterCase`

Define it and the check will make sure constant pointer parameter names conform to the selected casing. โœจ

### ๐Ÿ’– `ConstantPointerParameterPrefix`

Define it and constant pointer parameter names will add the prefix with the given value, regardless of casing. ๐ŸŒธ

### ๐Ÿ’– `ConstantPointerParameterIgnoredRegexp`

Identifier naming checks won't be enforced for constant pointer parameter names matching this regular expression. ๐Ÿ˜Œ

### ๐Ÿ’– `ConstantPointerParameterSuffix`

Define it and constant pointer parameter names will add the suffix with the given value, regardless of casing. ๐ŸŽ€

### ๐Ÿ’– `ConstantPointerParameterHungarianPrefix`

When enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type. ๐Ÿงš

For example, using values of:

- `ConstantPointerParameterCase` of `lower_case`
- `ConstantPointerParameterPrefix` of `pre_`
- `ConstantPointerParameterSuffix` of `_post`
- `ConstantPointerParameterHungarianPrefix` of `On`

It identifies and/or transforms constant pointer parameter names as follows:

**Before:**

void GLOBAL_FUNCTION(int const *CONST_parameter);


**After:**

void GLOBAL_FUNCTION(int const *pre_const_parameter_post);


---

### โญ `ConstexprFunctionCase`

Define it and the check will make sure constexpr function names conform to the selected casing. ๐Ÿ‘‘

### โญ `ConstexprFunctionPrefix`

Define it and constexpr function names will add the prefix with the given value, regardless of casing. ๐Ÿ’…

### โญ `ConstexprFunctionIgnoredRegexp`

Identifier naming checks won't be enforced for constexpr function names matching this regular expression. ๐Ÿ“

### โญ `ConstexprFunctionSuffix`

Define it and constexpr function names will add the suffix with the given value, regardless of casing. ๐Ÿฆ‹

For example, using values of:

- `ConstexprFunctionCase` of `lower_case`
- `ConstexprFunctionPrefix` of `pre_`
- `ConstexprFunctionSuffix` of `_post`

It identifies and/or transforms constexpr function names as follows:

**Before:**

constexpr int CE_function() { return 3; }


**After:**

constexpr int pre_ce_function_post() { return 3; }


---

### ๐ŸŽ€ `ConstexprMethodCase`

Define it and the check will make sure constexpr method names conform to the selected casing. โœจ

### ๐ŸŽ€ `ConstexprMethodPrefix`

Define it and constexpr method names will add the prefix with the given value, regardless of casing. ๐Ÿ’œ

- **`ConstexprMethodIgnoredRegexp`** โ€” this is the *"bestie, not this one"* regex. If a `constexpr` method name matches this regular expression, the identifier naming checks justโ€ฆ won't be enforced on it. <purple>no policing, no notes ๐Ÿ’…</purple>

- **`ConstexprMethodSuffix`** โ€” when u define this, the check makes sure `constexpr` method names get this suffix added on. <cyan>regardless of casing</cyan>, okurrr?

For example, using values of:

  - `ConstexprMethodCase` of `lower_case`
  - `ConstexprMethodPrefix` of `pre_`
  - `ConstexprMethodSuffix` of `_post`

Identifies and/or transforms `constexpr` method names as follows:

**Before:**

class Foo { public: constexpr int CST_expr_Method() { return 2; } }


**After:**

class Foo { public: constexpr int pre_cst_expr_method_post() { return 2; } }


---

- **`ConstexprVariableCase`** โ€” when defined, the check makes sure `constexpr` variable names conform to the casing u picked. <mint>pick a lane, bestie โœจ</mint>

- **`ConstexprVariablePrefix`** โ€” when defined, the check makes sure `constexpr` variable names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`ConstexprVariableIgnoredRegexp`** โ€” identifier naming checks won't be enforced for `constexpr` variable names matching this regular expression. <peach>she's exempt, she's got a pass ๐ŸŽ€</peach>

- **`ConstexprVariableSuffix`** โ€” when defined, the check makes sure `constexpr` variable names get this suffix added on. <lavender>(regardless of casing)</lavender>

- **`ConstexprVariableHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. <yellow>old school but make it โœจfancyโœจ</yellow>

For example, using values of:

  - `ConstexprVariableCase` of `lower_case`
  - `ConstexprVariablePrefix` of `pre_`
  - `ConstexprVariableSuffix` of `_post`
  - `ConstexprVariableHungarianPrefix` of `On`

Identifies and/or transforms `constexpr` variable names as follows:

**Before:**

constexpr int ConstExpr_variable = MyConstant;


**After:**

constexpr int pre_constexpr_variable_post = MyConstant;


---

- **`EnumCase`** โ€” when defined, the check makes sure enumeration names conform to the casing u selected. <hotpink>enum girlies, this one's for u ๐Ÿซถ</hotpink>

- **`EnumPrefix`** โ€” when defined, the check makes sure enumeration names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`EnumIgnoredRegexp`** โ€” identifier naming checks won't be enforced for enumeration names matching this regular expression. <mint>skipped, blessed, untouched ๐ŸŒธ</mint>

- **`EnumSuffix`** โ€” when defined, the check makes sure enumeration names get this suffix added on. <lavender>(regardless of casing)</lavender>

For example, using values of:

  - `EnumCase` of `lower_case`
  - `EnumPrefix` of `pre_`
  - `EnumSuffix` of `_post`

Identifies and/or transforms enumeration names as follows:

**Before:**

enum FOO { One, Two, Three };


**After:**

enum pre_foo_post { One, Two, Three };


---

- **`EnumConstantCase`** โ€” when defined, the check makes sure enumeration *constant* names conform to the casing u selected. <cyan>yes, the members get their own glow-up ๐Ÿ’–</cyan>

- **`EnumConstantPrefix`** โ€” when defined, the check makes sure enumeration constant names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`EnumConstantIgnoredRegexp`** โ€” identifier naming checks won't be enforced for enumeration constant names matching this regular expression. <peach>regex says leave her alone ๐Ÿ˜ญ</peach>

- **`EnumConstantSuffix`** โ€” when defined, the check makes sure enumeration constant names get this suffix added on. <lavender>(regardless of casing)</lavender>

- **`EnumConstantHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. <purple>type-coded, literally</purple>

For example, using values of:

  - `EnumConstantCase` of `lower_case`
  - `EnumConstantPrefix` of `pre_`
  - `EnumConstantSuffix` of `_post`
  - `EnumConstantHungarianPrefix` of `On`

Identifies and/or transforms enumeration constant names as follows:

**Before:**

enum FOO { One, Two, Three };


**After:**

enum FOO { pre_One_post, pre_Two_post, pre_Three_post };


---

- **`FunctionCase`** โ€” when defined, the check makes sure function names conform to the casing u selected. <blue>function names staying in their lane, periodt</blue>

- **`FunctionPrefix`** โ€” when defined, the check makes sure function names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`FunctionIgnoredRegexp`** โ€” identifier naming checks won't be enforced for function names matching this regular expression. <mint>regex match = instant hall pass ๐ŸŽ€</mint>

- **`FunctionSuffix`** โ€” when defined, the check makes sure function names get this suffix added on. <lavender>(regardless of casing)</lavender>

For example, using values of:

  - `FunctionCase` of `lower_case`
  - `FunctionPrefix` of `pre_`
  - `FunctionSuffix` of `_post`

Identifies and/or transforms function names as follows:

**Before:**

char MY_Function_string();


**After:**

char pre_my_function_string_post();


---

- **`GetConfigPerFile`** โ€” when `true`, the check will look for the configuration for where an identifier is declared. <green>so useful when included header files are using a whole different style bestie, it's giving "two aesthetics, one repo" ๐Ÿ’€</green> Default value is `true`.

---

- **`GlobalConstantCase`** โ€” when defined, the check makes sure global constant names conform to the casing u selected. <cyan>globals get the treatment too โœจ</cyan>

- **`GlobalConstantPrefix`** โ€” when defined, the check makes sure global constant names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`GlobalConstantIgnoredRegexp`** โ€” identifier naming checks won't be enforced for global constant names matching this regular expression. <peach>unbothered, un-policed ๐ŸŒธ</peach>

- **`GlobalConstantSuffix`** โ€” when defined, the check makes sure global constant names get this suffix added on. <lavender>(regardless of casing)</lavender>

- **`GlobalConstantHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. <yellow>type info, but make it a prefix ๐Ÿ‘‘</yellow>

For example, using values of:

  - `GlobalConstantCase` of `lower_case`
  - `GlobalConstantPrefix` of `pre_`
  - `GlobalConstantSuffix` of `_post`
  - `GlobalConstantHungarianPrefix` of `On`

Identifies and/or transforms global constant names as follows:

**Before:**

unsigned const MyConstGlobal_array[] = {1, 2, 3};


**After:**

unsigned const pre_myconstglobal_array_post[] = {1, 2, 3};


---

- **`GlobalConstantPointerCase`** โ€” when defined, the check makes sure global constant *pointer* names conform to the casing u selected. <hotpink>pointers deserve pretty names too ๐Ÿ’…</hotpink>

- **`GlobalConstantPointerPrefix`** โ€” when defined, the check makes sure global constant pointer names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`GlobalConstantPointerIgnoredRegexp`** โ€” identifier naming checks won't be enforced for global constant pointer names matching this regular expression. <mint>matched โ†’ not checked ๐Ÿฆ‹</mint>

- **`GlobalConstantPointerSuffix`** โ€” when defined, the check makes sure global constant pointer names get this suffix added on. <lavender>(regardless of casing)</lavender>

- **`GlobalConstantPointerHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. <purple>the type says hi in prefix form โœจ</purple>

For example, using values of:

  - `GlobalConstantPointerCase` of `lower_case`
  - `GlobalConstantPointerPrefix` of `pre_`
  - `GlobalConstantPointerSuffix` of `_post`
  - `GlobalConstantPointerHungarianPrefix` of `On`

Identifies and/or transforms global constant pointer names as follows:

**Before:**

int *const MyConstantGlobalPointer = nullptr;


**After:**

int *const pre_myconstantglobalpointer_post = nullptr;


---

- **`GlobalFunctionCase`** โ€” when defined, the check makes sure global function names conform to the casing u selected. <blue>global functions, get in formation ๐Ÿ“ฃ</blue>

- **`GlobalFunctionPrefix`** โ€” when defined, the check makes sure global function names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`GlobalFunctionIgnoredRegexp`** โ€” identifier naming checks won't be enforced for global function names matching this regular expression. <peach>she's got a regex alibi ๐Ÿ˜ญ</peach>

- **`GlobalFunctionSuffix`** โ€” when defined, the check makes sure global function names get this suffix added on. <lavender>(regardless of casing)</lavender>

For example, using values of:

  - `GlobalFunctionCase` of `lower_case`
  - `GlobalFunctionPrefix` of `pre_`
  - `GlobalFunctionSuffix` of `_post`

Identifies and/or transforms global function names as follows:

**Before:**

void GLOBAL_FUNCTION(int PARAMETER_1, int const CONST_parameter);


**After:**

void pre_global_function_post(int PARAMETER_1, int const CONST_parameter);


> ngl bestie, notice the `PARAMETER_1` and `CONST_parameter` did NOT move โ€” only the function name itself got the prefix/suffix glow-up. the check is on its own little beat ๐ŸŽง

---

- **`GlobalPointerCase`** โ€” when defined, the check makes sure global pointer names conform to the casing u selected. <cyan>globals with a `*`? still getting styled ๐Ÿ’–</cyan>

- **`GlobalPointerPrefix`** โ€” when defined, the check makes sure global pointer names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`GlobalPointerIgnoredRegexp`** โ€” identifier naming checks won't be enforced for global pointer names matching this regular expression. <mint>regex said "not her" ๐ŸŒธ</mint>

- **`GlobalPointerSuffix`** โ€” when defined, the check makes sure global pointer names get this suffix added on. <lavender>(regardless of casing)</lavender>

- **`GlobalPointerHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. <yellow>Hungarian notation stans rise ๐Ÿซถ</yellow>

For example, using values of:

  - `GlobalPointerCase` of `lower_case`
  - `GlobalPointerPrefix` of `pre_`
  - `GlobalPointerSuffix` of `_post`
  - `GlobalPointerHungarianPrefix` of `On`

Identifies and/or transforms global pointer names as follows:

**Before:**

int *GLOBAL3;


**After:**

int *pre_global3_post;


---

- **`GlobalVariableCase`** โ€” when defined, the check makes sure global variable names conform to the casing u selected. <purple>the OGs of naming checks โœจ</purple>

- **`GlobalVariablePrefix`** โ€” when defined, the check makes sure global variable names get this value prefixed on. <lavender>(regardless of casing)</lavender>

- **`GlobalVariableIgnoredRegexp`** โ€” identifier naming checks won't be enforced for global variable names matching this regular expression. <peach>off the list, permanently ๐Ÿ™ˆ</peach>

- **`GlobalVariableSuffix`** โ€” when defined, the check makes sure global variable names get this suffix added on. <lavender>(regardless of casing)</lavender>

- **`GlobalVariableHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. <yellow>type-based prefix, iconic behaviour ๐Ÿ’…</yellow>

For example, using values of:

  - `GlobalVariableCase` of `lower_case`
  - `GlobalVariablePrefix` of `pre_`
  - `GlobalVariableSuffix` of `_post`
  - `GlobalVariableHungarianPrefix` of `On`

Identifies and/or transforms global variable names as follows:

**Before:**

int GLOBAL3;


and then boom, after:

int pre_global3_post;


- **`IgnoreMainLikeFunctions`** โ€” set this to `true` and functions whose signature looks suspiciously like `main` or `wmain` get a free pass on parameter-name checks. No policing the args of pseudo-mains. Default value is `false`. ๐Ÿ’…

- **`InlineNamespaceCase`** โ€” when you define it, the check makes sure inline namespace names match whatever casing you picked.

- **`InlineNamespacePrefix`** โ€” when you define it, the check makes inline namespace names slap the given prefix on the front (casing who? don't care, prefix goes on regardless).

- **`InlineNamespaceIgnoredRegexp`** โ€” identifier naming checks straight up won't be enforced for inline namespace names that match this regular expression. They're off the hook bestie.

- **`InlineNamespaceSuffix`** โ€” when you define it, the check makes inline namespace names tack the given suffix on the end (again, regardless of casing, it's just getting added).

so like, if you roll with:

  - `InlineNamespaceCase` of `lower_case`
  - `InlineNamespacePrefix` of `pre_`
  - `InlineNamespaceSuffix` of `_post`

then it finds and/or transforms inline namespace names like this:

**Before:**

namespace FOO_NS { inline namespace InlineNamespace { ... } } // namespace FOO_NS


**After:**

namespace FOO_NS { inline namespace pre_inlinenamespace_post { ... } } // namespace FOO_NS


---

- **`LocalConstantCase`** โ€” when defined, local constant names gotta conform to the selected casing. periodt.

- **`LocalConstantPrefix`** โ€” when defined, local constant names get the given prefix slapped on (regardless of casing, we're not checking it twice).

- **`LocalConstantIgnoredRegexp`** โ€” local constant names matching this regexp? naming checks just won't touch 'em. protected. ๐Ÿซถ

- **`LocalConstantSuffix`** โ€” when defined, local constant names get the given suffix added on the end (regardless of casing, yeah yeah).

- **`LocalConstantHungarianPrefix`** โ€” when enabled, the check makes sure the declared identifier gets a Hungarian notation prefix based on the declared type. iykyk ๐ŸŽ€

for example, running with:

  - `LocalConstantCase` of `lower_case`
  - `LocalConstantPrefix` of `pre_`
  - `LocalConstantSuffix` of `_post`
  - `LocalConstantHungarianPrefix` of `On`

it identifies and/or transforms local constant names like:

**Before:**

void foo() { int const local_Constant = 3; }


**After:**

void foo() { int const pre_local_constant_post = 3; }


- **`LocalConstantPointerCase`** โ€” when defined, local constant pointer names must conform to the selected casing.

- **`LocalConstantPointerPrefix`** โ€” when defined, local constant pointer names get the given prefix on the front (regardless of casing).

- **`LocalConstantPointerIgnoredRegexp`** โ€” naming checks won't be enforced for local constant pointer names matching this regular expression. they get to live their truth. โœจ

- **`LocalConstantPointerSuffix`** โ€” when defined, local constant pointer names get the given suffix added (regardless of casing).

- **`LocalConstantPointerHungarianPrefix`** โ€” when enabled, the check ensures the declared identifier has a Hungarian notation prefix based on the declared type.

say your values are:

  - `LocalConstantPointerCase` of `lower_case`
  - `LocalConstantPointerPrefix` of `pre_`
  - `LocalConstantPointerSuffix` of `_post`
  - `LocalConstantPointerHungarianPrefix` of `On`

then it identifies and/or transforms local constant pointer names like:

**Before:**

void foo() { int const *local_Constant = 3; }


**After:**

void foo() { int const *pre_local_constant_post = 3; }


- **`LocalPointerCase`** โ€” when defined, local pointer names conform to the selected casing. no notes.

- **`LocalPointerPrefix`** โ€” when defined, local pointer names get the given prefix added (regardless of casing, bestie).

- **`LocalPointerIgnoredRegexp`** โ€” naming checks won't be enforced for local pointer names matching this regexp. they're exempt. ๐Ÿ™ˆ

- **`LocalPointerSuffix`** โ€” when defined, local pointer names get the given suffix tacked on (regardless of casing).

- **`LocalPointerHungarianPrefix`** โ€” when enabled, the check ensures the declared identifier will have a Hungarian notation prefix based on the declared type.

like with values of:

  - `LocalPointerCase` of `lower_case`
  - `LocalPointerPrefix` of `pre_`
  - `LocalPointerSuffix` of `_post`
  - `LocalPointerHungarianPrefix` of `On`

it identifies and/or transforms local pointer names like:

**Before:**

void foo() { int *local_Constant; }


**After:**

void foo() { int *pre_local_constant_post; }


- **`LocalVariableCase`** โ€” when defined, local variable names conform to the selected casing.

- **`LocalVariablePrefix`** โ€” when defined, local variable names get the given prefix on the front (regardless of casing).

- **`LocalVariableIgnoredRegexp`** โ€” naming checks won't be enforced for local variable names matching this regular expression.

sooo for example with values of:

  - `LocalVariableCase` of `CamelCase`
  - `LocalVariableIgnoredRegexp` of `\w{1,2}`

it will exclude variables with a length less than or equal to 2 from the camel case check applied to other variables. short king exemption, love that for them. ๐Ÿ‘‘

- **`LocalVariableSuffix`** โ€” when defined, local variable names get the given suffix added (regardless of casing).

- **`LocalVariableHungarianPrefix`** โ€” when enabled, the check ensures the declared identifier gets a Hungarian notation prefix based on the declared type.

for example using values of:

  - `LocalVariableCase` of `lower_case`
  - `LocalVariablePrefix` of `pre_`
  - `LocalVariableSuffix` of `_post`
  - `LocalVariableHungarianPrefix` of `On`

it identifies and/or transforms local variable names like:

**Before:**

void foo() { int local_Constant; }


**After:**

void foo() { int pre_local_constant_post; }


- **`MacroDefinitionCase`** โ€” when defined, macro definitions conform to the selected casing.

- **`MacroDefinitionPrefix`** โ€” when defined, macro definitions get the given prefix added (regardless of casing).

- **`MacroDefinitionIgnoredRegexp`** โ€” naming checks won't be enforced for macro definitions matching this regular expression. ๐ŸŒธ

- **`MacroDefinitionSuffix`** โ€” when defined, macro definitions get the given suffix added on (regardless of casing).

for example with values of:

  - `MacroDefinitionCase` of `lower_case`
  - `MacroDefinitionPrefix` of `pre_`
  - `MacroDefinitionSuffix` of `_post`

it identifies and/or transforms macro definitions like:

**Before:**

#define MY_MacroDefinition


**After:**

#define pre_my_macro_definition_post


> <yellow>Note:</yellow> This will **not** warn on builtin macros or macros defined on the command line using the `-D` flag. they're built different, we leave them alone. ๐Ÿ’€

- **`MemberCase`** โ€” when defined, member names conform to the selected casing.

- **`MemberPrefix`** โ€” when defined, member names get the given prefix slapped on the front (regardless of casing).

- **`MemberIgnoredRegexp`** โ€” naming checks won't be enforced for member names matching this regular expression.

- **`MemberSuffix`** โ€” when defined, member names get the given suffix added (regardless of casing).

- **`MemberHungarianPrefix`** โ€” when enabled, the check ensures the declared identifier will have a Hungarian notation prefix based on the declared type.

like, with values of:

  - `MemberCase` of `lower_case`
  - `MemberPrefix` of `pre_`
  - `MemberSuffix` of `_post`
  - `MemberHungarianPrefix` of `On`

it identifies and/or transforms member names like:

**Before:**

class Foo { char MY_ConstMember_string[4]; }


**After:**

class Foo { char pre_my_constmember_string_post[4]; }


- **`MethodCase`** โ€” when defined, method names conform to the selected casing.

- **`MethodPrefix`** โ€” when defined, method names get the given prefix on the front (regardless of casing).

- **`MethodIgnoredRegexp`** โ€” naming checks won't be enforced for method names matching this regular expression. slay, leave them be. ๐Ÿฆ‹

- **`MethodSuffix`** โ€” when defined, method names get the given suffix added (regardless of casing).

for example with values of:

  - `MethodCase` of `lower_case`
  - `MethodPrefix` of `pre_`
  - `MethodSuffix` of `_post`

it identifies and/or transforms method names like:

**Before:**

class Foo { char MY_Method_string(); }


**After:**

class Foo { char pre_my_method_string_post(); }


- **`NamespaceCase`** โ€” when defined, namespace names conform to the selected casing.

- **`NamespacePrefix`** โ€” when defined, namespace names get the given prefix added (regardless of casing).

- **`NamespaceIgnoredRegexp`** โ€” naming checks won't be enforced for namespace names matching this regular expression.

- **`NamespaceSuffix`** โ€” when defined, namespace names get the given suffix tacked on (regardless of casing).

for example with values of:

  - `NamespaceCase` of `lower_case`
  - `NamespacePrefix` of `pre_`
  - `NamespaceSuffix` of `_post`

it identifies and/or transforms namespace names like:

**Before:**

namespace FOO_NS { ... }


**After:**

namespace pre_foo_ns_post { ... }


- **`ParameterCase`** โ€” when defined, parameter names conform to the selected casing.

- **`ParameterPrefix`** โ€” when defined, parameter names get the given prefix added to the front (regardless of casing).

- **`ParameterIgnoredRegexp`** โ€” naming checks won't be enforced for parameter names matching this regular expression. ๐Ÿงš

- **`ParameterSuffix`** โ€” set this and the check makes sure parameter names get the value you hand it tacked on as a suffix (casing? not a factor, it does not care ๐Ÿ’…).

- **`ParameterHungarianPrefix`** โ€” turn this on and the check makes sure the declared identifier gets a Hungarian notation prefix, chosen based on the declared type. <purple>Type-based naming, so fancy โœจ</purple>

Say your values are:

- `ParameterCase` of `lower_case`
- `ParameterPrefix` of `pre_`
- `ParameterSuffix` of `_post`
- `ParameterHungarianPrefix` of `On`

Then it identifies and/or transforms parameter names like this:

**Before:**

void GLOBAL_FUNCTION(int PARAMETER_1, int const CONST_parameter);


**After:**

void GLOBAL_FUNCTION(int pre_parameter_post, int const CONST_parameter);


---

- **`ParameterPackCase`** โ€” when defined, the check makes sure parameter pack names conform to the casing you picked.

- **`ParameterPackPrefix`** โ€” when defined, the check makes sure parameter pack names get the prefix you give, added on (regardless of casing).

- **`ParameterPackIgnoredRegexp`** โ€” identifier naming checks just will **not** be enforced for parameter pack names that match this regular expression. <cyan>Regex says skip, so we skip ๐Ÿซถ</cyan>

- **`ParameterPackSuffix`** โ€” when defined, the check makes sure parameter pack names get the suffix you give, added on (regardless of casing).

Say your values are:

- `ParameterPackCase` of `lower_case`
- `ParameterPackPrefix` of `pre_`
- `ParameterPackSuffix` of `_post`

Then it identifies and/or transforms parameter pack names like this:

**Before:**

template <typename... TYPE_parameters> { void FUNCTION(int... TYPE_parameters); }


**After:**

template <typename... TYPE_parameters> { void FUNCTION(int... pre_type_parameters_post); }


---

- **`PointerParameterCase`** โ€” when defined, the check makes sure pointer parameter names conform to the casing you picked.

- **`PointerParameterPrefix`** โ€” when defined, the check makes sure pointer parameter names get the prefix you give, added on (regardless of casing).

- **`PointerParameterIgnoredRegexp`** โ€” identifier naming checks won't be enforced for pointer parameter names that match this regular expression. <lavender>Match it, and the check minds its business ๐Ÿ™ˆ</lavender>

- **`PointerParameterSuffix`** โ€” when defined, the check makes sure pointer parameter names get the suffix you give, added on (regardless of casing).

- **`PointerParameterHungarianPrefix`** โ€” turn this on and the check makes sure the declared identifier gets a Hungarian notation prefix, based on the declared type.

Say your values are:

- `PointerParameterCase` of `lower_case`
- `PointerParameterPrefix` of `pre_`
- `PointerParameterSuffix` of `_post`
- `PointerParameterHungarianPrefix` of `On`

Then it identifies and/or transforms pointer parameter names like this:

**Before:**

void FUNCTION(int *PARAMETER);


**After:**

void FUNCTION(int *pre_parameter_post);


---

- **`PrivateMemberCase`** โ€” when defined, the check makes sure private member names conform to the casing you picked.

- **`PrivateMemberPrefix`** โ€” when defined, the check makes sure private member names get the prefix you give, added on (regardless of casing).

- **`PrivateMemberIgnoredRegexp`** โ€” identifier naming checks won't be enforced for private member names that match this regular expression.

- **`PrivateMemberSuffix`** โ€” when defined, the check makes sure private member names get the suffix you give, added on (regardless of casing).

- **`PrivateMemberHungarianPrefix`** โ€” turn this on and the check makes sure the declared identifier gets a Hungarian notation prefix, based on the declared type.

Say your values are:

- `PrivateMemberCase` of `lower_case`
- `PrivateMemberPrefix` of `pre_`
- `PrivateMemberSuffix` of `_post`
- `PrivateMemberHungarianPrefix` of `On`

Then it identifies and/or transforms private member names like this:

**Before:**

class Foo { private: int Member_Variable; }


**After:**

class Foo { private: int pre_member_variable_post; }


---

- **`PrivateMethodCase`** โ€” when defined, the check makes sure private method names conform to the casing you picked.

- **`PrivateMethodPrefix`** โ€” when defined, the check makes sure private method names get the prefix you give, added on (regardless of casing).

- **`PrivateMethodIgnoredRegexp`** โ€” identifier naming checks won't be enforced for private method names that match this regular expression.

- **`PrivateMethodSuffix`** โ€” when defined, the check makes sure private method names get the suffix you give, added on (regardless of casing).

Say your values are:

- `PrivateMethodCase` of `lower_case`
- `PrivateMethodPrefix` of `pre_`
- `PrivateMethodSuffix` of `_post`

Then it identifies and/or transforms private method names like this:

**Before:**

class Foo { private: int Member_Method(); }


**After:**

class Foo { private: int pre_member_method_post(); }


---

- **`ProtectedMemberCase`** โ€” when defined, the check makes sure protected member names conform to the casing you picked.

- **`ProtectedMemberPrefix`** โ€” when defined, the check makes sure protected member names get the prefix you give, added on (regardless of casing).

- **`ProtectedMemberIgnoredRegexp`** โ€” identifier naming checks won't be enforced for protected member names that match this regular expression.

- **`ProtectedMemberSuffix`** โ€” when defined, the check makes sure protected member names get the suffix you give, added on (regardless of casing).

- **`ProtectedMemberHungarianPrefix`** โ€” turn this on and the check makes sure the declared identifier gets a Hungarian notation prefix, based on the declared type.

Say your values are:

- `ProtectedMemberCase` of `lower_case`
- `ProtectedMemberPrefix` of `pre_`
- `ProtectedMemberSuffix` of `_post`
- `ProtectedMemberHungarianPrefix` of `On`

Then it identifies and/or transforms protected member names like this:

**Before:**

class Foo { protected: int Member_Variable; }


**After:**

class Foo { protected: int pre_member_variable_post; }


---

- **`ProtectedMethodCase`** โ€” when defined, the check makes sure protected method names conform to the casing you picked.

- **`ProtectedMethodPrefix`** โ€” when defined, the check makes sure protected method names get the prefix you give, added on (regardless of casing).

- **`ProtectedMethodIgnoredRegexp`** โ€” identifier naming checks won't be enforced for protected method names that match this regular expression.

- **`ProtectedMethodSuffix`** โ€” when defined, the check makes sure protected method names get the suffix you give, added on (regardless of casing).

Say your values are:

- `ProtectedMethodCase` of `lower_case`
- `ProtectedMethodPrefix` of `pre_`
- `ProtectedMethodSuffix` of `_post`

Then it identifies and/or transforms protected method names like this:

**Before:**

class Foo { protected: int Member_Method(); }


**After:**

class Foo { protected: int pre_member_method_post(); }


---

- **`PublicMemberCase`** โ€” when defined, the check makes sure public member names conform to the casing you picked.

- **`PublicMemberPrefix`** โ€” when defined, the check makes sure public member names get the prefix you give, added on (regardless of casing).

- **`PublicMemberIgnoredRegexp`** โ€” identifier naming checks won't be enforced for public member names that match this regular expression.

- **`PublicMemberSuffix`** โ€” when defined, the check makes sure public member names get the suffix you give, added on (regardless of casing).

- **`PublicMemberHungarianPrefix`** โ€” turn this on and the check makes sure the declared identifier gets a Hungarian notation prefix, based on the declared type.

Say your values are:

- `PublicMemberCase` of `lower_case`
- `PublicMemberPrefix` of `pre_`
- `PublicMemberSuffix` of `_post`
- `PublicMemberHungarianPrefix` of `On`

Then it identifies and/or transforms public member names like this:

**Before:**

class Foo { public: int Member_Variable; }


**After:**

class Foo { public: int pre_member_variable_post; }


---

- **`PublicMethodCase`** โ€” when defined, the check makes sure public method names conform to the casing you picked.

- **`PublicMethodPrefix`** โ€” when defined, the check makes sure public method names get the prefix you give, added on (regardless of casing).

- **`PublicMethodIgnoredRegexp`** โ€” identifier naming checks won't be enforced for public method names that match this regular expression.

- **`PublicMethodSuffix`** โ€” when defined, the check makes sure public method names get the suffix you give, added on (regardless of casing).

Say your values are:

- `PublicMethodCase` of `lower_case`
- `PublicMethodPrefix` of `pre_`
- `PublicMethodSuffix` of `_post`

Then it identifies and/or transforms public method names like this:

**Before:**

class Foo { public: int Member_Method(); }


**After:**

class Foo { public: int pre_member_method_post(); }


---

- **`ScopedEnumConstantCase`** โ€” when defined, the check makes sure scoped enum constant names conform to the casing you picked. <mint>Scoped enums getting the glow-up they deserve โœจ</mint>

- **`ScopedEnumConstantPrefix`** โ€” when defined, the check makes sure scoped enum constant names get the prefix you give, added on (regardless of casing).

- **`ScopedEnumConstantIgnoredRegexp`** โ€” identifier naming checks won't be enforced for scoped enum constant names that match this regular expression.

- **`ScopedEnumConstantSuffix`** โ€” when defined, the check makes sure scoped enum constant names get the suffix you give, added on (regardless of casing).

- **`ScopedEnumConstantHungarianPrefix`** โ€” turn this on and the check makes sure the declared identifier gets a Hungarian notation prefix, based on the declared type.

Say your values are:

- `ScopedEnumConstantCase` of `lower_case`
- `ScopedEnumConstantPrefix` of `pre_`
- `ScopedEnumConstantSuffix` of `_post`
- `ScopedEnumConstantHungarianPrefix` of `On`

okay so **enumeration constant names** โ€” the check identifies and/or transforms them, and here's the glow-up ๐Ÿ’…

**Before:**

enum class FOO { One, Two, Three };


**After:**

enum class FOO { pre_One_post, pre_Two_post, pre_Three_post };


---

next up bestie, the **static constant** girlies โœจ

- **`StaticConstantCase`** โ€” when it's defined, the check makes sure your static constant names conform to the casing you picked.
- **`StaticConstantPrefix`** โ€” when it's defined, the check makes sure static constant names get prefixed with the given value (regardless of casing, no cap).
- **`StaticConstantIgnoredRegexp`** โ€” identifier naming checks won't be enforced for static constant names that match this regular expression. she's exempt, periodt.
- **`StaticConstantSuffix`** โ€” when it's defined, the check makes sure static constant names get the given suffix added (regardless of casing).
- **`StaticConstantHungarianPrefix`** โ€” when enabled, the check ensures the declared identifier will have a Hungarian notation prefix based on the declared type. iconic tbh.

so like, for example using values of:

- `StaticConstantCase` of `lower_case`
- `StaticConstantPrefix` of `pre_`
- `StaticConstantSuffix` of `_post`
- `StaticConstantHungarianPrefix` of `On`

it identifies and/or transforms static constant names like this:

**Before:**

static unsigned const MyConstStatic_array[] = {1, 2, 3};


**After:**

static unsigned const pre_myconststatic_array_post[] = {1, 2, 3};


---

**static variables**, same energy, different girl ๐Ÿฆ‹

- **`StaticVariableCase`** โ€” when it's defined, the check makes sure static variable names conform to the selected casing.
- **`StaticVariablePrefix`** โ€” when it's defined, the check makes sure static variable names get prefixed with the given value (regardless of casing).
- **`StaticVariableIgnoredRegexp`** โ€” identifier naming checks won't be enforced for static variable names matching this regular expression.
- **`StaticVariableSuffix`** โ€” when it's defined, the check makes sure static variable names get the given suffix added (regardless of casing).
- **`StaticVariableHungarianPrefix`** โ€” when enabled, the check ensures the declared identifier will have a Hungarian notation prefix based on the declared type.

for example, using values of:

- `StaticVariableCase` of `lower_case`
- `StaticVariablePrefix` of `pre_`
- `StaticVariableSuffix` of `_post`
- `StaticVariableHungarianPrefix` of `On`

it identifies and/or transforms static variable names like:

**Before:**

static unsigned MyStatic_array[] = {1, 2, 3};


**After:**

static unsigned pre_mystatic_array_post[] = {1, 2, 3};


---

**structs**!! the main characters ๐ŸŽ€

- **`StructCase`** โ€” when it's defined, the check makes sure struct names conform to the selected casing.
- **`StructPrefix`** โ€” when it's defined, the check makes sure struct names get prefixed with the given value (regardless of casing).
- **`StructIgnoredRegexp`** โ€” identifier naming checks won't be enforced for struct names matching this regular expression.
- **`StructSuffix`** โ€” when it's defined, the check makes sure struct names get the given suffix added (regardless of casing).

for example using values of:

- `StructCase` of `lower_case`
- `StructPrefix` of `pre_`
- `StructSuffix` of `_post`

it identifies and/or transforms struct names as follows:

**Before:**

struct FOO { FOO(); ~FOO(); };


**After:**

struct pre_foo_post { pre_foo_post(); ~pre_foo_post(); };


note the constructors and destructor got renamed too. she committed to the bit ๐Ÿ’€

---

**template parameters** โ€” yes, even the `<typename T>` besties get a makeover ๐ŸŒธ

- **`TemplateParameterCase`** โ€” when it's defined, the check makes sure template parameter names conform to the selected casing.
- **`TemplateParameterPrefix`** โ€” when it's defined, the check makes sure template parameter names get prefixed with the given value (regardless of casing).
- **`TemplateParameterIgnoredRegexp`** โ€” identifier naming checks won't be enforced for template parameter names matching this regular expression.
- **`TemplateParameterSuffix`** โ€” when it's defined, the check makes sure template parameter names get the given suffix added (regardless of casing).

for example using values of:

- `TemplateParameterCase` of `lower_case`
- `TemplateParameterPrefix` of `pre_`
- `TemplateParameterSuffix` of `_post`

it identifies and/or transforms template parameter names as follows:

**Before:**

template <typename T> class Foo {};


**After:**

template <typename pre_t_post> class Foo {};


---

**template template parameters** โ€” the inception vibes, we love her ๐Ÿ’œ

- **`TemplateTemplateParameterCase`** โ€” when it's defined, the check makes sure template template parameter names conform to the selected casing.
- **`TemplateTemplateParameterPrefix`** โ€” when it's defined, the check makes sure template template parameter names get prefixed with the given value (regardless of casing).
- **`TemplateTemplateParameterIgnoredRegexp`** โ€” identifier naming checks won't be enforced for template template parameter names matching this regular expression.
- **`TemplateTemplateParameterSuffix`** โ€” when it's defined, the check makes sure template template parameter names get the given suffix added (regardless of casing).

for example using values of:

- `TemplateTemplateParameterCase` of `lower_case`
- `TemplateTemplateParameterPrefix` of `pre_`
- `TemplateTemplateParameterSuffix` of `_post`

it identifies and/or transforms template template parameter names as follows:

**Before:**

template <template <typename> class TPL_parameter, int COUNT_params, typename... TYPE_parameters>


**After:**

template <template <typename> class pre_tpl_parameter_post, int COUNT_params, typename... TYPE_parameters>


see how `COUNT_params` and `TYPE_parameters` just stayed there? yeah, that's a *different* check, bestie. we're not touching them here ๐Ÿ™ˆ

---

**type aliases** โ€” `using` girlies rise up โญ

- **`TypeAliasCase`** โ€” when it's defined, the check makes sure type alias names conform to the selected casing.
- **`TypeAliasPrefix`** โ€” when it's defined, the check makes sure type alias names get prefixed with the given value (regardless of casing).
- **`TypeAliasIgnoredRegexp`** โ€” identifier naming checks won't be enforced for type alias names matching this regular expression.
- **`TypeAliasSuffix`** โ€” when it's defined, the check makes sure type alias names get the given suffix added (regardless of casing).

for example using values of:

- `TypeAliasCase` of `lower_case`
- `TypeAliasPrefix` of `pre_`
- `TypeAliasSuffix` of `_post`

it identifies and/or transforms type alias names as follows:

**Before:**

using MY_STRUCT_TYPE = my_structure;


**After:**

using pre_my_struct_type_post = my_structure;


---

**typedefs** โ€” the OGs of type naming, we still respect them ๐Ÿ’–

- **`TypedefCase`** โ€” when it's defined, the check makes sure typedef names conform to the selected casing.
- **`TypedefPrefix`** โ€” when it's defined, the check makes sure typedef names get prefixed with the given value (regardless of casing).
- **`TypedefIgnoredRegexp`** โ€” identifier naming checks won't be enforced for typedef names matching this regular expression.
- **`TypedefSuffix`** โ€” when it's defined, the check makes sure typedef names get the given suffix added (regardless of casing).

for example using values of:

- `TypedefCase` of `lower_case`
- `TypedefPrefix` of `pre_`
- `TypedefSuffix` of `_post`

it identifies and/or transforms typedef names as follows:

**Before:**

typedef int MYINT;


**After:**

typedef int pre_myint_post;


---

**type template parameters** โ€” okay so this is the one that actually matches `<typename... TYPE_parameters>` ๐Ÿ‘‘

- **`TypeTemplateParameterCase`** โ€” when it's defined, the check makes sure type template parameter names conform to the selected casing.
- **`TypeTemplateParameterPrefix`** โ€” when it's defined, the check makes sure type template parameter names get prefixed with the given value (regardless of casing).
- **`TypeTemplateParameterIgnoredRegexp`** โ€” identifier naming checks won't be enforced for type template names matching this regular expression.
- **`TypeTemplateParameterSuffix`** โ€” when it's defined, the check makes sure type template parameter names get the given suffix added (regardless of casing).

for example using values of:

- `TypeTemplateParameterCase` of `lower_case`
- `TypeTemplateParameterPrefix` of `pre_`
- `TypeTemplateParameterSuffix` of `_post`

it identifies and/or transforms type template parameter names as follows:

**Before:**

template <template <typename> class TPL_parameter, int COUNT_params, typename... TYPE_parameters>


**After:**

template <template <typename> class TPL_parameter, int COUNT_params, typename... pre_type_parameters_post>


---

**unions** โ€” underrated, honestly ๐Ÿซถ

- **`UnionCase`** โ€” when it's defined, the check makes sure union names conform to the selected casing.
- **`UnionPrefix`** โ€” when it's defined, the check makes sure union names get prefixed with the given value (regardless of casing).
- **`UnionIgnoredRegexp`** โ€” identifier naming checks won't be enforced for union names matching this regular expression.
- **`UnionSuffix`** โ€” when it's defined, the check makes sure union names get the given suffix added (regardless of casing).

for example using values of:

- `UnionCase` of `lower_case`
- `UnionPrefix` of `pre_`
- `UnionSuffix` of `_post`

it identifies and/or transforms union names as follows:

**Before:**

union FOO { int a; char b; };


**After:**

union pre_foo_post { int a; char b; };


---

and finally **value template parameters** โ€” the non-type template parameter icons ๐Ÿ”ฅ

- **`ValueTemplateParameterCase`** โ€” when it's defined, the check makes sure value template parameter names conform to the selected casing.
- **`ValueTemplateParameterPrefix`** โ€” when it's defined, the check makes sure value template parameter names get prefixed with the given value (regardless of casing).
- **`ValueTemplateParameterIgnoredRegexp`** โ€” identifier naming checks won't be enforced for value template parameter names matching this regular expression.
- **`ValueTemplateParameterSuffix`** โ€” when it's defined, the check makes sure value template parameter names get the given suffix added (regardless of casing).

for example using values of:

- `ValueTemplateParameterCase` of `lower_case`
- `ValueTemplateParameterPrefix` of `pre_`
- `ValueTemplateParameterSuffix` of `_post`

it identifies and/or transforms value template parameter names as follows:

**Before:**

template <template <typename> class TPL_parameter, int COUNT_params, typename... TYPE_parameters>


**After:**

template <template <typename> class TPL_parameter, int pre_count_params_post, typename... TYPE_parameters>


see?? `COUNT_params` finally got her moment โœจ the way these checks each claim *exactly one* name category and leave the rest alone is lowkey genius, iykyk ๐Ÿ’…

- **`VariableCase`** โ€” bestie, set this and the check will make sure variable names conform to the casing you picked. โœจ

- **`VariablePrefix`** โ€” set this and the check will make variable names add the prefix you give it (regardless of casing). ๐ŸŽ€

- **`VariableIgnoredRegexp`** โ€” identifier naming checks won't be enforced for variable names that match this regular expression. lowkey a hall pass, fr. ๐Ÿ™ˆ

- **`VariableSuffix`** โ€” set this and the check will make variable names add the suffix you give it (regardless of casing). ๐Ÿ’…

- **`VariableHungarianPrefix`** โ€” when enabled, the check ensures that the declared identifier will have a Hungarian notation prefix based on the declared type. it's giving old-school, iykyk. ๐Ÿ‘‘

So like, for example, using values of:

- `VariableCase` of `lower_case`
- `VariablePrefix` of `pre_`
- `VariableSuffix` of `_post`
- `VariableHungarianPrefix` of `On`

It identifies and/or transforms variable names as follows:

**Before:**

unsigned MyVariable;


**After:**

unsigned pre_myvariable_post;


---

- **`VirtualMethodCase`** โ€” set this and the check will ensure virtual method names conform to the selected casing. ๐ŸŒธ

- **`VirtualMethodPrefix`** โ€” set this and the check will ensure virtual method names will add the prefix you give it (regardless of casing). โœจ

- **`VirtualMethodIgnoredRegexp`** โ€” identifier naming checks won't be enforced for virtual method names matching this regular expression. no vibes check for these ones ๐Ÿฆ‹

- **`VirtualMethodSuffix`** โ€” set this and the check will ensure virtual method names will add the suffix you give it (regardless of casing). ๐ŸŽ€

So for example using values of:

- `VirtualMethodCase` of `lower_case`
- `VirtualMethodPrefix` of `pre_`
- `VirtualMethodSuffix` of `_post`

It identifies and/or transforms virtual method names as follows:

**Before:**

class Foo { public: virtual int MemberFunction(); }


**After:**

class Foo { public: virtual int pre_member_function_post(); }


---

## ๐Ÿงฎ The default mapping table of Hungarian Notation ๐Ÿ’ซ

Okay so in Hungarian notation, a variable name starts with a group of lower-case letters which are mnemonics for the type or purpose of that variable, followed by whatever name the programmer has chosen. that last part is sometimes distinguished as the *given name*. ๐Ÿ’–

The first character of the given name can be capitalized to separate it from the type indicators (see also `CamelCase`). Otherwise the case of this character denotes scope. periodt. ๐ŸŒˆ

Ok so here's the default mapping table for Hungarian Notation โ€” it's the one that maps a <pink>`Decl`</pink> to its prefix string โœจ. And bestie, you're not stuck with it: you can have your own style in a config file. ๐Ÿ’…

---

### ๐ŸŒธ Primitive Type โ†’ Prefix (column one) ๐ŸŒธ

- **`int8_t`** โ†’ `i8`
- **`int16_t`** โ†’ `i16`
- **`int32_t`** โ†’ `i32`
- **`int64_t`** โ†’ `i64`
- **`uint8_t`** โ†’ `u8`
- **`uint16_t`** โ†’ `u16`
- **`uint32_t`** โ†’ `u32`
- **`uint64_t`** โ†’ `u64`
- **`char8_t`** โ†’ `c8`
- **`char16_t`** โ†’ `c16`
- **`char32_t`** โ†’ `c32`
- **`float`** โ†’ `f`
- **`double`** โ†’ `d`
- **`char`** โ†’ `c`
- **`bool`** โ†’ `b`
- **`_Bool`** โ†’ `b`
- **`int`** โ†’ `i`
- **`size_t`** โ†’ `n`
- **`short`** โ†’ `s`
- **`signed`** โ†’ `i`
- **`unsigned`** โ†’ `u`
- **`long`** โ†’ `l`
- **`long long`** โ†’ `ll`
- **`unsigned long`** โ†’ `ul`
- **`long double`** โ†’ `ld`
- **`ptrdiff_t`** โ†’ `p`
- **`wchar_t`** โ†’ `wc`
- **`short int`** โ†’ `si`
- **`short`** โ†’ `s`

<lavender>(yep, `short` shows up twice in that column, we don't ask questions, we just transcribe ๐Ÿซถ)</lavender>

---

### ๐Ÿฆ‹ The middle column (Type โ†’ Prefix) ๐Ÿฆ‹

This one has no fancy name up top, it's just Type and Prefix, but the lore is real:

- **`signed int`** โ†’ `si`
- **`signed short`** โ†’ `ss`
- **`signed short int`** โ†’ `ssi`
- **`signed long long int`** โ†’ `slli`
- **`signed long long`** โ†’ `sll`
- **`signed long int`** โ†’ `sli`
- **`signed long`** โ†’ `sl`
- **`signed`** โ†’ `s`
- **`unsigned long long int`** โ†’ `ulli`
- **`unsigned long long`** โ†’ `ull`
- **`unsigned long int`** โ†’ `uli`
- **`unsigned long`** โ†’ `ul`
- **`unsigned short int`** โ†’ `usi`
- **`unsigned short`** โ†’ `us`
- **`unsigned int`** โ†’ `ui`
- **`unsigned char`** โ†’ `uc`
- **`unsigned`** โ†’ `u`
- **`long long int`** โ†’ `lli`
- **`long double`** โ†’ `ld`
- **`long long`** โ†’ `ll`
- **`long int`** โ†’ `li`
- **`long`** โ†’ `l`
- **`ptrdiff_t`** โ†’ `p`
- **`void`** โ†’ `none`

---

### ๐Ÿ’œ Microsoft Type โ†’ Prefix ๐Ÿ’œ

- **`BOOL`** โ†’ `b`
- **`BOOLEAN`** โ†’ `b`
- **`BYTE`** โ†’ `by`
- **`CHAR`** โ†’ `c`
- **`UCHAR`** โ†’ `uc`
- **`SHORT`** โ†’ `s`
- **`USHORT`** โ†’ `us`
- **`WORD`** โ†’ `w`
- **`DWORD`** โ†’ `dw`
- **`DWORD32`** โ†’ `dw32`
- **`DWORD64`** โ†’ `dw64`
- **`LONG`** โ†’ `l`
- **`ULONG`** โ†’ `ul`
- **`ULONG32`** โ†’ `ul32`
- **`ULONG64`** โ†’ `ul64`
- **`ULONGLONG`** โ†’ `ull`
- **`HANDLE`** โ†’ `h`
- **`INT`** โ†’ `i`
- **`INT8`** โ†’ `i8`
- **`INT16`** โ†’ `i16`
- **`INT32`** โ†’ `i32`
- **`INT64`** โ†’ `i64`
- **`UINT`** โ†’ `ui`
- **`UINT8`** โ†’ `u8`
- **`UINT16`** โ†’ `u16`
- **`UINT32`** โ†’ `u32`
- **`UINT64`** โ†’ `u64`
- **`PVOID`** โ†’ `p`

---

There are more trivial options for Hungarian Notation, and honestly? They're giving organized queen ๐Ÿ‘‘

- **`HungarianNotation.General.*`** โ€” options that are not belonging to any specific `Decl`.
- **`HungarianNotation.CString.*`** โ€” options for `NULL`-terminated string.
- **`HungarianNotation.DerivedType.*`** โ€” options for derived types.
- **`HungarianNotation.PrimitiveType.*`** โ€” options for primitive types.
- **`HungarianNotation.UserDefinedType.*`** โ€” options for user-defined types.

---

## โœจ Options for Hungarian Notation โœจ

- **`HungarianNotation.General.TreatStructAsClass`**

- <pink>`HungarianNotation.DerivedType.Array`</pink>

- <pink>`HungarianNotation.DerivedType.Pointer`</pink>

- <pink>`HungarianNotation.DerivedType.FunctionPointer`</pink>

- <pink>`HungarianNotation.CString.CharPointer`</pink>

- <pink>`HungarianNotation.CString.CharArray`</pink>

- <pink>`HungarianNotation.CString.WideCharPointer`</pink>

- <pink>`HungarianNotation.CString.WideCharArray`</pink>

- <pink>`HungarianNotation.PrimitiveType.*`</pink>

- <pink>`HungarianNotation.UserDefinedType.*`</pink>

---

### `HungarianNotation.General.TreatStructAsClass` ๐Ÿ’–

okay so when this one is defined, the check will treat the naming of a `struct` as if it were a `class`. The default value is `false`. yes bestie, `false` โ€” no surprise glow-ups here.

### `HungarianNotation.DerivedType.Array` ๐ŸŽ€

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `a`.

### `HungarianNotation.DerivedType.Pointer` ๐ŸŽ€

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `p`.

### `HungarianNotation.DerivedType.FunctionPointer` ๐ŸŽ€

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `fn`.

**Before:**

// Array int DataArray[2] = {0};

// Pointer void *DataBuffer = NULL;

// FunctionPointer typedef void (*FUNC_PTR)(); FUNC_PTR FuncPtr = NULL;


**After:**

// Array int aDataArray[2] = {0};

// Pointer void *pDataBuffer = NULL;

// FunctionPointer typedef void (*FUNC_PTR)(); FUNC_PTR fnFuncPtr = NULL;


### `HungarianNotation.CString.CharPointer` ๐ŸŒธ

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `sz`.

### `HungarianNotation.CString.CharArray` ๐ŸŒธ

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `sz`.

### `HungarianNotation.CString.WideCharPointer` ๐ŸŒธ

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `wsz`.

### `HungarianNotation.CString.WideCharArray` ๐ŸŒธ

When defined, the check will ensure the variable name will add the prefix with the given string. The default prefix is `wsz`.

**Before:**

// CharPointer const char *NamePtr = "Name";

// CharArray const char NameArray[] = "Name";

// WideCharPointer const wchar_t *WideNamePtr = L"Name";

// WideCharArray const wchar_t WideNameArray[] = L"Name";


**After:**

// CharPointer const char *szNamePtr = "Name";

// CharArray const char szNameArray[] = "Name";

// WideCharPointer const wchar_t *wszWideNamePtr = L"Name";

// WideCharArray const wchar_t wszWideNameArray[] = L"Name";


### `HungarianNotation.PrimitiveType.*` โœจ

When defined, the check will ensure the variable name of involved primitive types will add the prefix with the given string. The default prefixes are defined in the default mapping table. (iykyk, the mapping table is the moment.)

### `HungarianNotation.UserDefinedType.*` โœจ

When defined, the check will ensure the variable name of involved primitive types will add the prefix with the given string. The default prefixes are defined in the default mapping table. yep, it says *primitive* types again โ€” we're just reading it out, no editorializing from me ๐Ÿ’€

**Before:**

int8_t ValueI8 = 0; int16_t ValueI16 = 0; int32_t ValueI32 = 0; int64_t ValueI64 = 0; uint8_t ValueU8 = 0; uint16_t ValueU16 = 0; uint32_t ValueU32 = 0; uint64_t ValueU64 = 0; float ValueFloat = 0.0; double ValueDouble = 0.0; ULONG ValueUlong = 0; DWORD ValueDword = 0;


**After:**

int8_t i8ValueI8 = 0; int16_t i16ValueI16 = 0; int32_t i32ValueI32 = 0; int64_t i64ValueI64 = 0; uint8_t u8ValueU8 = 0; uint16_t u16ValueU16 = 0; uint32_t u32ValueU32 = 0; uint64_t u64ValueU64 = 0; float fValueFloat = 0.0; double dValueDouble = 0.0; ULONG ulValueUlong = 0; DWORD dwValueDword = 0;


---

## `readability-implicit-bool-cast` ๐Ÿฆ‹

This check has been renamed to <cyan>`readability-implicit-bool-conversion`</cyan>. that's it, that's the whole entry โ€” the old name is giving "legacy icon" energy.

## `readability-implicit-bool-conversion` ๐Ÿ‘‘

This check can be used to find implicit conversions between built-in types and booleans. Depending on use case, it may simply help with readability of the code, or in some cases, point to potential bugs which remain unnoticed due to implicit conversions. <pink>both a glow-up tool and a bug-hunting tool, obsessed.</pink>

The following is a real-world example of a bug which was hiding behind an implicit bool conversion:

class Foo { int m_foo;

public: void setFoo(bool foo) { m_foo = foo; } // warning: implicit conversion bool -> int int getFoo() { return m_foo; } };

void use(Foo& foo) { bool value = foo.getFoo(); // warning: implicit conversion int -> bool }


This code is the result of unsuccessful refactoring, where the type of `m_foo` changed from `bool` to `int`. The programmer forgot to change all occurrences of `bool`, and the remaining code is no longer correct, yet it still compiles without any visible warnings. <red>imagine the compiler just... not telling you. screaming.</red>

In addition to issuing warnings, fix-it hints are provided to help solve the reported issues. This can be used for improving readability of code, for example:

void conversionsToBool() { float floating; bool boolean = floating; // ^ propose replacement: bool boolean = floating != 0.0f;

int integer; if (integer) {} // ^ propose replacement: if (integer != 0) {}

int* pointer; if (!pointer) {} // ^ propose replacement: if (pointer == nullptr) {}

while (1) {} // ^ propose replacement: while (true) {} }

void functionTakingInt(int param);

void conversionsFromBool() { bool boolean; functionTakingInt(boolean); // ^ propose replacement: functionTakingInt(static_cast<int>(boolean));

functionTakingInt(true); // ^ propose replacement: functionTakingInt(1); }


In general, the following conversion types are checked:

- integer expression/literal to boolean (conversion from a single bit bitfield to boolean is explicitly allowed, since there's no ambiguity / information loss in this case),

- floating expression/literal to boolean,

- pointer/pointer to member/`nullptr`/`NULL` to boolean,

- boolean expression/literal to integer (conversion from boolean to a single bit bitfield is explicitly allowed),

- boolean expression/literal to floating.

The rules for generating fix-it hints are:

- in case of conversions from other built-in type to `bool`, an explicit comparison is proposed to make it clear what exactly is being compared:

  - `bool boolean = floating;` is changed to `bool boolean = floating == 0.0f;`,

  - for other types, appropriate literals are used (`0`, `0u`, `0.0f`, `0.0`, `nullptr`),

- in case of negated expressions conversion to `bool`, the proposed replacement with comparison is simplified:

  - `if (!pointer)` is changed to `if (pointer == nullptr)`,

- in case of conversions from `bool` to other built-in types, an explicit `static_cast` (or a C-style cast since C23) is proposed to make it clear that a conversion is taking place:

  - `int integer = boolean;` is changed to `int integer = static_cast<int>(boolean);`,

- if the conversion is performed on type literals, an equivalent literal is proposed, according to what type is actually expected, for example:

  - `functionTakingBool(0);` is changed to `functionTakingBool(false);`,

  - `functionTakingInt(true);` is changed to `functionTakingInt(1);`,

  - for other types, appropriate literals are used (`false`, `true`, `0`, `1`, `0u`, `1u`, `0.0f`, `1.0f`, `0.0`, `1.0f`).

Some additional accommodations are made for pre-C++11 dialects:

- `false` literal conversion to pointer is detected,

- instead of `nullptr` literal, `0` is proposed as replacement.

Occurrences of implicit conversions inside macros and template instantiations are deliberately ignored, as it is not clear how to deal with such cases. <lavender>yeah they said "not our problem bestie" and honestly? valid.</lavender>

### Options ๐ŸŒˆ

- **`AllowIntegerConditions`** โ€” When `true`, the check will allow conditional integer conversions. Default is `false`.

- **`AllowPointerConditions`** โ€” When `true`, the check will allow conditional pointer conversions. Default is `false`.

---

## `readability-inconsistent-declaration-parameter-name` ๐Ÿ’…

Find function declarations which differ in parameter names. literally just that. snitch behavior, but make it useful.

Example:

// in foo.hpp: void foo(int a, int b, int c);

// in foo.cpp: void foo(int d, int e, int f); // warning


This check should help to enforce consistency in large projects, where it often happens that a definition of a function is refactored, changing the parameter names, but its declaration in the header file is not updated. With this check, we can easily find and correct such inconsistencies, keeping declaration and definition always in sync. <mint>main character energy: keeping everyone on the same page.</mint>

Unnamed parameters are allowed and are not taken into account when comparing function declarations, for example:

void foo(int a); void foo(int); // no warning


One name is also allowed to be a case-insensitive prefix/suffix of the other:

void foo(int count); void foo(int count_input) { // no warning int count = adjustCount(count_input); }


To help with refactoring, in some cases fix-it hints are generated to align parameter names to a single naming convention. This works with the assumption that the function definition is the most up-to-date version, as it directly references parameter names in its body. Example:

void foo(int a); // warning and fix-it hint (replace "a" to "b") int foo(int b) { return b + 2; } // definition with use of "b"


In the case of multiple redeclarations or function template specializations, a warning is issued for every redeclaration or specialization inconsistent with the definition or the first declaration seen in a translation unit. <yellow>every single one. no skipping.</yellow>

- **`IgnoreMacros`** โ€” If this option is set to `true` (default is `true`), the check will not warn about names declared inside macros.

- **`Strict`** โ€” If this option is set to `true` (default is `false`), then names must match exactly (or be absent).

---

## `readability-isolate-declaration` ๐Ÿซถ

Detects local variable declarations declaring more than one variable and tries to refactor the code to one statement per declaration. one girl, one declaration. periodt.

The automatic code-transformation will use the same indentation as the original for every created statement and add a line break after each statement. It keeps the order of the variable declarations consistent, too. <rainbow>โœจ respectful, orderly, iconic โœจ</rainbow>

void f() { int * pointer = nullptr, value = 42, * const const_ptr = &value; // This declaration will be diagnosed and transformed into: // int * pointer = nullptr; // int value = 42; // int * const const_ptr = &value; }


<pink>But bestie, it's not gonna come for every single multi-decl in existence ๐Ÿ’–</pink> The check <lavender>skips</lavender> the places where declaring several variables in one statement is genuinely necessary or just plain normal, and where the language literally gives you no other way to do it. And heads up: <yellow>structured bindings are not considered</yellow>. Not even a little. ๐Ÿ™ˆ

// It is not possible to transform this declaration and doing the declaration // before the loop will increase the scope of the variable 'Begin' and 'End' // which is undesirable. for (int Begin = 0, End = 100; Begin < End; ++Begin); if (int Begin = 42, Result = some_function(Begin); Begin == Result);

// It is not possible to transform this declaration because the result is // not functionality preserving as 'j' and 'k' would not be part of the // 'if' statement anymore. if (SomeCondition()) int i = 42, j = 43, k = function(i,j);


### Limitations ๐Ÿซถ

<purple>Global variables and member variables are excluded.</purple> Sorry, they're not invited to the party. ๐Ÿ’…

The check currently does <red>not</red> support the automatic transformation of member-pointer-types.

struct S { int a; const int b; void f() {} };

void f() { // Only a diagnostic message is emitted int S::*p = &S::a, S::*const q = &S::a; }


Furthermore, the transformation is <hotpink>very cautious</hotpink> when it detects various kinds of macros or preprocessor directives in the range of the statement. In this case the transformation will not happen, so that we avoid unexpected side-effects due to macros. <blue>(She's being careful and honestly? Queen behavior. ๐Ÿ‘‘)</blue>

#define NULL 0 #define MY_NICE_TYPE int ** #define VAR_NAME(name) name##__LINE__ #define A_BUNCH_OF_VARIABLES int m1 = 42, m2 = 43, m3 = 44;

void macros() { int *p1 = NULL, *p2 = NULL; // Will be transformed to // int *p1 = NULL; // int *p2 = NULL;

MY_NICE_TYPE p3, v1, v2; // Won't be transformed, but a diagnostic is emitted.

int VAR_NAME(v3), VAR_NAME(v4), VAR_NAME(v5); // Won't be transformed, but a diagnostic is emitted.

A_BUNCH_OF_VARIABLES // Won't be transformed, but a diagnostic is emitted.

int Unconditional, #if CONFIGURATION IfConfigured = 42, #else IfConfigured = 0; #endif // Won't be transformed, but a diagnostic is emitted. }


---

## readability-magic-numbers โœจ๐Ÿ”ข

Catches <hotpink>magic numbers</hotpink> โ€” integer or floating point literals that are just *sitting there* embedded in the code, not introduced via constants or symbols. It's giving "what is 3.1415926535 and why is it in my source file" ๐Ÿ’€

So many coding guidelines are like "replace the magic values with symbolic constants for readability" and honestly they ate. Here are a few references:

- Rule <cyan>ES.45</cyan>: Avoid "magic constants"; use symbolic constants in C++ Core Guidelines
- Rule <cyan>5.1.1</cyan> Use symbolic names instead of literal values in code in High Integrity C++
- Item <cyan>17</cyan> in "C++ Coding Standards: 101 Rules, Guidelines and Best Practices" by Herb Sutter and Andrei Alexandrescu
- Chapter <cyan>17</cyan> in "Clean Code - A handbook of agile software craftsmanship." by Robert C. Martin
- Rule <cyan>20701</cyan> in "TRAIN REAL TIME DATA PROTOCOL Coding Rules" by Armin-Hagen Weiss, Bombardier
- <cyan>http://wiki.c2.com/?MagicNumber</cyan>

Examples of magic values:

template<typename T, size_t N> struct CustomType { T arr[N]; };

struct OtherType { CustomType<int, 30> container; } CustomType<int, 30> values;

double circleArea = 3.1415926535 * radius * radius;

double totalCharge = 1.08 * itemPrice;

int getAnswer() { return -3; // FILENOTFOUND }

for (int mm = 1; mm <= 12; ++mm) { std::cout << month[mm] << '\n'; }


Example with magic values refactored <mint>(glow up fr fr ๐ŸŒˆ)</mint>:

template<typename T, size_t N> struct CustomType { T arr[N]; };

const size_t NUMBER_OF_ELEMENTS = 30; using containerType = CustomType<int, NUMBER_OF_ELEMENTS>;

struct OtherType { containerType container; } containerType values;

double circleArea = M_PI * radius * radius;

const double TAX_RATE = 0.08; // or make it variable and read from a file

double totalCharge = (1.0 + TAX_RATE) * itemPrice;

int getAnswer() { return E_FILE_NOT_FOUND; }

for (int mm = 1; mm <= MONTHS_IN_A_YEAR; ++mm) { std::cout << month[mm] << '\n'; }


For integral literals, by default only <yellow>`0`</yellow> and <yellow>`1`</yellow> (and <yellow>`-1`</yellow>) integer values are accepted without a warning. You can override that with the <lavender>`IgnoredIntegerValues`</lavender> option. Negative values are accepted if their absolute value is present in the <lavender>`IgnoredIntegerValues`</lavender> list. โœจ

As a special case for integral values, <hotpink>all powers of two</hotpink> can be accepted without warning by enabling the <lavender>`IgnorePowersOf2IntegerValues`</lavender> option.

For floating point literals, by default the <yellow>`0.0`</yellow> floating point value is accepted without a warning. The set of ignored floating point literals can be configured using the <lavender>`IgnoredFloatingPointValues`</lavender> option. For each value in that set, the given string value is converted to a floating-point value representation used by the target architecture. If a floating-point literal value compares equal to one of the converted values, then that literal is not diagnosed by this check. Because <red>floating-point equality</red> is used to determine whether to diagnose or not, the user needs to be aware of the details of floating-point representations for any values that cannot be precisely represented for their target architecture. <peach>(this is the technical equivalent of "it's complicated" ๐Ÿ˜ญ)</peach>

For each value in the <lavender>`IgnoredFloatingPointValues`</lavender> set, both the single-precision form and double-precision form are accepted (for example, if <yellow>`3.14`</yellow> is in the set, neither <yellow>`3.14f`</yellow> nor <yellow>`3.14`</yellow> will produce a warning).

Scientific notation is supported for both source code input and option. Alternatively, the check for the floating point numbers can be disabled for all floating point values by enabling the <lavender>`IgnoreAllFloatingPointValues`</lavender> option.

Since values <yellow>`0`</yellow> and <yellow>`0.0`</yellow> are so common as the base counter of loops, or initialization values for sums, they are <green>always accepted without warning</green>, even if not present in the respective ignored values list. They're the main characters of numbers, they get a pass. ๐Ÿ’…

### Options ๐ŸŽ€

- **`IgnoredIntegerValues`** โ€” Semicolon-separated list of magic positive integers that will be accepted without a warning. Default values are `{1, 2, 3, 4}`, and `0` is accepted unconditionally.
- **`IgnorePowersOf2IntegerValues`** โ€” Boolean value indicating whether to accept all powers-of-two integer values without warning. Default value is `false`.
- **`IgnoredFloatingPointValues`** โ€” Semicolon-separated list of magic positive floating point values that will be accepted without a warning. Default values are `{1.0, 100.0}` and `0.0` is accepted unconditionally.
- **`IgnoreAllFloatingPointValues`** โ€” Boolean value indicating whether to accept all floating point values without warning. Default value is `false`.
- **`IgnoreBitFieldsWidths`** โ€” Boolean value indicating whether to accept magic numbers as bit field widths without warning. This is useful for example for register definitions which are generated from hardware specifications. Default value is `true`.
- **`IgnoreTypeAliases`** โ€” Boolean value indicating whether to accept magic numbers in typedef or using declarations. Default value is `false`.
- **`IgnoreUserDefinedLiterals`** โ€” Boolean value indicating whether to accept magic numbers in user-defined literals. Default value is `false`.

---

## readability-make-member-function-const ๐ŸŒธ

Finds non-static member functions that can be made `const`, because they don't use `this` in a non-const way. It's giving "you could be const, bestie, why aren't you" ๐Ÿ’…

This check tries to annotate methods according to <purple>logical constness</purple> (not physical constness). So it will only suggest adding a `const` qualifier to a non-const method if that method does something that is <mint>already possible through the public interface on a const pointer to the object</mint>:

- reading a public member variable
- calling a public const-qualified member function
- returning const-qualified `this`
- passing const-qualified `this` as a parameter.

This check will also suggest adding a `const` qualifier to a non-const method if the method uses <blue>private</blue> data and functions in a limited number of ways where logical constness and physical constness coincide:

- reading a member variable of builtin type

Specifically, this check will <red>not</red> suggest adding a `const` to a non-const method if the method reads a private member variable of pointer type, because that allows to modify the pointee, which might not preserve logical constness. For the same reason, it does not allow calling private member functions or member functions on private member variables.

In addition, this check <yellow>ignores</yellow> functions that

- are declared `virtual`
- contain a `const_cast`
- are templated or part of a class template
- have an empty body
- do not (implicitly) use `this` at all (see `readability-convert-member-functions-to-static`).

The following real-world examples will be preserved by the check:

class E1 { Pimpl &getPimpl() const; public: int &get() { // Calling a private member function disables this check. return getPimpl()->i; } ... };

class E2 { public: const int get() const; // const_cast disables this check. S *get() { return const_cast<int>(const_cast<const C*>(this)->get()); } ... };


After applying modifications as suggested by the check, running the check again might find <hotpink>more</hotpink> opportunities to mark member functions const. It's a whole cascade, bestie โ€” one const leads to another. ๐Ÿฆ‹

---

## readability-math-missing-parentheses ๐Ÿงฎ

Checks for missing parentheses in mathematical expressions that involve operators of different priorities.

Parentheses in mathematical expressions clarify the order of operations, especially with different-priority operators. Lengthy or multiline expressions can obscure this order, leading to coding errors. IDEs can aid clarity by highlighting parentheses. Explicitly using parentheses also clarifies what the developer had in mind when writing the expression. Ensuring their presence reduces ambiguity and errors, promoting clearer and more maintainable code. <lavender>(yes queen, parenthesize, communicate your intentions ๐Ÿ’–)</lavender>

Before:

int x = 1 + 2 * 3 - 4 / 5;


After:

int x = 1 + (2 * 3) - (4 / 5);


### ๐Ÿฆ‹ `readability-misleading-indentation` ๐Ÿฆ‹

Okay bestie, indentation is not just aesthetic โ€” it's how humans read code. When the syntactical structure and the indentation of the code don't match up, <pink>serious problems</pink> can just hide in plain sight. ๐Ÿ™ˆ And missing braces? That makes code *so* much harder to read, so fr fr, use the braces.

The way to dodge a dangling else is to <hotpink>always check that an `else` belongs to the `if` that begins in the same column</hotpink>.

You *can* omit braces when the inner part of, like, an `if` statement has only one statement in it. But even then you should start the next statement in the same column as the `if`. Periodt.

Examples:

// Dangling else: if (cond1) if (cond2) foo1(); else foo2(); // Wrong indentation: else belongs to if(cond2) statement.

// Missing braces: if (cond1) foo1(); foo2(); // Not guarded by if(cond1).


#### Limitations

Heads up bestie: this check only works as expected when tabs or spaces are used <yellow>consistently</yellow> and <red>not mixed</red>. Mixing them is giving chaos, and the check cannot save you. ๐Ÿ’€

---

### ๐ŸŽ€ `readability-misplaced-array-index` ๐ŸŽ€

This check warns for unusual array index syntax. That's it, that's the whole job.

The following code has unusual array index syntax:

void f(int *X, int Y) { Y[X] = 0; }


becomes

void f(int *X, int Y) { X[Y] = 0; }


The check warns about such unusual syntax for readability reasons:

- There are programmers that are not familiar with this unusual syntax.
- It is possible that variables are mixed up.

Lowkey both of those are terrifying. ๐Ÿ˜ญ

---

### ๐Ÿ’œ `readability-named-parameter` ๐Ÿ’œ

Find functions with unnamed arguments. Like, name your params, they deserve names. ๐Ÿฅบ

The check implements the following rule originating in the Google C++ Style Guide:

- `https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions`

All parameters should have the same name in both the function declaration and definition. If a parameter is not utilized, its name can be commented out in a function definition.

int doingSomething(int a, int b, int c);

int doingSomething(int a, int b, int /c/) { // Ok: the third param is not used return a + b; }


Corresponding `cpplint.py` check name: `readability/function`.

---

### โœจ `readability-non-const-parameter` โœจ

The check finds function parameters of a pointer type that could be changed to point to a constant type instead. <mint>const</mint> is your bestie and you're sleeping on it.

When `const` is used properly, many mistakes can be avoided. Advantages when using `const` properly:

- prevent unintentional modification of data;
- get additional warnings such as using uninitialized data;
- make it easier for developers to see possible side effects.

This check is **not strict** about constness, it only warns when the constness will make the function interface safer. Very demure, very mindful. ๐Ÿซถ

// warning here; the declaration "const char *p" would make the function // interface safer. char f1(char *p) { return *p; }

// no warning; the declaration could be more const "const int * const p" but // that does not make the function interface safer. int f2(const int *p) { return *p; }

// no warning; making x const does not make the function interface safer int f3(int x) { return x; }

// no warning; Technically, *p can be const ("const struct S *p"). But making // *p const could be misleading. People might think that it's safe to pass // const data to this function. struct S { int *a; int *b; }; int f3(struct S *p) { *(p->a) = 0; }

// no warning; p is referenced by an lvalue. void f4(int *p) { int &x = *p; }


---

### ๐ŸŒˆ `readability-operators-representation` ๐ŸŒˆ

Enforces consistent token representation for invoked binary, unary and overloaded operators in C++ code. The check supports both traditional and alternative representations of operators, such as `&&` and `and`, `||` and `or`, and so on. Slay, both are valid, pick one and commit. ๐Ÿ’…

In the realm of C++ programming, developers have the option to choose between two distinct representations for operators: **traditional token representation** and **alternative token representation**. Traditional tokens utilize symbols, such as `&&`, `||`, and `!`, while alternative tokens employ more descriptive words like `and`, `or`, and `not`.

In the following mapping table, a comprehensive list of traditional and alternative tokens, along with their corresponding representations, is presented:

#### Token Representation Mapping Table

- `&&` โ‡„ `and`
- `&=` โ‡„ `and_eq`
- `&` โ‡„ `bitand`
- `|` โ‡„ `bitor`
- `~` โ‡„ `compl`
- `!` โ‡„ `not`
- `!=` โ‡„ `not_eq`
- `||` โ‡„ `or`
- `|=` โ‡„ `or_eq`
- `^` โ‡„ `xor`
- `^=` โ‡„ `xor_eq`

#### Example

// Traditional Token Representation:

if (!a||!b) { // do something }

// Alternative Token Representation:

if (not a or not b) { // do something }


#### Options

Due to the distinct benefits and drawbacks of each representation, the default configuration **doesn't enforce either**. Explicit configuration is needed โ€” the check is not gonna guess your vibe.

To configure check to enforce Traditional Token Representation for all operators set options to `&&;&=;&;|;~;!;!=;||;|=;^;^=`.

To configure check to enforce Alternative Token Representation for all operators set options to `and;and_eq;bitand;bitor;compl;not;not_eq;or;or_eq;xor;xor_eq`.

Developers do not need to enforce all operators, and can mix the representations as desired by specifying a semicolon-separated list of both traditional and alternative tokens in the configuration, such as `and;||;not`.

- **`BinaryOperators`** โ€” This option allows you to specify a semicolon-separated list of binary operators for which you want to enforce specific token representation. The default value is empty string.
- **`OverloadedOperators`** โ€” This option allows you to specify a semicolon-separated list of overloaded operators for which you want to enforce specific token representation. The default value is empty string.

---

### ๐Ÿซถ `readability-qualified-auto` ๐Ÿซถ

Adds pointer qualifications to `auto`-typed variables that are deduced to pointers. So your `auto` stops being mysterious and starts being honest. ๐Ÿ‘‘

LLVM Coding Standards advises to make it obvious if an `auto` typed variable is a pointer. This check will transform `auto` to `auto *` when the type is deduced to be a pointer.

for (auto Data : MutatablePtrContainer) { change(*Data); } for (auto Data : ConstantPtrContainer) { observe(*Data); }


Would be transformed into:

for (auto *Data : MutatablePtrContainer) { change(*Data); } for (const auto *Data : ConstantPtrContainer) { observe(*Data); }


Note `const volatile` qualified types will retain their `const` and `volatile` qualifiers. Pointers to pointers will **not** be fully qualified. Noted, bestie. ๐Ÿ“

const auto Foo = cast<int *>(Baz1); const auto Bar = cast<const int *>(Baz2); volatile auto FooBar = cast<int *>(Baz3); auto BarFoo = cast<int **>(Baz4);


Would be transformed into:

auto *const Foo = cast<int *>(Baz1); const auto *const Bar = cast<const int *>(Baz2); auto *volatile FooBar = cast<int *>(Baz3); auto *BarFoo = cast<int **>(Baz4);


#### Options

- **`AddConstToQualified`** โ€” When set to `true` the check will add `const` qualifiers variables defined as `auto *` or `auto &` when applicable. Default value is `true`.

auto Foo1 = cast<const int *>(Bar1); auto *Foo2 = cast<const int *>(Bar2); auto &Foo3 = cast<const int &>(Bar3);


If `AddConstToQualified` is set to `false`, it will be transformed into:

const auto *Foo1 = cast<const int *>(Bar1); auto *Foo2 = cast<const int *>(Bar2); auto &Foo3 = cast<const int &>(Bar3);


Otherwise it will be transformed into:

const auto *Foo1 = cast<const int *>(Bar1); const auto *Foo2 = cast<const int *>(Bar2); const auto &Foo3 = cast<const int &>(Bar3);


> Note: in the LLVM alias, the default value is `false`. Sneaky little difference, iykyk. ๐Ÿ‘€

---

### ๐ŸŒธ `readability-redundant-access-specifiers` ๐ŸŒธ

Finds classes, structs, and unions containing redundant member (field and method) access specifiers. Redundant = extra = unnecessary. The way it just says the same thing twiceโ€ฆ not iconic. ๐Ÿ™ˆ

#### Example

class Foo { public: int x; int y; public: int z; protected: int a; public: int c; }


In the example above, the second `public` declaration can be removed without any changes of behavior.

#### Options

- **`CheckFirstDeclaration`** โ€” If set to `true`, the check will also diagnose if the first access specifier declaration is redundant (e.g. `private` inside class, or `public` inside struct or union). Default is `false`.

#### Example

struct Bar { public: int x; }


If `CheckFirstDeclaration` option is enabled, a warning about redundant access specifier will be emitted, because `public` is the default member access for structs. ๐Ÿ’…

## readability-redundant-casting

Okay so bestie, this check is on a mission ๐Ÿ’… โ€” it sniffs out explicit type casts where the source type and the destination type are literally the *same*, and then it's like "girl, why are you doing all that?" and recommends you delete them. It covers a whole bunch of explicit casting ops: `static_cast`, `const_cast`, C-style casts, and `reinterpret_cast`. The whole point is making your code more readable and maintainable by yeeting unnecessary casts.

int value = 42; int result = static_cast<int>(value);


In that little example, `static_cast<int>(value)` is giving *redundant* โœ‹ โ€” it's casting an `int` intoโ€ฆ another `int`. Same thing. Nothing happened.

But heads up, the check is NOT coming for these, it minds its own business:

- casting operations involving constructor conversions
- user-defined conversions
- functional casts
- type-dependent casts
- casts between distinct type aliases that refer to the same underlying type
- bitfield-related casts
- casts directly from lvalue to rvalue

All of those get a pass. No warnings. Periodt.

### Options

- **`IgnoreMacros`** โ€” if you set this to `true`, the check will not give warnings inside macros. Default is `true`.
- **`IgnoreTypeAliases`** โ€” set it to `false` and the check will consider type aliases; set it to `true` and it resolves all the type aliases and works on the underlying types instead. Default is `false`.

---

## readability-redundant-control-flow

This one is hunting for procedures (aka functions that return no value) with `return` statements chillin' at the very end of the function. Bestie, those returns are redundant. They're doing nothing. They're the friend who shows up to the party already over ๐Ÿ˜ญ.

It also checks loop statements โ€” `for`, `while`, `do while` โ€” for redundant `continue` statements sitting at the end of the loop body.

Examples:

The following function `f` contains a redundant `return` statement:

extern void g(); void f() { g(); return; }


becomes

extern void g(); void f() { g(); }


The following function `k` contains a redundant `continue` statement:

void k() { for (int i = 0; i < 10; ++i) { continue; } }


becomes

void k() { for (int i = 0; i < 10; ++i) { } }


---

## readability-redundant-declaration

Finds redundant variable and function declarations. Like, declaring the same thing twice. Twice. TWICE. ๐Ÿซ 

extern int X; extern int X;


becomes

extern int X;


These redundant declarations can be removed without changing program behavior. They can for instance be unintentional left overs from previous refactorings when code has been moved around. And worst case? Having redundant declarations could mean there are typos in the code that cause bugs. So it's not just clutter โ€” it's lowkey a hazard ๐Ÿ’€.

Normally the code can be automatically fixed โ€” `clang-tidy` can remove the second declaration. BUT there are 2 cases when you need to fix the code manually:

- When the declarations are in different header files;
- When multiple variables are declared together.

### Options

- **`IgnoreMacros`** โ€” if you set this to `true`, the check will not give warnings inside macros. Default is `true`.

---

## readability-redundant-function-ptr-dereference

Finds redundant dereferences of a function pointer. Yes, you *can* dereference a function pointer more times than you need to, and yes, this check WILL call you out on it ๐ŸŽ€.

Before:

int f(int,int); int (*p)(int, int) = &f;

int i = (**p)(10, 50);


After:

int f(int,int); int (*p)(int, int) = &f;

int i = (*p)(10, 50);


---

## readability-redundant-inline-specifier

Detects redundant `inline` specifiers on function and variable declarations. It's giving "I said it twice and nobody asked" ๐Ÿ™ˆ.

Examples:

constexpr inline void f() {}


In the example above the keyword `inline` is redundant since `constexpr` functions are implicitly inlined.

class MyClass { inline void myMethod() {} };


In the example above the keyword `inline` is redundant since member functions defined entirely inside a class/struct/union definition are implicitly inlined.

### Options

- **`StrictMode`** โ€” if set to `true`, the check will also flag functions and variables that already have internal linkage as redundant.

---

## readability-redundant-member-init

Finds member initializations that are unnecessary because the same default constructor would be called if they were not present. Basically, you're initializing stuff that would've initialized itself anyway. Iconic of you to try, but no ๐Ÿ’….

### Example

// Explicitly initializing the member s and v is unnecessary. class Foo { public: Foo() : s() {}

private: std::string s; std::vector<int> v {}; };


### Options

- **`IgnoreBaseInCopyConstructors`** โ€” default is `false`.

  When `true`, the check will ignore unnecessary base class initializations within copy constructors, since some compilers issue warnings/errors when base classes are not explicitly initialized in copy constructors. For example, `gcc` with `-Wextra` or `-Werror=extra` issues warning or error `base class 'Bar' should be explicitly initialized in the copy constructor` if `Bar()` were removed in the following example:

// Explicitly initializing member s and base class Bar is unnecessary. struct Foo : public Bar { // Remove s() below. If IgnoreBaseInCopyConstructors!=0, keep Bar(). Foo(const Foo& foo) : Bar(), s() {} std::string s; };


---

## readability-redundant-preprocessor

Finds potentially redundant preprocessor directives. At the moment the following cases are detected:

- `#ifdef .. #endif` pairs which are nested inside an outer pair with the same condition. For example:

#ifdef FOO #ifdef FOO // inner ifdef is considered redundant void f(); #endif #endif


- Same for `#ifndef .. #endif` pairs. For example:

#ifndef FOO #ifndef FOO // inner ifndef is considered redundant void f(); #endif #endif


- `#ifndef` inside an `#ifdef` with the same condition:

#ifdef FOO #ifndef FOO // inner ifndef is considered redundant void f(); #endif #endif


- `#ifdef` inside an `#ifndef` with the same condition:

#ifndef FOO #ifdef FOO // inner ifdef is considered redundant void f(); #endif #endif


- `#if .. #endif` pairs which are nested inside an outer pair with the same condition. For example:

#define FOO 4 #if FOO == 4 #if FOO == 4 // inner if is considered redundant void f(); #endif #endif


---

## readability-redundant-smartptr-get

Find and remove redundant calls to a smart pointer's `.get()` method. Bestie, `.get()` is doing NOTHING here, let it go ๐Ÿฆ‹.

Examples:

ptr.get()->Foo() ==> ptr->Foo() *ptr.get() ==> *ptr *ptr->get() ==> **ptr if (ptr.get() == nullptr) ... => if (ptr == nullptr) ...


- **`IgnoreMacros`** โ€” if this option is set to `true` (default is `true`), the check will not warn about calls inside macros.

---

## readability-redundant-string-cstr

Finds unnecessary calls to `std::string::c_str()` and `std::string::data()`. You don't need to c-string-ify it, the function takes it fine, bestie ๐Ÿ˜ญ.

### Options

- **`StringParameterFunctions`** โ€” a semicolon-separated list of (fully qualified) function/method/operator names, with the requirement that any parameter currently accepting a `const char*` input should also be able to accept `std::string` inputs, or proper overload candidates that can do so should exist. This can be used to configure functions such as `fmt::format`, `spdlog::logger::info`, or wrappers around these and similar functions. The default value is the empty string.

---

## readability-redundant-string-init

Finds unnecessary string initializations. Empty string literal? Not needed. Slay.

### Examples

// Initializing string with empty string literal is unnecessary. std::string a = ""; std::string b("");

// becomes

std::string a; std::string b;

// Initializing a string_view with an empty string literal produces an // instance that compares equal to string_view(). std::string_view a = ""; std::string_view b("");

// becomes std::string_view a; std::string_view b;


### Options

- **`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.

---

## readability-reference-to-constructed-temporary

Detects C++ code where a reference variable is used to extend the lifetime of a temporary object that has just been constructed. And bestie, that is a TRAP ๐Ÿšฉ.

This construction is often the result of multiple code refactorings or a lack of developer knowledge, leading to confusion or subtle bugs. In most cases, what the developer really wanted to do is create a new variable rather than extending the lifetime of a temporary object.

Examples of problematic code include:

const std::string& str("hello");

struct Point { int x; int y; }; const Point& p = { 1, 2 };


In the first example, a `const std::string&` reference variable `str` is assigned a temporary object created by the `std::string("hello")` constructor. In the second example, a `const Point&` reference variable `p` is assigned an object that is constructed from an initializer list `{ 1, 2 }`. Both of these examples extend the lifetime of the temporary object to the lifetime of the reference variable, which can make it difficult to reason about and may lead to subtle bugs or misunderstanding.

To avoid these issues, it is recommended to change the reference variable to a (const) value variable. Do that, and you're golden โœจ.

---

## readability-simplify-boolean-expr

Looks for boolean expressions involving boolean constants and simplifies them to use the appropriate boolean expression directly. Simplifies boolean expressions by application of DeMorgan's Theorem. Math besties, this one's for you ๐Ÿ’œ.

<pink>okay so</pink> here's the glow-up cheat sheet for what the check turns your messy boolean soup into:

- `if (b == true)` โ†’ `if (b)`
- `if (b == false)` โ†’ `if (!b)`
- `if (b && true)` โ†’ `if (b)`
- `if (b && false)` โ†’ `if (false)`
- `if (b || true)` โ†’ `if (true)`
- `if (b || false)` โ†’ `if (b)`
- `e ? true : false` โ†’ `e`
- `e ? false : true` โ†’ `!e`
- `if (true) t(); else f();` โ†’ `t();`
- `if (false) t(); else f();` โ†’ `f();`
- `if (e) return true; else return false;` โ†’ `return e;`
- `if (e) return false; else return true;` โ†’ `return !e;`
- `if (e) b = true; else b = false;` โ†’ `b = e;`
- `if (e) b = false; else b = true;` โ†’ `b = !e;`
- `if (e) return true; return false;` โ†’ `return e;`
- `if (e) return false; return true;` โ†’ `return !e;`
- `!(!a || b)` โ†’ `a && !b`
- `!(a || !b)` โ†’ `!a && b`
- `!(!a || !b)` โ†’ `a && b`
- `!(!a && b)` โ†’ `a || !b`
- `!(a && !b)` โ†’ `!a || b`
- `!(!a && !b)` โ†’ `a || b`

And bestie, the resulting expression `e` gets <hotpink>glow-up-ified</hotpink> like this:

1. Unnecessary parentheses around the expression are removed.

2. Negated applications of `!` are eliminated.

3. Negated applications of comparison operators are changed to use the opposite condition.

4. Implicit conversions of pointers, including pointers to members, to `bool` are replaced with explicit comparisons to `nullptr` in C++11 or `NULL` in C++98/03.

5. Implicit casts to `bool` are replaced with explicit casts to `bool`.

6. Object expressions with explicit `operator bool` conversion operators are replaced with explicit casts to `bool`.

7. Implicit conversions of integral types to `bool` are replaced with explicit comparisons to `0`.

<purple>Examples, because we love a demo:</purple>

1. The ternary assignment `bool b = (i < 0) ? true : false;` has redundant parentheses and becomes `bool b = i < 0;`.

2. The conditional return `if (!b) return false; return true;` has an implied double negation and becomes `return b;`.

3. The conditional return `if (i < 0) return false; return true;` becomes `return i >= 0;`.

   The conditional return `if (i != 0) return false; return true;` becomes `return i == 0;`.

4. The conditional return `if (p) return true; return false;` has an implicit conversion of a pointer to `bool` and becomes `return p != nullptr;`.

   The ternary assignment `bool b = (i & 1) ? true : false;` has an implicit conversion of `i & 1` to `bool` and becomes `bool b = (i & 1) != 0;`.

5. The conditional return `if (i & 1) return true; else return false;` has an implicit conversion of an integer quantity `i & 1` to `bool` and becomes `return (i & 1) != 0;`

6. Given `struct X { explicit operator bool(); };`, and an instance `x` of struct `X`, the conditional return `if (x) return true; return false;` becomes `return static_cast<bool>(x)`

---

### โœจ Options โœจ

- **`IgnoreMacros`** โ€” if `true`, ignore boolean expressions originating from expanded macros. Default is `false`.
- **`ChainedConditionalReturn`** โ€” if `true`, conditional boolean return statements at the end of an `if/else if` chain will be transformed. Default is `false`.
- **`ChainedConditionalAssignment`** โ€” if `true`, conditional boolean assignments at the end of an `if/else if` chain will be transformed. Default is `false`.
- **`SimplifyDeMorgan`** โ€” if `true`, DeMorgan's Theorem will be applied to simplify negated conjunctions and disjunctions. Default is `true`.
- **`SimplifyDeMorganRelaxed`** โ€” if `true`, `SimplifyDeMorgan` will also transform negated conjunctions and disjunctions where there is no negation on either operand. This option has no effect if `SimplifyDeMorgan` is `false`. Default is `false`.

  When Enabled:

bool X = !(A && B) bool Y = !(A || B)


Would be transformed to:

bool X = !A || !B bool Y = !A && !B


---

## ๐ŸŒธ readability-simplify-subscript-expr ๐ŸŒธ

This check simplifies subscript expressions. Currently this covers calling `.data()` and immediately doing an array subscript operation to obtain a single element, in which case simply calling `operator[]` suffice.

Examples:

std::string s = ...; char c = s.data()[i]; // char c = s[i];


### โœจ Options โœจ

- **`Types`** โ€” The list of type(s) that triggers this check. Default is `::std::basic_string;::std::basic_string_view;::std::vector;::std::array;::std::span`

---

## ๐Ÿ’… readability-static-accessed-through-instance ๐Ÿ’…

Checks for member expressions that access static members through instances, and replaces them with uses of the appropriate qualified-id.

Example:

The following code:

struct C { static void foo(); static int x; enum { E1 }; enum E { E2 }; };

C *c1 = new C(); c1->foo(); c1->x; c1->E1; c1->E2;


is changed to:

C *c1 = new C(); C::foo(); C::x; C::E1; C::E2;


The `--fix` commandline option provides default support for safe fixes, whereas `--fix-notes` enables fixes that may replace expressions with side effects, potentially altering the program's behavior.

> <red>heads up bestie</red>: `--fix-notes` can swap out expressions that have side effects, which can literally change how your program behaves. not a vibe, be careful.

---

## ๐Ÿฆ‹ readability-static-definition-in-anonymous-namespace ๐Ÿฆ‹

Finds `static` function and variable definitions in anonymous namespace.

In this case, `static` is redundant, because anonymous namespace limits the visibility of definitions to a single translation unit.

namespace { static int a = 1; // Warning. static const int b = 1; // Warning. namespace inner { static int c = 1; // Warning. } }


The check will apply a fix by removing the redundant `static` qualifier.

---

## ๐ŸŽ€ readability-string-compare ๐ŸŽ€

Finds string comparisons using the `compare` method.

A common mistake is to use the string's `compare` method instead of using the equality or inequality operators. The `compare` method is intended for sorting functions and thus returns a negative number, a positive number or zero depending on the lexicographical relationship between the strings compared. If an equality or inequality check can suffice, that is recommended. This is recommended to avoid the risk of incorrect interpretation of the return value and to simplify the code. The string equality and inequality operators can also be faster than the `compare` method due to early termination.

### โœจ Example โœจ

// The same rules apply to std::string_view. std::string str1{"a"}; std::string str2{"b"};


okay bestie, here's the rest of the receipts ๐Ÿ’… this is the whole list of `if`-statements that get flagged by this check, and every single one is out here doing the most with `compare` when the operators were *right there*:

// use str1 != str2 instead. if (str1.compare(str2)) { }

// use str1 == str2 instead. if (!str1.compare(str2)) { }

// use str1 == str2 instead. if (str1.compare(str2) == 0) { }

// use str1 != str2 instead. if (str1.compare(str2) != 0) { }

// use str1 == str2 instead. if (0 == str1.compare(str2)) { }

// use str1 != str2 instead. if (0 != str1.compare(str2)) { }

// Use str1 == "foo" instead. if (str1.compare("foo") == 0) { }


so basically: everything up there is using `compare` to do an equality or inequality vibe check on two strings instead of just... using the correct operators. <pink>embarrassing for them, honestly.</pink> ๐Ÿ˜ญ

### โœจ Options โœจ

- **`StringLikeClasses`** โ€” a string holding semicolon-separated names of string-like classes. <lavender>By default it's just</lavender> `::std::basic_string` and `::std::basic_string_view`. If some class from this list has a `compare` method that works like the one on `std::string`, it gets checked exactly the same way. ๐Ÿซถ

### ๐Ÿงช Example ๐Ÿงช

struct CustomString { public: int compare (const CustomString& other) const; }

CustomString str1; CustomString str2;

// use str1 != str2 instead. if (str1.compare(str2)) { }


and if `StringLikeClasses` contains `CustomString`, the check will <hotpink>pop off</hotpink> and suggest swapping that `compare` for an equality operator. โœจ

---

## ๐Ÿ’… readability-suspicious-call-argument

<rainbow>okay this one is iconic and also mildly terrifying</rainbow> โ€” it finds function calls where the arguments got passed in the wrong order, based on how different the argument names are from the parameter names.

so like, given a call `f(foo, bar);` and a signature `void f(T tvar, U uvar)`, the arguments `foo` and `bar` are considered swapped if `foo` (the argument name) is <purple>more similar to</purple> `uvar` (the *other* parameter) than to `tvar` (the parameter it's currently being passed to), AND `bar` is more similar to `tvar` than to `uvar`. ๐Ÿ’€

a warning here might mean the args are genuinely swapped, or it might just mean the names' cross-similarity is making the code harder to read. either way, <cyan>bestie, look at it</cyan>.

### ๐Ÿ•ต๏ธ Heuristics

these are the heuristics this check ships with. if <hotpink>any</hotpink> of the enabled ones decides the arguments are out of order, you get a warning. ๐Ÿ”ฅ

the heuristics themselves work by looking at <lavender>pairs</lavender> of strings, and they're symmetric โ€” so below, there's no distinction between which string is the argument name and which is the parameter name. no cap.

### Equality

the most trivial one, bestie. it just compares the two strings for <pink>case-insensitive equality</pink>. that's it. that's the heuristic. ๐Ÿ’…

### Abbreviation

you can register common abbreviations that make strings count as similar when the abbreviated thing and the abbreviation show up together. for example, if `src` is registered as an abbreviation for `source`, then this lil code example gets warned about:

void foo(int source, int x);

foo(b, src);


the abbreviations to recognise are configurable with the `Abbreviations` check option. this heuristic is case-insensitive. ๐ŸŽ€

### Prefix

the prefix heuristic yells if one of the strings is a <mint>sufficiently long prefix</mint> of the other, e.g. `target` to `targetPtr`. the similarity percentage is the length ratio of the prefix to the longer string โ€” in that previous example it'd be `6 / 9 = 66.66...%`.

you can configure this one with bounds. the default bounds are: <yellow>below 25% dissimilar and above 30% similar</yellow>. this heuristic is case-insensitive. โœจ

### Suffix

analogous to the Prefix heuristic, just flipped around. for `oldValue` and `value` compared, the similarity percentage is `8 / 5 = 62.5%`.

configurable with bounds, same deal. the default bounds are: <yellow>below 25% dissimilar and above 30% similar</yellow>. case-insensitive. ๐Ÿฆ‹

### Substring

the substring heuristic is the prefix heuristic and the suffix heuristic's <hotpink>collab</hotpink> โ€” it tries to find the longest common substring in the two strings you gave it. the similarity percentage is the ratio of that found longest common substring against the longer of the two input strings. for example, given `val` and `rvalue`, the similarity is `3 / 6 = 50%`. if there are no characters in common between the two strings, it's 0%.

you know the drill, bounds. the default bounds are: <yellow>below 40% dissimilar and above 50% similar</yellow>. this heuristic is case-insensitive. ๐ŸŒธ

### Levenshtein distance (as Levenshtein)

the Levenshtein distance describes how many single-character changes (additions, changes, or removals) you'd have to apply to transform one string into another. โœ๏ธ

it gets translated into a similarity percentage by dividing it by the length of the longer string, then taking its complement with regards to 100%. for example, given `something` and `anything`, the distance is <purple>4 edits</purple>, and the similarity percentage is `100% - 4 / 9 = 55.55...%`.

configurable with bounds. the default bounds are: <yellow>below 50% dissimilar and above 66% similar</yellow>. โš ๏ธ this heuristic is <red>case-sensitive</red>.

### Jaro--Winkler distance (as JaroWinkler)

the Jaro--Winkler distance is an edit distance, same family as the Levenshtein one. it's calculated from how many common characters are <mint>sufficiently close to each other in position</mint>, plus the to-be-changed characters. the original Jaro definition got extended by Winkler to weigh prefix similarities more. the similarity percentage is expressed as an average of the common and non-common characters against the length of both strings. ๐Ÿงฎ

configurable with bounds. the default bounds are: <yellow>below 75% dissimilar and above 85% similar</yellow>. this heuristic is case-insensitive. ๐Ÿ’œ

### Sรธrensen--Dice coefficient (as Dice)

the Sรธrensen--Dice coefficient was originally defined to measure the similarity of two <pink>sets</pink>. formally, the coefficient is calculated by dividing `2 * #(intersection)` with `#(set1) + #(set2)`, where `#()` is the cardinality function of sets. for strings, this metric gets applied by creating bigrams (substring sequences of length 2) of the two strings and using the set of bigrams for each string as the two sets. ๐ŸŽฒ

configurable with bounds. the default bounds are: <yellow>below 60% dissimilar and above 70% similar</yellow>. this heuristic is case-insensitive. ๐ŸŒˆ

### โš™๏ธ Options โš™๏ธ

- **`MinimumIdentifierNameLength`** โ€” sets the minimum required length that the argument and parameter names have to hit. names shorter than this get ignored entirely. defaults to `3`.

- **`Abbreviations`** โ€” for the Abbreviation heuristic (see here ๐Ÿ‘†), this option configures the abbreviations in the `"abbreviation=abbreviated_value"` format. it's a string, with each value joined by `;`.

  by default, these abbreviations are set:

  - `addr=address`
  - `arr=array`
  - `attr=attribute`
  - `buf=buffer`
  - `cl=client`
  - `cnt=count`
  - `col=column`
  - `cpy=copy`
  - `dest=destination`
  - `dist=distance`
  - `dst=distance`
  - `elem=element`
  - `hght=height`
  - `i=index`
  - `idx=index`
  - `len=length`
  - `ln=line`
  - `lst=list`
  - `nr=number`
  - `num=number`
  - `pos=position`
  - `ptr=pointer`
  - `ref=reference`
  - `src=source`
  - `srv=server`
  - `stmt=statement`
  - `str=string`
  - `val=value`
  - `var=variable`
  - `vec=vector`
  - `wdth=width`

the configuration options for each implemented heuristic (see above) get constructed <hotpink>dynamically</hotpink>. from here on, `<HeuristicName>` refers to one of the keys from the heuristics implemented. iykyk. ๐Ÿซถ

- **`<HeuristicName>`** โ€” True or False, whether a particular heuristic, like Equality or Levenshtein, is enabled.

  defaults to `True` for every single heuristic. <green>all on by default, bestie, we're thorough.</green>

- **`<HeuristicName>DissimilarBelow`, `<HeuristicName>SimilarAbove`** โ€” a value between `0` and `100`, expressing a percentage. these bounds set what percentage of similarity the heuristic has to deduce for the two identifiers to be considered similar or dissimilar by the check.

  given arguments `arg1` and `arg2` passed to `param1` and `param2`, respectively, the bounds check goes down like this: if the similarity of the currently passed argument order (`arg1` to `param1`) is <purple>below</purple> the `DissimilarBelow` threshold, AND the similarity of the suggested swapped order (`arg1` to `param2`) is <hotpink>above</hotpink> the `SimilarAbove` threshold, the swap gets reported. ๐Ÿ””

  for the defaults of each heuristic, see above. ๐Ÿ‘†

### ๐Ÿงฌ Name synthesis

when it's comparing argument names and parameter names, here's the logic it uses to gather up the names for comparison:

parameter names are the identifiers <lavender>exactly as written in the source code</lavender>.

argument names are:

- if a variable is passed, the variable's name.
- if a subsequent function call's return value is used as the argument, the called function's name.
- otherwise, <red>empty string</red>.

empty argument or parameter names are ignored by the heuristics. ๐Ÿšซ

---

## ๐ŸŒŸ readability-uniqueptr-delete-release

swap `delete <unique_ptr>.release()` for `<unique_ptr> = nullptr`. the latter is shorter, simpler, and doesn't require you to touch raw pointer APIs at all. <mint>glow up.</mint> โœจ

std::unique_ptr<int> P; delete P.release();

// becomes

std::unique_ptr<int> P; P = nullptr;


### โš™๏ธ Options โš™๏ธ

- **`PreferResetCall`** โ€” if true, refactor by calling the `reset` member function instead of assigning to `nullptr`. default value is `false`.

std::unique_ptr<int> P; delete P.release();

// becomes

std::unique_ptr<int> P; P.reset();


---

## ๐Ÿ’– readability-uppercase-literal-suffix

okay so `cert-dcl16-c` redirects here as an alias for this check. <purple>by default, only the suffixes that begin with `l`</purple> (`l`, `ll`, `lu`, `llu`, but <red>not</red> `u`, `ul`, `ull`) are diagnosed by that alias. so pay attention to which one you're actually getting. ๐Ÿ‘€

`hicpp-uppercase-literal-suffix` also redirects here as an alias for this check. ๐ŸŽ€

it detects when an integral literal or a floating point (decimal or hexadecimal) literal has a non-uppercase suffix, and hands you a fix-it hint with the uppercase suffix. ๐Ÿ’…

all valid combinations of suffixes are supported. <rainbow>every single one, no crumbs.</rainbow>

auto x = 1; // OK, no suffix.

auto x = 1u; // warning: integer literal suffix 'u' is not upper-case

auto x = 1U; // OK, suffix is uppercase.

...


### โœจ Options โœจ

okay so this check takes a couple of options and honestly they're lowkey iconic ๐Ÿ’…

- **`NewSuffixes`** โ€” Optionally, a list of the destination suffixes can be provided. When the suffix is found, a <pink>case-insensitive</pink> lookup in that list is made, and if a replacement is found that is different from the current suffix, then the diagnostic is issued. It's giving <purple>fine-grained control</purple>: you decide exactly what suffixes to consider and what their replacements should be. ๐ŸŽ€

---

### ๐ŸŒˆ Example ๐ŸŒˆ

say you hand over the list `L;uL` โ€” here's the whole vibe check:

- `l` -> `L`
- `L` will be kept as is.
- `ul` -> `uL`
- `Ul` -> `uL`
- `UL` -> `uL`
- `uL` will be kept as is.
- `ull` will be kept as is, since it is not in the list
- and so on.

fr fr, that's it โ€” if it's not on the list, she's minding her own business โœจ

and then, back in option-town:

- **`IgnoreMacros`** โ€” If this option is set to `true` (**default is `true`**), the check will not warn about literal suffixes inside macros. so macros get a lil pass ๐Ÿซถ

---

### ๐Ÿ‘‘ `readability-use-anyofallof`

Finds range-based `for` loops that can be replaced by a call to `std::any_of` or `std::all_of`. And when you're in <cyan>C++20 mode</cyan>, she gets fancy and suggests `std::ranges::any_of` or `std::ranges::all_of` instead. ๐ŸŒธ

Example:

bool all_even(std::vector<int> V) { for (int I : V) { if (I % 2) return false; } return true; // Replace loop by // return std::ranges::all_of(V, [](int I) { return I % 2 == 0; }); }


---

### ๐Ÿ”ฅ `readability-use-std-min-max`

Replaces certain conditional statements with equivalent calls to `std::min` or `std::max`. Clean, slay, no cap ๐Ÿ’…

> โš ๏ธ okay heads up bestie: **Note:** This may impact performance in critical code due to potential additional stores compared to the original `if` statement. not the vibe if you're counting every single cycle.

Before:

void foo() { int a = 2, b = 3; if (a < b) a = b; }


After:

void foo() { int a = 2, b = 3; a = std::max(a, b); }


---

### ๐Ÿ’œ `zircon-temporary-objects`

Warns on construction of specific temporary objects in the <purple>Zircon kernel</purple>. ๐Ÿ›‘ If the object should be flagged, If the object should be flagged, the fully qualified type name must be explicitly passed to the check. (yes, she said it twice, she's *that* serious ๐Ÿ˜ค)

For example, given the list of classes `"Foo"` and `"NS::Bar"`, all of the following will trigger the warning:

Foo(); Foo F = Foo(); func(Foo());

namespace NS {

Bar();

}


With the same list, the following will **not** trigger the warning:

Foo F; // Non-temporary construction okay Foo F(param); // Non-temporary construction okay Foo *F = new Foo(); // New construction okay

Bar(); // Not NS::Bar, so okay NS::Bar B; // Non-temporary construction okay


Note that objects must be **explicitly** specified in order to be flagged, and so objects that inherit a specified object will not be flagged. ๐Ÿ‘€

This check matches temporary objects without regard for inheritance, and so a prohibited base class type does not similarly prohibit derived class types.

class Derived : Foo {} // Derived is not explicitly disallowed Derived(); // and so temporary construction is okay


### โœจ Options โœจ

- **`Names`** โ€” a semi-colon-separated list of fully-qualified names of C++ classes that should <hotpink>not</hotpink> be constructed as temporaries. Default is <cyan>empty</cyan>. ๐Ÿซถ

---

okay bestie, so now we get to the part everyone actually came for: the <rainbow>big list of check names</rainbow> ๐Ÿ’– Each one has a name, and then whether it offers fixes or not. That's it, that's the tea. Scroll responsibly. ๐ŸŒธ

- **`abseil-cleanup-ctad`** โ€” offers fixes โœ…
- **`abseil-duration-addition`** โ€” offers fixes โœ…
- **`abseil-duration-comparison`** โ€” offers fixes โœ…
- **`abseil-duration-conversion-cast`** โ€” offers fixes โœ…
- **`abseil-duration-division`** โ€” offers fixes โœ…
- **`abseil-duration-factory-float`** โ€” offers fixes โœ…
- **`abseil-duration-factory-scale`** โ€” offers fixes โœ…
- **`abseil-duration-subtraction`** โ€” offers fixes โœ…
- **`abseil-duration-unnecessary-conversion`** โ€” offers fixes โœ…
- **`abseil-faster-strsplit-delimiter`** โ€” offers fixes โœ…
- **`abseil-no-internal-dependencies`** โ€” no fixes, it's just judging you ๐Ÿ™ˆ
- **`abseil-no-namespace`** โ€” also no fixes ๐Ÿ’€
- **`abseil-redundant-strcat-calls`** โ€” offers fixes โœ…
- **`abseil-str-cat-append`** โ€” offers fixes โœ…
- **`abseil-string-find-startswith`** โ€” offers fixes โœ…
- **`abseil-string-find-str-contains`** โ€” offers fixes โœ…
- **`abseil-time-comparison`** โ€” offers fixes โœ…
- **`abseil-time-subtraction`** โ€” offers fixes โœ…
- **`abseil-upgrade-duration-conversions`** โ€” offers fixes โœ…
- **`altera-id-dependent-backward-branch`** โ€” no fixes ๐Ÿซ 
- **`altera-kernel-name-restriction`** โ€” no fixes ๐Ÿ˜ญ
- **`altera-single-work-item-barrier`** โ€” no fixes, sorry bestie ๐Ÿ’…
- **`altera-struct-pack-align`** โ€” offers fixes โœ…
- **`altera-unroll-loops`** โ€” no fixes ๐ŸŒธ
- **`android-cloexec-accept`** โ€” offers fixes โœ…
- **`android-cloexec-accept4`** โ€” offers fixes โœ…
- **`android-cloexec-creat`** โ€” offers fixes โœ…
- **`android-cloexec-dup`** โ€” offers fixes โœ…
- **`android-cloexec-epoll-create`** โ€” offers fixes โœ…
- **`android-cloexec-epoll-create1`** โ€” offers fixes โœ…
- **`android-cloexec-fopen`** โ€” offers fixes โœ…
- **`android-cloexec-inotify-init`** โ€” offers fixes โœ…
- **`android-cloexec-inotify-init1`** โ€” offers fixes โœ…
- **`android-cloexec-memfd-create`** โ€” offers fixes โœ…
- **`android-cloexec-open`** โ€” offers fixes โœ…
- **`android-cloexec-pipe`** โ€” offers fixes โœ…

okay bestie, the giant checklist just keeps going โ€” here's the next chunk of it, same deal as before, second column is either a `Yes` or just vibes (empty). ๐Ÿ‘‘

- **`android-cloexec-pipe2`** โ€” <green>Yes</green> โœจ
- **`android-cloexec-socket`** โ€” <green>Yes</green> โœจ
- **`android-comparison-in-temp-failure-retry`** โ€” <lavender>โ€”</lavender>
- **`boost-use-ranges`** โ€” <green>Yes</green> โœจ
- **`boost-use-to-string`** โ€” <green>Yes</green> โœจ
- **`bugprone-argument-comment`** โ€” <green>Yes</green> โœจ
- **`bugprone-assert-side-effect`** โ€” <lavender>โ€”</lavender>
- **`bugprone-assignment-in-if-condition`** โ€” <lavender>โ€”</lavender>
- **`bugprone-bad-signal-to-kill-thread`** โ€” <lavender>โ€”</lavender>
- **`bugprone-bool-pointer-implicit-conversion`** โ€” <green>Yes</green> โœจ
- **`bugprone-branch-clone`** โ€” <lavender>โ€”</lavender>
- **`bugprone-casting-through-void`** โ€” <lavender>โ€”</lavender>
- **`bugprone-chained-comparison`** โ€” <lavender>โ€”</lavender>
- **`bugprone-compare-pointer-to-member-virtual-function`** โ€” <lavender>โ€”</lavender>
- **`bugprone-copy-constructor-init`** โ€” <green>Yes</green> โœจ
- **`bugprone-crtp-constructor-accessibility`** โ€” <green>Yes</green> โœจ
- **`bugprone-dangling-handle`** โ€” <lavender>โ€”</lavender>
- **`bugprone-dynamic-static-initializers`** โ€” <lavender>โ€”</lavender>
- **`bugprone-easily-swappable-parameters`** โ€” <lavender>โ€”</lavender>
- **`bugprone-empty-catch`** โ€” <lavender>โ€”</lavender>
- **`bugprone-exception-escape`** โ€” <lavender>โ€”</lavender>
- **`bugprone-fold-init-type`** โ€” <lavender>โ€”</lavender>
- **`bugprone-forward-declaration-namespace`** โ€” <lavender>โ€”</lavender>
- **`bugprone-forwarding-reference-overload`** โ€” <lavender>โ€”</lavender>
- **`bugprone-implicit-widening-of-multiplication-result`** โ€” <green>Yes</green> โœจ
- **`bugprone-inaccurate-erase`** โ€” <green>Yes</green> โœจ
- **`bugprone-inc-dec-in-conditions`** โ€” <lavender>โ€”</lavender>
- **`bugprone-incorrect-enable-if`** โ€” <green>Yes</green> โœจ
- **`bugprone-incorrect-roundings`** โ€” <lavender>โ€”</lavender>
- **`bugprone-infinite-loop`** โ€” <lavender>โ€”</lavender>
- **`bugprone-integer-division`** โ€” <lavender>โ€”</lavender>
- **`bugprone-lambda-function-name`** โ€” <lavender>โ€”</lavender>
- **`bugprone-macro-parentheses`** โ€” <green>Yes</green> โœจ
- **`bugprone-macro-repeated-side-effects`** โ€” <lavender>โ€”</lavender>
- **`bugprone-misplaced-operator-in-strlen-in-alloc`** โ€” <green>Yes</green> โœจ
- **`bugprone-misplaced-pointer-arithmetic-in-alloc`** โ€” <green>Yes</green> โœจ
- **`bugprone-misplaced-widening-cast`** โ€” <lavender>โ€”</lavender>
- **`bugprone-move-forwarding-reference`** โ€” <green>Yes</green> โœจ
- **`bugprone-multi-level-implicit-pointer-conversion`** โ€” <lavender>โ€”</lavender>

ngl bestie the `bugprone-` girlies are carrying this whole stretch fr fr ๐Ÿ’… and we are <pink>still</pink> not done โ€” more of the list is coming, so don't you dare close the tab. ๐Ÿฆ‹

still deep in the check list, bestie โ€” same two columns as always, check name on the left, that right-hand column on the right. carrying straight on through the `bugprone-*` crowd ๐Ÿ’–

- **`bugprone-multiple-new-in-one-expression`** โ€” <lavender>right-hand column is empty, no `Yes` here</lavender>
- **`bugprone-multiple-statement-macro`** โ€” <lavender>blank again ๐Ÿ™ˆ</lavender>
- **`bugprone-no-escape`** โ€” <lavender>nothing in that column</lavender>
- **`bugprone-non-zero-enum-to-bool-conversion`** โ€” <lavender>blank, no `Yes`</lavender>
- **`bugprone-not-null-terminated-result`** โ€” <green>`Yes`</green> โœจ
- **`bugprone-optional-value-conversion`** โ€” <green>`Yes`</green> ๐Ÿ’…
- **`bugprone-parent-virtual-call`** โ€” <green>`Yes`</green>
- **`bugprone-pointer-arithmetic-on-polymorphic-object`** โ€” <lavender>blank</lavender>
- **`bugprone-posix-return`** โ€” <green>`Yes`</green>
- **`bugprone-redundant-branch-condition`** โ€” <green>`Yes`</green>
- **`bugprone-reserved-identifier`** โ€” <green>`Yes`</green> ๐Ÿ‘‘
- **`bugprone-return-const-ref-from-parameter`** โ€” <lavender>blank, no `Yes`</lavender>
- **`bugprone-shared-ptr-array-mismatch`** โ€” <green>`Yes`</green>
- **`bugprone-signal-handler`** โ€” <lavender>blank</lavender>
- **`bugprone-signed-char-misuse`** โ€” <lavender>blank</lavender>
- **`bugprone-sizeof-container`** โ€” <lavender>blank</lavender>
- **`bugprone-sizeof-expression`** โ€” <lavender>blank</lavender>

three `sizeof`-ish neighbours with nothing in that column, the streak is lowkey real ๐Ÿ˜ญ and it keeps going:

- **`bugprone-spuriously-wake-up-functions`** โ€” <lavender>blank</lavender>
- **`bugprone-standalone-empty`** โ€” <green>`Yes`</green>
- **`bugprone-string-constructor`** โ€” <green>`Yes`</green>
- **`bugprone-string-integer-assignment`** โ€” <green>`Yes`</green>
- **`bugprone-string-literal-with-embedded-nul`** โ€” <lavender>blank</lavender>
- **`bugprone-stringview-nullptr`** โ€” <green>`Yes`</green>
- **`bugprone-suspicious-enum-usage`** โ€” <lavender>blank</lavender>
- **`bugprone-suspicious-include`** โ€” <lavender>blank</lavender>
- **`bugprone-suspicious-memory-comparison`** โ€” <lavender>blank</lavender>
- **`bugprone-suspicious-memset-usage`** โ€” <green>`Yes`</green>
- **`bugprone-suspicious-missing-comma`** โ€” <lavender>blank</lavender>
- **`bugprone-suspicious-realloc-usage`** โ€” <lavender>blank</lavender>
- **`bugprone-suspicious-semicolon`** โ€” <green>`Yes`</green>
- **`bugprone-suspicious-string-compare`** โ€” <green>`Yes`</green>
- **`bugprone-suspicious-stringview-data-usage`** โ€” <lavender>blank</lavender>
- **`bugprone-swapped-arguments`** โ€” <green>`Yes`</green>
- **`bugprone-switch-missing-default-case`** โ€” <lavender>blank</lavender>
- **`bugprone-terminating-continue`** โ€” <green>`Yes`</green>
- **`bugprone-throw-keyword-missing`** โ€” <lavender>blank</lavender>
- **`bugprone-too-small-loop-variable`** โ€” <lavender>blank</lavender>
- **`bugprone-unchecked-optional-access`** โ€” <lavender>blank</lavender>

okay continuing the list bestie, no stopping, we're in the zone now โœจ

- `bugprone-undefined-memory-manipulation`
- `bugprone-undelegated-constructor`
- `bugprone-unhandled-exception-at-new`
- `bugprone-unhandled-self-assignment`
- **`bugprone-unique-ptr-array-mismatch`** โ€” ๐Ÿ’… <green>Yes</green>
- `bugprone-unsafe-functions`
- `bugprone-unused-local-non-trivial-variable`
- **`bugprone-unused-raii`** โ€” ๐Ÿ’… <green>Yes</green>
- `bugprone-unused-return-value`
- `bugprone-use-after-move`
- **`bugprone-virtual-near-miss`** โ€” ๐Ÿ’… <green>Yes</green>
- `cert-dcl50-cpp`
- `cert-dcl58-cpp`
- `cert-env33-c`
- `cert-err33-c`
- `cert-err34-c`
- `cert-err52-cpp`
- `cert-err58-cpp`
- `cert-err60-cpp`
- `cert-flp30-c`
- `cert-mem57-cpp`
- `cert-msc50-cpp`
- `cert-msc51-cpp`
- `cert-oop57-cpp`
- `cert-oop58-cpp`
- `concurrency-mt-unsafe`
- `concurrency-thread-canceltype-asynchronous`
- `cppcoreguidelines-avoid-capturing-lambda-coroutines`
- `cppcoreguidelines-avoid-const-or-ref-data-members`
- `cppcoreguidelines-avoid-do-while`
- `cppcoreguidelines-avoid-goto`
- `cppcoreguidelines-avoid-non-const-global-variables`
- `cppcoreguidelines-avoid-reference-coroutine-parameters`
- **`cppcoreguidelines-init-variables`** โ€” ๐Ÿ’… <green>Yes</green>
- `cppcoreguidelines-interfaces-global-init`
- `cppcoreguidelines-macro-usage`
- **`cppcoreguidelines-misleading-capture-default-by-value`** โ€” ๐Ÿ’… <green>Yes</green>
- `cppcoreguidelines-missing-std-forward`

<purple>and that's where this lil chunk of the table ends, bestie โ€” more coming, don't you dare close the pager ๐Ÿ’œ</purple>

okay continuing the list bestie, we're deep in the `cppcoreguidelines-*` zone and honestly it's giving โœจ *"my code is a guideline violation and I know it"* โœจ โ€” here's the rest of the roster:

- **`cppcoreguidelines-narrowing-conversions`** โ€” the one that clocks you silently shrinking a value into a smaller type ๐Ÿซฃ
- **`cppcoreguidelines-no-malloc`** โ€” calling out raw `malloc` energy in C++ land ๐Ÿ’€
- **`cppcoreguidelines-no-suspend-with-lock`** โ€” do NOT suspend while you're holding a lock, that's a jump scare fr fr
- **`cppcoreguidelines-owning-memory`** โ€” the ownership-vibes check ๐Ÿ‘‘
- **`cppcoreguidelines-prefer-member-initializer`** โ€” <yellow>Yes</yellow> โ€” it wants that member initializer list, periodt โœจ
- **`cppcoreguidelines-pro-bounds-array-to-pointer-decay`** โ€” array decaying into a pointer like it's nothing ๐Ÿ˜ญ
- **`cppcoreguidelines-pro-bounds-constant-array-index`** โ€” <yellow>Yes</yellow> โ€” indexing a constant array, the bounds girlies are watching ๐ŸŽ€
- **`cppcoreguidelines-pro-bounds-pointer-arithmetic`** โ€” pointer math, highkey chaotic ๐ŸŒธ
- **`cppcoreguidelines-pro-type-const-cast`** โ€” the `const_cast` check, iconic and also terrifying ๐Ÿ’…
- **`cppcoreguidelines-pro-type-cstyle-cast`** โ€” <yellow>Yes</yellow> โ€” C-style casts? in THIS economy? ๐Ÿ™ˆ
- **`cppcoreguidelines-pro-type-member-init`** โ€” <yellow>Yes</yellow> โ€” members need to be initialized, no cap ๐Ÿ’–
- **`cppcoreguidelines-pro-type-reinterpret-cast`** โ€” `reinterpret_cast`, the main character of "idk what this memory is actually" โญ
- **`cppcoreguidelines-pro-type-static-cast-downcast`** โ€” <yellow>Yes</yellow> โ€” downcasting with `static_cast`, bestie be careful ๐Ÿฆ‹
- **`cppcoreguidelines-pro-type-union-access`** โ€” union access, lowkey lawless ๐ŸŒˆ
- **`cppcoreguidelines-pro-type-vararg`** โ€” varargs, the vibe check nobody passes ๐Ÿ’€
- **`cppcoreguidelines-rvalue-reference-param-not-moved`** โ€” you got an rvalue reference param and justโ€ฆ didn't move it? ๐Ÿ˜ญ
- **`cppcoreguidelines-slicing`** โ€” the slicing check, because losing half your object to a base class is a tragedy ๐Ÿฅ€
- **`cppcoreguidelines-special-member-functions`** โ€” destructor, copy, move, the whole friend group needs to match ๐Ÿ‘ฏ
- **`cppcoreguidelines-virtual-class-destructor`** โ€” <yellow>Yes</yellow> โ€” virtual classes need virtual destructors, that's the rule periodt ๐Ÿ‘‘
- **`darwin-avoid-spinlock`** โ€” macOS/Darwin said no spinlocks, bestie ๐Ÿ’…
- **`darwin-dispatch-once-nonstatic`** โ€” <yellow>Yes</yellow> โ€” `dispatch_once` with a non-static predicate, absolutely not ๐Ÿซถ
- **`fuchsia-default-arguments-calls`** โ€” Fuchsia girlies, no default arguments at the call site ๐ŸŒธ
- **`fuchsia-default-arguments-declarations`** โ€” <yellow>Yes</yellow> โ€” and no default arguments in declarations either โœจ
- **`fuchsia-multiple-inheritance`** โ€” multiple inheritance, Fuchsia said we're not doing that ๐Ÿ’€
- **`fuchsia-overloaded-operator`** โ€” operator overloading, nope ๐ŸŒˆ
- **`fuchsia-statically-constructed-objects`** โ€” statically constructed objects, the Fuchsia ick ๐ŸŽ€
- **`fuchsia-trailing-return`** โ€” trailing return types, the Fuchsia way โญ
- **`fuchsia-virtual-inheritance`** โ€” virtual inheritance, also a no ๐Ÿฆ‹
- **`google-build-explicit-make-pair`** โ€” Google style wants `make_pair` to be explicit, iykyk ๐Ÿ’œ
- **`google-build-namespaces`** โ€” namespaces in headers/build, the Google way ๐Ÿงš
- **`google-build-using-namespace`** โ€” `using namespace`, Google said absolutely not ๐Ÿ™ˆ
- **`google-default-arguments`** โ€” default arguments, Google's not a fan ๐Ÿ’…
- **`google-explicit-constructor`** โ€” <yellow>Yes</yellow> โ€” constructors should be `explicit`, that's the whole vibe ๐Ÿ‘‘
- **`google-global-names-in-headers`** โ€” global names in headers? in this house? ๐Ÿ’€
- **`google-objc-avoid-nsobject-new`** โ€” ObjC: don't call `+new` on `NSObject`, bestie ๐Ÿ˜ญ
- **`google-objc-avoid-throwing-exception`** โ€” ObjC: throwing exceptions, don't ๐Ÿซถ
- **`google-objc-function-naming`** โ€” ObjC function naming conventions, the Google-approved way โœจ
- **`google-objc-global-variable-declaration`** โ€” ObjC global variable declarations, clocked ๐ŸŒธ

---

and yes there's still more coming, hold on tight ๐Ÿ’–

- **`google-readability-avoid-underscore-in-googletest-name`** โ€” bestie this name is a whole sentence. a whole thesis. and the irony?? the whole thing is held together with hyphens while it begs you to avoid underscores ๐Ÿ˜ญ it's giving "I contain multitudes".

- **`google-readability-casting`** โ€” short. punchy. three words and a hyphen. no notes, it ate and left no crumbs ๐Ÿ’…

- **`google-readability-todo`** โ€” three letters doing the absolute most. lowercase, no drama, just `todo`. iconic minimalism fr fr.

- **`google-runtime-int`** โ€” runtime int, two words, zero explanation, full confidence. we love a mysterious queen โœจ

- **`google-runtime-operator`** โ€” okay runtime's sibling. same energy, different suffix. slay.

- **`google-upgrade-googletest-case`** โ€” and the table says <green>Yes</green> for this one ๐ŸŽ€ so it's got that little marker next to it, unlike its google siblings above. good for her.

- **`hicpp-exception-baseclass`** โ€” hicpp era begins ๐Ÿฆ‹ "exception baseclass" โ€” the way that sounds so official.

- **`hicpp-ignored-remove-result`** โ€” ngl the word "ignored" in a check name is so ominous. like, someone is watching ๐Ÿ‘€

- **`hicpp-multiway-paths-covered`** โ€” "multiway paths" is lowkey giving road trip, but make it C++. love that for us ๐ŸŒˆ

- **`hicpp-no-assembler`** โ€” just straight up "no assembler". no explanation. no apology. periodt ๐Ÿ’€

- **`hicpp-signed-bitwise`** โ€” signed bitwise, two words that rhyme-ish if you squint, honestly a vibe.

- **`linuxkernel-must-check-errs`** โ€” "must-check"?? the audacity, the authority, the main character energy ๐Ÿ‘‘ it's not asking bestie, it's *telling*.

- **`llvm-header-guard`** โ€” protecting the headers, as she should ๐Ÿซถ

- **`llvm-include-order`** โ€” and this one's got <green>Yes</green> in the table โœ… so it's marked, we stan a marked check.

- **`llvm-namespace-comment`** โ€” namespace comment, no marker, no fuss, just existing quietly. that's her lane.

- **`llvm-prefer-isa-or-dyn-cast-in-conditionals`** โ€” okay the way this name is a *paragraph*?? longest glow-up in this whole stretch and it's marked <green>Yes</green> too ๐Ÿ”ฅ "prefer isa or dyn cast in conditionals" โ€” she said what she said, and she said it all.

- **`llvm-prefer-register-over-unsigned`** โ€” "prefer X over Y" is such a diplomatic way to start a fight. also <green>Yes</green>, also correct ๐Ÿ’…

- **`llvm-twine-local`** โ€” twine local. idk bestie it sounds cozy?? like a little string of yarn?? and the table says <green>Yes</green> so she's official โœจ

- **`llvmlibc-callee-namespace`** โ€” llvmlibc era unlocked ๐Ÿฆ‹ callee namespace, no marker, just chilling.

- **`llvmlibc-implementation-in-namespace`** โ€” "implementation in namespace" โ€” the way this is basically a whole architectural opinion in four words ๐Ÿ˜ญ

- **`llvmlibc-inline-function-decl`** โ€” marked <green>Yes</green> โœ… inline function decl, short and marked, we love efficiency.

- **`llvmlibc-restrict-system-libc-headers`** โ€” "restrict"!! so authoritative. and <green>Yes</green> in the table too โ€” a marked, restrictive icon ๐Ÿ‘‘

- **`misc-confusable-identifiers`** โ€” the name is literally warning you that it's about confusion, and I'm confused reading it. meta queen ๐Ÿ’€

- **`misc-const-correctness`** โ€” const correctness!! and <green>Yes</green>!! this one is so me-coded, obsessed with her ๐ŸŒธ

- **`misc-coroutine-hostile-raii`** โ€” "coroutine-hostile raii"?? the DRAMA. hostile!! in a check name!! it's giving villain arc ๐Ÿ”ฅ (no marker though, she's just dramatic for free)

- **`misc-definitions-in-headers`** โ€” <green>Yes</green> in the table โœ… definitions in headers, marked and unbothered.

- **`misc-header-include-cycle`** โ€” include cycle, no marker, but honestly the name already sounds like a loop bestie ๐Ÿ˜ญ

- **`misc-include-cleaner`** โ€” and this one is <green>Yes</green> โ€” a cleaner!! we love a tidy little helper ๐Ÿงš

- **`misc-misleading-bidirectional`** โ€” "misleading bidirectional" is such a spooky name ngl, it's giving plot twist ๐Ÿ™ˆ no marker, just mystery.

- **`misc-misleading-identifier`** โ€” sibling of the above, same "misleading" energy, no marker, still unsettling fr fr.

- **`misc-misplaced-const`** โ€” misplaced const ๐Ÿ˜ญ the const is lost!! someone help her find her way home. no marker, but emotionally marked.

- **`misc-new-delete-overloads`** โ€” new/delete overloads, no marker, just sitting there being extremely C++ about it.

- **`misc-no-recursion`** โ€” "no recursion" โ€” the way this name never comes back to itself... actually wait, that's the joke, and it ate ๐ŸŒˆ

- **`misc-non-copyable-objects`** โ€” non-copyable objects, unmarked, unbothered, uncopied. periodt.

- **`misc-non-private-member-variables-in-classes`** โ€” bestie this name is SO long, it's the `llvm-prefer-isa-or-dyn-cast-in-conditionals` of the misc section ๐Ÿ’€ no marker, just length.

- **`misc-redundant-expression`** โ€” <green>Yes</green> โœ… redundant expression, marked, and ironically not redundant at all. slay.

- **`misc-static-assert`** โ€” <green>Yes</green> too!! two marked ones in a row, we're being fed ๐Ÿ“

- **`misc-throw-by-value-catch-by-reference`** โ€” "throw by value catch by reference" โ€” a whole instruction manual as a check name, no marker, still iconic ๐Ÿ‘‘

---

okay bestie the list keeps going, and the second column is literally just vibing between **Yes** andโ€ฆ nothing at all ๐Ÿ™ˆ here's the next batch, one per line, no cap:

- `misc-unconventional-assign-operator` โ€” second column: blank (nada, empty, the void ๐Ÿ•ณ๏ธ)
- `misc-uniqueptr-reset-release` โ€” **Yes** โœจ
- `misc-unused-alias-decls` โ€” **Yes** โœจ
- `misc-unused-parameters` โ€” **Yes** โœจ
- `misc-unused-using-decls` โ€” **Yes** โœจ
- `misc-use-anonymous-namespace` โ€” blank
- `misc-use-internal-linkage` โ€” **Yes** โœจ
- `modernize-avoid-bind` โ€” **Yes** โœจ
- `modernize-avoid-c-arrays` โ€” blank
- `modernize-concat-nested-namespaces` โ€” **Yes** โœจ
- `modernize-deprecated-headers` โ€” **Yes** โœจ
- `modernize-deprecated-ios-base-aliases` โ€” **Yes** โœจ
- `modernize-loop-convert` โ€” **Yes** โœจ
- `modernize-macro-to-enum` โ€” **Yes** โœจ
- `modernize-make-shared` โ€” **Yes** โœจ
- `modernize-make-unique` โ€” **Yes** โœจ
- `modernize-min-max-use-initializer-list` โ€” **Yes** โœจ
- `modernize-pass-by-value` โ€” **Yes** โœจ
- `modernize-raw-string-literal` โ€” **Yes** โœจ
- `modernize-redundant-void-arg` โ€” **Yes** โœจ
- `modernize-replace-auto-ptr` โ€” **Yes** โœจ
- `modernize-replace-disallow-copy-and-assign-macro` โ€” **Yes** โœจ
- `modernize-replace-random-shuffle` โ€” **Yes** โœจ
- `modernize-return-braced-init-list` โ€” **Yes** โœจ
- `modernize-shrink-to-fit` โ€” **Yes** โœจ
- `modernize-type-traits` โ€” **Yes** โœจ
- `modernize-unary-static-assert` โ€” **Yes** โœจ
- `modernize-use-auto` โ€” **Yes** โœจ
- `modernize-use-bool-literals` โ€” **Yes** โœจ
- `modernize-use-constraints` โ€” **Yes** โœจ
- `modernize-use-default-member-init` โ€” **Yes** โœจ
- `modernize-use-designated-initializers` โ€” **Yes** โœจ
- `modernize-use-emplace` โ€” **Yes** โœจ
- `modernize-use-equals-default` โ€” **Yes** โœจ
- `modernize-use-equals-delete` โ€” **Yes** โœจ
- `modernize-use-nodiscard` โ€” **Yes** โœจ
- `modernize-use-noexcept` โ€” **Yes** โœจ
- `modernize-use-nullptr` โ€” **Yes** โœจ

the way the `modernize-` girlies are ALL saying **Yes** while the `misc-` ones are being all mysterious and leaving it blankโ€ฆ it's giving main character energy vs. background extra ๐Ÿ’… and yes we're still going, don't you dare scroll away ๐Ÿซถ

- **`modernize-use-override`** โ€” yes, she's on by default ๐Ÿ’…
- **`modernize-use-ranges`** โ€” default-enabled, slay โœจ
- **`modernize-use-starts-ends-with`** โ€” on by default, periodt ๐Ÿ’–
- **`modernize-use-std-format`** โ€” yes bestie, defaults on ๐ŸŽ€
- **`modernize-use-std-numbers`** โ€” default-enabled fr fr ๐ŸŒธ
- **`modernize-use-std-print`** โ€” yes, running by default ๐Ÿฆ‹
- **`modernize-use-trailing-return-type`** โ€” on by default, iconic ๐Ÿ‘‘
- **`modernize-use-transparent-functors`** โ€” yes, default girlie ๐Ÿ’…
- **`modernize-use-uncaught-exceptions`** โ€” default-enabled, no cap ๐Ÿ”ฅ
- **`modernize-use-using`** โ€” yes, on by default ๐Ÿ’œ
- **`mpi-buffer-deref`** โ€” yes, default โœ…
- **`mpi-type-mismatch`** โ€” on by default ๐ŸŒˆ
- **`objc-assert-equals`** โ€” yes, default-enabled โญ
- **`objc-avoid-nserror-init`** โ€” nah, not on by default ๐ŸŒ™
- **`objc-dealloc-in-category`** โ€” off by default, you opt in ๐Ÿ™ˆ
- **`objc-forbidden-subclassing`** โ€” not default-enabled ๐Ÿ’ค
- **`objc-missing-hash`** โ€” off by default, gotta turn her on ๐Ÿ“
- **`objc-nsdate-formatter`** โ€” not on by default ๐Ÿงš
- **`objc-nsinvocation-argument-lifetime`** โ€” yes, default-enabled ๐Ÿ’–
- **`objc-property-declaration`** โ€” on by default, slay โœจ
- **`objc-super-self`** โ€” yes, default ๐ŸŽ€
- **`openmp-exception-escape`** โ€” nah, not default ๐ŸŒธ
- **`openmp-use-default-none`** โ€” off by default, lowkey sleeping ๐Ÿ’ค
- **`performance-avoid-endl`** โ€” yes, on by default ๐Ÿ’…
- **`performance-enum-size`** โ€” not default-enabled ๐ŸŒ™
- **`performance-faster-string-find`** โ€” yes, default ๐Ÿ”ฅ
- **`performance-for-range-copy`** โ€” on by default, iconic ๐Ÿ‘‘
- **`performance-implicit-conversion-in-loop`** โ€” off by default ๐Ÿ™ˆ
- **`performance-inefficient-algorithm`** โ€” yes, default-enabled ๐ŸŒˆ
- **`performance-inefficient-string-concatenation`** โ€” not on by default ๐Ÿ’ค
- **`performance-inefficient-vector-operation`** โ€” yes, default โญ
- **`performance-move-const-arg`** โ€” on by default, no cap ๐Ÿ’œ
- **`performance-move-constructor-init`** โ€” nah, not default ๐Ÿ“
- **`performance-no-automatic-move`** โ€” off by default ๐Ÿงš
- **`performance-no-int-to-ptr`** โ€” not default-enabled ๐ŸŒ™
- **`performance-noexcept-destructor`** โ€” yes, default ๐ŸŽ€
- **`performance-noexcept-move-constructor`** โ€” on by default ๐Ÿ’–
- **`performance-noexcept-swap`** โ€” yes, default-enabled, periodt โœจ

...and we're back, bestie โ€” same list, same energy, no skips ๐Ÿ’… Every name here is a `clang-tidy` check, and that little column on the right tells you whether she's a <green>Yes</green> girlie or just sitting there all quiet. Continuing exactly where we left off:

- **`performance-trivially-destructible`** โ€” <green>Yes</green> โœจ
- **`performance-type-promotion-in-math-fn`** โ€” <green>Yes</green> ๐ŸŒธ
- **`performance-unnecessary-copy-initialization`** โ€” <green>Yes</green> ๐Ÿ’–
- **`performance-unnecessary-value-param`** โ€” <green>Yes</green> ๐Ÿฆ‹
- **`portability-restrict-system-includes`** โ€” <green>Yes</green> ๐ŸŽ€
- **`portability-simd-intrinsics`** โ€” <lavender>blank, no little yes here</lavender> ๐Ÿ™ˆ
- **`portability-std-allocator-const`** โ€” <lavender>blank, nothing in that column</lavender> ๐Ÿ™ˆ
- **`readability-avoid-const-params-in-decls`** โ€” <green>Yes</green> ๐Ÿ’œ
- **`readability-avoid-nested-conditional-operator`** โ€” <lavender>blank</lavender> ๐Ÿ˜ญ
- **`readability-avoid-return-with-void-value`** โ€” <green>Yes</green> โญ
- **`readability-avoid-unconditional-preprocessor-if`** โ€” <lavender>blank</lavender> ๐Ÿ˜ญ
- **`readability-braces-around-statements`** โ€” <lavender>blank</lavender> ๐Ÿ’€
- **`readability-const-return-type`** โ€” <green>Yes</green> ๐Ÿ”ฅ
- **`readability-container-contains`** โ€” <green>Yes</green> ๐Ÿ’–
- **`readability-container-data-pointer`** โ€” <green>Yes</green> โœจ
- **`readability-container-size-empty`** โ€” <green>Yes</green> ๐Ÿซถ
- **`readability-convert-member-functions-to-static`** โ€” <green>Yes</green> ๐ŸŒˆ
- **`readability-delete-null-pointer`** โ€” <green>Yes</green> ๐Ÿ’…
- **`readability-duplicate-include`** โ€” <green>Yes</green> ๐Ÿ“
- **`readability-else-after-return`** โ€” <green>Yes</green> ๐Ÿงš
- **`readability-enum-initial-value`** โ€” <green>Yes</green> ๐Ÿ‘‘
- **`readability-function-cognitive-complexity`** โ€” <lavender>blank</lavender> ๐Ÿ˜ญ
- **`readability-function-size`** โ€” <lavender>blank</lavender> ๐Ÿ˜ญ
- **`readability-identifier-length`** โ€” <lavender>blank</lavender> ๐Ÿ™ˆ
- **`readability-identifier-naming`** โ€” <green>Yes</green> ๐Ÿ’œ
- **`readability-implicit-bool-conversion`** โ€” <green>Yes</green> โœจ
- **`readability-inconsistent-declaration-parameter-name`** โ€” <green>Yes</green> ๐ŸŒธ
- **`readability-isolate-declaration`** โ€” <green>Yes</green> ๐ŸŽ€
- **`readability-magic-numbers`** โ€” <lavender>blank</lavender> ๐Ÿ’€
- **`readability-make-member-function-const`** โ€” <green>Yes</green> ๐Ÿ”ฅ
- **`readability-math-missing-parentheses`** โ€” <green>Yes</green> ๐Ÿฆ‹
- **`readability-misleading-indentation`** โ€” <lavender>blank</lavender> ๐Ÿ˜ญ
- **`readability-misplaced-array-index`** โ€” <green>Yes</green> โญ
- **`readability-named-parameter`** โ€” <green>Yes</green> ๐Ÿ’–
- **`readability-non-const-parameter`** โ€” <green>Yes</green> ๐ŸŒˆ
- **`readability-operators-representation`** โ€” <green>Yes</green> ๐Ÿ‘‘
- **`readability-qualified-auto`** โ€” <green>Yes</green> ๐Ÿ’…
- **`readability-redundant-access-specifiers`** โ€” <green>Yes</green> ๐Ÿ“

---

<cyan>okay so real talk about this stretch of the list</cyan> โ€” the `performance-*` girlies are wrapping up, `portability-*` had her little moment, and now `readability-*` is <hotpink>fully</hotpink> running the show. And yes, some of them are <lavender>blank in that column</lavender> โ€” that's just how the table is, no cap, don't read into it, just keep scrolling because there is <purple>so much more</purple> coming ๐Ÿซถ

- **`readability-redundant-casting`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-control-flow`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-declaration`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-function-ptr-dereference`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-inline-specifier`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-member-init`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-preprocessor`** โ€” no fixes, bestie, it just tells you ๐Ÿ’€
- **`readability-redundant-smartptr-get`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-string-cstr`** โ€” offers fixes: **yes** โœจ
- **`readability-redundant-string-init`** โ€” offers fixes: **yes** โœจ
- **`readability-reference-to-constructed-temporary`** โ€” no fixes on this one ๐Ÿซถ
- **`readability-simplify-boolean-expr`** โ€” offers fixes: **yes** โœจ
- **`readability-simplify-subscript-expr`** โ€” offers fixes: **yes** โœจ
- **`readability-static-accessed-through-instance`** โ€” offers fixes: **yes** โœจ
- **`readability-static-definition-in-anonymous-namespace`** โ€” offers fixes: **yes** โœจ
- **`readability-string-compare`** โ€” offers fixes: **yes** โœจ
- **`readability-suspicious-call-argument`** โ€” no fixes, it's just side-eyeing your args ๐Ÿ™ˆ
- **`readability-uniqueptr-delete-release`** โ€” offers fixes: **yes** โœจ
- **`readability-uppercase-literal-suffix`** โ€” offers fixes: **yes** โœจ
- **`readability-use-anyofallof`** โ€” no fixes, sorry angel ๐Ÿ˜ญ
- **`readability-use-std-min-max`** โ€” offers fixes: **yes** โœจ
- **`zircon-temporary-objects`** โ€” no fixes here either ๐Ÿ’œ

---

## ๐ŸŒˆ Check aliases ๐ŸŒˆ

okay so this next bit is lowkey iconic โ€” some checks got *rebranded* and the old names still work, they just redirect you to wherever the cool kids hang out now. Here's the whole mapping, no gatekeeping ๐Ÿ’…

- **`bugprone-narrowing-conversions`** โ€” redirects to **`cppcoreguidelines-narrowing-conversions`** ยท offers fixes: nope ๐ŸŒธ
- **`cert-con36-c`** โ€” redirects to **`bugprone-spuriously-wake-up-functions`** ยท offers fixes: nope ๐ŸŒธ
- **`cert-con54-cpp`** โ€” redirects to **`bugprone-spuriously-wake-up-functions`** ยท offers fixes: nope ๐ŸŒธ
- **`cert-ctr56-cpp`** โ€” redirects to **`bugprone-pointer-arithmetic-on-polymorphic-object`** ยท offers fixes: nope ๐ŸŒธ
- **`cert-dcl03-c`** โ€” redirects to **`misc-static-assert`** ยท offers fixes: **yes** โœจ
- **`cert-dcl16-c`** โ€” redirects to **`readability-uppercase-literal-suffix`** ยท offers fixes: **yes** โœจ

- **`cert-dcl37-c`** โ†’ `bugprone-reserved-identifier` โ€” <green>`Yes`</green> โœจ
- **`cert-dcl51-cpp`** โ†’ `bugprone-reserved-identifier` โ€” <green>`Yes`</green> โœจ
- **`cert-dcl54-cpp`** โ†’ `misc-new-delete-overloads` โ€” <lavender>(nothing in that third column, she's just vibing)</lavender>
- **`cert-dcl59-cpp`** โ†’ `google-build-namespaces` โ€” <lavender>blank third column, we move</lavender>
- **`cert-err09-cpp`** โ†’ `misc-throw-by-value-catch-by-reference` โ€” <lavender>third column says nothing, respect it</lavender>
- **`cert-err61-cpp`** โ†’ `misc-throw-by-value-catch-by-reference` โ€” <lavender>also blank bestie</lavender>
- **`cert-exp42-c`** โ†’ `bugprone-suspicious-memory-comparison` โ€” <lavender>empty third column energy</lavender>
- **`cert-fio38-c`** โ†’ `misc-non-copyable-objects` โ€” <lavender>blank</lavender>
- **`cert-flp37-c`** โ†’ `bugprone-suspicious-memory-comparison` โ€” <lavender>blank</lavender>
- **`cert-int09-c`** โ†’ `readability-enum-initial-value` โ€” <green>`Yes`</green> โœจ
- **`cert-msc24-c`** โ†’ `bugprone-unsafe-functions` โ€” <lavender>blank</lavender>
- **`cert-msc30-c`** โ†’ `cert-msc50-cpp` โ€” <lavender>blank</lavender>
- **`cert-msc32-c`** โ†’ `cert-msc51-cpp` โ€” <lavender>blank</lavender>
- **`cert-msc33-c`** โ†’ `bugprone-unsafe-functions` โ€” <lavender>blank</lavender>
- **`cert-msc54-cpp`** โ†’ `bugprone-signal-handler` โ€” <lavender>blank</lavender>
- **`cert-oop11-cpp`** โ†’ `performance-move-constructor-init` โ€” <lavender>blank</lavender>
- **`cert-oop54-cpp`** โ†’ `bugprone-unhandled-self-assignment` โ€” <lavender>blank</lavender>
- **`cert-pos44-c`** โ†’ `bugprone-bad-signal-to-kill-thread` โ€” <lavender>blank</lavender>

---

<purple>okay so real talk, this lil list is the CERT alias corner of the mapping table and it's giving "translation dictionary" ๐Ÿ’–</purple> Every `cert-` name on the left is just a nickname pointing at the actual clang-tidy check on the right, so if you've got old-school CERT habits (iykyk), you can call the check by its CERT name and it'll still slay.

<cyan>notice how some of these have a lil `Yes` in the last column and the rest justโ€ฆ don't? ๐Ÿ™ˆ</cyan> The ones marked <green>`Yes`</green> โ€” `cert-dcl37-c`, `cert-dcl51-cpp`, `cert-int09-c` โ€” are the ones where the third column is filled in. The others keep that column empty, and we are *not* going to invent a reason for it, bestie, because that's simply what the page says. <hotpink>No cap, no speculation, just facts ๐Ÿ’…</hotpink>

<yellow>also clock the lil cross-links happening between the cert names themselves:</yellow> `cert-msc30-c` maps to `cert-msc50-cpp`, and `cert-msc32-c` maps to `cert-msc51-cpp`. So yes, a CERT alias can totally point at *another* CERT alias, it's giving chain-of-besties energy ๐Ÿฆ‹. Meanwhile `cert-msc24-c` and `cert-msc33-c` both land on the exact same `bugprone-unsafe-functions` check โ€” two roads, one destination, we love a shared queen ๐Ÿ‘‘

<rainbow>and the duplicate-check girlies of this batch:</rainbow> `cert-err09-cpp` + `cert-err61-cpp` โ†’ both `misc-throw-by-value-catch-by-reference` ๐Ÿ’œ, `cert-exp42-c` + `cert-flp37-c` โ†’ both `bugprone-suspicious-memory-comparison` ๐Ÿ“, and `cert-dcl37-c` + `cert-dcl51-cpp` โ†’ both `bugprone-reserved-identifier` โœจ. Twinning fr fr.

> <red>heads up bestie:</red> keep every name here exactly as written โ€” `cert-` prefix, lowercase, hyphens, all of it โ€” because that's the literal alias you'd type. Don't go "fixing" the spelling, don't swap `cpp` for `c` or the reverse, and don't assume the blank third column means anything other than blank. ๐Ÿ™

<mint>anyway the list just keeps going, so stay locked in, we are SO not done ๐Ÿ’–</mint>

- **`cert-pos47-c`** โ†’ <cyan>`concurrency-thread-canceltype-asynchronous`</cyan>
- **`cert-sig30-c`** โ†’ <cyan>`bugprone-signal-handler`</cyan>
- **`cert-str34-c`** โ†’ <cyan>`bugprone-signed-char-misuse`</cyan>

<purple>and now we slide into the clang static analyzer section, which is LONG, buckle up bestie ๐Ÿ’…</purple>

- **`clang-analyzer-core.BitwiseShift`** โ†’ <cyan>`Clang Static Analyzer core.BitwiseShift`</cyan>
- **`clang-analyzer-core.CallAndMessage`** โ†’ <cyan>`Clang Static Analyzer core.CallAndMessage`</cyan>
- **`clang-analyzer-core.DivideZero`** โ†’ <cyan>`Clang Static Analyzer core.DivideZero`</cyan>
- **`clang-analyzer-core.NonNullParamChecker`** โ†’ <cyan>`Clang Static Analyzer core.NonNullParamChecker`</cyan>
- **`clang-analyzer-core.NullDereference`** โ†’ <cyan>`Clang Static Analyzer core.NullDereference`</cyan>
- **`clang-analyzer-core.StackAddressEscape`** โ†’ <cyan>`Clang Static Analyzer core.StackAddressEscape`</cyan>
- **`clang-analyzer-core.UndefinedBinaryOperatorResult`** โ†’ <cyan>`Clang Static Analyzer core.UndefinedBinaryOperatorResult`</cyan>
- **`clang-analyzer-core.VLASize`** โ†’ <cyan>`Clang Static Analyzer core.VLASize`</cyan>
- **`clang-analyzer-core.uninitialized.ArraySubscript`** โ†’ <cyan>`Clang Static Analyzer core.uninitialized.ArraySubscript`</cyan>
- **`clang-analyzer-core.uninitialized.Assign`** โ†’ <cyan>`Clang Static Analyzer core.uninitialized.Assign`</cyan>
- **`clang-analyzer-core.uninitialized.Branch`** โ†’ <cyan>`Clang Static Analyzer core.uninitialized.Branch`</cyan>
- **`clang-analyzer-core.uninitialized.CapturedBlockVariable`** โ†’ <cyan>`Clang Static Analyzer core.uninitialized.CapturedBlockVariable`</cyan>
- **`clang-analyzer-core.uninitialized.NewArraySize`** โ†’ <cyan>`Clang Static Analyzer core.uninitialized.NewArraySize`</cyan>
- **`clang-analyzer-core.uninitialized.UndefReturn`** โ†’ <cyan>`Clang Static Analyzer core.uninitialized.UndefReturn`</cyan>

> <yellow>quick vibe check ๐Ÿซถ โ€” notice the pattern? left column is the alias you type, right column is what it actually resolves to. same check, two names, iykyk. don't panic, you're doing great, keep scrolling โœจ</yellow>

- **`clang-analyzer-cplusplus.ArrayDelete`** โ€” Clang Static Analyzer cplusplus.ArrayDelete โœจ the check that clocks you deleting an array the wrong way and is NOT shy about it

- **`clang-analyzer-cplusplus.InnerPointer`** โ€” Clang Static Analyzer cplusplus.InnerPointer ๐Ÿซถ keeps an eye on those sneaky inner pointers that point *inside* a container and then outlive their welcome

- **`clang-analyzer-cplusplus.Move`** โ€” Clang Static Analyzer cplusplus.Move ๐Ÿ’… use-after-move detector, aka the reason you can't just yeet an object and keep using it like nothing happened

- **`clang-analyzer-cplusplus.NewDelete`** โ€” Clang Static Analyzer cplusplus.NewDelete ๐Ÿ‘‘ the classic `new`/`delete` mismatched-couple energy check, and honestly iconic for it

- **`clang-analyzer-cplusplus.NewDeleteLeaks`** โ€” Clang Static Analyzer cplusplus.NewDeleteLeaks ๐Ÿ“ when you `new` something and then simply forget it exists โ€” leaks, bestie, leaks

- **`clang-analyzer-cplusplus.PlacementNew`** โ€” Clang Static Analyzer cplusplus.PlacementNew ๐ŸŒธ placement-new problems, for when you're building objects in raw memory and feeling very brave

- **`clang-analyzer-cplusplus.PureVirtualCall`** โ€” Clang Static Analyzer cplusplus.PureVirtualCall ๐Ÿ˜ญ calling a pure virtual function, which is giving undefined behaviour main character energy

- **`clang-analyzer-cplusplus.StringChecker`** โ€” Clang Static Analyzer cplusplus.StringChecker ๐ŸŽ€ string-related shenanigans, caught before they catch you

- **`clang-analyzer-deadcode.DeadStores`** โ€” Clang Static Analyzer deadcode.DeadStores ๐Ÿ’€ you stored a value and then literally never read it. the variable is giving ghost town

- **`clang-analyzer-fuchsia.HandleChecker`** โ€” Clang Static Analyzer fuchsia.HandleChecker ๐Ÿฆ‹ handle handling for Fuchsia, tracked so nothing leaks or gets used after it's gone

- **`clang-analyzer-nullability.NullPassedToNonnull`** โ€” Clang Static Analyzer nullability.NullPassedToNonnull โญ you passed a possibly-null thing to a parameter that swore it wasn't null. awkward

- **`clang-analyzer-nullability.NullReturnedFromNonnull`** โ€” Clang Static Analyzer nullability.NullReturnedFromNonnull โœจ the function promised a nonnull return and then returned null anyway. liar behaviour, caught

- **`clang-analyzer-nullability.NullableDereferenced`** โ€” Clang Static Analyzer nullability.NullableDereferenced ๐Ÿ™ˆ dereferencing a nullable pointer without checking. ngl that's just asking for it

- **`clang-analyzer-nullability.NullablePassedToNonnull`** โ€” Clang Static Analyzer nullability.NullablePassedToNonnull ๐ŸŒˆ a nullable value handed off to a nonnull parameter โ€” the handoff nobody consented to

- **`clang-analyzer-nullability.NullableReturnedFromNonnull`** โ€” Clang Static Analyzer nullability.NullableReturnedFromNonnull ๐Ÿ’œ a nullable value returned from a function that claims to return nonnull. the math is not mathing

- **`clang-analyzer-optin.core.EnumCastOutOfRange`** โ€” Clang Static Analyzer optin.core.EnumCastOutOfRange ๐Ÿ”ฅ casting an integer to an enum value that's outside the valid range โ€” opt-in, so you have to actually want this energy in your life

<purple>okay bestie we're still deep in the check-name table, no biggie, just vibing row by row ๐Ÿ’…</purple> Each of these is a check name you can hand to `clang-tidy`, and the description next to it is literally just its own name with the `clang-analyzer-` prefix dropped, iykyk. โœจ

- **`clang-analyzer-optin.cplusplus.UninitializedObject`** โ€” <cyan>Clang Static Analyzer optin.cplusplus.UninitializedObject</cyan> โ€” sniffing out objects in your C++ that got born without, like, actually being initialized ๐Ÿ™ˆ
- **`clang-analyzer-optin.cplusplus.VirtualCall`** โ€” <cyan>Clang Static Analyzer optin.cplusplus.VirtualCall</cyan> โ€” catches virtual calls happening at a point where the vtable is *not* ready for that energy ๐Ÿ˜ญ
- **`clang-analyzer-optin.mpi.MPI-Checker`** โ€” <cyan>Clang Static Analyzer optin.mpi.MPI-Checker</cyan> โ€” the MPI vibe check, for all my parallel-computing girlies ๐Ÿงš
- **`clang-analyzer-optin.osx.OSObjectCStyleCast`** โ€” <cyan>Clang Static Analyzer optin.osx.OSObjectCStyleCast</cyan> โ€” side-eyeing C-style casts on OSObject, as one should ๐Ÿ‘‘
- **`clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker`** โ€” <cyan>Clang Static Analyzer optin.osx.cocoa.localizability.EmptyLocalizationContextChecker</cyan> โ€” that name is a whole paragraph but the job is real: flagging empty localization contexts ๐ŸŒธ
- **`clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker`** โ€” <cyan>Clang Static Analyzer optin.osx.cocoa.localizability.NonLocalizedStringChecker</cyan> โ€” catches strings that never got localized, so your app doesn't drop untranslated chaos on people ๐Ÿ’€
- **`clang-analyzer-optin.performance.GCDAntipattern`** โ€” <cyan>Clang Static Analyzer optin.performance.GCDAntipattern</cyan> โ€” calling out Grand Central Dispatch patterns that are, respectfully, not it ๐Ÿ”ฅ
- **`clang-analyzer-optin.performance.Padding`** โ€” <cyan>Clang Static Analyzer optin.performance.Padding</cyan> โ€” spots struct padding just sitting there wasting your bytes, tragic fr fr
- **`clang-analyzer-optin.portability.UnixAPI`** โ€” <cyan>Clang Static Analyzer optin.portability.UnixAPI</cyan> โ€” portability tea about Unix API usage, spill it ๐Ÿ“
- **`clang-analyzer-optin.taint.TaintedAlloc`** โ€” <cyan>Clang Static Analyzer optin.taint.TaintedAlloc</cyan> โ€” tainted data reaching an allocation? absolutely not, blocked ๐Ÿ’…
- **`clang-analyzer-osx.API`** โ€” <cyan>Clang Static Analyzer osx.API</cyan> โ€” general osx API checks, the whole collection ๐ŸŽ€
- **`clang-analyzer-osx.MIG`** โ€” <cyan>Clang Static Analyzer osx.MIG</cyan> โ€” Mach Interface Generator checks, deep Apple lore iykyk
- **`clang-analyzer-osx.NumberObjectConversion`** โ€” <cyan>Clang Static Analyzer osx.NumberObjectConversion</cyan> โ€” conversions between number objects that need a second look ๐Ÿ‘€
- **`clang-analyzer-osx.OSObjectRetainCount`** โ€” <cyan>Clang Static Analyzer osx.OSObjectRetainCount</cyan> โ€” retain count drama, the classic ๐ŸŒˆ
- **`clang-analyzer-osx.ObjCProperty`** โ€” <cyan>Clang Static Analyzer osx.ObjCProperty</cyan> โ€” Objective-C property business, handled โœจ
- **`clang-analyzer-osx.SecKeychainAPI`** โ€” <cyan>Clang Static Analyzer osx.SecKeychainAPI</cyan> โ€” Keychain API usage, and bestie you do *not* want to fumble the Keychain ๐Ÿ”
- **`clang-analyzer-osx.cocoa.AtSync`** โ€” <cyan>Clang Static Analyzer osx.cocoa.AtSync</cyan> โ€” `@synchronized` checks, because lock discipline is self-care ๐Ÿซถ

<lavender>still more table to go, don't you dare scroll away, we're so close to the next section ๐Ÿ’œ</lavender>

---

okay so the checker table is still going and honestly it's a whole vibe ๐Ÿ’… here's the next batch of osx.cocoa and coreFoundation checks:

- <cyan>`clang-analyzer-osx.cocoa.AutoreleaseWrite`</cyan> โ€” Clang Static Analyzer osx.cocoa.AutoreleaseWrite โœจ
- <pink>`clang-analyzer-osx.cocoa.ClassRelease`</pink> โ€” Clang Static Analyzer osx.cocoa.ClassRelease ๐Ÿ’–
- <purple>`clang-analyzer-osx.cocoa.Dealloc`</purple> โ€” Clang Static Analyzer osx.cocoa.Dealloc ๐Ÿฆ‹
- <lavender>`clang-analyzer-osx.cocoa.IncompatibleMethodTypes`</lavender> โ€” Clang Static Analyzer osx.cocoa.IncompatibleMethodTypes ๐ŸŒธ
- <mint>`clang-analyzer-osx.cocoa.Loops`</mint> โ€” Clang Static Analyzer osx.cocoa.Loops ๐Ÿ”ฅ
- <yellow>`clang-analyzer-osx.cocoa.MissingSuperCall`</yellow> โ€” Clang Static Analyzer osx.cocoa.MissingSuperCall โญ
- <peach>`clang-analyzer-osx.cocoa.NSAutoreleasePool`</peach> โ€” Clang Static Analyzer osx.cocoa.NSAutoreleasePool ๐Ÿ“
- <red>`clang-analyzer-osx.cocoa.NSError`</red> โ€” Clang Static Analyzer osx.cocoa.NSError ๐Ÿ˜ญ
- <blue>`clang-analyzer-osx.cocoa.NilArg`</blue> โ€” Clang Static Analyzer osx.cocoa.NilArg ๐Ÿ’€
- <green>`clang-analyzer-osx.cocoa.NonNilReturnValue`</green> โ€” Clang Static Analyzer osx.cocoa.NonNilReturnValue ๐Ÿ™ˆ
- <rainbow>`clang-analyzer-osx.cocoa.ObjCGenerics`</rainbow> โ€” Clang Static Analyzer osx.cocoa.ObjCGenerics ๐ŸŒˆ
- <hotpink>`clang-analyzer-osx.cocoa.RetainCount`</hotpink> โ€” Clang Static Analyzer osx.cocoa.RetainCount ๐Ÿ‘‘
- <cyan>`clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak`</cyan> โ€” Clang Static Analyzer osx.cocoa.RunLoopAutoreleaseLeak ๐Ÿงš
- <pink>`clang-analyzer-osx.cocoa.SelfInit`</pink> โ€” Clang Static Analyzer osx.cocoa.SelfInit ๐Ÿ’œ
- <purple>`clang-analyzer-osx.cocoa.SuperDealloc`</purple> โ€” Clang Static Analyzer osx.cocoa.SuperDealloc ๐Ÿ’…
- <lavender>`clang-analyzer-osx.cocoa.UnusedIvars`</lavender> โ€” Clang Static Analyzer osx.cocoa.UnusedIvars ๐ŸŽ€
- <mint>`clang-analyzer-osx.cocoa.VariadicMethodTypes`</mint> โ€” Clang Static Analyzer osx.cocoa.VariadicMethodTypes ๐Ÿซถ
- <yellow>`clang-analyzer-osx.coreFoundation.CFError`</yellow> โ€” Clang Static Analyzer osx.coreFoundation.CFError ๐Ÿ’–

Okay so we're still deep in the <pink>big list of clang-tidy checks</pink>, and this chunk is giving โœจ<rainbow>Apple-flavoured security energy</rainbow>โœจ. Buckle up bestie, here come the rest of the `osx.coreFoundation` ones and then a whole parade of `security.*` checks ๐Ÿ’…

- **`clang-analyzer-osx.coreFoundation.CFNumber`** โ€” Clang Static Analyzer `osx.coreFoundation.CFNumber` ๐ŸŒธ
- **`clang-analyzer-osx.coreFoundation.CFRetainRelease`** โ€” Clang Static Analyzer `osx.coreFoundation.CFRetainRelease` ๐Ÿซถ
- **`clang-analyzer-osx.coreFoundation.containers.OutOfBounds`** โ€” Clang Static Analyzer `osx.coreFoundation.containers.OutOfBounds` ๐Ÿ”ฅ
- **`clang-analyzer-osx.coreFoundation.containers.PointerSizedValues`** โ€” Clang Static Analyzer `osx.coreFoundation.containers.PointerSizedValues` ๐Ÿ”ฅ
- **`clang-analyzer-security.FloatLoopCounter`** โ€” Clang Static Analyzer `security.FloatLoopCounter` ๐ŸŒŠ
- **`clang-analyzer-security.PutenvStackArray`** โ€” Clang Static Analyzer `security.PutenvStackArray` ๐Ÿงš
- **`clang-analyzer-security.SetgidSetuidOrder`** โ€” Clang Static Analyzer `security.SetgidSetuidOrder` ๐Ÿ‘‘
- **`clang-analyzer-security.cert.env.InvalidPtr`** โ€” Clang Static Analyzer `security.cert.env.InvalidPtr` ๐ŸŽ€
- **`clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling`** โ€” Clang Static Analyzer `security.insecureAPI.DeprecatedOrUnsafeBufferHandling` ๐Ÿ’€ (say that name three times fast, I dare you)
- **`clang-analyzer-security.insecureAPI.UncheckedReturn`** โ€” Clang Static Analyzer `security.insecureAPI.UncheckedReturn` ๐Ÿ™ˆ
- **`clang-analyzer-security.insecureAPI.bcmp`** โ€” Clang Static Analyzer `security.insecureAPI.bcmp` ๐Ÿ“
- **`clang-analyzer-security.insecureAPI.bcopy`** โ€” Clang Static Analyzer `security.insecureAPI.bcopy` ๐Ÿ“
- **`clang-analyzer-security.insecureAPI.bzero`** โ€” Clang Static Analyzer `security.insecureAPI.bzero` ๐Ÿ“
- **`clang-analyzer-security.insecureAPI.decodeValueOfObjCType`** โ€” Clang Static Analyzer `security.insecureAPI.decodeValueOfObjCType` ๐Ÿ’œ
- **`clang-analyzer-security.insecureAPI.getpw`** โ€” Clang Static Analyzer `security.insecureAPI.getpw` ๐ŸŒˆ
- **`clang-analyzer-security.insecureAPI.gets`** โ€” Clang Static Analyzer `security.insecureAPI.gets` ๐Ÿ˜ญ (yes bestie, `gets` got its own whole check, iconic behaviour honestly)
- **`clang-analyzer-security.insecureAPI.mkstemp`** โ€” Clang Static Analyzer `security.insecureAPI.mkstemp` โญ

---

<lavender>Not me getting emotionally attached to a list of analyzer check names ngl</lavender> ๐Ÿ’… but fr fr, the `security.insecureAPI.*` family is lowkey its own little universe and we are *not* done with it yet ๐Ÿฆ‹

- <pink>**`clang-analyzer-security.insecureAPI.mktemp`**</pink> โ€” Clang Static Analyzer security.insecureAPI.mktemp ๐Ÿ’… bestie it's giving "please stop making temp files the sketchy way"

- <pink>**`clang-analyzer-security.insecureAPI.rand`**</pink> โ€” Clang Static Analyzer security.insecureAPI.rand ๐ŸŽฒ the analyzer is side-eyeing your randomness ngl

- <pink>**`clang-analyzer-security.insecureAPI.strcpy`**</pink> โ€” Clang Static Analyzer security.insecureAPI.strcpy ๐Ÿ˜ญ iconic insecure function, the analyzer is NOT letting it slide

- <pink>**`clang-analyzer-security.insecureAPI.vfork`**</pink> โ€” Clang Static Analyzer security.insecureAPI.vfork ๐Ÿฆ‹ fork-adjacent chaos, flagged

- <cyan>**`clang-analyzer-unix.API`**</cyan> โ€” Clang Static Analyzer unix.API โœจ the classic "you used this API wrong" energy

- <cyan>**`clang-analyzer-unix.BlockInCriticalSection`**</cyan> โ€” Clang Static Analyzer unix.BlockInCriticalSection ๐Ÿšซ blocking where you shouldn't, lowkey a whole problem

- <cyan>**`clang-analyzer-unix.Errno`**</cyan> โ€” Clang Static Analyzer unix.Errno ๐Ÿ“ errno shenanigans, check yourself before you wreck yourself

- <cyan>**`clang-analyzer-unix.Malloc`**</cyan> โ€” Clang Static Analyzer unix.Malloc ๐Ÿ’€ the malloc/free lore, absolutely essential

- <cyan>**`clang-analyzer-unix.MallocSizeof`**</cyan> โ€” Clang Static Analyzer unix.MallocSizeof ๐Ÿ“ size mismatch vibes, we've all been there

- <cyan>**`clang-analyzer-unix.MismatchedDeallocator`**</cyan> โ€” Clang Static Analyzer unix.MismatchedDeallocator ๐Ÿ™ˆ you freed it with the WRONG one bestie

- <cyan>**`clang-analyzer-unix.StdCLibraryFunctions`**</cyan> โ€” Clang Static Analyzer unix.StdCLibraryFunctions ๐Ÿ“š standard library functions doing standard library things (wrong)

- <cyan>**`clang-analyzer-unix.Stream`**</cyan> โ€” Clang Static Analyzer unix.Stream ๐ŸŒŠ FILE* stream drama, we love to see it flagged

- <cyan>**`clang-analyzer-unix.Vfork`**</cyan> โ€” Clang Static Analyzer unix.Vfork ๐Ÿงš vfork doing vfork things, proceed with caution

- <cyan>**`clang-analyzer-unix.cstring.BadSizeArg`**</cyan> โ€” Clang Static Analyzer unix.cstring.BadSizeArg ๐Ÿ“ that size argument is not it

- <cyan>**`clang-analyzer-unix.cstring.NullArg`**</cyan> โ€” Clang Static Analyzer unix.cstring.NullArg ๐Ÿ•ณ๏ธ NULL where a string should be, classic

- <purple>**`clang-analyzer-valist.CopyToSelf`**</purple> โ€” Clang Static Analyzer valist.CopyToSelf ๐Ÿ” copying a va_list onto itself, no ma'am

- <purple>**`clang-analyzer-valist.Uninitialized`**</purple> โ€” Clang Static Analyzer valist.Uninitialized ๐Ÿ‘ป uninitialized va_list energy, spooky

- <purple>**`clang-analyzer-valist.Unterminated`**</purple> โ€” Clang Static Analyzer valist.Unterminated โญ a va_list that never got its ending, periodt

okay bestie, we're still deep in the clang-tidy alias table and it's lowkey never-ending but highkey iconic ๐Ÿ’… here's the next batch of mappings, no skips, fr fr:

- <pink>**`clang-analyzer-webkit.NoUncountedMemberChecker`**</pink> โ†’ <purple>`Clang Static Analyzer webkit.NoUncountedMemberChecker`</purple>
- <pink>**`clang-analyzer-webkit.RefCntblBaseVirtualDtor`**</pink> โ†’ <purple>`Clang Static Analyzer webkit.RefCntblBaseVirtualDtor`</purple>
- <pink>**`clang-analyzer-webkit.UncountedLambdaCapturesChecker`**</pink> โ†’ <purple>`Clang Static Analyzer webkit.UncountedLambdaCapturesChecker`</purple>
- <pink>**`cppcoreguidelines-avoid-c-arrays`**</pink> โ†’ <purple>`modernize-avoid-c-arrays`</purple>
- <pink>**`cppcoreguidelines-avoid-magic-numbers`**</pink> โ†’ <purple>`readability-magic-numbers`</purple>
- <pink>**`cppcoreguidelines-c-copy-assignment-signature`**</pink> โ†’ <purple>`misc-unconventional-assign-operator`</purple>
- <pink>**`cppcoreguidelines-explicit-virtual-functions`**</pink> โ†’ <purple>`modernize-use-override`</purple> <yellow>**Yes**</yellow>
- <pink>**`cppcoreguidelines-macro-to-enum`**</pink> โ†’ <purple>`modernize-macro-to-enum`</purple> <yellow>**Yes**</yellow>
- <pink>**`cppcoreguidelines-noexcept-destructor`**</pink> โ†’ <purple>`performance-noexcept-destructor`</purple> <yellow>**Yes**</yellow>
- <pink>**`cppcoreguidelines-noexcept-move-operations`**</pink> โ†’ <purple>`performance-noexcept-move-constructor`</purple> <yellow>**Yes**</yellow>
- <pink>**`cppcoreguidelines-noexcept-swap`**</pink> โ†’ <purple>`performance-noexcept-swap`</purple> <yellow>**Yes**</yellow>
- <pink>**`cppcoreguidelines-non-private-member-variables-in-classes`**</pink> โ†’ <purple>`misc-non-private-member-variables-in-classes`</purple>
- <pink>**`cppcoreguidelines-use-default-member-init`**</pink> โ†’ <purple>`modernize-use-default-member-init`</purple> <yellow>**Yes**</yellow>
- <pink>**`fuchsia-header-anon-namespaces`**</pink> โ†’ <purple>`google-build-namespaces`</purple>
- <pink>**`google-readability-braces-around-statements`**</pink> โ†’ <purple>`readability-braces-around-statements`</purple>
- <pink>**`google-readability-function-size`**</pink> โ†’ <purple>`readability-function-size`</purple>
- <pink>**`google-readability-namespace-comments`**</pink> โ†’ <purple>`llvm-namespace-comment`</purple>
- <pink>**`hicpp-avoid-c-arrays`**</pink> โ†’ <purple>`modernize-avoid-c-arrays`</purple>

more checks incoming, bestie ๐Ÿ’–

- **`hicpp-avoid-goto`** โ†’ **`cppcoreguidelines-avoid-goto`** โ€” no third-column tea, bestie. just the mapping, and that's the whole vibe. <lavender>โœจ</lavender>

- **`hicpp-braces-around-statements`** โ†’ **`readability-braces-around-statements`** โ€” braces stay ON, no cap. nothing extra in that last column either. <cyan>๐Ÿ’…</cyan>

- **`hicpp-deprecated-headers`** โ†’ **`modernize-deprecated-headers`** โ€” and this one gets a lil **`Yes`** in the third column ๐Ÿซถ (the previous parts already explained what that column means, so iykyk, we're not re-litigating it here).

- **`hicpp-explicit-conversions`** โ†’ **`google-explicit-constructor`** โ€” third column says **`Yes`**. periodt. <pink>๐Ÿ’–</pink>

- **`hicpp-function-size`** โ†’ **`readability-function-size`** โ€” nothing in the third column, just the mapping, she's lowkey minimal.

- **`hicpp-invalid-access-moved`** โ†’ **`bugprone-use-after-move`** โ€” also blank in that third column. <purple>๐Ÿฆ‹</purple>

- **`hicpp-member-init`** โ†’ **`cppcoreguidelines-pro-type-member-init`** โ€” **`Yes`** in the third column. iconic behavior fr fr.

- **`hicpp-move-const-arg`** โ†’ **`performance-move-const-arg`** โ€” third column: **`Yes`** โœจ

- **`hicpp-named-parameter`** โ†’ **`readability-named-parameter`** โ€” **`Yes`**. she said "name ur parameters" and she meant it. <mint>๐ŸŒธ</mint>

- **`hicpp-new-delete-operators`** โ†’ **`misc-new-delete-overloads`** โ€” third column is empty, so just the mapping, no extra lore.

- **`hicpp-no-array-decay`** โ†’ **`cppcoreguidelines-pro-bounds-array-to-pointer-decay`** โ€” blank third column. that mapping is a whole mouthful but we respect her. ๐Ÿ˜ญ

- **`hicpp-no-malloc`** โ†’ **`cppcoreguidelines-no-malloc`** โ€” nothing in the third column, just vibes and a mapping.

- **`hicpp-noexcept-move`** โ†’ **`performance-noexcept-move-constructor`** โ€” third column: **`Yes`** ๐Ÿ”ฅ

- **`hicpp-special-member-functions`** โ†’ **`cppcoreguidelines-special-member-functions`** โ€” third column blank. special members still special tho. <hotpink>๐Ÿ‘‘</hotpink>

- **`hicpp-static-assert`** โ†’ **`misc-static-assert`** โ€” **`Yes`** in the third column. slay.

- **`hicpp-undelegated-constructor`** โ†’ **`bugprone-undelegated-constructor`** โ€” third column empty, mapping only.

- **`hicpp-uppercase-literal-suffix`** โ†’ **`readability-uppercase-literal-suffix`** โ€” **`Yes`**. uppercase suffixes, no exceptions, bestie. <yellow>โญ</yellow>

- **`hicpp-use-auto`** โ†’ **`modernize-use-auto`** โ€” **`Yes`** in the third column. the way this one just *ate* ๐Ÿ’…

---

- **`hicpp-use-emplace`** โ†’ **`modernize-use-emplace`** โ€” third column says **Yes** โœจ
- **`hicpp-use-equals-default`** โ†’ **`modernize-use-equals-default`** โ€” **Yes** ๐Ÿ’–
- **`hicpp-use-equals-delete`** โ†’ **`modernize-use-equals-delete`** โ€” **Yes** ๐ŸŽ€
- **`hicpp-use-noexcept`** โ†’ **`modernize-use-noexcept`** โ€” **Yes** ๐Ÿฆ‹
- **`hicpp-use-nullptr`** โ†’ **`modernize-use-nullptr`** โ€” **Yes** ๐ŸŒธ
- **`hicpp-use-override`** โ†’ **`modernize-use-override`** โ€” **Yes** ๐Ÿ’…
- **`hicpp-vararg`** โ†’ **`cppcoreguidelines-pro-type-vararg`** โ€” third column is just empty, no cap, she said nothing ๐Ÿ˜ญ
- **`llvm-else-after-return`** โ†’ **`readability-else-after-return`** โ€” **Yes** ๐Ÿ”ฅ
- **`llvm-qualified-auto`** โ†’ **`readability-qualified-auto`** โ€” **Yes** ๐Ÿ‘‘

---

## ๐ŸŒˆ Clang-tidy IDE/Editor Integrations ๐ŸŒˆ

okay so bestie, here's the tea ๐Ÿซ– โ€” clang-tidy isn't just some standalone little tool you run in your terminal and forget about. <pink>Nope.</pink> It's been <hotpink>integrated</hotpink> into a whole bunch of <purple>IDEs</purple>, <lavender>code analyzers</lavender>, and <cyan>editors</cyan>. And the official rec? They straight-up recommend <green>clangd</green>, because it integrates clang-tidy *and* it's available in basically every major editor through plugins โ€” Vim, Emacs, Visual Studio Code, Sublime Text and more. it's giving universal icon energy fr fr โœจ

The following table shows the most well-known clang-tidy integrations in detail. it's a whole vibe check across tools, so let's go tool by tool ๐Ÿ’…

### ๐Ÿงš A.L.E. for Vim ๐Ÿงš

- **On-the-fly inspection** โ€” `+` โœจ
- **Check list configuration (GUI)** โ€” `-`
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `-`
- **Custom clang-tidy binary** โ€” `+` ๐Ÿ”ฅ

### ๐Ÿงš Clang Power Tools for Visual Studio ๐Ÿงš

- **On-the-fly inspection** โ€” `-`
- **Check list configuration (GUI)** โ€” `+` ๐Ÿ’–
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `+` ๐Ÿ’–
- **Custom clang-tidy binary** โ€” `-`

### ๐Ÿงš Clangd ๐Ÿงš

- **On-the-fly inspection** โ€” `+` โœจ
- **Check list configuration (GUI)** โ€” `-`
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `+` ๐ŸŒธ
- **Custom clang-tidy binary** โ€” `-`

### ๐Ÿงš CLion IDE ๐Ÿงš

- **On-the-fly inspection** โ€” `+` โœจ
- **Check list configuration (GUI)** โ€” `+` โœจ
- **Options to checks (GUI)** โ€” `+` โœจ
- **Configuration via `.clang-tidy` files** โ€” `+` โœจ
- **Custom clang-tidy binary** โ€” `+` โœจ

> not CLion hitting every single column with a `+` ๐Ÿ˜ญ the way she ate and left no crumbs, main character energy fr ๐Ÿ‘‘

### ๐Ÿงš CodeChecker ๐Ÿงš

- **On-the-fly inspection** โ€” `-`
- **Check list configuration (GUI)** โ€” `-`
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `-`
- **Custom clang-tidy binary** โ€” `+` ๐Ÿ”ฅ

### ๐Ÿงš CPPCheck ๐Ÿงš

- **On-the-fly inspection** โ€” `-`
- **Check list configuration (GUI)** โ€” `-`
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `-`
- **Custom clang-tidy binary** โ€” `-`

### ๐Ÿงš CPPDepend ๐Ÿงš

- **On-the-fly inspection** โ€” `-`
- **Check list configuration (GUI)** โ€” `-`
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `-`
- **Custom clang-tidy binary** โ€” `-`

### ๐Ÿงš Flycheck for Emacs ๐Ÿงš

- **On-the-fly inspection** โ€” `+` โœจ
- **Check list configuration (GUI)** โ€” `-`
- **Options to checks (GUI)** โ€” `-`
- **Configuration via `.clang-tidy` files** โ€” `+` ๐Ÿ’œ
- **Custom clang-tidy binary** โ€” `+` ๐Ÿ’œ

### ๐Ÿงš KDevelop IDE ๐Ÿงš

- **On-the-fly inspection** โ€” `-`
- **Check list configuration (GUI)** โ€” `+` ๐ŸŽ€
- **Options to checks (GUI)** โ€” `+` ๐ŸŽ€
- **Configuration via `.clang-tidy` files** โ€” `+` ๐ŸŽ€
- **Custom clang-tidy binary** โ€” `+` ๐ŸŽ€

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Qt Creator โ”‚ + โ”‚ + โ”‚ - โ”‚ + โ”‚ + โ”‚ โ”‚ IDE โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ ReSharper โ”‚ + โ”‚ + โ”‚ - โ”‚ + โ”‚ + โ”‚ โ”‚ C++ for โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Visual โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Studio โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Syntastic โ”‚ + โ”‚ - โ”‚ - โ”‚ - โ”‚ + โ”‚ โ”‚ for Vim โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Visual โ”‚ + โ”‚ + โ”‚ - โ”‚ - โ”‚ - โ”‚ โ”‚ Assist for โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Visual โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Studio โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜


### ๐Ÿ’ป IDEs ๐Ÿ’…

<cyan>CLion 2017.2</cyan> and later is out here integrating `clang-tidy` as an extension to the built-in code analyzer, periodt. And starting from <cyan>2018.2 EAP</cyan>, CLion lets you use `clang-tidy` via <pink>Clangd</pink>. Inspections and applicable quick-fixes happen <hotpink>on the fly</hotpink>, and checks can be configured in standard command line format. In this integration you can switch to the `clang-tidy` binary that's different from the bundled one, pass the configuration in `.clang-tidy` files instead of using the IDE settings, and configure options for particular checks. so many options bestie, iconic ๐Ÿ’–

<purple>KDevelop</purple> with the <pink>`kdev-clang-tidy`</pink> plugin, starting from version <cyan>5.1</cyan>, performs static analysis using `clang-tidy`. The plugin launches the `clang-tidy` binary from the specified location and parses its output to provide a list of issues. she's doing the work fr fr ๐Ÿซถ

<purple>QtCreator 4.6</purple> integrates `clang-tidy` warnings into the editor diagnostics under the <pink>Clang Code Model</pink>. To employ `clang-tidy` inspection in QtCreator, you need to create a copy of one of the presets and choose the checks to be performed. Since <cyan>QtCreator 4.7</cyan> project-wide analysis is possible with the <pink>Clang Tools</pink> analyzer. โœจ

<cyan>MS Visual Studio</cyan> can integrate `clang-tidy` by means of <hotpink>three</hotpink> different tools, no cap. The <pink>ReSharper C++</pink> extension, version <cyan>2017.3</cyan> and later, provides seamless `clang-tidy` integration: checks and quick-fixes run alongside native inspections. Apart from that, ReSharper C++ incorporates `clang-tidy` as a separate step of its code clean-up process. <pink>Visual Assist</pink> build <cyan>2210</cyan> includes a subset of `clang-tidy` checklist to inspect the code as you edit. Another way to bring `clang-tidy` functionality to Visual Studio is the <pink>Clang Power Tools</pink> plugin, which includes most of the `clang-tidy` checks and runs them during compilation or as a separate step of code analysis. ๐Ÿ˜ญ so many ways bestie

### ๐Ÿ“ Editors ๐ŸŽ€

<cyan>Emacs24</cyan>, when expanded with the <pink>Flycheck</pink> plugin, incorporates the `clang-tidy` inspection into the syntax analyzer. For <cyan>Vim</cyan>, you can use <pink>Syntastic</pink>, which includes `clang-tidy`, or <pink>A.L.E.</pink>, a lint engine that applies `clang-tidy` along with other linters. ๐Ÿฆ‹

### ๐Ÿ”ฌ Analyzers โญ

`clang-tidy` is integrated in <cyan>CPPDepend</cyan> starting from version <cyan>2018.1</cyan> and <cyan>CPPCheck 1.82</cyan>. CPPCheck integration lets you import Visual Studio solutions and run the `clang-tidy` inspection on them. The <pink>CodeChecker</pink> application of version <cyan>5.3</cyan> or later, which also comes as a plugin for <cyan>Eclipse</cyan>, supports `clang-tidy` as a static analysis instrument and allows to use a custom `clang-tidy` binary. ๐ŸŒˆ

### ๐ŸŒŸ Getting Involved ๐ŸŒŸ

`clang-tidy` has several own checks and can run Clang static analyzer checks, but its <rainbow>power</rainbow> is in the ability to easily write custom checks. ๐Ÿ’ช

Checks are organized in <pink>modules</pink>, which can be linked into `clang-tidy` with minimal or no code changes in `clang-tidy`. โœจ

Checks can plug into the analysis on the preprocessor level using <pink>`PPCallbacks`</pink> or on the AST level using <pink>AST Matchers</pink>. When an error is found, checks can report them in a way similar to how Clang diagnostics work. A <pink>fix-it hint</pink> can be attached to a diagnostic message. ๐Ÿ’…

The interface provided by `clang-tidy` makes it easy to write useful and precise checks in just a few lines of code. If you have an idea for a good check, the rest of this document explains how to do this. ๐Ÿซถ

There are a few tools particularly useful when developing `clang-tidy` checks:

- `add_new_check.py` is a script to automate the process of adding a new check, it will create the check, update the CMake file and create a test;
- `rename_check.py` does what the script name suggests, renames an existing check;
- `pp-trace` logs method calls on `PPCallbacks` for a source file and is invaluable in understanding the preprocessor mechanism;
- `clang-query` is invaluable for interactive prototyping of AST matchers and exploration of the Clang AST;
- `clang-check` with the `-ast-dump` (and optionally `-ast-dump-filter`) provides a convenient way to dump AST of a C++ program.

> โš ๏ธ If CMake is configured with `CLANG_TIDY_ENABLE_STATIC_ANALYZER=NO`, `clang-tidy` will <red>not</red> be built with support for the `clang-analyzer-*` checks or the `mpi-*` checks. bestie that's a whole vibe check, read it twice ๐Ÿ’€

### ๐Ÿค” Choosing the Right Place for your Check ๐Ÿงš

If you have an idea of a check, you should decide whether it should be implemented as a:

- <pink>Clang diagnostic</pink>: if the check is generic enough, targets code patterns that most probably are bugs (rather than style or readability issues), can be implemented effectively and with extremely low false positive rate, it may make a good Clang diagnostic.
- <pink>Clang static analyzer check</pink>: if the check requires some sort of control flow analysis, it should probably be implemented as a static analyzer check.
- <pink>`clang-tidy` check</pink> is a good choice for linter-style checks, checks that are related to a certain coding style, checks that address code readability, etc.

### ๐Ÿ› ๏ธ Preparing your Workspace ๐Ÿ› ๏ธ

If you are new to LLVM development, you should read the <pink>Getting Started with the LLVM System</pink>, <pink>Using Clang Tools</pink> and <pink>How To Setup Clang Tooling For LLVM</pink> documents to check out and build LLVM, Clang and Clang Extra Tools with CMake. ๐Ÿ“š

Once you are done, change to the `llvm/clang-tools-extra` directory, and let's start! ๐Ÿš€

When you configure the CMake build, make sure that you enable the `clang` and `clang-tools-extra` projects to build `clang-tidy`. Because your new check will have associated documentation, you will also want to install <pink>Sphinx</pink> and enable it in the CMake configuration. To save build time of the core Clang libraries you may want to only enable the <pink>X86</pink> target in the CMake configuration. โญ

### ๐Ÿ“ The Directory Structure ๐Ÿ“

`clang-tidy` source code resides in the `llvm/clang-tools-extra` directory and is structured as follows:

clang-tidy/ # Clang-tidy core.

-- ClangTidy.h # Interfaces for users.
-- ClangTidyCheck.h # Interfaces for checks.
-- ClangTidyModule.h # Interface for clang-tidy modules.
-- ClangTidyModuleRegistry.h # Interface for registering of modules.

...

-- google/ # Google clang-tidy module.
-+
-- GoogleTidyModule.cpp
-- GoogleTidyModule.h

...

-- llvm/ # LLVM clang-tidy module.
-+
-- LLVMTidyModule.cpp
-- LLVMTidyModule.h

...

-- objc/ # Objective-C clang-tidy module.
-+
-- ObjCTidyModule.cpp
-- ObjCTidyModule.h

...

-- tool/ # Sources of the clang-tidy binary.

... test/clang-tidy/ # Integration tests. ... unittests/clang-tidy/ # Unit tests.

-- ClangTidyTest.h
-- GoogleModuleTest.cpp
-- LLVMModuleTest.cpp
-- ObjCModuleTest.cpp

...


### โœ๏ธ Writing a clang-tidy Check โœ๏ธ

So you have an idea of a useful check for `clang-tidy`. ๐Ÿ‘‘

First, if you're not familiar with LLVM development, read through the <pink>Getting Started with LLVM</pink> document for instructions on setting up your workflow and the <pink>LLVM Coding Standards</pink> document to familiarize yourself with the coding style used in the project. For code reviews we mostly use <pink>LLVM Phabricator</pink>. ๐Ÿ’…

Next, you need to decide which <pink>module</pink> the check belongs to. Modules are located in subdirectories of `clang-tidy/` and contain checks targeting a certain aspect of code quality (performance, readability, etc.), certain coding style or standard (Google, LLVM, CERT, etc.) or a widely used API (e.g. MPI). Their names are the same as the user-facing check group names described above. โœจ

After choosing the module and the name for the check, run the `clang-tidy/add_new_check.py` script to create the skeleton of the check and plug it to `clang-tidy`. It's the recommended way of adding new checks. ๐Ÿซถ

If we want to create a `readability-awesome-function-names`, we would run:

$ clang-tidy/add_new_check.py readability awesome-function-names


The `add_new_check.py` script will:

- create the class for your check inside the specified module's directory and register it in the module and in the build system;
- create a `lit` test file in the `test/clang-tidy/` directory;
- create a documentation file and include it into the `docs/clang-tidy/checks/list.rst`.

Let's see in more detail at the check class definition:

...

#include "../ClangTidyCheck.h"

namespace clang { namespace tidy { namespace readability {

... class AwesomeFunctionNamesCheck : public ClangTidyCheck { public: AwesomeFunctionNamesCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; };

} // namespace readability } // namespace tidy } // namespace clang

...


Okay so <pink>the constructor of the check receives the `Name` and `Context` parameters</pink>, and it <hotpink>**must**</hotpink> forward them to the `ClangTidyCheck` constructor. No skipping that step bestie ๐Ÿ’…

In our case the check needs to operate on the <purple>AST level</purple> and it overrides the `registerMatchers` and `check` methods. But if we wanted to analyze code on the <purple>preprocessor level</purple>, we'd need instead to override the `registerPPCallbacks` method. Different method, different vibe ๐Ÿฆ‹

In the `registerMatchers` method we create an AST Matcher (see <cyan>AST Matchers</cyan> for more info) that will find the pattern in the AST that we wanna inspect. The results of the matching get passed to the `check` method, which can further inspect them and report diagnostics. โœจ

using namespace ast_matchers;

void AwesomeFunctionNamesCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher(functionDecl().bind("x"), this); }

void AwesomeFunctionNamesCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x"); if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_")) return; diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome") << MatchedDecl << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); }


(If you wanna see an example of a *useful* check, look at `clang-tidy/google/ExplicitConstructorCheck.h` and `clang-tidy/google/ExplicitConstructorCheck.cpp`). ๐Ÿ‘‘

If you need to interact with macros or preprocessor directives, you'll wanna override the method `registerPPCallbacks`. <yellow>Heads up tho:</yellow> the `add_new_check.py` script does <red>**not**</red> generate an override for this method in the starting point for your new check. ๐Ÿ™ˆ

If your check applies only under a specific set of language options, be sure to override the method `isLanguageVersionSupported` to reflect that. Periodt. ๐Ÿ’…

---

### โœจ Check development tips โœจ

Writing your first check can be a <lavender>*daunting*</lavender> task, particularly if you're unfamiliar with the LLVM and Clang code bases. Here are some suggestions for orienting yourself in the codebase and working on your check incrementally. ๐Ÿงš

### ๐Ÿ“š Guide to useful documentation ๐Ÿ“š

Many of the support classes created for LLVM are used by Clang, such as `StringRef` and `SmallVector`. These and other commonly used classes are described in the <cyan>Important and useful LLVM APIs</cyan> and <cyan>Picking the Right Data Structure for the Task</cyan> sections of the LLVM Programmer's Manual. You do **not** need to memorize all the details of these classes; the generated doxygen documentation has everything if you need it. In the header `LLVM/ADT/STLExtras.h` you'll find useful versions of the STL algorithms that operate on LLVM containers, such as `llvm::all_of`. ๐ŸŒธ

Clang is implemented on top of LLVM and introduces its own set of classes that you'll interact with while writing your check. When a check issues diagnostics and fix-its, these are associated with locations in the source code. Source code locations, source files, ranges of source locations and the `SourceManager` class provide the mechanisms for describing such locations. These and other topics are described in the <purple>"Clang" CFE Internals Manual</purple>. Whereas the doxygen generated documentation serves as a reference to the internals of Clang, this document serves as a guide to other developers. Topics in that manual of interest to a check developer are:

- The Clang "Basic" Library for information about diagnostics, fix-it hints and source locations.
- The Lexer and Preprocessor Library for information about tokens, lexing (transforming characters into tokens) and the preprocessor.
- The AST Library for information about how C++ source statements are represented as an abstract syntax tree (AST).

Most checks will interact with C++ source code via the AST. Some checks will interact with the preprocessor. The input source file is lexed and preprocessed and then parsed into the AST. Once the AST is fully constructed, the check is run by applying the check's registered AST matchers against the AST and invoking the check with the set of matched nodes from the AST. Monitoring the actions of the preprocessor is detached from the AST construction, but a check can collect information during preprocessing for later use by the check when nodes are matched by the AST. ๐Ÿ”ฅ

Every syntactic (and sometimes semantic) element of the C++ source code is represented by different classes in the AST. You select the portions of the AST you're interested in by composing AST matcher functions. You'll wanna study carefully the <cyan>AST Matcher Reference</cyan> to understand the relationship between the different matcher functions. ๐Ÿ‘‘

### ๐Ÿ”„ Using the Transformer library ๐Ÿ”„

The Transformer library lets you write a check that transforms source code by expressing the transformation as a `RewriteRule`. The Transformer library provides functions for composing edits to source code to create rewrite rules. Unless you need to perform low-level source location manipulation, you may wanna consider writing your check with the Transformer library. The <cyan>Clang Transformer Tutorial</cyan> describes the Transformer library in detail. โœจ

To use the Transformer library, make the following changes to the code generated by the `add_new_check.py` script:

- Include `../utils/TransformerClangTidyCheck.h` instead of `../ClangTidyCheck.h`
- Change the base class of your check from `ClangTidyCheck` to `TransformerClangTidyCheck`
- Delete the override of the `registerMatchers` and `check` methods in your check class.
- Write a function that creates the `RewriteRule` for your check.
- Call the function in your check's constructor to pass the rewrite rule to `TransformerClangTidyCheck`'s constructor.

### ๐ŸŒฑ Developing your check incrementally ๐ŸŒฑ

The best way to develop your check is to start with the simple test cases and increase complexity incrementally. The test file created by the `add_new_check.py` script is a starting point for your test cases. A rough outline of the process looks like this:

- Write a test case for your check.
- Prototype matchers on the test file using `clang-query`.
- Capture the working matchers in the `registerMatchers` method.
- Issue the necessary diagnostics and fix-its in the `check` method.
- Add the necessary `CHECK-MESSAGES` and `CHECK-FIXES` annotations to your test case to validate the diagnostics and fix-its.
- Build the target `check-clang-tool` to confirm the test passes.
- Repeat the process until all aspects of your check are covered by tests.

The quickest way to prototype your matcher is to use `clang-query` to interactively build up your matcher. For complicated matchers, build up a matching expression incrementally and use `clang-query`'s `let` command to save named matching expressions to simplify your matcher. Just like breaking up a huge function into smaller chunks with intention-revealing names can help you understand a complex algorithm, breaking up a matcher into smaller matchers with intention-revealing names can help you understand a complicated matcher. Once you have a working matcher, the C++ API will be virtually identical to your interactively constructed matcher. You can use local variables to preserve your intention-revealing names that you applied to nested matchers. ๐Ÿ’œ

### ๐Ÿ”’ Creating private matchers ๐Ÿ”’

Sometimes you wanna match a specific aspect of the AST that isn't provided by the existing AST matchers. You can create your own private matcher using the same infrastructure as the public matchers. A private matcher can simplify the processing in your `check` method by eliminating complex hand-crafted AST traversal of the matched nodes. Using the private matcher allows you to select the desired portions of the AST directly in the matcher and refer to it by a bound name in the `check` method. ๐ŸŽ€

### ๐Ÿงช Unit testing helper code ๐Ÿงช

Private custom matchers are a good example of auxiliary support code for your check that can be tested with a unit test. It'll be easier to test your matchers or other support classes by writing a unit test than by writing a FileCheck integration test. The `ASTMatchersTests` target contains unit tests for the public AST matcher classes and is a good source of testing idioms for matchers. ๐ŸŒˆ

You can build the Clang-tidy unit tests by building the `ClangTidyTests` target. Test targets in LLVM and Clang are excluded from the "build all" style action of IDE-based CMake generators, so you need to explicitly build the target for the unit tests to be built. โญ

### ๐Ÿ’ช Making your check robust ๐Ÿ’ช

Once you've covered your check with the basic "happy path" scenarios, you'll wanna torture your check with as many edge cases as you can cover in order to ensure your check is robust. Running your check on a large code base, such as Clang/LLVM, is a good way to catch things you forgot to account for in your matchers. However, the LLVM code base may be insufficient for testing purposes as it was developed against a particular set of coding styles and quality measures. The larger the corpus of code the check is tested against, the higher confidence the community will have in the check's efficacy and false positive rate. ๐Ÿ”ฅ

Some suggestions to ensure your check is robust:

- Create header files that contain code matched by your check.
- Validate that fix-its are properly applied to test header files with clang-tidy. You will need to perform this test manually until automated support for checking messages and fix-its is added to the `check_clang_tidy.py` script.
- Define macros that contain code matched by your check.
- Define template classes that contain code matched by your check.
- Define template specializations that contain code matched by your check.
- Test your check under both Windows and Linux environments.
- Watch out for high false positive rates. Ideally, a check would have no false positives, but given that matching against an AST is not control- or data flow- sensitive, a number of false positives are expected. The higher the false positive rate, the less likely the check will be adopted in practice. Mechanisms should be put in place to help the user manage false positives. ๐Ÿซถ

- There are **two primary mechanisms** for managing false positives: <pink>supporting a code pattern</pink> which lets the programmer silence the diagnostic in an ad hoc manner, and <purple>check configuration options</purple> to control the behavior of the check.

- Consider supporting a code pattern to allow the programmer to silence the diagnostic whenever such a code pattern can clearly express the programmer's intent. For example, allowing an explicit cast to `void` to silence an unused variable diagnostic. <mint>the vibe: let the human say "yes I meant that" ๐Ÿ’…</mint>

- Consider adding check configuration options to allow the user to opt into more aggressive checking behavior without burdening users for the common high-confidence cases. <cyan>opt-in chaos, always</cyan> โœจ

---

### ๐ŸŽ€ Documenting your check ๐ŸŽ€

The `add_new_check.py` script creates entries in the release notes, the list of checks and a new file for the check documentation itself. It is recommended that you have a concise summation of what your check does in a single sentence that is repeated in the release notes, as the first sentence in the doxygen comments in the header file for your check class and as the first sentence of the check documentation. <yellow>Avoid the phrase "this check" in your check summation and check documentation.</yellow>

If your check relates to a published coding guideline (C++ Core Guidelines, MISRA, etc.) or style guide, provide links to the relevant guideline or style guide sections in your check documentation. <lavender>iykyk, citation is a love language</lavender> ๐Ÿฆ‹

Provide enough examples of the diagnostics and fix-its provided by the check so that a user can easily understand what will happen to their code when the check is run. If there are exceptions or limitations to your check, document them thoroughly. This will help users understand the scope of the diagnostics and fix-its provided by the check. <pink>no cap, your future self reading this will thank you</pink> ๐Ÿ’–

Building the target `docs-clang-tools-html` will run the Sphinx documentation generator and create documentation HTML files in the `tools/clang/tools/extra/docs/html` directory in your build tree. Make sure that your check is correctly shown in the release notes and the list of checks. Make sure that the formatting and structure of your check's documentation looks correct. <mint>aesthetic AND correct, the dream ๐ŸŒˆ</mint>

---

### ๐Ÿ‘‘ Registering your Check ๐Ÿ‘‘

> (The `add_new_check.py` script takes care of registering the check in an existing module. If you want to create a new module or know the details, read on.)

The check should be registered in the corresponding module with a distinct name:

class MyModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck<ExplicitConstructorCheck>( "my-explicit-constructor"); } };


Now we need to register the module in the `ClangTidyModuleRegistry` using a statically initialized variable:

static ClangTidyModuleRegistry::Add<MyModule> X("my-module", "Adds my lint checks.");


When using LLVM build system, we need to use the following hack to ensure the module is linked into the `clang-tidy` binary:

Add this near the `ClangTidyModuleRegistry::Add<MyModule>` variable:

// This anchor is used to force the linker to link in the generated object file // and thus register the MyModule. volatile int MyModuleAnchorSource = 0;


And this to the main translation unit of the `clang-tidy` binary (or the binary you link the `clang-tidy` library in) `clang-tidy/ClangTidyForceLinker.h`:

// This anchor is used to force the linker to link the MyModule. extern volatile int MyModuleAnchorSource; static int MyModuleAnchorDestination = MyModuleAnchorSource;


---

### โš™๏ธ Configuring Checks โš™๏ธ

If a check needs configuration options, it can access check-specific options using the `Options.get<Type>("SomeOption", DefaultValue)` call in the check constructor. In this case the check should also override the `ClangTidyCheck::storeOptions` method to make the options provided by the check discoverable. This method lets `clang-tidy` know which options the check implements and what the current values are (e.g. for the `-dump-config` command line option). <cyan>so the tool can brag about itself, slay</cyan>

class MyCheck : public ClangTidyCheck { const unsigned SomeOption1; const std::string SomeOption2;

public: MyCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), SomeOption(Options.get("SomeOption1", -1U)), SomeOption(Options.get("SomeOption2", "some default")) {}

void storeOptions(ClangTidyOptions::OptionMap &Opts) override { Options.store(Opts, "SomeOption1", SomeOption1); Options.store(Opts, "SomeOption2", SomeOption2); } ...


Assuming the check is registered with the name `"my-check"`, the option can then be set in a `.clang-tidy` file in the following way:

CheckOptions: my-check.SomeOption1: 123 my-check.SomeOption2: 'some other value'


If you need to specify check options on a command line, you can use the inline YAML format:

$ clang-tidy -config="{CheckOptions: {a: b, x: y}}" ...


---

### ๐Ÿงš Testing Checks ๐Ÿงš

To run tests for `clang-tidy`, build the `check-clang-tools` target. For instance, if you configured your CMake build with the `ninja` project generator, use the command:

$ ninja check-clang-tools


`clang-tidy` checks can be tested using either unit tests or lit tests. Unit tests may be more convenient to test complex replacements with strict checks. Lit tests allow using partial text matching and regular expressions which makes them more suitable for writing compact tests for diagnostic messages. <purple>pick your fighter ๐Ÿ’…</purple>

The `check_clang_tidy.py` script provides an easy way to test both diagnostic messages and fix-its. It filters out `CHECK` lines from the test file, runs `clang-tidy` and verifies messages and fixes with two separate `FileCheck` invocations: once with `FileCheck`'s directive prefix set to `CHECK-MESSAGES`, validating the diagnostic messages, and once with the directive prefix set to `CHECK-FIXES`, running against the fixed code (i.e., the code after generated fix-its are applied). In particular, `CHECK-FIXES:` can be used to check that code was not modified by fix-its, by checking that it is present unchanged in the fixed code. The full set of `FileCheck` directives is available (e.g., `CHECK-MESSAGES-SAME:`, `CHECK-MESSAGES-NOT:`), though typically the basic `CHECK` forms (`CHECK-MESSAGES` and `CHECK-FIXES`) are sufficient for `clang-tidy` tests. Note that the `FileCheck` documentation mostly assumes the default prefix (`CHECK`), and hence describes the directive as `CHECK:`, `CHECK-SAME:`, `CHECK-NOT:`, etc. Replace `CHECK` by either `CHECK-FIXES` or `CHECK-MESSAGES` for `clang-tidy` tests. <lavender>the way that one prefix does so muchโ€ฆ</lavender> ๐Ÿซถ

An additional check enabled by `check_clang_tidy.py` ensures that if `CHECK-MESSAGES:` is used in a file then every warning or error must have an associated `CHECK` in that file. Or, you can use `CHECK-NOTES:` instead, if you want to also ensure that all the notes are checked.

To use the `check_clang_tidy.py` script, put a `.cpp` file with the appropriate `RUN` line in the `test/clang-tidy` directory. Use `CHECK-MESSAGES:` and `CHECK-FIXES:` lines to write checks against diagnostic messages and fixed code.

It's advised to make the checks as specific as possible to avoid checks matching to incorrect parts of the input. Use `[[@LINE+X]]`/`[[@LINE-X]]` substitutions and distinct function and variable names in the test code.

Here's an example of a test using the `check_clang_tidy.py` script (the full source code is at โ€ `test/clang-tidy/checkers/google/readability-casting.cpp`):

// RUN: %check_clang_tidy %s google-readability-casting %t

void f(int a) { int b = (int)a; // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type [google-readability-casting] // CHECK-FIXES: int b = a; }


To check more than one scenario in the same test file use `-check-suffix=SUFFIX-NAME` on `check_clang_tidy.py` command line or `-check-suffixes=SUFFIX-NAME-1,SUFFIX-NAME-2,...`. With `-check-suffix[es]=SUFFIX-NAME` you need to replace your `CHECK-*` directives with `CHECK-MESSAGES-SUFFIX-NAME` and `CHECK-FIXES-SUFFIX-NAME`.

Here's an example:

// RUN: %check_clang_tidy -check-suffix=USING-A %s misc-unused-using-decls %t -- -- -DUSING_A // RUN: %check_clang_tidy -check-suffix=USING-B %s misc-unused-using-decls %t -- -- -DUSING_B // RUN: %check_clang_tidy %s misc-unused-using-decls %t ... // CHECK-MESSAGES-USING-A: :[[@LINE-8]]:10: warning: using decl 'A' {{.}} // CHECK-MESSAGES-USING-B: :[[@LINE-7]]:10: warning: using decl 'B' {{.}} // CHECK-MESSAGES: :[[@LINE-6]]:10: warning: using decl 'C' {{.*}} // CHECK-FIXES-USING-A-NOT: using a::A;$ // CHECK-FIXES-USING-B-NOT: using a::B;$ // CHECK-FIXES-NOT: using a::C;$


There are many dark corners in the C++ language, and it may be difficult to make your check work perfectly in all cases, especially if it issues fix-it hints. <red>the most frequent pitfalls are macros and templates:</red>

1. code written in a macro body/template definition may have a different meaning depending on the macro expansion/template instantiation;

2. multiple macro expansions/template instantiations may result in the same code being inspected by the check multiple times (possibly, with different meanings, see 1), and the same warning (or a slightly different one) may be issued by the check multiple times; `clang-tidy` will deduplicate *identical* warnings, but if the warnings are slightly different, all of them will be shown to the user (and used for applying fixes, if any);

3. making replacements to a macro body/template definition may be fine for some macro expansions/template instantiations, but easily break some other expansions/instantiations.

If you need multiple files to exercise all the aspects of your check, it is recommended you place them in a subdirectory named for the check under the `Inputs` directory for the module containing your check. This keeps the test directory from getting cluttered. <mint>tidy queen behavior โœจ</mint>

If you need to validate how your check interacts with system header files, a set of simulated system header files is located in the `checkers/Inputs/Headers` directory. The path to this directory is available in a lit test with the variable `%clang_tidy_headers`.

---

### ๐ŸŒˆ Out-of-tree check plugins ๐ŸŒˆ

Developing an out-of-tree check as a plugin largely follows the steps outlined above. The plugin is a shared library whose code lives outside the `clang-tidy` build system. Build and link this shared library against LLVM as done for other kinds of Clang plugins. <hotpink>living outside the build system, main character energy fr fr</hotpink> ๐Ÿ’…

The plugin can be loaded by passing `-load` to `clang-tidy` in addition to the names of the checks to enable.

$ clang-tidy --checks=-*,my-explicit-constructor -list-checks -load myplugin.so


<pink>okay so</pink> real talk bestie: there is **no** expectations regarding ABI and API stability, so the plugin <hotpink>must</hotpink> be compiled against the version of `clang-tidy` that will be loading the plugin. like, it has to match. no vibes-based compatibility, fr fr ๐Ÿ˜ญ

The plugins can use threads, TLS, or any other facilities available to in-tree code which is accessible from the external headers. go off, do your thing ๐Ÿ’…

---

## ๐Ÿƒโ€โ™€๏ธ Running clang-tidy on LLVM

To test a check it's best to try it out on a larger code base. LLVM and Clang are the natural targets as you already have the source code around. <purple>The most convenient way to run `clang-tidy` is with a compile command database</purple>; CMake can automatically generate one, for a description of how to enable it see *How To Setup Clang Tooling For LLVM*. Once `compile_commands.json` is in place and a working version of `clang-tidy` is in `PATH` the entire code base can be analyzed with `clang-tidy/tool/run-clang-tidy.py`. The script executes `clang-tidy` with the default set of checks on every translation unit in the compile command database and displays the resulting warnings and errors. The script provides multiple configuration flags. โœจ

- The default set of checks can be overridden using the `-checks` argument, taking the identical format as `clang-tidy` does. For example `-checks=-*,modernize-use-override` will run the `modernize-use-override` check only. ๐ŸŽฏ

- To restrict the files examined you can provide one or more regex arguments that the file names are matched against. `run-clang-tidy.py clang-tidy/.*Check\.cpp` will only analyze clang-tidy checks. It may also be necessary to restrict the header files that warnings are displayed from using the `-header-filter` flag. It has the same behavior as the corresponding `clang-tidy` flag. ๐Ÿ”

- To apply suggested fixes `-fix` can be passed as an argument. This gathers all changes in a temporary directory and applies them. Passing `-format` will run `clang-format` over changed lines. ๐Ÿ› ๏ธ

---

## ๐Ÿ“Š On checks profiling

`clang-tidy` can collect per-check profiling info, and output it for each processed source file (translation unit). <lavender>obsessed tbh</lavender>

To enable profiling info collection, use the `-enable-check-profile` argument. The timings will be output to `stderr` as a table. Example output:

$ clang-tidy -enable-check-profile -checks=-*,readability-function-size source.cpp ===-------------------------------------------------------------------------=== clang-tidy checks profiling ===-------------------------------------------------------------------------=== Total Execution Time: 1.0282 seconds (1.0258 wall clock)

---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- 0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) readability-function-size 0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) Total


It can also store that data as JSON files for further processing. Example output:

$ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp $ # Note that there won't be timings table printed to the console. $ ls /tmp/out/ 20180516161318717446360-source.cpp.json $ cat 20180516161318717446360-source.cpp.json { "file": "/path/to/source.cpp", "timestamp": "2018-05-16 16:13:18.717446360", "profile": { "time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00, "time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01, "time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01 } }


There is only one argument that controls profile storage: ๐Ÿ‘‘

- **`-store-check-profile=<prefix>`** โ€” the one and only knob for where your profiles go.

  By default reports are printed in tabulated format to `stderr`. When this option is passed, these per-TU profiles are instead stored as JSON. If the prefix is not an absolute path, it is considered to be relative to the directory from where you have run `clang-tidy`. All `.` and `..` patterns in the path are collapsed, and symlinks are resolved. ๐ŸŒ€

  Example: Let's suppose you have a source file named `example.cpp`, located in the `/source` directory. Only the input filename is used, not the full path to the source file. Additionally, it is prefixed with the current timestamp. โฐ

  - If you specify `-store-check-profile=/tmp`, then the profile will be saved to `/tmp/<ISO8601-like timestamp>-example.cpp.json`

  - If you run `clang-tidy` from within `/foo` directory, and specify `-store-check-profile=.`, then the profile will still be saved to `/foo/<ISO8601-like timestamp>-example.cpp.json`

---

<rainbow>and now, the main character energy ๐Ÿ’–</rainbow>

`clang-tidy` is a clang-based C++ "linter" tool. Its purpose is to provide an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. `clang-tidy` is modular and provides a convenient interface for writing new checks. <cyan>periodt.</cyan> โœจ

## ๐Ÿงฐ Using clang-tidy

`clang-tidy` is a LibTooling-based tool, and it's easier to work with if you set up a compile command database for your project (for an example of how to do this, see *How To Setup Tooling For LLVM*). You can also specify compilation options on the command line after `--`: ๐Ÿซถ

$ clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...


`clang-tidy` has its own checks and can also run Clang Static Analyzer checks. Each check has a name and the checks to run can be chosen using the `-checks=` option, which specifies a comma-separated list of positive and negative (prefixed with `-`) globs. Positive globs add subsets of checks, and negative globs remove them. For example, ๐Ÿ‘‡

$ clang-tidy test.cpp -checks=-,clang-analyzer-,-clang-analyzer-cplusplus*


will disable all default checks (`-*`) and enable all `clang-analyzer-*` checks except for `clang-analyzer-cplusplus*` ones. ๐Ÿ’€

The `-list-checks` option lists all the enabled checks. When used without `-checks=`, it shows checks enabled by default. Use `-checks=*` to see all available checks or with any other value of `-checks=` to see which checks are enabled by this value. ๐ŸŽ€

...and the groups of checks we've got rn are literally these, bestie โ€” each one is a *name prefix* you'll see at the front of a check name:

- **`abseil-`** โ€” Checks related to Abseil library.
- **`altera-`** โ€” Checks related to OpenCL programming for FPGAs.
- **`android-`** โ€” Checks related to Android.
- **`boost-`** โ€” Checks related to Boost library.
- **`bugprone-`** โ€” Checks that target bug-prone code constructs.
- **`cert-`** โ€” Checks related to CERT Secure Coding Guidelines.
- **`clang-analyzer-`** โ€” Clang Static Analyzer checks.
- **`concurrency-`** โ€” Checks related to concurrent programming (including threads, fibers, coroutines, etc.).
- **`cppcoreguidelines-`** โ€” Checks related to C++ Core Guidelines.
- **`darwin-`** โ€” Checks related to Darwin coding conventions.
- **`fuchsia-`** โ€” Checks related to Fuchsia coding conventions.
- **`google-`** โ€” Checks related to Google coding conventions.
- **`hicpp-`** โ€” Checks related to High Integrity C++ Coding Standard.
- **`linuxkernel-`** โ€” Checks related to the Linux Kernel coding conventions.
- **`llvm-`** โ€” Checks related to the LLVM coding conventions.
- **`llvmlibc-`** โ€” Checks related to the LLVM-libc coding standards.
- **`misc-`** โ€” Checks that we didn't have a better category for.
- **`modernize-`** โ€” Checks that advocate usage of modern (currently "modern" means "C++11") language constructs.
- **`mpi-`** โ€” Checks related to MPI (Message Passing Interface).
- **`objc-`** โ€” Checks related to Objective-C coding conventions.
- **`openmp-`** โ€” Checks related to OpenMP API.
- **`performance-`** โ€” Checks that target performance-related issues.
- **`portability-`** โ€” Checks that target portability-related issues that don't relate to any particular coding style.
- **`readability-`** โ€” Checks that target readability-related issues that don't relate to any particular coding style.
- **`zircon-`** โ€” Checks related to Zircon kernel coding conventions.

---

Okay so <pink>Clang diagnostics</pink> get treated basically the same way as check diagnostics, and honestly that's such a slay design choice. โœจ They're displayed by `clang-tidy` and you can filter them out with the `-checks=` option. <yellow>BUT</yellow> โ€” and this is the plot twist, bestie โ€” the `-checks=` option does **not** affect compilation arguments, so it literally cannot turn on Clang warnings that aren't already turned on in the build configuration. No cap. ๐Ÿ™ˆ

The `-warnings-as-errors=` option upgrades any warnings emitted under the `-checks=` flag to errors (but it does <red>not</red> enable any checks itself โ€” it's giving elevator, not builder ๐Ÿ’…).

Clang diagnostics have check names starting with `clang-diagnostic-`. And if a diagnostic has a corresponding warning option, it gets named `clang-diagnostic-<warning-option>`, like โ€” for example โ€” a Clang warning controlled by `-Wliteral-conversion` will be reported with the check name `clang-diagnostic-literal-conversion`. ๐ŸŽ€

The `-fix` flag instructs `clang-tidy` to fix found errors <green>if supported by corresponding checks</green>. (Emphasis on *if supported*, okay? It's not out here fixing everything for you. ๐Ÿ’€)

---

An overview of all the command-line options, bestie โ€” look at this absolute buffet:

$ clang-tidy --help USAGE: clang-tidy [options] <source0> [... <sourceN>]

OPTIONS:

Generic Options:

--help - Display available options (--help-hidden for more) --help-list - Display list of available options (--help-list-hidden for more) --version - Display the version of this program

clang-tidy options:


- **`--checks=<string>`** โ€” a comma-separated list of globs, and each glob can have an optional `-` prefix. Globs get processed in the order they show up in the list. Globs with **no** `-` prefix add checks with matching names to the set, and globs **with** the `-` prefix yeet checks with matching names out of the enabled set. Heads up: this option's value gets appended to whatever the `Checks` option says in the `.clang-tidy` file, if there is one. ๐Ÿ’…

- **`--config=<string>`** โ€” you're handing over a configuration in YAML/JSON format, like:

-config="{Checks: '*', CheckOptions: {x: y}}"


And when the value is empty, clang-tidy will go hunting for a file named `.clang-tidy` for every source file, checking its parent directories. ๐Ÿฆ‹

- **`--config-file=<string>`** โ€” specify the path of `.clang-tidy` or a custom config file, e.g. `--config-file=/some/path/myTidyConfigFile`. Internally this works *exactly* the same way as the `--config` option once it's read the config file you gave it. And bestie, listen: use either `--config-file` or `--config`, **not both**. ๐Ÿ™ˆ

- **`--dump-config`** โ€” dumps the configuration in YAML format straight to stdout. You can use this with a file name (plus `--` if that file lives outside a project that has a configured compilation database). The configuration used for that file is what gets printed. Pro tip straight from the page: use it along with `-checks=*` to include the configuration of all checks. โœจ

- **`--enable-check-profile`** โ€” turns on per-check timing profiles and prints a report to stderr.

- **`--enable-module-headers-parsing`** โ€” enables preprocessor-level module header parsing for C++20 and above, which empowers specific checks to detect macro definitions inside modules. > Lowkey important: this feature may cause performance and parsing issues, so it's considered **experimental**. ๐Ÿ‘‘

- **`--exclude-header-filter=<string>`** โ€” a regular expression matching the names of the headers to exclude diagnostics from. Diagnostics from the main file of each translation unit are **always** displayed, no exceptions. Must be used together with `--header-filter`. Can be used together with `-line-filter`. And this option overrides the `ExcludeHeaderFilterRegex` option in the `.clang-tidy` file, if any.

- **`--explain-config`** โ€” for each enabled check, it explains *where* it's enabled: in the clang-tidy binary, on the command line, or in a specific configuration file. It's giving detective energy ๐Ÿ”

- **`--export-fixes=<filename>`** โ€” a YAML file to store suggested fixes in. Those stored fixes can then be applied to the input source code with `clang-apply-replacements`. ๐ŸŽ€

- **`--extra-arg=<string>`** โ€” an additional argument to **append** to the compiler command line.

- **`--extra-arg-before=<string>`** โ€” an additional argument to **prepend** to the compiler command line.

- **`--fix`** โ€” apply suggested fixes. Without `-fix-errors`, clang-tidy will bail out if any compilation errors were found. ๐Ÿ˜ญ

- **`--fix-errors`** โ€” apply suggested fixes **even if** compilation errors were found. And if compiler errors have attached fix-its, clang-tidy will apply those too.

- **`--fix-notes`** โ€” if a warning has no fix, but a single fix can be found through an associated diagnostic note, apply that fix. Specifying this flag implicitly enables the `--fix` flag, so it's a two-for-one. ๐Ÿซถ

- **`--format-style=<string>`** โ€” style for formatting code around applied fixes:
- `'none'` (**default**) turns formatting off entirely
- `'file'` (literally `'file'`, not a placeholder, bestie) uses the `.clang-format` file in the closest parent directory
- `'{ <json> }'` specifies options inline, e.g. `-format-style='{BasedOnStyle: llvm, IndentWidth: 8}'`
- `'llvm'`, `'google'`, `'webkit'`, `'mozilla'`

See the clang-format documentation for the up-to-date info about formatting styles and options. This option overrides the `'FormatStyle\`` option in the `.clang-tidy` file, if any.

- **`--header-filter=<string>`** โ€” a regular expression matching the names of the headers to output diagnostics from. Diagnostics from the main file of each translation unit are always displayed. Can be used together with `-line-filter`. This option overrides the `HeaderFilterRegex` option in the `.clang-tidy` file, if any. ๐ŸŒธ

- **`--line-filter=<string>`** โ€” a list of files with line ranges to filter the warnings. Can be used together with `-header-filter`. The format of the list is a JSON array of objects:

[ {"name":"file1.cpp","lines":[[1,3],[5,7]]}, {"name":"file2.h"} ]


- **`--list-checks`** โ€” list all enabled checks and exit. Use it with `-checks=*` to list **all available** checks. Iconic behavior, honestly.

- **`--load=<pluginfilename>`** โ€” load the specified plugin.

- **`-p <string>`** โ€” build path.

- **`--quiet`** โ€” run clang-tidy in quiet mode. This suppresses printing statistics about ignored warnings and about warnings treated as errors, if the respective options are specified. ๐Ÿคซ

- **`--store-check-profile=<prefix>`** โ€” by default, reports are printed in tabulated format to stderr. When this option is passed, those per-TU profiles are stored as JSON instead.

- **`--system-headers`** โ€” display the errors from system headers. This option overrides the `SystemHeaders` option in the `.clang-tidy` file, if any.

- **`--use-color`** โ€” use colors in diagnostics. If it's not set, colors will be used if the terminal connected to standard output supports colors. This option overrides the `UseColor` option in the `.clang-tidy` file, if any. ๐ŸŒˆ

- **`--verify-config`** โ€” check the config files to make sure each check and option is actually recognized.

- **`--vfsoverlay=<filename>`** โ€” overlay the virtual filesystem described by *file* over the real file system. Yes ma'am, a whole fake filesystem on top of reality ๐Ÿงš

- **`--warnings-as-errors=<string>`** โ€” upgrades warnings to errors. Same format as `-checks`. This option's value is appended to the value of the `WarningsAsErrors` option in the `.clang-tidy` file, if any. ๐Ÿ’€

- **`--allow-no-checks`** โ€” allow empty enabled checks. This suppresses the `"no checks enabled"` error when you're disabling all of the checks.

---

`-p <build-path>` is used to read a compile command database. ๐Ÿ‘‘

So like, for example, it can be a CMake build directory where a file named `compile_commands.json` exists โ€” use the `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` CMake option to get that output. When no build path is specified, a search for `compile_commands.json` will be attempted through **all parent paths** of the first input file. See:

https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html


for an example of setting up Clang Tooling on a source tree. ๐Ÿฆ‹

---

`<source0> ...` โ€” these specify the paths of source files. And the way these paths get looked up in the compile command database is *very* specific, so pay attention bestie:

- If the path of a file is **absolute**, it needs to point into CMake's source tree.
- If the path is **relative**, the current working directory needs to be in the CMake source tree, and the file must be in a subdirectory of the current working directory.
- `"./"` prefixes in the relative files will be automatically removed, but the rest of a relative path must be a suffix of a path in the compile command database.

That last bit is the part people trip over, fr fr. ๐Ÿ’…

<pink>Okay bestie, we were literally just talking about config files, so let's keep going because the glow-up continues ๐Ÿ’…</pink>

- **`CheckOptions`** โ€” a lil list of key-value pairs that define check-specific options. It's giving โœจcustomizationโœจ. Example:

CheckOptions: some-check.SomeOption: 'some value'


- **`Checks`** โ€” same as `--checks`. Bonus: you can hand the list of globs over as an actual list instead of one big string, no cap ๐ŸŽ€
- **`ExcludeHeaderFilterRegex`** โ€” same as `--exclude-header-filter`.
- **`ExtraArgs`** โ€” same as `--extra-args`.
- **`ExtraArgsBefore`** โ€” same as `--extra-args-before`.
- **`FormatStyle`** โ€” same as `--format-style`.
- **`HeaderFileExtensions`** โ€” the file extensions it looks at to figure out if a given diagnostic is sitting in a header file.
- **`HeaderFilterRegex`** โ€” same as `--header-filter-regex`.
- **`ImplementationFileExtensions`** โ€” the file extensions it looks at to figure out if a given diagnostic is sitting in an implementation file.
- **`InheritParentConfig`** โ€” if this one's `true` in a config file, then the config file up in the parent directory (if there even is one) gets taken, and the current config file gets applied right on top of the parent one. It's giving inheritance but make it config ๐Ÿงš
- **`SystemHeaders`** โ€” same as `--system-headers`.
- **`UseColor`** โ€” same as `--use-color`.
- **`User`** โ€” specifies the name or e-mail of the user running clang-tidy. This one's used, for example, to drop the correct user name into `TODO()` comments in the relevant check. main character identification fr fr ๐Ÿ‘‘
- **`WarningsAsErrors`** โ€” same as `--warnings-as-errors`.

Wanna see the *effective* configuration, like the one that's actually in play? Peep it with `--dump-config`:

$ clang-tidy --dump-config


Checks: '-*,some-check' WarningsAsErrors: '' HeaderFileExtensions: ['', 'h','hh','hpp','hxx'] ImplementationFileExtensions: ['c','cc','cpp','cxx'] HeaderFilterRegex: '' FormatStyle: none InheritParentConfig: true User: user CheckOptions: some-check.SomeOption: 'some value' ...


---

## โœจ Suppressing Undesired Diagnostics โœจ

So the whole vibe of clang-tidy diagnostics is to call out code that doesn't stick to a coding standard, or is just problematic in some way. But โ€” and hear me out โ€” if the code is *known* to be correct, it might be useful to silence the warning. Periodt. Some clang-tidy checks give you a check-specific way to silence the diagnostics, like:

- `bugprone-use-after-move` can be silenced by re-initializing the variable after it's been moved out
- `bugprone-string-integer-assignment` can be suppressed by explicitly casting the integer to `char`
- `readability-implicit-bool-conversion` can also be suppressed by using explicit casts

etc., etc. ๐Ÿ’–

But if there's no specific suppression mechanism for a certain warning, or you just don't wanna use it for some reason, clang-tidy has a *generic* mechanism to suppress diagnostics using `NOLINT`, `NOLINTNEXTLINE`, and `NOLINTBEGIN` ... `NOLINTEND` comments.

The `NOLINT` comment tells clang-tidy to ignore warnings on the **same line**. And bestie, listen โ€” it does *not* apply to a function, a block of code, or any other language construct. It applies to the line of code it's on. If dropping the comment on the same line would mess up the formatting in an undesired way, the `NOLINTNEXTLINE` comment lets you suppress clang-tidy warnings on the **next line** instead. And the `NOLINTBEGIN` and `NOLINTEND` comments let you suppress clang-tidy warnings on **multiple lines** (affecting all the lines between the two comments). Slay ๐ŸŒธ

All of these comments can be followed by an optional list of check names in parentheses (formal syntax is down below). The list of check names supports globbing, with the same format and semantics as for enabling checks. > Heads up: negative globs are ignored here, since they'd effectively just re-activate the warning. Not the move. ๐Ÿ’€

For example:

class Foo { // Suppress all the diagnostics for the line Foo(int param); // NOLINT

// Consider explaining the motivation to suppress the warning Foo(char param); // NOLINT: Allow implicit conversion from char, because <some valid reason>

// Silence only the specified checks for the line Foo(double param); // NOLINT(google-explicit-constructor, google-runtime-int)

// Silence all checks from the google module Foo(bool param); // NOLINT(google*)

// Silence all checks ending with -avoid-c-arrays int array[10]; // NOLINT(*-avoid-c-arrays)

// Silence only the specified diagnostics for the next line // NOLINTNEXTLINE(google-explicit-constructor, google-runtime-int) Foo(bool param);

// Silence all checks from the google module for the next line // NOLINTNEXTLINE(google*) Foo(bool param);

// Silence all checks ending with -avoid-c-arrays for the next line // NOLINTNEXTLINE(*-avoid-c-arrays) int array[10];

// Silence only the specified checks for all lines between the BEGIN and END // NOLINTBEGIN(google-explicit-constructor, google-runtime-int) Foo(short param); Foo(long param); // NOLINTEND(google-explicit-constructor, google-runtime-int)

// Silence all checks from the google module for all lines between the BEGIN and END // NOLINTBEGIN(google*) Foo(bool param); // NOLINTEND(google*)

// Silence all checks ending with -avoid-c-arrays for all lines between the BEGIN and END // NOLINTBEGIN(-avoid-c-arrays) int array[10]; // NOLINTEND(-avoid-c-arrays) };


The formal syntax of `NOLINT`, `NOLINTNEXTLINE`, and `NOLINTBEGIN` ... `NOLINTEND` is the following:

lint-comment: lint-command lint-command lint-args

lint-args: ( check-name-list )

check-name-list: check-name check-name-list , check-name

lint-command: NOLINT NOLINTNEXTLINE NOLINTBEGIN NOLINTEND


Note โ€” and this is the sneaky bit, iykyk โ€” whitespaces between `NOLINT`/`NOLINTNEXTLINE`/`NOLINTBEGIN`/`NOLINTEND` and the opening parenthesis are **not allowed**. If you put one there, the comment just gets treated as a plain `NOLINT`/`NOLINTNEXTLINE`/`NOLINTBEGIN`/`NOLINTEND`. But *inside* the check names list (inside the parentheses), whitespaces are totally fine and will be ignored. The duality of whitespace ๐Ÿฆ‹

All `NOLINTBEGIN` comments must be paired up with an equal number of `NOLINTEND` comments. And more than that โ€” a pair of comments has to have **matching arguments**. So for example, `NOLINTBEGIN(check-name)` can be paired with `NOLINTEND(check-name)`, but *not* with `NOLINTEND` (zero arguments). If any `NOLINTBEGIN`/`NOLINTEND` comment breaks these requirements, clang-tidy will generate a `clang-tidy-nolint` error diagnostic. Keep them matched or else ๐Ÿ˜ญ

# ๐Ÿ’– CLANG-INCLUDE-FIXER ๐Ÿ’–

## ๐Ÿ“š Contents

- Clang-Include-Fixer
  - Setup
    - Creating a Symbol Index From a Compilation Database
    - Integrate with Vim
    - Integrate with Emacs
  - How it Works

---

okay bestie, real talk: one of the major nuisances of C++ compared to other languages is the manual management of `#include` directives in any file. <pink>literally why</pink> ๐Ÿ˜ญ

`clang-include-fixer` addresses one aspect of this problem by providing an automated way of adding `#include` directives for missing symbols in one translation unit. <rainbow>slay</rainbow> โœจ

While inserting missing `#include`, `clang-include-fixer` adds missing namespace qualifiers to all instances of an unidentified symbol if the symbol is missing some prefix namespace qualifiers. ๐Ÿซถ

## ๐Ÿ› ๏ธ Setup

To use `clang-include-fixer` two databases are required. Both can be generated with existing tools. ๐Ÿ‘‘

- <purple>Compilation database.</purple> Contains the compiler commands for any given file in a project and can be generated by CMake, see How To Setup Tooling For LLVM.
- <mint>Symbol index.</purple> Contains all symbol information in a project to match a given identifier to a header file.

Ideally both databases (`compile_commands.json` and `find_all_symbols_db.yaml`) are linked into the root of the source tree they correspond to. Then the `clang-include-fixer` can automatically pick them up if called with a source file from that tree. Note that by default `compile_commands.json` as generated by CMake does not include header files, so only implementation files can be handled by tools. <yellow>heads up bestie</yellow> ๐Ÿ’…

### ๐Ÿ—‚๏ธ Creating a Symbol Index From a Compilation Database

The include fixer contains `find-all-symbols`, a tool to create a symbol database in YAML format from a compilation database by parsing all source files listed in it. The following list of commands shows how to set up a database for LLVM, any project built by CMake should follow similar steps. ๐Ÿฆ‹

$ cd path/to/llvm-build $ ninja find-all-symbols // build find-all-symbols tool. $ ninja clang-include-fixer // build clang-include-fixer tool. $ ls compile_commands.json # Make sure compile_commands.json exists. compile_commands.json $ path/to/llvm/source/clang-tools-extra/clang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py ... wait as clang indexes the code base ... $ ln -s $PWD/find_all_symbols_db.yaml path/to/llvm/source/ # Link database into the source tree. $ ln -s $PWD/compile_commands.json path/to/llvm/source/ # Also link compilation database if it's not there already. $ cd path/to/llvm/source $ /path/to/clang-include-fixer -db=yaml path/to/file/with/missing/include.cpp Added #include "foo.h"


### ๐Ÿ’œ Integrate with Vim

To run `clang-include-fixer` on a potentially unsaved buffer in Vim. Add the following key binding to your `.vimrc`: โœจ

noremap <leader>cf :pyf path/to/llvm/source/clang-tools-extra/clang-include-fixer/tool/clang-include-fixer.py<cr>


This enables `clang-include-fixer` for NORMAL and VISUAL mode. Change `<leader>cf` to another binding if you need `clang-include-fixer` on a different key. The `<leader>` key is a reference to a specific key defined by the `mapleader` variable and is bound to backslash by default. ๐ŸŒธ

Make sure vim can find `clang-include-fixer`:

- Add the path to `clang-include-fixer` to the `PATH` environment variable.
- Or set `g:clang_include_fixer_path` in vimrc: `let g:clang_include_fixer_path=path/to/clang-include-fixer`

You can customize the number of headers being shown by setting `let g:clang_include_fixer_maximum_suggested_headers=5` ๐Ÿ’…

Customized settings in `.vimrc`:

- `let g:clang_include_fixer_path = "clang-include-fixer"`

  Set clang-include-fixer binary file path.

- `let g:clang_include_fixer_maximum_suggested_headers = 3`

  Set the maximum number of #includes to show. Default is 3.

- `let g:clang_include_fixer_increment_num = 5`

  Set the increment number of #includes to show every time when pressing m. Default is 5.

- `let g:clang_include_fixer_jump_to_include = 0`

  Set to 1 if you want to jump to the new inserted #include line. Default is 0.

- `let g:clang_include_fixer_query_mode = 0`

  Set to 1 if you want to insert #include for the symbol under the cursor. Default is 0. Compared to normal mode, this mode won't parse the source file and only search the symbol from database, which is faster than normal mode. ๐Ÿ”ฅ

See `clang-include-fixer.py` for more details.

### ๐Ÿงš Integrate with Emacs

To run `clang-include-fixer` on a potentially unsaved buffer in Emacs. Ensure that Emacs finds `clang-include-fixer.el` by adding the directory containing the file to the `load-path` and requiring the `clang-include-fixer` in your `.emacs`: ๐Ÿ’–

(add-to-list 'load-path "path/to/llvm/source/clang-tools-extra/clang-include-fixer/tool/" (require 'clang-include-fixer)


Within Emacs the tool can be invoked with the command `M-x clang-include-fixer`. This will insert the header that defines the first undefined symbol; if there is more than one header that would define the symbol, the user is prompted to select one. <pink>iconic tbh</pink>

To include the header that defines the symbol at point, run `M-x clang-include-fixer-at-point`.

Make sure Emacs can find `clang-include-fixer`:

- Either add the parent directory of `clang-include-fixer` to the `PATH` environment variable, or customize the Emacs user option `clang-include-fixer-executable` to point to the file name of the program. ๐ŸŒˆ

## ๐Ÿ” How it Works

To get the most information out of Clang at parse time, `clang-include-fixer` runs in tandem with the parse and receives callbacks from Clang's semantic analysis. In particular it reuses the existing support for typo corrections. Whenever Clang tries to correct a potential typo it emits a callback to the include fixer which then looks for a corresponding file. At this point rich lookup information is still available, which is not available in the AST at a later stage. ๐Ÿ’ซ

The identifier that should be typo corrected is then sent to the database, if a header file is returned it is added as an include directive at the top of the file. ๐ŸŽ€

Currently `clang-include-fixer` only inserts a single include at a time to avoid getting caught in follow-up errors. If multiple `#include` additions are desired the program can be rerun until a fix-point is reached. <yellow>one at a time bestie, patience is a virtue</yellow> โœจ

---

# ๐Ÿงฉ MODULARIZE USER'S MANUAL ๐Ÿงฉ

## ๐Ÿš€ Modularize Usage

modularize [<modularize-options>] [<module-map>|<include-files-list>]* [<front-end-options>...]


`<modularize-options>` is a place-holder for options specific to modularize, which are described below in Modularize Command Line Options. ๐Ÿ’…

`<module-map>` specifies the path of a file name for an existing module map. The module map must be well-formed in terms of syntax. Modularize will extract the header file names from the map. Only normal headers are checked, assuming headers marked "private", "textual", or "exclude" are not to be checked as a top-level include, assuming they either are included by other headers which are checked, or they are not suitable for modules. ๐Ÿซถ

`<include-files-list>` specifies the path of a file name for a file containing the newline-separated list of headers to check with respect to each other. Lines beginning with '#' and empty lines are ignored. Header file names followed by a colon and other space-separated file names will include those extra files as dependencies. The file names can be relative or full paths, but must be on the same line. For example: ๐Ÿ‘‘

header1.h header2.h header3.h: header1.h header2.h


Note that unless a `-prefix` (header path) option is specified, non-absolute file paths in the header list file will be relative to the header list file directory. Use `-prefix` to specify a different directory. ๐ŸŒธ

`<front-end-options>` is a place-holder for regular Clang front-end arguments, which must follow the `<include-files-list>`. Note that by default, modularize assumes `.h` files contain C++ source, so if you are using a different language, you might need to use a `-x` option to tell Clang that the header contains another language, i.e.: `-x c` ๐Ÿ˜ญ

Note also that because modularize does not use the clang driver, you will likely need to pass in additional compiler front-end arguments to match those passed in by default by the driver. <cyan>fr fr, don't forget this one</cyan>

## ๐ŸŽ›๏ธ Modularize Command Line Options

- **`-prefix=<header-path>`** โ€” Prepend the given path to non-absolute file paths in the header list file. By default, headers are assumed to be relative to the header list file directory. Use `-prefix` to specify a different directory. ๐Ÿฆ‹

- **`-module-map-path=<module-map-path>`** โ€” Generate a module map and output it to the given file. See the description in Module Map Generation.

- **`-problem-files-list=<problem-files-list-file-name>`** โ€” For use only with module map assistant. Input list of files that have problems with respect to modules. These will still be included in the generated module map, but will be marked as "excluded" headers.

- **`-root-module=<root-name>`** โ€” Put modules generated by the `-module-map-path` option in an enclosing module with the given name. See the description in Module Map Generation.

- **`-block-check-header-list-only`** โ€” Limit the `#include`-inside-extern-or-namespace-block check to only those headers explicitly listed in the header list. This is a work-around for avoiding error messages for private includes that purposefully get included inside blocks. ๐Ÿ’…

- **`-no-coverage-check`** โ€” Don't do the coverage check for a module map.

- **`-coverage-check-only`** โ€” Only do the coverage check for a module map.

- **`-display-file-lists`** โ€” Display lists of good files (no compile errors), problem files, and a combined list with problem files preceded by a '#'. This can be used to quickly determine which files have problems. The latter combined list might be useful in starting to modularize a set of headers. You can start with a full list of headers, use `-display-file-lists` option, and then use the combined list as your intermediate list, uncommenting-out headers as you fix them. ๐Ÿ”ฅ

modularize is a standalone tool that checks whether a set of headers provides the consistent definitions required to use modules. For example, it detects whether the same entity (say, a NULL macro or size_t typedef) is defined in multiple headers or whether a header produces different definitions under different circumstances. These conditions cause modules built from the headers to behave poorly, and should be fixed before introducing a module map. <yellow>fix it before you ship it bestie</yellow> โญ

modularize also has an assistant mode option for generating a module map file based on the provided header list. The generated file is a functional module map that can be used as a starting point for a `module.modulemap` file. ๐Ÿ‘‘

## ๐ŸŒฑ Getting Started

To build from source:

1. Go read **Getting Started with the LLVM System** and **Clang Tools Documentation** bestie, that's where you find out how to get sources for LLVM, Clang, and Clang Extra Tools. ๐Ÿ’–

2. Then **Getting Started with the LLVM System** and **Building LLVM with CMake** hand you the directions for how to build. Once all your sources are checked out into the right place, the LLVM build will build Clang Extra Tools and their dependencies <pink>automatically</pink>, no extra effort required. โœจ

  - If you're using CMake, you can also use the `modularize` target to build just the `modularize` tool and its dependencies. ๐ŸŽ€

Before you keep going, go peek at **Modularize Usage** to see how to invoke `modularize`. iykyk.

---

## ๐Ÿ” What Modularize Checks

`Modularize` will check for the following, bestie, listen up:

- Duplicate global type and variable definitions ๐Ÿ‘€
- Duplicate macro definitions
- Macro instances, `'defined(macro)'`, or `#if`, `#elif`, `#ifdef`, `#ifndef` conditions that evaluate differently in a header
- `#include` directives inside `'extern "C/C++" {}'` or `'namespace (name) {}'` blocks
- Module map header coverage completeness (in the case of a module map input only)

`Modularize` will do normal C/C++ parsing, reporting normal errors and warnings, but it will **also** report special error messages like the following:

error: '(symbol)' defined at multiple locations: (file):(row):(column) (file):(row):(column)

error: header '(file)' has different contents depending on how it was included


The latter might be followed by messages like the following:

note: '(symbol)' in (file) at (row):(column) not always provided


Checks will also be performed for macro expansions, `defined(macro)` expressions, and preprocessor conditional directives that evaluate inconsistently, and can produce error messages like the following:

(...)/SubHeader.h:11:5: #if SYMBOL == 1 ^ error: Macro instance 'SYMBOL' has different values in this header, depending on how it was included. 'SYMBOL' expanded to: '1' with respect to these inclusion paths: (...)/Header1.h (...)/SubHeader.h (...)/SubHeader.h:3:9: #define SYMBOL 1 ^ Macro defined here. 'SYMBOL' expanded to: '2' with respect to these inclusion paths: (...)/Header2.h (...)/SubHeader.h (...)/SubHeader.h:7:9: #define SYMBOL 2 ^ Macro defined here.


Checks will also be performed for `'#include'` directives that are nested inside `'extern "C/C++" {}'` or `'namespace (name) {}'` blocks, and can produce error message like the following:

IncludeInExtern.h:2:3: #include "Empty.h" ^ error: Include directive within extern "C" {}. IncludeInExtern.h:1:1: extern "C" { ^ The "extern "C" {}" block is here.


---

## ๐Ÿ—บ๏ธ Module Map Coverage Check

The coverage check uses the Clang library to read and parse the module map file. Starting at the module map file directory, or just the include paths, if specified, it will collect the names of all the files it considers headers (no extension, `.h`, or `.inc` โ€” if you need more, modify the `isHeader` function). It then compares the headers against those referenced in the module map, either explicitly named, or implicitly named via an umbrella directory or umbrella file, as parsed by the `ModuleMap` object. If headers are found which are not referenced or covered by an umbrella directory or file, warning messages will be produced, and this program will return an error code of <red>1</red>. If no problems are found, an error code of <green>0</green> is returned. ๐ŸŒˆ

Note that in the case of umbrella headers, this tool invokes the compiler to preprocess the file, and uses a callback to collect the header files included by the umbrella header or any of its nested includes. If any front end options are needed for these compiler invocations, these can be included on the command line after the module map file argument.

Warning message have the form:

warning: module.modulemap does not account for file: Level3A.h


Note that for the case of the module map referencing a file that does not exist, the module map parser in Clang will (at the time of this writing) display an error message. ๐Ÿ˜ญ

To limit the checks `modularize` does to just the module map coverage check, use the `-coverage-check-only` option.

For example:

modularize -coverage-check-only module.modulemap


---

## ๐Ÿงš Module Map Generation

If you specify the `-module-map-path=<module map file>`, `modularize` will output a module map based on the input header list. A module will be created for each header. Also, if the header in the header list is a partial path, a nested module hierarchy will be created in which a module will be created for each subdirectory component in the header path, with the header itself represented by the innermost module. If other headers use the same subdirectories, they will be enclosed in these same modules also. ๐Ÿฆ‹

For example, for the header list:

SomeTypes.h SomeDecls.h SubModule1/Header1.h SubModule1/Header2.h SubModule2/Header3.h SubModule2/Header4.h SubModule2.h


The following module map will be generated:

// Output/NoProblemsAssistant.txt // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \ -root-module=Root NoProblemsAssistant.modularize

module SomeTypes { header "SomeTypes.h" export * } module SomeDecls { header "SomeDecls.h" export * } module SubModule1 { module Header1 { header "SubModule1/Header1.h" export * } module Header2 { header "SubModule1/Header2.h" export * } } module SubModule2 { module Header3 { header "SubModule2/Header3.h" export * } module Header4 { header "SubModule2/Header4.h" export * } header "SubModule2.h" export * }


An optional `-root-module=<root-name>` option can be used to cause a root module to be created which encloses all the modules. ๐Ÿ‘‘

An optional `-problem-files-list=<problem-file-name>` can be used to input a list of files to be excluded, perhaps as a temporary stop-gap measure until problem headers can be fixed.

For example, with the same header list from above:

// Output/NoProblemsAssistant.txt // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \ -root-module=Root NoProblemsAssistant.modularize

module Root { module SomeTypes { header "SomeTypes.h" export * } module SomeDecls { header "SomeDecls.h" export * } module SubModule1 { module Header1 { header "SubModule1/Header1.h" export * } module Header2 { header "SubModule1/Header2.h" export * } } module SubModule2 { module Header3 { header "SubModule2/Header3.h" export * } module Header4 { header "SubModule2/Header4.h" export * } header "SubModule2.h" export * } }


Note that headers with dependents will be ignored with a warning, as the Clang module mechanism doesn't support headers that rely on other headers being included first. ๐Ÿ’€

The module map format defines some keywords which can't be used in module names. If a header has one of these names, an underscore (`'_'`) will be prepended to the name. For example, if the header name is `header.h`, because `header` is a keyword, the module name will be `_header`. For a list of the module map keywords, please see: **Lexical structure** ๐ŸŒธ

# ๐Ÿ’… PP-TRACE USER'S MANUAL ๐Ÿ’…

okay bestie, meet <pink>pp-trace</pink> โœจ it's a standalone tool that traces preprocessor activity. It's also used as a test of Clang's `PPCallbacks` interface ๐Ÿ‘‘ What it does: it runs a given source file through the Clang preprocessor, displaying selected information from callback functions overridden in a `PPCallbacks` derivation. The output is in a high-level YAML format, described in <cyan>pp-trace Output Format</cyan> ๐Ÿฆ‹

---

## ๐ŸŒธ pp-trace Usage ๐ŸŒธ

### โœจ Command Line Format โœจ

pp-trace [<pp-trace-options>] <source-file> [-- <front-end-options>]


- `<pp-trace-options>` is a place-holder for options specific to `pp-trace`, which are described below in <cyan>Command Line Options</cyan> ๐Ÿ’–
- `<source-file>` specifies the source file to run through the preprocessor. no cap.
- `<front-end-options>` is a place-holder for regular Clang Compiler Options, which **must** follow the `<source-file>`. periodt ๐Ÿ’…

### ๐ŸŽ€ Command Line Options ๐ŸŽ€

- **`-callbacks <comma-separated-globs>`** โ€” This option specifies a comma-separated list of globs describing the list of callbacks that should be traced. Globs are processed in order of appearance. Positive globs add matched callbacks to the set, negative globs (those with the `-` prefix) remove callacks from the set. ๐Ÿ‘€

  - `FileChanged`
  - `FileSkipped`
  - `InclusionDirective`
  - `moduleImport`
  - `EndOfMainFile`
  - `Ident`
  - `PragmaDirective`
  - `PragmaComment`
  - `PragmaDetectMismatch`
  - `PragmaDebug`
  - `PragmaMessage`
  - `PragmaDiagnosticPush`
  - `PragmaDiagnosticPop`
  - `PragmaDiagnostic`
  - `PragmaOpenCLExtension`
  - `PragmaWarning`
  - `PragmaWarningPush`
  - `PragmaWarningPop`
  - `MacroExpands`
  - `MacroDefined`
  - `MacroUndefined`
  - `Defined`
  - `SourceRangeSkipped`
  - `If`
  - `Elif`
  - `Ifdef`
  - `Ifndef`
  - `Else`
  - `Endif`

- **`-output <output-file>`** โ€” By default, `pp-trace` outputs the trace information to `stdout`. Use this option to output the trace information to a file. ๐Ÿ’พ

---

## ๐ŸŒˆ pp-trace Output Format ๐ŸŒˆ

The `pp-trace` output is formatted as YAML. See `https://yaml.org/` for general YAML information. It's arranged as a sequence of information about the callback call, including the callback name and argument information, for example:

  • Callback: Name Argument1: Value1 Argument2: Value2 (etc.) ...

With real data:

  • Callback: FileChanged Loc: "c:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:1:1" Reason: EnterFile FileType: C_User PrevFID: (invalid) (etc.)
  • Callback: FileChanged Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:5:1" Reason: ExitFile FileType: C_User PrevFID: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/Input/Level1B.h"
  • Callback: EndOfMainFile ...

In all but one case (`MacroDirective`) the "Argument" scalars have the same name as the argument in the corresponding `PPCallbacks` callback function. ๐Ÿ’ฏ

---

## ๐Ÿฆ‹ Callback Details ๐Ÿฆ‹

The following sections describe the purpose and output format for each callback.

Click on the callback name in the section heading to see the Doxygen documentation for the callback. ๐Ÿ”—

The argument descriptions table describes the callback argument information displayed.

The **Argument Name** field in most (but not all) cases is the same name as the callback function parameter.

The **Argument Value Syntax** field describes the values that will be displayed for the argument value. It uses an ad hoc representation that mixes literal and symbolic representations. Enumeration member symbols are shown as the actual enum member in a `(member1|member2|...)` form. A name in parentheses can either represent a place holder for the described value, or confusingly, it might be a literal, such as `(null)`, for a null pointer. Locations are shown as quoted only to avoid confusing the documentation generator. ๐Ÿ˜…

The **Clang C++ Type** field is the type from the callback function declaration.

The **description** describes the argument or what is displayed for it.

Note that in some cases, such as when a structure pointer is an argument value, only some key member or members are shown to represent the value, instead of trying to display all members of the structure. fr fr.

---

### ๐Ÿ’– FileChanged Callback ๐Ÿ’–

`FileChanged` is called when the preprocessor enters or exits a file, both the top level file being compiled, as well as any `#include` directives. It will also be called as a result of a system header pragma or in internal renaming of a file.

Argument descriptions:

- **`Loc`** โ€” *Argument Value Syntax:* `"(file):(line):(col)"` โ€” *Clang C++ Type:* `SourceLocation` โ€” The location of the directive.
- **`Reason`** โ€” *Argument Value Syntax:* `(EnterFile|ExitFile|SystemHeaderPragma|RenameFile)` โ€” *Clang C++ Type:* `PPCallbacks::FileChangeReason` โ€” Reason for change.
- **`FileType`** โ€” *Argument Value Syntax:* `(C_User|C_System|C_ExternCSystem)` โ€” *Clang C++ Type:* `SrcMgr::CharacteristicKind` โ€” Include type.
- **`PrevFID`** โ€” *Argument Value Syntax:* `((file)|(invalid))` โ€” *Clang C++ Type:* `FileID` โ€” Previous file, if any.

Example:
  • Callback: FileChanged Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:1:1" Reason: EnterFile FileType: C_User PrevFID: (invalid)

---

### ๐Ÿ’œ FileSkipped Callback ๐Ÿ’œ

`FileSkipped` is called when a source file is skipped as the result of header guard optimization. ๐Ÿšชโœจ

Argument descriptions:

- **`ParentFile`** โ€” *Argument Value Syntax:* `("(file)" or (null))` โ€” *Clang C++ Type:* `const FileEntry` โ€” The file that `#included` the skipped file.
- **`FilenameTok`** โ€” *Argument Value Syntax:* `(token)` โ€” *Clang C++ Type:* `const Token` โ€” The token in `ParentFile` that indicates the skipped file.
- **`FileType`** โ€” *Argument Value Syntax:* `(C_User|C_System|C_ExternCSystem)` โ€” *Clang C++ Type:* `SrcMgr::CharacteristicKind` โ€” The file type.

Example:
  • Callback: FileSkipped ParentFile: "/path/filename.h" FilenameTok: "filename.h" FileType: C_User

### InclusionDirective Callback

<pink>`InclusionDirective`</pink> fires when an inclusion directive of <hotpink>any</hotpink> kind (`#include`, `#import`, etc.) has been processed โ€” and bestie, hear me when I say it does <em>not</em> care whether that inclusion actually results in an inclusion. It ran, so it tells you. ๐Ÿ’…

Argument descriptions:

- **`HashLoc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation` โ€” the location of the `#` that starts the inclusion directive. <cyan>That little octothorpe that started it all.</cyan>
- **`IncludeTok`** โ€” value syntax `(token)`, Clang C++ type `const Token` โ€” the token that indicates the kind of inclusion directive, e.g., `'include'` or `'import'`. ๐ŸŽ€
- **`FileName`** โ€” value syntax `"(file)"`, Clang C++ type `StringRef` โ€” the name of the file being included, <yellow>exactly as written in the source code</yellow> (as-written, not as-resolved, iykyk).
- **`IsAngled`** โ€” value syntax `(true|false)`, Clang C++ type `bool` โ€” whether the file name was enclosed in angle brackets; otherwise, it was enclosed in quotes. ๐ŸŒธ
- **`FilenameRange`** โ€” value syntax `"(file)"`, Clang C++ type `CharSourceRange` โ€” the character range of the quotes or angle brackets for the written file name. <purple>the costume, not the file.</purple>
- **`File`** โ€” value syntax `"(file)"`, Clang C++ type `const FileEntry` โ€” the actual file that may be included by this inclusion directive.
- **`SearchPath`** โ€” value syntax `"(path)"`, Clang C++ type `StringRef` โ€” contains the search path which was used to find the file in the file system. ๐Ÿ”
- **`RelativePath`** โ€” value syntax `"(path)"`, Clang C++ type `StringRef` โ€” the path relative to `SearchPath`, at which the include file was found.
- **`Imported`** โ€” value syntax `((module name)|(null))`, Clang C++ type `const Module` โ€” the module, whenever an inclusion directive was automatically turned into a module import, or null otherwise. <mint>surprise module arc โœจ</mint>

Example:
  • Callback: InclusionDirective HashLoc: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:4:1" IncludeTok: include FileName: "Input/Level1B.h" IsAngled: false FilenameRange: "Input/Level1B.h" File: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/Input/Level1B.h" SearchPath: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace" RelativePath: "Input/Level1B.h" Imported: (null)

---

### moduleImport Callback

<pink>`moduleImport`</pink> gets called when there was an <hotpink>explicit</hotpink> module-import syntax. No subtlety, no sneaky automatic conversions โ€” you wrote the import, it reports the import. ๐Ÿฆ‹

Argument descriptions:

- **`ImportLoc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation` โ€” the location of the import directive token. <cyan>where the drama begins ๐Ÿ“</cyan>
- **`Path`** โ€” value syntax `"(path)"`, Clang C++ type `ModuleIdPath` โ€” the identifiers (and their locations) of the module `"path"`.
- **`Imported`** โ€” value syntax `((module name)|(null))`, Clang C++ type `const Module` โ€” the imported module; can be null if importing failed. <red>can be null if importing failed</red> โ€” yes, that's a real possibility, plan accordingly ๐Ÿ˜ญ

Example:
  • Callback: moduleImport ImportLoc: "d:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:2" Path: [{Name: Level1B, Loc: "d:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:9"}, {Name: Level2B, Loc: "d:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:17"}] Imported: Level2B

---

### EndOfMainFile Callback

<pink>`EndOfMainFile`</pink> is called when the end of the main file is reached. <lavender>that's it, that's the whole callback โ€” it just shows up at the finish line to say we made it ๐Ÿ๐Ÿ‘‘</lavender>

Argument descriptions, bestie โ€” here's what `EndOfMainFile` takes:

- **Argument Name**: *(no arguments)* โ€” **Argument Value Syntax**: *(nothing)* โ€” **Clang C++ Type**: *(nothing)* โ€” **Description**: *(nothing)* ๐Ÿ’€

Like, it truly takes zero arguments. Iconic of it honestly.

Example:
  • Callback: EndOfMainFile

---

### โœจ Ident Callback โœจ

`Ident` gets called when a `#ident` or `#sccs` directive is read. ๐ŸŽ€

Argument descriptions:

- **`Loc`** โ€” Argument Value Syntax: `"(file):(line):(col)"` โ€” Clang C++ Type: `SourceLocation` โ€” the location of the directive. ๐Ÿ“
- **`str`** โ€” Argument Value Syntax: `(name)` โ€” Clang C++ Type: `const std::string` โ€” the text of the directive.

Example:
  • Callback: Ident Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-ident.cpp:3:1" str: "$Id$"

---

### ๐ŸŒธ PragmaDirective Callback ๐ŸŒธ

`PragmaDirective` gets called when it starts reading any pragma directive. Periodt.

Argument descriptions:

- **`Loc`** โ€” Argument Value Syntax: `"(file):(line):(col)"` โ€” Clang C++ Type: `SourceLocation` โ€” the location of the directive. ๐Ÿ“
- **`Introducer`** โ€” Argument Value Syntax: `(PIK_HashPragma|PIK__Pragma|PIK___pragma)` โ€” Clang C++ Type: `PragmaIntroducerKind` โ€” the type of the pragma directive. ๐Ÿ‘‘

Example:
  • Callback: PragmaDirective Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Introducer: PIK_HashPragma

---

### ๐Ÿ’œ PragmaComment Callback ๐Ÿ’œ

`PragmaComment` gets called when a `#pragma comment` directive is read.

Argument descriptions:

- **`Loc`** โ€” Argument Value Syntax: `"(file):(line):(col)"` โ€” Clang C++ Type: `SourceLocation` โ€” the location of the directive. ๐Ÿ“
- **`Kind`** โ€” Argument Value Syntax: `((name)|(null))` โ€” Clang C++ Type: `const IdentifierInfo` โ€” the comment kind symbol.
- **`Str`** โ€” Argument Value Syntax: `(message directive)` โ€” Clang C++ Type: `const std::string` โ€” the comment message directive.

Example:
  • Callback: PragmaComment Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Kind: library Str: kernel32.lib

---

### ๐Ÿฆ‹ PragmaDetectMismatch Callback ๐Ÿฆ‹

`PragmaDetectMismatch` gets called when a `#pragma detect_mismatch` directive is read.

Argument descriptions:

- **`Loc`** โ€” Argument Value Syntax: `"(file):(line):(col)"` โ€” Clang C++ Type: `SourceLocation` โ€” the location of the directive. ๐Ÿ“
- **`Name`** โ€” Argument Value Syntax: `"(name)"` โ€” Clang C++ Type: `const std::string` โ€” the name.
- **`Value`** โ€” Argument Value Syntax: `(string)` โ€” Clang C++ Type: `const std::string` โ€” the value.

Example:
  • Callback: PragmaDetectMismatch Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Name: name Value: value

---

### โญ PragmaDebug Callback โญ

`PragmaDebug` gets called when a `#pragma clang __debug` directive is read.

Argument descriptions:

- **`Loc`** โ€” Argument Value Syntax: `"(file):(line):(col)"` โ€” Clang C++ Type: `SourceLocation` โ€” the location of the directive. ๐Ÿ“
- **`DebugType`** โ€” Argument Value Syntax: `(string)` โ€” Clang C++ Type: `StringRef` โ€” indicates type of debug message.

Example:
  • Callback: PragmaDebug Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" DebugType: warning

---

### ๐Ÿซถ PragmaMessage Callback ๐Ÿซถ

`PragmaMessage` gets called when a `#pragma message` directive is read.

Argument descriptions:

Okay so here's the receipts for that callback โ€” every arg, every type, no skips: ๐Ÿซถ

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation` โ€” the location of the directive.
- **`Namespace`** โ€” value syntax `(name)`, Clang C++ type `StringRef` โ€” the namespace of the message directive.
- **`Kind`** โ€” value syntax `(PMK_Message|PMK_Warning|PMK_Error)`, Clang C++ type `PPCallbacks::PragmaMessageKind` โ€” the type of the message directive.
- **`Str`** โ€” value syntax `(string)`, Clang C++ type `StringRef` โ€” the text of the message directive.

Example::
  • Callback: PragmaMessage Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC" Kind: PMK_Message Str: The message text.

---

### ๐ŸŽ€ PragmaDiagnosticPush Callback

<lavender>`PragmaDiagnosticPush`</lavender> fires the second a `#pragma gcc diagnostic push` directive gets read. like, the moment clang peeks at it. ๐Ÿ’…

Argument descriptions:

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation` โ€” the location of the directive.
- **`Namespace`** โ€” value syntax `(name)`, Clang C++ type `StringRef` โ€” namespace name.

Example::
  • Callback: PragmaDiagnosticPush Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC"

---

### ๐Ÿฆ‹ PragmaDiagnosticPop Callback

<cyan>`PragmaDiagnosticPop`</cyan> is the matching bestie โ€” it gets called when a `#pragma gcc diagnostic pop` directive is read. push then pop, the way it's supposed to go โœจ

Argument descriptions:

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation` โ€” the location of the directive.
- **`Namespace`** โ€” value syntax `(name)`, Clang C++ type `StringRef` โ€” namespace name.

Example::
  • Callback: PragmaDiagnosticPop Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC"

---

### ๐ŸŒธ PragmaDiagnostic Callback

<hotpink>`PragmaDiagnostic`</hotpink> is called when a `#pragma gcc diagnostic` directive is read. yes it has *mapping* energy and yes it's iconic for it. ๐Ÿ”ฅ

Argument descriptions:

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation` โ€” the location of the directive.
- **`Namespace`** โ€” value syntax `(name)`, Clang C++ type `StringRef` โ€” namespace name.
- **`mapping`** โ€” value syntax `(0|MAP_IGNORE|MAP_WARNING|MAP_ERROR|MAP_FATAL)`, Clang C++ type `diag::Severity` โ€” mapping type.
- **`Str`** โ€” value syntax `(string)`, Clang C++ type `StringRef` โ€” warning/error name.

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜


Bestie the table's done, and now we get the <rainbow>receipts</rainbow> โ€” the actual output that callback spits out ๐Ÿ‘‡
  • Callback: PragmaDiagnostic Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC" mapping: MAP_WARNING Str: WarningName

---

## ๐Ÿ’– PragmaOpenCLExtension Callback ๐Ÿ’–

okay so <pink>`PragmaOpenCLExtension`</pink> gets called when an OpenCL extension is either <green>disabled</green> or <red>enabled</red> with a pragma. no cap, that's the whole job ๐Ÿฆ‹

Argument descriptions, laid out all pretty:

- **`NameLoc`** โ€” value syntax: `"(file):(line):(col)"` โ€” Clang C++ type: `SourceLocation` โ€” the location of the name.
- **`Name`** โ€” value syntax: `(name)` โ€” Clang C++ type: `const IdentifierInfo` โ€” name symbol.
- **`StateLoc`** โ€” value syntax: `"(file):(line):(col)"` โ€” Clang C++ type: `SourceLocation` โ€” the location of the state.
- **`State`** โ€” value syntax: `(1|0)` โ€” Clang C++ type: `unsigned` โ€” enabled/disabled state.

Example, and yes this is *literally* what it prints โœจ
  • Callback: PragmaOpenCLExtension NameLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:10" Name: Name StateLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:18" State: 1

---

## ๐ŸŒธ PragmaWarning Callback ๐ŸŒธ

<pink>`PragmaWarning`</pink> fires the second a `#pragma warning` directive gets read. it's giving <yellow>"hey, heads up, warning stuff happening"</yellow> ๐Ÿ’…

Argument descriptions:

- **`Loc`** โ€” value syntax: `"(file):(line):(col)"` โ€” Clang C++ type: `SourceLocation` โ€” the location of the directive.
- **`WarningSpec`** โ€” value syntax: `(string)` โ€” Clang C++ type: `StringRef` โ€” the warning specifier.
- **`Ids`** โ€” value syntax: `[(number)[, ...]]` โ€” Clang C++ type: `ArrayRef<int>` โ€” the warning numbers.

Example time ๐ŸŽ€
  • Callback: PragmaWarning Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" WarningSpec: disable Ids: 1,2,3

---

## โœจ PragmaWarningPush Callback โœจ

<pink>`PragmaWarningPush`</pink> is the callback for when a `#pragma warning(push)` directive is read. it's the "saving my spot real quick" energy ๐Ÿซถ

Argument descriptions:

- **`Loc`** โ€” value syntax: `"(file):(line):(col)"` โ€” Clang C++ type: `SourceLocation` โ€” the location of the directive.
- **`Level`** โ€” value syntax: `(number)` โ€” Clang C++ type: `int` โ€” warning level.

Example:
  • Callback: PragmaWarningPush Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Level: 1

---

## ๐Ÿฆ‹ PragmaWarningPop Callback ๐Ÿฆ‹

<pink>`PragmaWarningPop`</pink> gets called when a `#pragma warning(pop)` directive is read. yes bestie, it's the matching un-push, the "okay we're back" moment ๐Ÿ˜Œ

Argument descriptions:

- **`Loc`** โ€” value syntax: `"(file):(line):(col)"` โ€” Clang C++ type: `SourceLocation` โ€” the location of the directive.

Example:
  • Callback: PragmaWarningPop Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"

---

## ๐Ÿ‘‘ MacroExpands Callback ๐Ÿ‘‘

<pink>`MacroExpands`</pink> is called when <purple>`::HandleMacroExpandedIdentifier`</purple> when a macro invocation is found. (fr fr that's how the original says it, and I am *not* touching it ๐Ÿ’€)

Argument descriptions:

- **`MacroNameTok`** โ€” value syntax: `(token)` โ€” Clang C++ type: `const Token` โ€” the macro name token.
- **`MacroDirective`** โ€” value syntax: `(MD_Define|MD_Undefine|MD_Visibility)` โ€” Clang C++ type: `const MacroDirective` โ€” the kind of macro directive from the `MacroDirective` structure.
- **`Range`** โ€” value syntax: `["(file):(line):(col)", "(file):(line):(col)"]` โ€” Clang C++ type: `SourceRange` โ€” the source range for the expansion.

- **`Args`** โ€” argument syntax is `[(name)|(number)|<(token name)>[, ...]]`, Clang C++ type is `const MacroArgs` โ€” and what even *is* it? The argument tokens, bestie. Names and numbers are literal, everything else is of the form `'<' tokenName '>'`. ๐Ÿ’…

<purple>Example, because a vibe check beats a description every time:</purple>
  • Callback: MacroExpands MacroNameTok: X_IMPL MacroDirective: MD_Define Range: [(nonfile), (nonfile)] Args: [a <plus> y, b]

---

### <hotpink>MacroDefined Callback ๐ŸŽ€</hotpink>

<lavender>`MacroDefined`</lavender> gets called when a macro definition is seen. That's it, that's the whole moment. โœจ

<purple>Argument descriptions, one by one:</purple>

- **`MacroNameTok`** โ€” syntax `(token)` ยท type `const Token` โ€” the macro name token. iconic. ๐Ÿ‘‘
- **`MacroDirective`** โ€” syntax `(MD_Define|MD_Undefine|MD_Visibility)` ยท type `const MacroDirective` โ€” the kind of macro directive from the `MacroDirective` structure. lowkey important, ngl.

<purple>Example:</purple>
  • Callback: MacroDefined MacroNameTok: X_IMPL MacroDirective: MD_Define

### <hotpink>MacroUndefined Callback ๐Ÿฆ‹</hotpink>

<lavender>`MacroUndefined`</lavender> is called when a macro `#undef` is seen. say bye to that macro bestie ๐Ÿ˜ญ

<purple>Argument descriptions, one by one:</purple>

- **`MacroNameTok`** โ€” syntax `(token)` ยท type `const Token` โ€” the macro name token. ๐Ÿ’–
- **`MacroDirective`** โ€” syntax `(MD_Define|MD_Undefine|MD_Visibility)` ยท type `const MacroDirective` โ€” the kind of macro directive from the `MacroDirective` structure. yes, same as before, she's consistent like that ๐Ÿซถ

<purple>Example:</purple>
  • Callback: MacroUndefined MacroNameTok: X_IMPL MacroDirective: MD_Define

### <hotpink>Defined Callback ๐ŸŒˆ</hotpink>

<lavender>`Defined`</lavender> is called when the `defined` operator is seen. the way that `#if defined(...)` finally gets its moment โ€” main character energy, fr fr. ๐Ÿ”ฅ

<purple>Argument descriptions, one by one:</purple>

- **`MacroNameTok`** โ€” syntax `(token)` ยท type `const Token` โ€” the macro name token. ๐Ÿ’œ
- **`MacroDirective`** โ€” syntax `(MD_Define|MD_Undefine|MD_Visibility)` ยท type `const MacroDirective` โ€” the kind of macro directive from the `MacroDirective` structure. ๐ŸŒธ
- **`Range`** โ€” syntax `["(file):(line):(col)", "(file):(line):(col)"]` ยท type `SourceRange` โ€” the source range for the directive. she knows exactly *where* it went down, and honestly? we love a receipts girlie. ๐Ÿ“

---

and here's the lil receipt for that one ๐Ÿ’–
  • Callback: Defined MacroNameTok: MACRO MacroDirective: (null) Range: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:5", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:19"]

---

### <purple>SourceRangeSkipped Callback</purple>

okay so <cyan>`SourceRangeSkipped`</cyan> gets called when a source range is skipped. that's it, that's the whole vibe ๐Ÿฆ‹

**Argument descriptions:**

- **`Range`** โ€” value syntax is `["(file):(line):(col)", "(file):(line):(col)"]`, Clang C++ type `SourceRange`. The source range skipped.

**Example:**
  • Callback: SourceRangeSkipped Range: [":/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2", ":/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:9:2"]

---

### <purple>If Callback</purple>

<cyan>`If`</cyan> shows up when an `#if` is seen. the classic. the OG ๐Ÿ’…

**Argument descriptions:**

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation`. The location of the directive.
- **`ConditionRange`** โ€” value syntax `["(file):(line):(col)", "(file):(line):(col)"]`, Clang C++ type `SourceRange`. The source range for the condition.
- **`ConditionValue`** โ€” value syntax `(true|false)`, Clang C++ type `bool`. The condition value.

**Example:**
  • Callback: If Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2" ConditionRange: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:4", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:9:1"] ConditionValue: false

---

### <purple>Elif Callback</purple>

<cyan>`Elif`</cyan> is called when an `#elif` is seen. the middle child of the conditional preprocessor world, we love her ๐ŸŒธ

**Argument descriptions:**

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation`. The location of the directive.
- **`ConditionRange`** โ€” value syntax `["(file):(line):(col)", "(file):(line):(col)"]`, Clang C++ type `SourceRange`. The source range for the condition.
- **`ConditionValue`** โ€” value syntax `(true|false)`, Clang C++ type `bool`. The condition value.
- **`IfLoc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation`. The location of the directive.

**Example:**
  • Callback: Elif Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2" ConditionRange: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:4", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:11:1"] ConditionValue: false IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"

---

### <purple>Ifdef Callback</purple>

<cyan>`Ifdef`</cyan> gets called when an `#ifdef` is seen. bestie, if you've ever written C, you know her ๐Ÿซถ

**Argument descriptions:**

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation`. The location of the directive.
- **`MacroNameTok`** โ€” value syntax `(token)`, Clang C++ type `const Token`. The macro name token.
- **`MacroDirective`** โ€” value syntax `(MD_Define|MD_Undefine|MD_Visibility)`, Clang C++ type `const MacroDirective`. The kind of macro directive from the `MacroDirective` structure.

**Example:**
  • Callback: Ifdef Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-conditional.cpp:3:1" MacroNameTok: MACRO MacroDirective: MD_Define

---

### <purple>Ifndef Callback</purple>

<cyan>`Ifndef`</cyan> is called when an `#ifndef` is seen โ€” the include-guard icon herself ๐Ÿ‘‘

**Argument descriptions:**

- **`Loc`** โ€” value syntax `"(file):(line):(col)"`, Clang C++ type `SourceLocation`. The location of the directive.
- **`MacroNameTok`** โ€” value syntax `(token)`, Clang C++ type `const Token`. The macro name token.

- **`MacroDirective`** โ€” value syntax is `(MD_Define|MD_Undefine|MD_Visibility)`, Clang C++ type is `const MacroDirective`, and it's the kind of macro directive straight out of the `MacroDirective` structure. ๐Ÿ’…

Example::
  • Callback: Ifndef Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-conditional.cpp:3:1" MacroNameTok: MACRO MacroDirective: MD_Define

### <purple>Else Callback</purple>

<pink>Else</pink> fires the moment an <cyan>`#else`</cyan> shows up. bestie spotted a fork in the preprocessor road ๐ŸŒธ

Argument descriptions:

- **`Loc`** โ€” value syntax is `"(file):(line):(col)"`, Clang C++ type is `SourceLocation`, and it's where the else directive lives. ๐Ÿ“
- **`IfLoc`** โ€” value syntax is `"(file):(line):(col)"`, Clang C++ type is `SourceLocation`, and it's where the if directive lives. ๐Ÿ“

Example::
  • Callback: Else Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2" IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"

### <purple>Endif Callback</purple>

<pink>Endif</pink> fires the moment an <cyan>`#endif`</cyan> shows up โ€” the little bow that ties the whole conditional off ๐ŸŽ€

Argument descriptions:

- **`Loc`** โ€” value syntax is `"(file):(line):(col)"`, Clang C++ type is `SourceLocation`, and it's where the endif directive lives. ๐Ÿ“
- **`IfLoc`** โ€” value syntax is `"(file):(line):(col)"`, Clang C++ type is `SourceLocation`, and it's where the if directive lives. ๐Ÿ“

Example::
  • Callback: Endif Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2" IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"

### <purple>Building pp-trace</purple>

Wanna build this from source? say less, here's the roadmap ๐Ÿ—บ๏ธโœจ

1. Read <cyan>Getting Started with the LLVM System</cyan> and <cyan>Clang Tools Documentation</cyan> for information on getting sources for LLVM, Clang, and Clang Extra Tools.

2. <cyan>Getting Started with the LLVM System</cyan> and <cyan>Building LLVM with CMake</cyan> give directions for how to build. With sources all checked out into the right place the LLVM build will build Clang Extra Tools and their dependencies automatically.

  - If using CMake, you can also use the <cyan>`pp-trace`</cyan> target to build just the pp-trace tool and its dependencies. ๐ŸŽฏ

---

# <rainbow>CLANG-RENAME</rainbow>

## <pink>Contents</pink> ๐Ÿ’–

- Clang-Rename
- Using Clang-Rename
- Vim Integration
- Emacs Integration

See also:

<pink>`clang-rename`</pink> is a C++ refactoring tool. Its whole ~purpose~ is to perform efficient renaming actions in large-scale projects โ€” think renaming classes, functions, variables, arguments, namespaces, etc. like it's nothing ๐Ÿ’…

> ok real talk though: the tool is in a <red>very early development stage</red>, so you might encounter bugs and crashes. ๐Ÿ˜ญ Submitting reports with info about how to reproduce the issue to the LLVM bugtracker will definitely help the project. And if you've got ideas or suggestions, you might wanna put a feature request there too. ๐Ÿซถ

## <pink>Using Clang-Rename</pink> โœจ

<pink>`clang-rename`</pink> is a LibTooling-based tool, and it's way easier to work with if you set up a compile command database for your project (for an example of how to do this see <cyan>How To Setup Tooling For LLVM</cyan>). You can also specify compilation options on the command line after <cyan>`--`</cyan>:

$ clang-rename -offset=42 -new-name=foo test.cpp -- -Imy_project/include -DMY_DEFINES ...


To get an offset of a symbol in a file run

$ grep -FUbo 'foo' file.cpp


The tool currently supports renaming actions inside a single translation unit only. It is planned to extend the tool's functionality to support multi-TU renaming actions in the future. ๐Ÿ”ฎ

<pink>`clang-rename`</pink> also aims to be easily integrated into popular text editors, such as Vim and Emacs, and improve the workflow of users. โœจ

Although a command line interface exists, it is <bold>highly recommended</bold> to use the text editor interface instead for better experience. trust. ๐Ÿซถ

You can also identify one or more symbols to be renamed by giving the fully qualified name:

$ clang-rename -qualified-name=foo -new-name=bar test.cpp


Renaming multiple symbols at once is supported, too. However, <pink>`clang-rename`</pink> doesn't accept both <cyan>`-offset`</cyan> and <cyan>`-qualified-name`</cyan> at the same time. So, you can either specify multiple <cyan>`-offset`</cyan> or <cyan>`-qualified-name`</cyan>. pick a lane bestie ๐Ÿ’…

$ clang-rename -offset=42 -new-name=bar1 -offset=150 -new-name=bar2 test.cpp


or

$ clang-rename -qualified-name=foo1 -new-name=bar1 -qualified-name=foo2 -new-name=bar2 test.cpp


Alternatively, <cyan>`{offset | qualified-name} / new-name`</cyan> pairs can be put into a YAML file:

  • Offset: 42 NewName: bar1
  • Offset: 150 NewName: bar2 ...

or

  • QualifiedName: foo1 NewName: bar1
  • QualifiedName: foo2 NewName: bar2 ...

That way you can avoid spelling out all the names as command line arguments:

$ clang-rename -input=test.yaml test.cpp


<pink>`clang-rename`</pink> offers the following options:

$ clang-rename --help USAGE: clang-rename [subcommand] [options] <source0> [... <sourceN>]

OPTIONS:

Generic Options:

-help - Display available options (-help-hidden for more) -help-list - Display list of available options (-help-list-hidden for more) -version - Display the version of this program

clang-rename common options:

-export-fixes=<filename> - YAML file to store suggested fixes in. -extra-arg=<string> - Additional argument to append to the compiler command line Can be used several times. -extra-arg-before=<string> - Additional argument to prepend to the compiler command line Can be used several times. -force - Ignore nonexistent qualified names. -i - Overwrite edited <file>s. -input=<string> - YAML file to load oldname-newname pairs from. -new-name=<string> - The new name to change the symbol to. -offset=<uint> - Locates the symbol by offset as opposed to <line>:<column>. -p <string> - Build path -pl - Print the locations affected by renaming to stderr. -pn - Print the found symbol's name prior to renaming to stderr. -qualified-name=<string> - The fully qualified name of the symbol.


## <pink>Vim Integration</pink> ๐Ÿงš

You can call <pink>`clang-rename`</pink> directly from Vim! To set up <pink>`clang-rename`</pink> integration for Vim see <cyan>`clang/tools/clang-rename/clang-rename.py`</cyan>. ๐ŸŒˆ

> Please note that you have to save <bold>all</bold> buffers in which the replacement will happen <bold>before</bold> running the tool. no cap, unsaved buffers will simply not get the glow-up ๐Ÿ’€

Once installed, you can point your cursor to symbols you want to rename, press <cyan>`<leader>cr`</cyan> and type the new desired name. The <cyan>`<leader>`</cyan> key is a reference to a specific key defined by the <cyan>`mapleader`</cyan> variable and is bound to backslash by default. ๐Ÿ‘‘

## <pink>Emacs Integration</pink> ๐Ÿฆ‹

You can also use <pink>`clang-rename`</pink> while using Emacs! To set up <pink>`clang-rename`</pink> integration for Emacs see <cyan>`clang-rename/tool/clang-rename.el`</cyan>. โœจ

Once installed, you can point your cursor to symbols you want to rename, press <cyan>`M-X`</cyan>, type <cyan>`clang-rename`</cyan> and the new desired name. ๐Ÿ’…

> Please note that you have to save <bold>all</bold> buffers in which the replacement will happen <bold>before</bold> running the tool. fr fr, save your work first ๐Ÿ™ˆ

# ๐Ÿฆ‹ CLANG-DOC ๐Ÿฆ‹

## โœจ Contents โœจ

- Clang-Doc
  - Use
  - Output
  - Configuration
    - Options

---

okay bestie, listen โ€” `clang-doc` is the tool that generates C and C++ documentation straight from your source code and comments. it's giving "doxygen but make it clang" ๐Ÿ’…

> <red>real talk though</red> โ€” this tool is in a **very early development stage**, so you might encounter bugs and crashes. ๐Ÿ˜ญ submitting reports with information about how to reproduce the issue to the LLVM bug tracker will *definitely* help the project. and if you have any ideas or suggestions, please put a feature request there. ๐Ÿซถ

---

## ๐ŸŒธ Use ๐ŸŒธ

`clang-doc` is a **LibTooling**-based tool, which means it needs a compile command database for your project (for an example of how to do this see *How To Setup Tooling For LLVM*). no database, no vibes, sorry.

by default it will process a list of files:

$ clang-doc File1.cpp File2.cpp ... FileN.cpp


it can also be used with a compile commands database:

$ clang-doc --executor=all-TUs compile_commands.json


and if you only want a *subset* of files from that database, hit it with the `--filter` flag:

$ clang-doc --executor=all-TUs --filter=File[0-9]+.cpp compile_commands.json


---

## ๐ŸŽ€ Output ๐ŸŽ€

`clang-doc` produces a **directory of documentation**. one file is produced for each namespace and record in the project source code, containing all documentation (including contained functions, methods, and enums) for that item. so like, one glow-up per namespace, periodt. โœจ

the top-level directory is configurable through the `output` flag:

$ clang-doc --output=output/directory/ compile_commands.json


---

## ๐Ÿ’œ Configuration ๐Ÿ’œ

configuration for `clang-doc` is currently limited to command-line options. in the future, it may develop the ability to use a configuration file, but no such efforts are currently in progress. (so don't hold your breath bestie ๐Ÿ’€)

---

## ๐Ÿ‘‘ Options ๐Ÿ‘‘

`clang-doc` offers the following options:

$ clang-doc --help OVERVIEW: Generates documentation from source code and comments.

Example usage for files without flags (default):

$ clang-doc File1.cpp File2.cpp ... FileN.cpp

Example usage for a project using a compile commands database:

$ clang-doc --executor=all-TUs compile_commands.json

USAGE: clang-doc [options] <source0> [... <sourceN>]

OPTIONS:

Generic Options:

-help - Display available options (-help-hidden for more) -help-list - Display list of available options (-help-list-hidden for more) -version - Display the version of this program

clang-doc options:

--doxygen - Use only doxygen-style comments to generate docs. --extra-arg=<string> - Additional argument to append to the compiler command line Can be used several times. --extra-arg-before=<string> - Additional argument to prepend to the compiler command line Can be used several times. --format=<value> - Format for outputted docs. =yaml - Documentation in YAML format. =md - Documentation in MD format. =html - Documentation in HTML format. --ignore-map-errors - Continue if files are not mapped correctly. --output=<string> - Directory for outputting generated files. -p <string> - Build path --project-name=<string> - Name of project. --public - Document only public declarations. --repository=<string> - URL of repository that hosts code. Used for links to definition locations. --source-root=<string> - Directory where processed files are stored. Links to definition locations will only be generated if the file is in this dir. --stylesheets=<string> - CSS stylesheets to extend the default styles.


here's the same energy in list form, because we love a readable layout ๐Ÿซถ

**Generic Options:**

- **`-help`** โ€” Display available options (`-help-hidden` for more)
- **`-help-list`** โ€” Display list of available options (`-help-list-hidden` for more)
- **`-version`** โ€” Display the version of this program

**clang-doc options:**

- **`--doxygen`** โ€” Use only doxygen-style comments to generate docs.
- **`--extra-arg=<string>`** โ€” Additional argument to append to the compiler command line. Can be used several times.
- **`--extra-arg-before=<string>`** โ€” Additional argument to prepend to the compiler command line. Can be used several times.
- **`--format=<value>`** โ€” Format for outputted docs.
  - **`=yaml`** โ€” Documentation in YAML format.
  - **`=md`** โ€” Documentation in MD format.
  - **`=html`** โ€” Documentation in HTML format.
- **`--ignore-map-errors`** โ€” Continue if files are not mapped correctly.
- **`--output=<string>`** โ€” Directory for outputting generated files.
- **`-p <string>`** โ€” Build path
- **`--project-name=<string>`** โ€” Name of project.
- **`--public`** โ€” Document only public declarations.
- **`--repository=<string>`** โ€” URL of repository that hosts code. Used for links to definition locations.
- **`--source-root=<string>`** โ€” Directory where processed files are stored. Links to definition locations will only be generated if the file is in this dir.
- **`--stylesheets=<string>`** โ€” CSS stylesheets to extend the default styles.

> โš ๏ธ <yellow>heads up bestie</yellow> โ€” the following flags should only be used if `format` is set to `html`: `-repository`, `-source-root`, `-stylesheets`

---

## ๐ŸŒˆ Doxygen Documentation ๐ŸŒˆ

the Doxygen documentation describes the **internal software** that makes up the tools of `clang-tools-extra`, *not* the external use of these tools. the Doxygen documentation contains **no instructions about how to use the tools**, only the APIs that make up the software. for usage instructions, please see the user's guide or reference manual for each tool. iykyk ๐Ÿ’…

- Doxygen documentation

> <red>NOTE:</red>
>
> This documentation is generated directly from the source code with doxygen. Since the tools of `clang-tools-extra` are constantly under active development, what you're about to read is out of date! ๐Ÿ˜ญ

- Index

- Search Page

---

# ๐Ÿ’– AUTHOR ๐Ÿ’–

The Clang Team

# โญ COPYRIGHT โญ

2007-2026, The Clang Team

---

and that's the whole thing bestie, we ate and left no crumbs ๐Ÿ“โœจ this was `EXTRACLANGTOOLS(1)` โ€” page **19**, dated **Mar 11, 2026** โ€” so go forth, document your C and C++ like the main character you are, and remember: bugs and crashes are possible, reports are iconic. ๐Ÿ‘‘๐Ÿ’œ
๐Ÿ“ฆ where this page comes from