Skip to content

Commit 7a73da4

Browse files
felix642PiotrZSL
authored andcommitted
[clang-tidy] Add support for optional parameters in config.
If a parameter value is either 'none', 'null', 'false', '-1' or '', we will in that case use the default value. Reviewed By: PiotrZSL Differential Revision: https://reviews.llvm.org/D159436
1 parent 1684c65 commit 7a73da4

File tree

7 files changed

+150
-40
lines changed

7 files changed

+150
-40
lines changed

clang-tools-extra/clang-tidy/ClangTidyCheck.h

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
184184
/// integral type ``T``.
185185
///
186186
/// Reads the option with the check-local name \p LocalName from the
187-
/// ``CheckOptions``. If the corresponding key is not present, return
188-
/// ``std::nullopt``.
187+
/// ``CheckOptions``. If the corresponding key is not present,
188+
/// return ``std::nullopt``.
189189
///
190190
/// If the corresponding key can't be parsed as a ``T``, emit a
191191
/// diagnostic and return ``std::nullopt``.
@@ -201,6 +201,31 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
201201
return std::nullopt;
202202
}
203203

204+
/// Read a named option from the ``Context`` and parse it as an
205+
/// integral type ``T``.
206+
///
207+
/// Reads the option with the check-local name \p LocalName from the
208+
/// ``CheckOptions``. If the corresponding key is `none`, `null`,
209+
/// `-1` or empty, return ``std::nullopt``. If the corresponding
210+
/// key is not present, return \p Default.
211+
///
212+
/// If the corresponding key can't be parsed as a ``T``, emit a
213+
/// diagnostic and return \p Default.
214+
template <typename T>
215+
std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
216+
get(StringRef LocalName, std::optional<T> Default) const {
217+
if (std::optional<StringRef> Value = get(LocalName)) {
218+
if (Value == "" || Value == "none" || Value == "null" ||
219+
(std::is_unsigned_v<T> && Value == "-1"))
220+
return std::nullopt;
221+
T Result{};
222+
if (!StringRef(*Value).getAsInteger(10, Result))
223+
return Result;
224+
diagnoseBadIntegerOption(NamePrefix + LocalName, *Value);
225+
}
226+
return Default;
227+
}
228+
204229
/// Read a named option from the ``Context`` and parse it as an
205230
/// integral type ``T``.
206231
///
@@ -245,6 +270,39 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
245270
return std::nullopt;
246271
}
247272

273+
/// Read a named option from the ``Context`` and parse it as an
274+
/// integral type ``T``.
275+
///
276+
/// Reads the option with the check-local name \p LocalName from local or
277+
/// global ``CheckOptions``. Gets local option first. If local is not
278+
/// present, falls back to get global option. If global option is not
279+
/// present either, return \p Default. If the value value was found
280+
/// and equals ``none``, ``null``, ``-1`` or empty, return ``std::nullopt``.
281+
///
282+
/// If the corresponding key can't be parsed as a ``T``, emit a
283+
/// diagnostic and return \p Default.
284+
template <typename T>
285+
std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
286+
getLocalOrGlobal(StringRef LocalName, std::optional<T> Default) const {
287+
std::optional<StringRef> ValueOr = get(LocalName);
288+
bool IsGlobal = false;
289+
if (!ValueOr) {
290+
IsGlobal = true;
291+
ValueOr = getLocalOrGlobal(LocalName);
292+
if (!ValueOr)
293+
return Default;
294+
}
295+
T Result{};
296+
if (ValueOr == "" || ValueOr == "none" || ValueOr == "null" ||
297+
(std::is_unsigned_v<T> && ValueOr == "-1"))
298+
return std::nullopt;
299+
if (!StringRef(*ValueOr).getAsInteger(10, Result))
300+
return Result;
301+
diagnoseBadIntegerOption(
302+
IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr);
303+
return Default;
304+
}
305+
248306
/// Read a named option from the ``Context`` and parse it as an
249307
/// integral type ``T``.
250308
///
@@ -286,8 +344,8 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
286344
/// enum type ``T``.
287345
///
288346
/// Reads the option with the check-local name \p LocalName from the
289-
/// ``CheckOptions``. If the corresponding key is not present, return
290-
/// \p Default.
347+
/// ``CheckOptions``. If the corresponding key is not present,
348+
/// return \p Default.
291349
///
292350
/// If the corresponding key can't be parsed as a ``T``, emit a
293351
/// diagnostic and return \p Default.
@@ -356,6 +414,19 @@ class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
356414
storeInt(Options, LocalName, Value);
357415
}
358416

417+
/// Stores an option with the check-local name \p LocalName with
418+
/// integer value \p Value to \p Options. If the value is empty
419+
/// stores ``
420+
template <typename T>
421+
std::enable_if_t<std::is_integral_v<T>>
422+
store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
423+
std::optional<T> Value) const {
424+
if (Value)
425+
storeInt(Options, LocalName, *Value);
426+
else
427+
store(Options, LocalName, "none");
428+
}
429+
359430
/// Stores an option with the check-local name \p LocalName as the string
360431
/// representation of the Enum \p Value to \p Options.
361432
///

clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,16 @@ class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
126126

127127
FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
128128
: ClangTidyCheck(Name, Context),
129-
LineThreshold(Options.get("LineThreshold", -1U)),
130-
StatementThreshold(Options.get("StatementThreshold", 800U)),
131-
BranchThreshold(Options.get("BranchThreshold", -1U)),
132-
ParameterThreshold(Options.get("ParameterThreshold", -1U)),
133-
NestingThreshold(Options.get("NestingThreshold", -1U)),
134-
VariableThreshold(Options.get("VariableThreshold", -1U)) {}
129+
LineThreshold(Options.get("LineThreshold", DefaultLineThreshold)),
130+
StatementThreshold(
131+
Options.get("StatementThreshold", DefaultStatementThreshold)),
132+
BranchThreshold(Options.get("BranchThreshold", DefaultBranchThreshold)),
133+
ParameterThreshold(
134+
Options.get("ParameterThreshold", DefaultParameterThreshold)),
135+
NestingThreshold(
136+
Options.get("NestingThreshold", DefaultNestingThreshold)),
137+
VariableThreshold(
138+
Options.get("VariableThreshold", DefaultVariableThreshold)) {}
135139

136140
void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
137141
Options.store(Opts, "LineThreshold", LineThreshold);
@@ -155,7 +159,7 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
155159
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
156160

157161
FunctionASTVisitor Visitor;
158-
Visitor.Info.NestingThreshold = NestingThreshold;
162+
Visitor.Info.NestingThreshold = NestingThreshold.value_or(-1);
159163
Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
160164
auto &FI = Visitor.Info;
161165

@@ -173,49 +177,51 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
173177

174178
unsigned ActualNumberParameters = Func->getNumParams();
175179

176-
if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
177-
FI.Branches > BranchThreshold ||
178-
ActualNumberParameters > ParameterThreshold ||
179-
!FI.NestingThresholders.empty() || FI.Variables > VariableThreshold) {
180+
if ((LineThreshold && FI.Lines > LineThreshold) ||
181+
(StatementThreshold && FI.Statements > StatementThreshold) ||
182+
(BranchThreshold && FI.Branches > BranchThreshold) ||
183+
(ParameterThreshold && ActualNumberParameters > ParameterThreshold) ||
184+
!FI.NestingThresholders.empty() ||
185+
(VariableThreshold && FI.Variables > VariableThreshold)) {
180186
diag(Func->getLocation(),
181187
"function %0 exceeds recommended size/complexity thresholds")
182188
<< Func;
183189
}
184190

185-
if (FI.Lines > LineThreshold) {
191+
if (LineThreshold && FI.Lines > LineThreshold) {
186192
diag(Func->getLocation(),
187193
"%0 lines including whitespace and comments (threshold %1)",
188194
DiagnosticIDs::Note)
189-
<< FI.Lines << LineThreshold;
195+
<< FI.Lines << LineThreshold.value();
190196
}
191197

192-
if (FI.Statements > StatementThreshold) {
198+
if (StatementThreshold && FI.Statements > StatementThreshold) {
193199
diag(Func->getLocation(), "%0 statements (threshold %1)",
194200
DiagnosticIDs::Note)
195-
<< FI.Statements << StatementThreshold;
201+
<< FI.Statements << StatementThreshold.value();
196202
}
197203

198-
if (FI.Branches > BranchThreshold) {
204+
if (BranchThreshold && FI.Branches > BranchThreshold) {
199205
diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
200-
<< FI.Branches << BranchThreshold;
206+
<< FI.Branches << BranchThreshold.value();
201207
}
202208

203-
if (ActualNumberParameters > ParameterThreshold) {
209+
if (ParameterThreshold && ActualNumberParameters > ParameterThreshold) {
204210
diag(Func->getLocation(), "%0 parameters (threshold %1)",
205211
DiagnosticIDs::Note)
206-
<< ActualNumberParameters << ParameterThreshold;
212+
<< ActualNumberParameters << ParameterThreshold.value();
207213
}
208214

209215
for (const auto &CSPos : FI.NestingThresholders) {
210216
diag(CSPos, "nesting level %0 starts here (threshold %1)",
211217
DiagnosticIDs::Note)
212-
<< NestingThreshold + 1 << NestingThreshold;
218+
<< NestingThreshold.value() + 1 << NestingThreshold.value();
213219
}
214220

215-
if (FI.Variables > VariableThreshold) {
221+
if (VariableThreshold && FI.Variables > VariableThreshold) {
216222
diag(Func->getLocation(), "%0 variables (threshold %1)",
217223
DiagnosticIDs::Note)
218-
<< FI.Variables << VariableThreshold;
224+
<< FI.Variables << VariableThreshold.value();
219225
}
220226
}
221227

clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,23 @@ class FunctionSizeCheck : public ClangTidyCheck {
4141
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
4242

4343
private:
44-
const unsigned LineThreshold;
45-
const unsigned StatementThreshold;
46-
const unsigned BranchThreshold;
47-
const unsigned ParameterThreshold;
48-
const unsigned NestingThreshold;
49-
const unsigned VariableThreshold;
44+
const std::optional<unsigned> LineThreshold;
45+
const std::optional<unsigned> StatementThreshold;
46+
const std::optional<unsigned> BranchThreshold;
47+
const std::optional<unsigned> ParameterThreshold;
48+
const std::optional<unsigned> NestingThreshold;
49+
const std::optional<unsigned> VariableThreshold;
50+
51+
static constexpr std::optional<unsigned> DefaultLineThreshold = std::nullopt;
52+
static constexpr std::optional<unsigned> DefaultStatementThreshold = 800U;
53+
static constexpr std::optional<unsigned> DefaultBranchThreshold =
54+
std::nullopt;
55+
static constexpr std::optional<unsigned> DefaultParameterThreshold =
56+
std::nullopt;
57+
static constexpr std::optional<unsigned> DefaultNestingThreshold =
58+
std::nullopt;
59+
static constexpr std::optional<unsigned> DefaultVariableThreshold =
60+
std::nullopt;
5061
};
5162

5263
} // namespace clang::tidy::readability

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ Changes in existing checks
312312
detect comparison between string and empty string literals and support
313313
``length()`` method as an alternative to ``size()``.
314314

315+
- Improved :doc:`readability-function-size
316+
<clang-tidy/checks/readability/function-size>` check configuration to use
317+
`none` rather than `-1` to disable some parameters.
318+
315319
- Improved :doc:`readability-identifier-naming
316320
<clang-tidy/checks/readability/identifier-naming>` check to issue accurate
317321
warnings when a type's forward declaration precedes its definition.
@@ -334,7 +338,6 @@ Changes in existing checks
334338
<clang-tidy/checks/readability/static-accessed-through-instance>` check to
335339
identify calls to static member functions with out-of-class inline definitions.
336340

337-
338341
Removed checks
339342
^^^^^^^^^^^^^^
340343

clang-tools-extra/docs/clang-tidy/checks/readability/function-size.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Options
1212

1313
.. option:: LineThreshold
1414

15-
Flag functions exceeding this number of lines. The default is `-1` (ignore
15+
Flag functions exceeding this number of lines. The default is `none` (ignore
1616
the number of lines).
1717

1818
.. option:: StatementThreshold
@@ -24,22 +24,22 @@ Options
2424
.. option:: BranchThreshold
2525

2626
Flag functions exceeding this number of control statements. The default is
27-
`-1` (ignore the number of branches).
27+
`none` (ignore the number of branches).
2828

2929
.. option:: ParameterThreshold
3030

3131
Flag functions that exceed a specified number of parameters. The default
32-
is `-1` (ignore the number of parameters).
32+
is `none` (ignore the number of parameters).
3333

3434
.. option:: NestingThreshold
3535

3636
Flag compound statements which create next nesting level after
3737
`NestingThreshold`. This may differ significantly from the expected value
38-
for macro-heavy code. The default is `-1` (ignore the nesting level).
38+
for macro-heavy code. The default is `none` (ignore the nesting level).
3939

4040
.. option:: VariableThreshold
4141

4242
Flag functions exceeding this number of variables declared in the body.
43-
The default is `-1` (ignore the number of variables).
43+
The default is `none` (ignore the number of variables).
4444
Please note that function parameters and variables declared in lambdas,
4545
GNU Statement Expressions, and nested class inline functions are not counted.

clang-tools-extra/test/clang-tidy/checkers/readability/function-size.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
// RUN: readability-function-size.VariableThreshold: 1 \
99
// RUN: }}'
1010

11+
12+
// RUN: %check_clang_tidy -check-suffixes=OPTIONAL %s readability-function-size %t -- \
13+
// RUN: -config='{CheckOptions: { \
14+
// RUN: readability-function-size.StatementThreshold: "-1", \
15+
// RUN: readability-function-size.BranchThreshold: "5", \
16+
// RUN: readability-function-size.ParameterThreshold: "none", \
17+
// RUN: readability-function-size.NestingThreshold: "", \
18+
// RUN: readability-function-size.VariableThreshold: "" \
19+
// RUN: }}'
20+
1121
// Bad formatting is intentional, don't run clang-format over the whole file!
1222

1323
void foo1() {
@@ -103,9 +113,11 @@ void baz0() { // 1
103113
// check that nested if's are not reported. this was broken initially
104114
void nesting_if() { // 1
105115
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'nesting_if' exceeds recommended size/complexity
106-
// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 23 lines including whitespace and comments (threshold 0)
116+
// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 25 lines including whitespace and comments (threshold 0)
107117
// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 18 statements (threshold 0)
108118
// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 6 branches (threshold 0)
119+
// CHECK-MESSAGES-OPTIONAL: :[[@LINE-5]]:6: warning: function 'nesting_if' exceeds recommended size/complexity
120+
// CHECK-MESSAGES-OPTIONAL: :[[@LINE-6]]:6: note: 6 branches (threshold 5)
109121
if (true) { // 2
110122
int j;
111123
} else if (true) { // 2
@@ -123,7 +135,7 @@ void nesting_if() { // 1
123135
} else if (true) { // 2
124136
int j;
125137
}
126-
// CHECK-MESSAGES: :[[@LINE-22]]:6: note: 6 variables (threshold 1)
138+
// CHECK-MESSAGES: :[[@LINE-24]]:6: note: 6 variables (threshold 1)
127139
}
128140

129141
// however this should warn

clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,12 @@
5555
// CHECK-CHILD6: Checks: {{.*-llvm-qualified-auto'? *$}}
5656
// CHECK-CHILD6-NOT: modernize-use-using.IgnoreMacros
5757

58+
// RUN: clang-tidy -dump-config \
59+
// RUN: --config='{CheckOptions: {readability-function-size.LineThreshold: ""}, \
60+
// RUN: Checks: readability-function-size}' \
61+
// RUN: %S/Inputs/config-files/4/44/- -- | FileCheck %s -check-prefix=CHECK-CHILD7
62+
// CHECK-CHILD7: readability-function-size.LineThreshold: none
63+
64+
5865
// Validate that check options are printed in alphabetical order:
5966
// RUN: clang-tidy --checks="-*,readability-identifier-naming" --dump-config %S/Inputs/config-files/- -- | grep "readability-identifier-naming\." | sort --check

0 commit comments

Comments
 (0)