# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES.
#                         All rights reserved.
# SPDX-License-Identifier: Apache-2.0
---
Checks:
  # enable everything first
  - 'performance-*'
  - 'modernize-*'
  - 'readability-*'
  - 'clang-analyzer-*'
  - 'clang-diagnostic-*'
  - 'bugprone-*'
  - 'misc-*'
  - 'core-*'
  - 'mpi-*'
  - 'cert-*'
  - 'portability-*'
  - 'google-*'
  - 'cppcoreguidelines-*'
  - 'concurrency-*'
  # TODO: BEGIN REMOVE ME
  - '-*'
  - 'performance-*'
  - '-performance-enum-size'
  - '-performance-unnecessary-value-param'
  - 'modernize-*'
  - '-modernize-use-integer-sign-comparison'
  - '-modernize-use-nodiscard'
  - '-modernize-return-braced-init-list'
  - '-modernize-use-auto'
  - 'clang-diagnostic-*'
  # END REMOVE ME
  # TODO(jfaibussowit):
  #
  # Enable these once we move to C++20. Normally, clang-tidy does not suggest checks for
  # cpp N+1 if you are compiling with -std=c++N. However some subprojects like c.parallel
  # actually require cpp N+1 so clang-tidy emits diagnostics for any headers included by
  # it.
  - '-modernize-use-designated-initializers'
  - '-modernize-use-constraints'
  - '-modernize-use-ranges'
  # Irrelevant for GPU code
  - '-modernize-pass-by-value'
  # HICPP is 99% aliased to other checks (mostly modernize-* and bugprone-*). We don't
  # want to also enable it because then we need to duplicate the NOLINT. The only 2 checks
  # that aren't aliases are:
  #
  # - hicpp-multiway-paths-covered. This check is useful for detecting degenerate if-else
  #   branches, but the switch cases are IMO better handled by -Wswitch. To make -Wswitch
  #   even stronger, we should ban default: cases entirely,
  #
  # - hicpp-signed-bitwise. This check is also covered by
  #   clang.llvm.org/extra/clang-tidy/checks/clang-analyzer/core.BitwiseShift.html
  #
  # - 'hicpp-*'
  #
  # LLVM checks are extremely specific to the LLVM project and aren't suitable for
  # downstream users.
  #
  # - 'llvm-*'
  #
  # then disable the stuff we don't want
  - '-cert-dcl21-cpp'  # returning non-const from operator-- or operator++
  - '-cert-dcl50-cpp'  # allow c-style variadics
  # No reserved identifiers, both of these are aliased to bugprone-reserved-identifier,
  # which we do enable. Leaving these enabled however, leads to needing to specify all
  # three (bugprone-reserved-identifier, cert-dcl51-cpp, and cert-dcl37-c) in NOLINT lines
  # which is a hassle. Since bugprone-reserved-identifier is enabled, the check still
  # fires.
  - '-cert-dcl51-cpp,-cert-dcl37-c,-cert-oop54-cpp'
  # Covered by bugprone-throwing-static-initialization
  - '-cert-err58-cpp'
  - '-modernize-use-trailing-return-type'
  - '-readability-function-cognitive-complexity'
  - '-readability-implicit-bool-conversion'
  - '-readability-braces-around-statements'
  - '-readability-qualified-auto'
  - '-readability-isolate-declaration'
  - '-modernize-avoid-c-arrays'
  - '-cppcoreguidelines-avoid-c-arrays'
  - '-readability-named-parameter'
  - '-readability-identifier-length'
  - '-misc-non-private-member-variables-in-classes'
  - '-bugprone-easily-swappable-parameters'
  - '-bugprone-implicit-widening-of-multiplication-result'
  - '-misc-include-cleaner'
  - '-misc-header-include-cycle'
  - '-modernize-macro-to-enum'
  - '-cppcoreguidelines-macro-to-enum'
  - '-misc-no-recursion'
  - '-bugprone-dynamic-static-initializers'
  - '-portability-avoid-pragma-once'
  # Generally speaking if something is static then it has an express reason to be. 99% of
  # the candidates identified by this check were because they just so happened not to
  # touch any member variables, not because they are logically static. So we disable the
  # check.
  - '-readability-convert-member-functions-to-static'
  # When running in a conda env, ignore options that may have been added by conda but are unused
  - '-clang-diagnostic-unused-command-line-argument'
  # Given
  #
  # std::make_pair<some_type, some_other_type>(...)
  #
  # Results in: error: for C++11-compatibility, use pair directly. But we don't care about
  # C++11, and so we don't care about this warning.
  - '-google-build-explicit-make-pair'
  # This check is incredibly expensive for absolutely no reason, and since we a) use
  # modern google-test and b) don't have clang-tidy enabled on our testing code, we don't
  # need to enable it!
  - '-google-upgrade-googletest-case'
  # This warning will warn if you have redundant default-initializers for class
  # members. For example:
  #
  # class Foo
  # {
  #   int x_{}; // WARNING: redundant initializer here
  #
  # public:
  #   Foo(int x) : x_{x} { }
  # };
  #
  # Since Foo can only ever be constructed with an explicit value for x_ via its constructor,
  # the default initializer is technically redundant. However, if we change the definition
  # of Foo to now allow a default ctor, then the initializer becomes non-redundant
  # again. It is easier to just follow the rule of "always explicitly default initialize
  # members" than to remember to change 2 places at once.
  - '-readability-redundant-member-init'
  # Alias for readability-enum-initial-value, disable this one because the readability-
  # name is easier to understand, and we don't want to silence 2 things for the same
  # warning.
  - '-cert-int09-c'
  # This one is potentially controversial. This check warns when iterating over unordered
  # containers of pointers:
  #
  # {
  #   int a = 1, b = 2;
  #   std::unordered_set<int *> set = {&a, &b};
  #
  #   for (auto *i : set) { // iteration order not deterministic
  #     f(i);
  #   }
  # }
  #
  # On the one hand, this is a clear case of non-determinism. But on the other hand, I
  # feel it is obvious that a user does not care about the order of iteration
  # because... they are using unordered containers!
  - '-bugprone-nondeterministic-pointer-iteration-order'
  - '-cppcoreguidelines-pro-type-reinterpret-cast'
  # We don't use GSL
  - '-cppcoreguidelines-owning-memory'
  # Covered by modernize-use-override already
  - '-cppcoreguidelines-explicit-virtual-functions'
  # Covered by readability-magic-numbers
  - '-cppcoreguidelines-avoid-magic-numbers'
  # This check does more harm than good, as it appears to be very rudimentary. For
  # example, it emits warnings saying that:
  #
  # #define DEFINE_OPERATOR(op)                            \
  #   _CCCL_HOST_DEVICE custom_numeric operator op() const \
  #   {                                                    \
  #     return custom_numeric(op value[0]);                \
  #   }
  #
  # Should be replaced by a template function, but this is not possible. We instead rely
  # on best judgment of reviewers to catch macros that can be functions.
  - '-cppcoreguidelines-macro-usage'
  # This check does not understand more complex macro usage where brackets are not allowed
  #
  # error: macro replacement list should be enclosed in parentheses [bugprone-macro-parentheses]
  # 36 | #define __THRUST_HOST_SYSTEM_INCLUDE(filename) <__THRUST_HOST_SYSTEM_ROOT/filename>
  #    |                                                                           ^
  #    |                                                                           (       )
  #
  # So better left disabled.
  - '-bugprone-macro-parentheses'
  # TODO: ironically enough, enable this someday
  - '-google-readability-todo'
  # Covered by modernize-avoid-c-style-cast, and also is buggier. It seems to fire on
  # `_v`-style constexpr bools as well sometimes.
  - '-google-readability-casting'
  # Covered by performance-noexcept-move-constructor
  - '-cppcoreguidelines-noexcept-move-operations'
  # REVIEW ME: This warns about any usage of operator[], suggesting usage of .at() instead. Could
  - '-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access'
  # Covered by bugprone-narrowing-conversions
  - '-cppcoreguidelines-narrowing-conversions'
  # Covered by misc-unconventional-assign-operator
  - '-cppcoreguidelines-c-copy-assignment-signature'
  # Covered by performance-noexcept-swap
  - '-cppcoreguidelines-noexcept-swap'
  # Covered by modernize-use-default-member-init
  - '-cppcoreguidelines-use-default-member-init'
  # clang-tidy documentation says that for a given file, it matches the closest
  # .clang-tidy up the directory stack and applies the configuration from it. However, by
  # "it" `clang-tidy` means the closest `.clang-tidy` to the *source* file, not the file
  # that was included by the source file.
  #
  # So `cuda/std/foo.h` included by `cccl/foo/bar/baz.cpp` will never "match" against
  # `cccl/libcudacxx/.clang-tidy` because `clang-tidy` finds `cccl/.clang-tidy` instead.
  #
  # So we disable this for now. In the future we should add a custom check that bans
  # reserved identifiers except symbols from libcu++.
  - '-bugprone-reserved-identifier'
WarningsAsErrors: '*'
HeaderFileExtensions:
  - ''
  - h
  - hh
  - hpp
  - hxx
  - cuh
  - inl
  - ipp
ImplementationFileExtensions:
  - c
  - cc
  - cpp
  - cxx
  - cu
SystemHeaders: false
HeaderFilterRegex: '.*/(thrust|cub|cuda|nv|cccl|c2h|cudastf|test)/.*'
ExtraArgsBefore:
  # To match _CCCL_DOXYGEN_INVOKED
  - '-D_CCCL_CLANG_TIDY_INVOKED=1'
  - '-ftemplate-backtrace-limit=0'
  - '-fmacro-backtrace-limit=0'
ExtraArgs:
  # These must come in ExtraArgs not ExtraArgsBefore, because they are overriding the
  # effects of -Wall and -Werror, where the last flag "wins".
  - '-Wno-unknown-warning-option'
  - '-Wno-unknown-cuda-version'
  - '-Wno-error=unused-command-line-argument'
CheckOptions:
  cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU'
  cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
  llvm-else-after-return.WarnOnUnfixable: 'false'
  cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false'
  cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true'
  google-readability-braces-around-statements.ShortStatementLines: '1'
  llvm-qualified-auto.AddConstToQualified: 'false'
  llvm-else-after-return.WarnOnConditionVariables: 'false'
  cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false'
  performance-move-const-arg.CheckTriviallyCopyableMove: 'false'
  performance-inefficient-string-concatenation.StrictMode: 'true'
  readability-simplify-boolean-expr.ChainedConditionalReturn: 'true'
  readability-simplify-boolean-expr.ChainedConditionalAssignment: 'true'
  bugprone-dangling-handle.HandleClasses: '::cuda::std::span;::cuda::std::mdspan'
  bugprone-misplaced-widening-cast.CheckImplicitCasts: 'true'
  bugprone-unused-return-value.AllowCastToVoid: 'true'
  readability-enum-initial-value.AllowExplicitZeroFirstInitialValue: 'false'
  readability-enum-initial-value.AllowExplicitSequentialInitialValues: 'false'
  readability-redundant-access-specifiers.CheckFirstDeclaration: 'true'
  bugprone-lambda-function-name.IgnoreMacros: 'true'
  # readability-identifier-naming.ClassCase: 'lower_case'
  # readability-identifier-naming.UnionCase: 'lower_case'
  # readability-identifier-naming.ClassConstantCase: 'UPPER_CASE'
  # readability-identifier-naming.ClassIgnoredRegexp: 'tuple|has_.*|is_.*|as_.*|.*_tag|tag|.*_of'
  # readability-identifier-naming.ConstantMemberCase: 'UPPER_CASE'
  # readability-identifier-naming.ConstantMemberIgnoredRegexp: 'value'
  # readability-identifier-naming.EnumCase: 'lower_case'
  # readability-identifier-naming.EnumConstantCase: 'UPPER_CASE'
  # readability-identifier-naming.FunctionCase: 'lower_case'
  # readability-identifier-naming.GlobalConstantCase: 'UPPER_CASE'
  # readability-identifier-naming.GlobalConstantIgnoredRegexp: '.*_v'
  # readability-identifier-naming.LocalVariableCase: 'lower_case'
  # # We want to allow constexpr auto MY_VAL1
  # readability-identifier-naming.LocalVariableIgnoredRegexp: '[A-Z_0-9]+'
  # readability-identifier-naming.MacroDefinitionCase: 'UPPER_CASE'
  # # We want to allow MY_MACRO_PRIVATE_1_
  # readability-identifier-naming.MacroDefinitionIgnoredRegexp: '[A-Z_0-9]+'
  # readability-identifier-naming.NamespaceCase: 'lower_case'
  # readability-identifier-naming.PrivateMemberCase: 'lower_case'
  # readability-identifier-naming.PrivateMemberSuffix: '_'
  # readability-identifier-naming.PrivateMethodCase: 'lower_case'
  # readability-identifier-naming.PrivateMethodSuffix: '_'
  # readability-identifier-naming.ProtectedMemberCase: 'lower_case'
  # readability-identifier-naming.ProtectedMemberSuffix: '_'
  # readability-identifier-naming.ProtectedMethodCase: 'lower_case'
  # readability-identifier-naming.ProtectedMethodSuffix: '_'
  # readability-identifier-naming.PublicMethodCase: 'lower_case'
  # readability-identifier-naming.ScopedEnumCase: 'lower_case'
  # readability-identifier-naming.ScopedEnumConstantCase: 'UPPER_CASE'
  readability-magic-numbers.IgnoredIntegerValues: '0;1;2;3;4;5;6;7;8;9'
  # There are some single-argument functions that we would not like to ignore
  # (`to_string(bool provenance = false)` comes to mind), but setting this to false makes
  # clang-tidy warn on a bunch of "obvious" calls, like `set_dim(1)` or
  # `with_concurrent(true)`.
  bugprone-argument-comment.IgnoreSingleArgument: true
  bugprone-argument-comment.CommentBoolLiterals: true
  bugprone-argument-comment.CommentIntegerLiterals: true
  bugprone-argument-comment.CommentFloatLiterals: true
  bugprone-argument-comment.CommentStringLiterals: true
  bugprone-argument-comment.CommentCharacterLiterals: true
  bugprone-argument-comment.CommentUserDefinedLiterals: true
  bugprone-argument-comment.CommentNullPtrs: true
  cppcoreguidelines-macro-usage.AllowedRegexp: '^DEBUG_.*|THRUST_PP_.*|_CCCL.*_PP_.*'
  bugprone-reserved-identifier.AllowedIdentifiers: '^_CCCL.*'
  performance-enum-size.EnumIgnoreList: '::cuda::std::.*'
  performance-unnecessary-value-param.AllowedTypes: '.*(_ref(erence)?|_ptr|pointer|(_)?[Ii]t(erator)?|IteratorT|__half(2)?|(__nv_bfloat.*)|_view)$'
...
