diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f3d977e..5392d670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ -## 0.2.4 - +## 0.3.0 +- Added `exclude` parameter for the following lints: + - `avoid_returning_widgets` + - `avoid_unused_parameters` + - `cyclomatic_complexity` + - `function_lines_of_code` + - `no_empty_bloc` + - `number_of_parameters` +- BREAKING CHANGE: Renamed `excludeNames` parameter to `exclude` for `function_lines_of_code` lint. - Fixed an issue with `prefer_early_retrun` for throw expression ## 0.2.3 diff --git a/lib/src/common/parameters/excluded_identifier_parameter.dart b/lib/src/common/parameters/excluded_identifier_parameter.dart index 8fb05354..f55f9704 100644 --- a/lib/src/common/parameters/excluded_identifier_parameter.dart +++ b/lib/src/common/parameters/excluded_identifier_parameter.dart @@ -1,15 +1,19 @@ /// Model class for ExcludeRule parameters class ExcludedIdentifierParameter { /// The name of the method that should be excluded from the lint. - final String methodName; + final String? methodName; /// The name of the class that should be excluded from the lint. final String? className; + /// The name of the plain Strings that should be excluded from the lint + final String? declarationName; + /// Constructor for [ExcludedIdentifierParameter] model const ExcludedIdentifierParameter({ - required this.methodName, - required this.className, + this.methodName, + this.className, + this.declarationName, }); /// diff --git a/lib/src/common/parameters/excluded_identifiers_list_parameter.dart b/lib/src/common/parameters/excluded_identifiers_list_parameter.dart index 2dc84989..4239106a 100644 --- a/lib/src/common/parameters/excluded_identifiers_list_parameter.dart +++ b/lib/src/common/parameters/excluded_identifiers_list_parameter.dart @@ -26,6 +26,12 @@ class ExcludedIdentifiersListParameter { for (final item in excludeList) { if (item is Map) { exclude.add(ExcludedIdentifierParameter.fromJson(item)); + } else if (item is String) { + exclude.add( + ExcludedIdentifierParameter( + declarationName: item, + ), + ); } } return ExcludedIdentifiersListParameter( @@ -33,12 +39,38 @@ class ExcludedIdentifiersListParameter { ); } + /// Method for creating from json data with default params + factory ExcludedIdentifiersListParameter.defaultFromJson( + Map json, + ) { + final excludeList = + json[ExcludedIdentifiersListParameter.excludeParameterName] + as Iterable? ?? + []; + + return ExcludedIdentifiersListParameter.fromJson( + excludeList: excludeList, + ); + } + /// Returns whether the target node should be ignored during analysis. bool shouldIgnore(Declaration node) { - final methodName = node.declaredElement?.name; + final declarationName = node.declaredElement?.name; - final excludedItem = - exclude.firstWhereOrNull((e) => e.methodName == methodName); + final excludedItem = exclude.firstWhereOrNull( + (e) { + if (e.declarationName == declarationName) { + return true; + } else if (node is ClassDeclaration) { + return e.className == declarationName; + } else if (node is MethodDeclaration) { + return e.methodName == declarationName; + } else if (node is FunctionDeclaration) { + return e.methodName == declarationName; + } + return false; + }, + ); if (excludedItem == null) return false; diff --git a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart index 7fbbf7aa..7947ea64 100644 --- a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart +++ b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart @@ -14,11 +14,7 @@ class AvoidReturningWidgetsParameters { /// Method for creating from json data factory AvoidReturningWidgetsParameters.fromJson(Map json) { return AvoidReturningWidgetsParameters( - exclude: ExcludedIdentifiersListParameter.fromJson( - excludeList: json[ExcludedIdentifiersListParameter.excludeParameterName] - as Iterable? ?? - [], - ), + exclude: ExcludedIdentifiersListParameter.defaultFromJson(json), ); } } diff --git a/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart b/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart index aaf8a095..c80ef2b7 100644 --- a/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart +++ b/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart @@ -1,5 +1,6 @@ import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:solid_lints/src/lints/avoid_unused_parameters/models/avoid_unused_parameters.dart'; import 'package:solid_lints/src/lints/avoid_unused_parameters/visitors/avoid_unused_parameters_visitor.dart'; import 'package:solid_lints/src/models/rule_config.dart'; import 'package:solid_lints/src/models/solid_lint_rule.dart'; @@ -64,7 +65,7 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// }; /// /// ``` -class AvoidUnusedParametersRule extends SolidLintRule { +class AvoidUnusedParametersRule extends SolidLintRule { /// This lint rule represents /// the error whether we use bad formatted double literals. static const String lintName = 'avoid_unused_parameters'; @@ -79,6 +80,7 @@ class AvoidUnusedParametersRule extends SolidLintRule { final rule = RuleConfig( configs: configs, name: lintName, + paramsParser: AvoidUnusedParameters.fromJson, problemMessage: (_) => 'Parameter is unused.', ); @@ -91,7 +93,11 @@ class AvoidUnusedParametersRule extends SolidLintRule { ErrorReporter reporter, CustomLintContext context, ) { - context.registry.addCompilationUnit((node) { + context.registry.addDeclaration((node) { + final isIgnored = config.parameters.exclude.shouldIgnore(node); + + if (isIgnored) return; + final visitor = AvoidUnusedParametersVisitor(); node.accept(visitor); diff --git a/lib/src/lints/avoid_unused_parameters/models/avoid_unused_parameters.dart b/lib/src/lints/avoid_unused_parameters/models/avoid_unused_parameters.dart new file mode 100644 index 00000000..f4dac97b --- /dev/null +++ b/lib/src/lints/avoid_unused_parameters/models/avoid_unused_parameters.dart @@ -0,0 +1,20 @@ +import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart'; + +/// A data model class that represents the "avoid returning widgets" input +/// parameters. +class AvoidUnusedParameters { + /// A list of methods that should be excluded from the lint. + final ExcludedIdentifiersListParameter exclude; + + /// Constructor for [AvoidUnusedParameters] model + AvoidUnusedParameters({ + required this.exclude, + }); + + /// Method for creating from json data + factory AvoidUnusedParameters.fromJson(Map json) { + return AvoidUnusedParameters( + exclude: ExcludedIdentifiersListParameter.defaultFromJson(json), + ); + } +} diff --git a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart index cc2949fb..a0a30fae 100644 --- a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart +++ b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart @@ -52,13 +52,19 @@ class CyclomaticComplexityRule CustomLintContext context, ) { context.registry.addBlockFunctionBody((node) { - final visitor = CyclomaticComplexityFlowVisitor(); - node.visitChildren(visitor); + context.registry.addDeclaration((declarationNode) { + final isIgnored = + config.parameters.exclude.shouldIgnore(declarationNode); + if (isIgnored) return; - if (visitor.complexityEntities.length + 1 > - config.parameters.maxComplexity) { - reporter.atNode(node, code); - } + final visitor = CyclomaticComplexityFlowVisitor(); + node.visitChildren(visitor); + + if (visitor.complexityEntities.length + 1 > + config.parameters.maxComplexity) { + reporter.atNode(node, code); + } + }); }); } } diff --git a/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart b/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart index 3ffa5ae1..93d3b2cd 100644 --- a/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart +++ b/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart @@ -1,19 +1,26 @@ +import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart'; + /// Cyclomatic complexity metric limits configuration. class CyclomaticComplexityParameters { /// Threshold cyclomatic complexity level, exceeding it triggers a warning. final int maxComplexity; + /// A list of methods that should be excluded from the lint. + final ExcludedIdentifiersListParameter exclude; + /// Reference: NIST 500-235 item 2.5 static const _defaultMaxComplexity = 10; /// Constructor for [CyclomaticComplexityParameters] model const CyclomaticComplexityParameters({ required this.maxComplexity, + required this.exclude, }); /// Method for creating from json data factory CyclomaticComplexityParameters.fromJson(Map json) => CyclomaticComplexityParameters( maxComplexity: json['max_complexity'] as int? ?? _defaultMaxComplexity, + exclude: ExcludedIdentifiersListParameter.defaultFromJson(json), ); } diff --git a/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart b/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart index c8d2623f..16593077 100644 --- a/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart +++ b/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart @@ -50,9 +50,15 @@ class FunctionLinesOfCodeRule ) { void checkNode(AstNode node) => _checkNode(resolver, reporter, node); - context.registry.addMethodDeclaration(checkNode); - context.registry.addFunctionDeclaration(checkNode); - context.registry.addFunctionExpression(checkNode); + context.registry.addDeclaration((declarationNode) { + final isIgnored = config.parameters.exclude.shouldIgnore(declarationNode); + + if (isIgnored) return; + + context.registry.addMethodDeclaration(checkNode); + context.registry.addFunctionDeclaration(checkNode); + context.registry.addFunctionExpression(checkNode); + }); } void _checkNode( @@ -60,12 +66,6 @@ class FunctionLinesOfCodeRule ErrorReporter reporter, AstNode node, ) { - final functionName = _getFunctionName(node); - if (functionName != null && - config.parameters.excludeNames.contains(functionName)) { - return; - } - final visitor = FunctionLinesOfCodeVisitor(resolver.lineInfo); node.visitChildren(visitor); @@ -84,16 +84,4 @@ class FunctionLinesOfCodeRule ); } } - - String? _getFunctionName(AstNode node) { - if (node is FunctionDeclaration) { - return node.name.lexeme; - } else if (node is MethodDeclaration) { - return node.name.lexeme; - } else if (node is FunctionExpression) { - return node.declaredElement?.name; - } else { - return null; - } - } } diff --git a/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart b/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart index 757f5baf..7df6da2a 100644 --- a/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart +++ b/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart @@ -1,3 +1,5 @@ +import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart'; + /// A data model class that represents the "function lines of code" input /// parameters. class FunctionLinesOfCodeParameters { @@ -5,22 +7,21 @@ class FunctionLinesOfCodeParameters { /// exceeding this limit triggers a warning. final int maxLines; - /// Function names to be excluded from the rule check - final List excludeNames; + /// A list of methods that should be excluded from the lint. + final ExcludedIdentifiersListParameter exclude; static const _defaultMaxLines = 200; /// Constructor for [FunctionLinesOfCodeParameters] model const FunctionLinesOfCodeParameters({ required this.maxLines, - required this.excludeNames, + required this.exclude, }); /// Method for creating from json data factory FunctionLinesOfCodeParameters.fromJson(Map json) => FunctionLinesOfCodeParameters( maxLines: json['max_lines'] as int? ?? _defaultMaxLines, - excludeNames: - List.from(json['excludeNames'] as Iterable? ?? []), + exclude: ExcludedIdentifiersListParameter.defaultFromJson(json), ); } diff --git a/lib/src/lints/no_empty_block/models/no_empty_block_parameters.dart b/lib/src/lints/no_empty_block/models/no_empty_block_parameters.dart new file mode 100644 index 00000000..42f1eb44 --- /dev/null +++ b/lib/src/lints/no_empty_block/models/no_empty_block_parameters.dart @@ -0,0 +1,20 @@ +import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart'; + +/// A data model class that represents the "no empty block" lint input +/// parameters. +class NoEmptyBlockParameters { + /// A list of methods that should be excluded from the lint. + final ExcludedIdentifiersListParameter exclude; + + /// Constructor for [NoEmptyBlockParameters] model + NoEmptyBlockParameters({ + required this.exclude, + }); + + /// Method for creating from json data + factory NoEmptyBlockParameters.fromJson(Map json) { + return NoEmptyBlockParameters( + exclude: ExcludedIdentifiersListParameter.defaultFromJson(json), + ); + } +} diff --git a/lib/src/lints/no_empty_block/no_empty_block_rule.dart b/lib/src/lints/no_empty_block/no_empty_block_rule.dart index fd469056..a30576f4 100644 --- a/lib/src/lints/no_empty_block/no_empty_block_rule.dart +++ b/lib/src/lints/no_empty_block/no_empty_block_rule.dart @@ -1,5 +1,6 @@ import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:solid_lints/src/lints/no_empty_block/models/no_empty_block_parameters.dart'; import 'package:solid_lints/src/lints/no_empty_block/visitors/no_empty_block_visitor.dart'; import 'package:solid_lints/src/models/rule_config.dart'; import 'package:solid_lints/src/models/solid_lint_rule.dart'; @@ -49,7 +50,7 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// } catch (_) {} // ignored by this rule /// } /// ``` -class NoEmptyBlockRule extends SolidLintRule { +class NoEmptyBlockRule extends SolidLintRule { /// This lint rule represents /// the error whether left empty block. static const String lintName = 'no_empty_block'; @@ -62,6 +63,7 @@ class NoEmptyBlockRule extends SolidLintRule { final config = RuleConfig( configs: configs, name: lintName, + paramsParser: NoEmptyBlockParameters.fromJson, problemMessage: (_) => 'Block is empty. Empty blocks are often indicators of missing code.', ); @@ -75,7 +77,10 @@ class NoEmptyBlockRule extends SolidLintRule { ErrorReporter reporter, CustomLintContext context, ) { - context.registry.addCompilationUnit((node) { + context.registry.addDeclaration((node) { + final isIgnored = config.parameters.exclude.shouldIgnore(node); + if (isIgnored) return; + final visitor = NoEmptyBlockVisitor(); node.accept(visitor); diff --git a/lib/src/lints/number_of_parameters/models/number_of_parameters_parameters.dart b/lib/src/lints/number_of_parameters/models/number_of_parameters_parameters.dart index e17e63d8..0ac631a4 100644 --- a/lib/src/lints/number_of_parameters/models/number_of_parameters_parameters.dart +++ b/lib/src/lints/number_of_parameters/models/number_of_parameters_parameters.dart @@ -1,19 +1,26 @@ +import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart'; + /// A data model class that represents the "number of parameters" input /// parameters. class NumberOfParametersParameters { /// Maximum number of parameters allowed before a warning is triggered. final int maxParameters; + /// A list of methods that should be excluded from the lint. + final ExcludedIdentifiersListParameter exclude; + static const _defaultMaxParameters = 2; /// Constructor for [NumberOfParametersParameters] model const NumberOfParametersParameters({ required this.maxParameters, + required this.exclude, }); /// Method for creating from json data factory NumberOfParametersParameters.fromJson(Map json) => NumberOfParametersParameters( maxParameters: json['max_parameters'] as int? ?? _defaultMaxParameters, + exclude: ExcludedIdentifiersListParameter.defaultFromJson(json), ); } diff --git a/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart b/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart index 8df3eea1..f03a9c7a 100644 --- a/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart +++ b/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart @@ -65,6 +65,7 @@ class NumberOfParametersRule CustomLintContext context, ) { context.registry.addDeclaration((node) { + final isIgnored = config.parameters.exclude.shouldIgnore(node); final parameters = switch (node) { (final MethodDeclaration node) => node.parameters?.parameters.length ?? 0, @@ -73,7 +74,7 @@ class NumberOfParametersRule _ => 0, }; - if (parameters > config.parameters.maxParameters) { + if (!isIgnored && parameters > config.parameters.maxParameters) { reporter.atOffset( offset: node.firstTokenAfterCommentAndMetadata.offset, length: node.end, diff --git a/lint_test/analysis_options.yaml b/lint_test/analysis_options.yaml index 3d8da192..10c18f33 100644 --- a/lint_test/analysis_options.yaml +++ b/lint_test/analysis_options.yaml @@ -6,23 +6,46 @@ custom_lint: rules: - cyclomatic_complexity: max_complexity: 4 + exclude: + - class_name: Exclude + method_name: excludeMethod + - method_name: excludeMethod - number_of_parameters: max_parameters: 2 + exclude: + - class_name: Exclude + method_name: avoidNumberOfParameters + - method_name: avoidNumberOfParameters - function_lines_of_code: max_lines: 50 - avoid_non_null_assertion - avoid_late_keyword: allow_initialized: true - avoid_global_state - - avoid_returning_widgets + - avoid_returning_widgets: + exclude: + - class_name: ExcludeWidget + method_name: excludeWidgetMethod + - method_name: excludeMethod - avoid_unnecessary_setstate - double_literal_format - avoid_unnecessary_type_assertions - avoid_unnecessary_type_casts - avoid_unrelated_type_assertions - - avoid_unused_parameters + - avoid_unused_parameters: + exclude: + - class_name: Exclude + method_name: excludeMethod + - method_name: excludeMethod + - simpleMethodName + - SimpleClassName + - exclude - newline_before_return - - no_empty_block + - no_empty_block: + exclude: + - class_name: Exclude + method_name: excludeMethod + - method_name: excludeMethod - no_equal_then_else - avoid_debug_print_in_release - prefer_early_return diff --git a/lint_test/avoid_returning_widget_test/analysis_options.yaml b/lint_test/avoid_returning_widget_test/analysis_options.yaml index 49cfe54e..0953ea1e 100644 --- a/lint_test/avoid_returning_widget_test/analysis_options.yaml +++ b/lint_test/avoid_returning_widget_test/analysis_options.yaml @@ -6,6 +6,6 @@ custom_lint: rules: - avoid_returning_widgets: exclude: - - method_name: excludeWidgetMethod - class_name: ExcludeWidget + - class_name: ExcludeWidget + method_name: excludeWidgetMethod - method_name: excludeMethod diff --git a/lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart b/lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart index a1f3677f..ad4398ba 100644 --- a/lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart +++ b/lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart @@ -65,7 +65,7 @@ Widget build() { return Offstage(); } -// expect_lint: avoid_returning_widgets +// no lint SizedBox excludeMethod() => const SizedBox(); class ExcludeWidget extends StatelessWidget { @@ -76,7 +76,7 @@ class ExcludeWidget extends StatelessWidget { return const Placeholder(); } - // expect_lint: avoid_returning_widgets + // no lint Widget excludeWidgetMethod() => const SizedBox(); // expect_lint: avoid_returning_widgets diff --git a/lint_test/avoid_unused_parameters_test.dart b/lint_test/avoid_unused_parameters_test.dart index b75e120f..b4eb7bd3 100644 --- a/lint_test/avoid_unused_parameters_test.dart +++ b/lint_test/avoid_unused_parameters_test.dart @@ -207,3 +207,37 @@ class UsingConstructorParameterInInitializer { print(_value); } } + +// no lint +void excludeMethod(String s) { + return; +} + +// no lint +void simpleMethodName(String s) { + return; +} + +class Exclude { + // no lint + void excludeMethod(String s) { + return; + } + +// expect_lint: avoid_unused_parameters + void excludeMethod2(String s) { + return; + } +} + +class SimpleClassName { + // no lint + void simpleMethodName(String s) { + return; + } + +// expect_lint: avoid_unused_parameters + void simpleMethodName2(String s) { + return; + } +} diff --git a/lint_test/avoid_unused_parameters_test/analysis_options.yaml b/lint_test/avoid_unused_parameters_test/analysis_options.yaml new file mode 100644 index 00000000..c90e9133 --- /dev/null +++ b/lint_test/avoid_unused_parameters_test/analysis_options.yaml @@ -0,0 +1,11 @@ +analyzer: + plugins: + - ../custom_lint + +custom_lint: + rules: + - avoid_unused_parameters: + exclude: + - class_name: Exclude + method_name: excludeMethod + - method_name: excludeMethod diff --git a/lint_test/cyclomatic_complexity_test.dart b/lint_test/cyclomatic_complexity_test.dart index 0fafcdab..116e1fe0 100644 --- a/lint_test/cyclomatic_complexity_test.dart +++ b/lint_test/cyclomatic_complexity_test.dart @@ -31,3 +31,27 @@ class A { if (true) {} } } + +// no lint +void excludeMethod() { + if (true) { + if (true) { + if (true) { + if (true) {} + } + } + } +} + +class Exclude { + // no lint + void excludeMethod() { + if (true) { + if (true) { + if (true) { + if (true) {} + } + } + } + } +} diff --git a/lint_test/function_lines_of_code_test/analysis_options.yaml b/lint_test/function_lines_of_code_test/analysis_options.yaml index 51f88abf..96985aa8 100644 --- a/lint_test/function_lines_of_code_test/analysis_options.yaml +++ b/lint_test/function_lines_of_code_test/analysis_options.yaml @@ -6,6 +6,6 @@ custom_lint: rules: - function_lines_of_code: max_lines: 5 - excludeNames: - - "longFunctionExcluded" - - "longMethodExcluded" + exclude: + - longFunctionExcluded + - longMethodExcluded diff --git a/lint_test/no_empty_block_test.dart b/lint_test/no_empty_block_test.dart index 385d30cb..96f2ef0d 100644 --- a/lint_test/no_empty_block_test.dart +++ b/lint_test/no_empty_block_test.dart @@ -49,3 +49,11 @@ class A { // TODO: implement toDoMethod } } + +// no lint +void excludeMethod() {} + +class Exclude { + // no lint + void excludeMethod() {} +} diff --git a/lint_test/number_of_parameters_test.dart b/lint_test/number_of_parameters_test.dart index 12fa6e21..40f9b5b8 100644 --- a/lint_test/number_of_parameters_test.dart +++ b/lint_test/number_of_parameters_test.dart @@ -1,7 +1,20 @@ -/// Check number of parameters fail +// ignore_for_file: prefer_match_file_name +// Check number of parameters fail /// /// `number_of_parameters: max_parameters` /// expect_lint: number_of_parameters String numberOfParameters(String a, String b, String c) { return a + b + c; } + +//no lint +String avoidNumberOfParameters(String a, String b, String c) { + return a + b + c; +} + +class Exclude { + //no lint + String avoidNumberOfParameters(String a, String b, String c) { + return a + b + c; + } +}