diff --git a/clang-tools-extra/clang-tidy/ClangTidyCheck.h b/clang-tools-extra/clang-tidy/ClangTidyCheck.h --- a/clang-tools-extra/clang-tidy/ClangTidyCheck.h +++ b/clang-tools-extra/clang-tidy/ClangTidyCheck.h @@ -184,8 +184,8 @@ /// integral type ``T``. /// /// Reads the option with the check-local name \p LocalName from the - /// ``CheckOptions``. If the corresponding key is not present, return - /// ``std::nullopt``. + /// ``CheckOptions``. If the corresponding key is not present or empty, + /// return ``std::nullopt``. /// /// If the corresponding key can't be parsed as a ``T``, emit a /// diagnostic and return ``std::nullopt``. @@ -193,6 +193,9 @@ std::enable_if_t, std::optional> get(StringRef LocalName) const { if (std::optional Value = get(LocalName)) { + if (Value == "" || Value == "none" || Value == "null" || + Value == "false" || (std::is_unsigned_v && Value == "-1")) + return std::nullopt; T Result{}; if (!StringRef(*Value).getAsInteger(10, Result)) return Result; @@ -238,6 +241,9 @@ return std::nullopt; } T Result{}; + if (ValueOr == "" || ValueOr == "none" || ValueOr == "null" || + ValueOr == "false" || (std::is_unsigned_v && ValueOr == "-1")) + return std::nullopt; if (!StringRef(*ValueOr).getAsInteger(10, Result)) return Result; diagnoseBadIntegerOption( @@ -286,8 +292,8 @@ /// enum type ``T``. /// /// Reads the option with the check-local name \p LocalName from the - /// ``CheckOptions``. If the corresponding key is not present, return - /// \p Default. + /// ``CheckOptions``. If the corresponding key is not present or empty, + /// return \p Default. /// /// If the corresponding key can't be parsed as a ``T``, emit a /// diagnostic and return \p Default. @@ -356,6 +362,19 @@ storeInt(Options, LocalName, Value); } + /// Stores an option with the check-local name \p LocalName with + /// integer value \p Value to \p Options. If the value is empty + /// stores `` + template + std::enable_if_t> + store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, + std::optional Value) const { + if (Value) + storeInt(Options, LocalName, *Value); + else + store(Options, LocalName, "none"); + } + /// Stores an option with the check-local name \p LocalName as the string /// representation of the Enum \p Value to \p Options. /// diff --git a/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h b/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h --- a/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h +++ b/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h @@ -41,12 +41,12 @@ void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: - const unsigned LineThreshold; - const unsigned StatementThreshold; - const unsigned BranchThreshold; - const unsigned ParameterThreshold; - const unsigned NestingThreshold; - const unsigned VariableThreshold; + const std::optional LineThreshold; + const std::optional StatementThreshold; + const std::optional BranchThreshold; + const std::optional ParameterThreshold; + const std::optional NestingThreshold; + const std::optional VariableThreshold; }; } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp b/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp @@ -126,12 +126,12 @@ FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), - LineThreshold(Options.get("LineThreshold", -1U)), - StatementThreshold(Options.get("StatementThreshold", 800U)), - BranchThreshold(Options.get("BranchThreshold", -1U)), - ParameterThreshold(Options.get("ParameterThreshold", -1U)), - NestingThreshold(Options.get("NestingThreshold", -1U)), - VariableThreshold(Options.get("VariableThreshold", -1U)) {} + LineThreshold(Options.get("LineThreshold")), + StatementThreshold(Options.get("StatementThreshold", 800U)), + BranchThreshold(Options.get("BranchThreshold")), + ParameterThreshold(Options.get("ParameterThreshold")), + NestingThreshold(Options.get("NestingThreshold")), + VariableThreshold(Options.get("VariableThreshold")) {} void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "LineThreshold", LineThreshold); @@ -155,7 +155,7 @@ const auto *Func = Result.Nodes.getNodeAs("func"); FunctionASTVisitor Visitor; - Visitor.Info.NestingThreshold = NestingThreshold; + Visitor.Info.NestingThreshold = NestingThreshold.value_or(-1); Visitor.TraverseDecl(const_cast(Func)); auto &FI = Visitor.Info; @@ -173,49 +173,51 @@ unsigned ActualNumberParameters = Func->getNumParams(); - if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold || - FI.Branches > BranchThreshold || - ActualNumberParameters > ParameterThreshold || - !FI.NestingThresholders.empty() || FI.Variables > VariableThreshold) { + if ((LineThreshold && FI.Lines > LineThreshold) || + (StatementThreshold && FI.Statements > StatementThreshold) || + (BranchThreshold && FI.Branches > BranchThreshold) || + (ParameterThreshold && ActualNumberParameters > ParameterThreshold) || + !FI.NestingThresholders.empty() || + (VariableThreshold && FI.Variables > VariableThreshold)) { diag(Func->getLocation(), "function %0 exceeds recommended size/complexity thresholds") << Func; } - if (FI.Lines > LineThreshold) { + if (LineThreshold && FI.Lines > LineThreshold) { diag(Func->getLocation(), "%0 lines including whitespace and comments (threshold %1)", DiagnosticIDs::Note) - << FI.Lines << LineThreshold; + << FI.Lines << LineThreshold.value(); } - if (FI.Statements > StatementThreshold) { + if (StatementThreshold && FI.Statements > StatementThreshold) { diag(Func->getLocation(), "%0 statements (threshold %1)", DiagnosticIDs::Note) - << FI.Statements << StatementThreshold; + << FI.Statements << StatementThreshold.value(); } - if (FI.Branches > BranchThreshold) { + if (BranchThreshold && FI.Branches > BranchThreshold) { diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note) - << FI.Branches << BranchThreshold; + << FI.Branches << BranchThreshold.value(); } - if (ActualNumberParameters > ParameterThreshold) { + if (ParameterThreshold && ActualNumberParameters > ParameterThreshold) { diag(Func->getLocation(), "%0 parameters (threshold %1)", DiagnosticIDs::Note) - << ActualNumberParameters << ParameterThreshold; + << ActualNumberParameters << ParameterThreshold.value(); } for (const auto &CSPos : FI.NestingThresholders) { diag(CSPos, "nesting level %0 starts here (threshold %1)", DiagnosticIDs::Note) - << NestingThreshold + 1 << NestingThreshold; + << NestingThreshold.value() + 1 << NestingThreshold.value(); } - if (FI.Variables > VariableThreshold) { + if (VariableThreshold && FI.Variables > VariableThreshold) { diag(Func->getLocation(), "%0 variables (threshold %1)", DiagnosticIDs::Note) - << FI.Variables << VariableThreshold; + << FI.Variables << VariableThreshold.value(); } } diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -113,6 +113,10 @@ - Improved `--dump-config` to print check options in alphabetical order. +- Added support for optional parameters. If a parameter that requires an integer + literal in the config file is set to `none`, `null`, `false`, or is left empty, + it will use its default value + New checks ^^^^^^^^^^ diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/optional-parameter.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/optional-parameter.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/optional-parameter.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-easily-swappable-parameters.MinimumLength: "", \ +// RUN: }}' -- + +// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-easily-swappable-parameters.MinimumLength: "none", \ +// RUN: }}' -- + +// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-easily-swappable-parameters.MinimumLength: "null", \ +// RUN: }}' -- + +// RUN: %check_clang_tidy %s bugprone-easily-swappable-parameters %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-easily-swappable-parameters.MinimumLength: "false", \ +// RUN: }}' -- + +void a(int b, int c) {} +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 2 adjacent parameters of 'a' of similar type ('int') are easily swapped by mistake [bugprone-easily-swappable-parameters] +// CHECK-MESSAGES: :[[@LINE-2]]:12: note: the first parameter in the range is 'b' +// CHECK-MESSAGES: :[[@LINE-3]]:19: note: the last parameter in the range is 'c'