From 348b76a96af75e82941bdf3fb25bfc4154789815 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 9 Jun 2026 18:48:47 +0300 Subject: [PATCH 1/3] refactor: migrate double_literal_format to analysis_server_plugin docs: improve code docs --- lib/main.dart | 8 + .../double_literal_format_rule.dart | 144 +++++++++++------- .../double_literal_format_utils.dart | 7 +- .../fixes/double_literal_format_fix.dart | 106 +++++++------ .../double_literal_format_visitor.dart | 46 ++++++ lint_test/double_literal_format_test.dart | 38 ----- pubspec.yaml | 4 +- .../double_literal_format_rule_test.dart | 128 ++++++++++++++++ 8 files changed, 337 insertions(+), 144 deletions(-) create mode 100644 lib/src/lints/double_literal_format/visitors/double_literal_format_visitor.dart delete mode 100644 lint_test/double_literal_format_test.dart create mode 100644 test/lints/double_literal_format/double_literal_format_rule_test.dart diff --git a/lib/main.dart b/lib/main.dart index 5657b7a1..010dcaff 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,8 @@ import 'package:analysis_server_plugin/registry.dart'; import 'package:solid_lints/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart'; import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; import 'package:solid_lints/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart'; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart'; +import 'package:solid_lints/src/lints/double_literal_format/fixes/double_literal_format_fix.dart'; import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart'; /// The entry point for the Solid Lints analyser server plugin. @@ -33,5 +35,11 @@ class SolidLintsPlugin extends Plugin { registry.registerLintRule( ProperSuperCallsRule(), ); + + final doubleLiteralFormatRule = DoubleLiteralFormatRule(); + registry.registerLintRule(doubleLiteralFormatRule); + for (final code in doubleLiteralFormatRule.diagnosticCodes) { + registry.registerFixForRule(code, DoubleLiteralFormatFix.new); + } } } diff --git a/lib/src/lints/double_literal_format/double_literal_format_rule.dart b/lib/src/lints/double_literal_format/double_literal_format_rule.dart index d472d04c..3a1020d1 100644 --- a/lib/src/lints/double_literal_format/double_literal_format_rule.dart +++ b/lib/src/lints/double_literal_format/double_literal_format_rule.dart @@ -1,17 +1,14 @@ -import 'package:analyzer/diagnostic/diagnostic.dart'; -import 'package:analyzer/error/listener.dart'; -import 'package:analyzer/source/source_range.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:solid_lints/src/models/rule_config.dart'; -import 'package:solid_lints/src/models/solid_lint_rule.dart'; - -part 'double_literal_format_utils.dart'; -part 'fixes/double_literal_format_fix.dart'; +import 'package:analyzer/analysis_rule/analysis_rule.dart'; +import 'package:analyzer/analysis_rule/rule_context.dart'; +import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:solid_lints/src/lints/double_literal_format/visitors/double_literal_format_visitor.dart'; /// A `double_literal_format` rule which /// checks that double literals should begin with 0. instead of just ., /// and should not end with a trailing 0. /// +/// {@template solid_lints.double_literal_format.example} /// ### Example /// /// #### BAD: @@ -25,75 +22,104 @@ part 'fixes/double_literal_format_fix.dart'; /// ```dart /// var a = 5.23, b = 0.16e+5, c = -0.25, d = -0.4e-5; /// ``` -class DoubleLiteralFormatRule extends SolidLintRule { +/// {@endtemplate} +class DoubleLiteralFormatRule extends MultiAnalysisRule { /// This lint rule represents /// the error whether we use bad formatted double literals. static const lintName = 'double_literal_format'; // Use different messages for different issues - /// This lint rule represents - /// the error whether we use double literals with a redundant leading 0. - static const _leadingZeroCode = LintCode( - name: lintName, - problemMessage: "Double literals shouldn't have redundant leading `0`.", + /// Reported when the double literal has a redundant leading 0 + /// + /// ### Example + /// + /// #### BAD: + /// + /// ```dart + /// var a = 05.23; + /// ``` + /// + /// #### GOOD: + /// + /// ```dart + /// var a = 5.23; + /// ``` + static const leadingZeroCode = LintCode( + lintName, + "Double literals shouldn't have redundant leading `0`.", correctionMessage: "Remove redundant leading `0`.", + uniqueName: 'leadingZero', ); - /// This lint rule represents - /// the error whether we use double literals with a leading decimal point. - static const _leadingDecimalCode = LintCode( - name: lintName, - problemMessage: - "Double literals shouldn't begin with the decimal point `.`.", + /// Reported when the double literal has a leading decimal point + /// without a zero before it. + /// + /// ### Example + /// + /// #### BAD: + /// + /// ```dart + /// var a = .23; + /// ``` + /// + /// #### GOOD: + /// + /// ```dart + /// var a = 0.23; + /// ``` + static const leadingDecimalCode = LintCode( + lintName, + "Double literals shouldn't begin with the decimal point `.`.", correctionMessage: "Add missing leading `0`.", + uniqueName: 'leadingDecimal', ); - /// This lint rule represents - /// the error whether we use double literals with a trailing 0. - static const _trailingZeroCode = LintCode( - name: lintName, - problemMessage: "Double literals should not end with a trailing `0`.", + /// Reported when the double literal has a redundant trailing 0. + /// + /// ### Example + /// + /// #### BAD: + /// + /// ```dart + /// var a = 5.230; + /// ``` + /// + /// #### GOOD: + /// + /// ```dart + /// var a = 5.23; + /// ``` + static const trailingZeroCode = LintCode( + lintName, + "Double literals should not end with a trailing `0`.", correctionMessage: "Remove redundant trailing `0`.", + uniqueName: 'trailingZero', ); - DoubleLiteralFormatRule._(super.config); + @override + List get diagnosticCodes => [ + leadingZeroCode, + leadingDecimalCode, + trailingZeroCode, + ]; /// Creates a new instance of [DoubleLiteralFormatRule] - /// based on the lint configuration. - factory DoubleLiteralFormatRule.createRule(CustomLintConfigs configs) { - final rule = RuleConfig( - configs: configs, - name: lintName, - problemMessage: (_) => 'Double literal formatting issue', - ); - - return DoubleLiteralFormatRule._(rule); - } + DoubleLiteralFormatRule() + : super( + name: lintName, + description: + 'Double literals should begin with `0.` instead of just `.`, ' + 'and should not end with a trailing 0', + ); @override - void run( - CustomLintResolver resolver, - DiagnosticReporter reporter, - CustomLintContext context, + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, ) { - context.registry.addDoubleLiteral((node) { - final lexeme = node.literal.lexeme; + super.registerNodeProcessors(registry, context); - if (lexeme.hasLeadingZero) { - reporter.atNode(node, _leadingZeroCode); - return; - } - if (lexeme.hasLeadingDecimalPoint) { - reporter.atNode(node, _leadingDecimalCode); - return; - } - if (lexeme.hasTrailingZero) { - reporter.atNode(node, _trailingZeroCode); - return; - } - }); + final visitor = DoubleLiteralFormatVisitor(this); + registry.addDoubleLiteral(this, visitor); } - - @override - List getFixes() => [_DoubleLiteralFormatFix()]; } diff --git a/lib/src/lints/double_literal_format/double_literal_format_utils.dart b/lib/src/lints/double_literal_format/double_literal_format_utils.dart index 99e7166e..beb2012c 100644 --- a/lib/src/lints/double_literal_format/double_literal_format_utils.dart +++ b/lib/src/lints/double_literal_format/double_literal_format_utils.dart @@ -1,7 +1,8 @@ -part of 'double_literal_format_rule.dart'; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart'; -/// Useful extensions for double literals representation -extension _StringDoubleEx on String { +/// Extension to quickly check double literal formatting according to +/// [DoubleLiteralFormatRule]. +extension DoubleLiteralFormatUtils on String { /// Returns true if a double literal starts with 00 bool get hasLeadingZero => startsWith('0') && this[1] != '.'; diff --git a/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart b/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart index b3b5af12..03ca7e54 100644 --- a/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart +++ b/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart @@ -1,44 +1,62 @@ -part of '../double_literal_format_rule.dart'; +import 'package:analysis_server_plugin/edit/dart/correction_producer.dart'; +import 'package:analysis_server_plugin/edit/dart/dart_fix_kind_priority.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; +import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart'; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_utils.dart'; -/// A Quick fix for `double_literal_format` rule +/// A Quick fix for [DoubleLiteralFormatRule] rule /// Suggests the correct value for an issue -class _DoubleLiteralFormatFix extends DartFix { +class DoubleLiteralFormatFix extends ParsedCorrectionProducer { + static const _doubleLiteralFormatKind = FixKind( + 'solid_lints.fix.${DoubleLiteralFormatRule.lintName}', + DartFixKindPriority.standard, + "Fix double literal format", + ); + + /// Creates a new instance of [DoubleLiteralFormatFix]. + DoubleLiteralFormatFix({required super.context}); + + @override + FixKind get fixKind => _doubleLiteralFormatKind; + @override - void run( - CustomLintResolver resolver, - ChangeReporter reporter, - CustomLintContext context, - Diagnostic analysisError, - List others, - ) { - context.registry.addDoubleLiteral((node) { - // checks that the literal declaration is where our warning is located - if (!analysisError.sourceRange.intersects(node.sourceRange)) return; + FixKind get multiFixKind => const FixKind( + 'solid_lints.fix.multi.${DoubleLiteralFormatRule.lintName}', + DartFixKindPriority.standard, + "Fix double literal format across files", + ); - final lexeme = node.literal.lexeme; - String? correctLexeme; + @override + CorrectionApplicability get applicability => + CorrectionApplicability.automatically; + + @override + Future compute(ChangeBuilder builder) async { + final doubleLiteralNode = node; + if (doubleLiteralNode is! DoubleLiteral) return; - if (lexeme.hasLeadingZero) { - correctLexeme = _correctLeadingZeroLexeme(lexeme); - } else if (lexeme.hasLeadingDecimalPoint) { - correctLexeme = _correctLeadingDecimalPointLexeme(lexeme); - } else if (lexeme.hasTrailingZero) { - correctLexeme = _correctTrailingZeroLexeme(lexeme); - } + final lexeme = doubleLiteralNode.literal.lexeme; + if (!lexeme.hasLeadingZero && + !lexeme.hasLeadingDecimalPoint && + !lexeme.hasTrailingZero) { + return; + } - if (correctLexeme != null) { - final changeBuilder = reporter.createChangeBuilder( - message: 'Replace by $correctLexeme', - priority: 1, - ); + final correctLexeme = _correctTrailingZeroLexeme( + _correctLeadingZeroLexeme( + _correctLeadingDecimalPointLexeme( + lexeme, + ), + ), + ); - changeBuilder.addDartFileEdit((builder) { - builder.addSimpleReplacement( - SourceRange(node.offset, node.length), - correctLexeme!, - ); - }); - } + await builder.addDartFileEdit(file, (builder) { + builder.addSimpleReplacement( + doubleLiteralNode.sourceRange, + correctLexeme, + ); }); } @@ -46,19 +64,21 @@ class _DoubleLiteralFormatFix extends DartFix { ? lexeme : _correctLeadingZeroLexeme(lexeme.substring(1)); - String _correctLeadingDecimalPointLexeme(String lexeme) => '0$lexeme'; + String _correctLeadingDecimalPointLexeme(String lexeme) => + lexeme.hasLeadingDecimalPoint ? '0$lexeme' : lexeme; String _correctTrailingZeroLexeme(String lexeme) { if (!lexeme.hasTrailingZero) { return lexeme; - } else { - final mantissa = lexeme.split('e').first; - return _correctTrailingZeroLexeme( - lexeme.replaceFirst( - mantissa, - mantissa.substring(0, mantissa.length - 1), - ), - ); } + + final mantissa = lexeme.split('e').first; + + return _correctTrailingZeroLexeme( + lexeme.replaceFirst( + mantissa, + mantissa.substring(0, mantissa.length - 1), + ), + ); } } diff --git a/lib/src/lints/double_literal_format/visitors/double_literal_format_visitor.dart b/lib/src/lints/double_literal_format/visitors/double_literal_format_visitor.dart new file mode 100644 index 00000000..fa6e4e96 --- /dev/null +++ b/lib/src/lints/double_literal_format/visitors/double_literal_format_visitor.dart @@ -0,0 +1,46 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart' + show DoubleLiteralFormatRule; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_utils.dart'; + +/// A visitor that checks that double literals are formatted according to +/// [DoubleLiteralFormatRule]. +/// {@macro solid_lints.double_literal_format.example} +class DoubleLiteralFormatVisitor extends SimpleAstVisitor { + final DoubleLiteralFormatRule _rule; + + /// Creates a new instance of [DoubleLiteralFormatVisitor]. + DoubleLiteralFormatVisitor(this._rule); + + @override + void visitDoubleLiteral(DoubleLiteral node) { + super.visitDoubleLiteral(node); + + final lexeme = node.literal.lexeme; + + if (lexeme.hasLeadingZero) { + _rule.reportAtNode( + node, + diagnosticCode: DoubleLiteralFormatRule.leadingZeroCode, + ); + return; + } + + if (lexeme.hasLeadingDecimalPoint) { + _rule.reportAtNode( + node, + diagnosticCode: DoubleLiteralFormatRule.leadingDecimalCode, + ); + return; + } + + if (lexeme.hasTrailingZero) { + _rule.reportAtNode( + node, + diagnosticCode: DoubleLiteralFormatRule.trailingZeroCode, + ); + return; + } + } +} diff --git a/lint_test/double_literal_format_test.dart b/lint_test/double_literal_format_test.dart deleted file mode 100644 index 195b01e8..00000000 --- a/lint_test/double_literal_format_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// ignore_for_file: avoid_global_state -// ignore_for_file: type_annotate_public_apis -// ignore_for_file: unused_local_variable - -/// Check the `double_literal_format` rule - -// expect_lint: double_literal_format -var badA = 05.23; -var goodA = 5.23; -var stringA = '05.23'; - -var intA = 0; - -// expect_lint: double_literal_format -double badB = -01.2; -double goodB = -1.2; - -// expect_lint: double_literal_format -double badC = -001.2; - -// expect_lint: double_literal_format -double badExpr = 5.23 + 05.23; - -double goodExpr = 5.23 + 5.23; - -class DoubleLiteralFormatTest { - // expect_lint: double_literal_format - var badA = .16e+5; - var goodA = 0.16e+5; - - void someMethod() { - // expect_lint: double_literal_format - const badA = -0.250; - const goodA = -0.25; - // expect_lint: double_literal_format - const badB = 0.160e+5; - } -} diff --git a/pubspec.yaml b/pubspec.yaml index df5633f6..7b7dd9b2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,9 @@ environment: sdk: ">=3.9.0 <4.0.0" dependencies: + # Needed until required types for fixes are exported by analyzer_server_plugin + # More details: https://github.com/dart-lang/sdk/issues/61821 + analyzer_plugin: ^0.14.2 analyzer: ^10.0.1 collection: ^1.19.0 analysis_server_plugin: ^0.3.3 @@ -23,4 +26,3 @@ dev_dependencies: args: ^2.6.0 analyzer_testing: ^0.1.9 test_reflective_loader: ^0.3.0 - diff --git a/test/lints/double_literal_format/double_literal_format_rule_test.dart b/test/lints/double_literal_format/double_literal_format_rule_test.dart new file mode 100644 index 00000000..3338b090 --- /dev/null +++ b/test/lints/double_literal_format/double_literal_format_rule_test.dart @@ -0,0 +1,128 @@ +import 'package:analyzer_testing/analysis_rule/analysis_rule.dart'; +import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(DoubleLiteralFormatRuleTest); + }); +} + +@reflectiveTest +class DoubleLiteralFormatRuleTest extends AnalysisRuleTest { + @override + void setUp() { + rule = DoubleLiteralFormatRule(); + super.setUp(); + } + + Future test_reports_on_leading_zeros() async { + await assertDiagnostics( + r''' +var badA = 05.23; +double badB = -01.2; +double badC = -001.2; +double badExpr = 5.23 + 05.23; + +class Test { + var badA = 05.23; + double badB = -01.2; + double badC = -001.2; + double badExpr = 5.23 + 05.23; +} +''', + [ + lint(11, 5), + lint(33, 4), + lint(54, 5), + lint(85, 5), + + lint(119, 5), + lint(143, 4), + lint(166, 5), + lint(199, 5), + ], + ); + } + + Future test_reports_on_trailing_zeros() async { + await assertDiagnostics( + r''' +class Test { + var badA = 5.230; + final badB = -1.20; + double get badC => -1.200; + double badExpr = 5.23 + 5.230; + + void someMethod() { + var badA = 5.230; + double badB = -1.20; + double badC = -1.200; + double badExpr = 5.23 + 5.230; + } +} +''', + [ + lint(26, 5), + lint(49, 4), + lint(77, 5), + lint(110, 5), + + lint(157, 5), + lint(183, 4), + lint(208, 5), + lint(243, 5), + ], + ); + } + + Future test_reports_on_leading_decimal_point() async { + await assertDiagnostics( + r''' +var badA = .23; +double badB = -.2; +double badExpr = 5.23 + .23; + +class Test { + var badA = .23; + double badB = -.2; + double get badExpr => 5.23 + .23; +} +''', + [ + lint(11, 3), + lint(31, 2), + lint(59, 3), + + lint(91, 3), + lint(113, 2), + lint(148, 3), + ], + ); + } + + void test_does_not_report_on_non_double_literals() async { + await assertNoDiagnostics(r''' +var badA = '05.23'; +var stringA = '5.23'; +final badB = '.04'; +var intA = 0; +'''); + } + + Future test_does_not_report_on_good_literals() async { + await assertNoDiagnostics(r''' +var goodA = 5.23; +double goodB = -1.2; + +double goodExpr = 5.23 + 5.23; +class DoubleLiteralFormatTest { + var goodA = 0.16e+5; + + void someMethod() { + const goodA = -0.25; + } +} +'''); + } +} From 37578826f7615a592b32fc2d11fa138e358bc8da Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 9 Jun 2026 19:00:48 +0300 Subject: [PATCH 2/3] fix: handle literals with capital E test: handle exponentials in test cases --- .../double_literal_format_utils.dart | 2 +- .../fixes/double_literal_format_fix.dart | 2 +- .../double_literal_format_rule_test.dart | 27 +++++++++++++------ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/src/lints/double_literal_format/double_literal_format_utils.dart b/lib/src/lints/double_literal_format/double_literal_format_utils.dart index beb2012c..9b9d75b6 100644 --- a/lib/src/lints/double_literal_format/double_literal_format_utils.dart +++ b/lib/src/lints/double_literal_format/double_literal_format_utils.dart @@ -11,7 +11,7 @@ extension DoubleLiteralFormatUtils on String { /// Returns true if a mantissa of a double literal ends with 0 bool get hasTrailingZero { - final mantissa = split('e').first; + final mantissa = toLowerCase().split('e').first; return mantissa.contains('.') && mantissa.endsWith('0') && diff --git a/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart b/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart index 03ca7e54..9b7e0426 100644 --- a/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart +++ b/lib/src/lints/double_literal_format/fixes/double_literal_format_fix.dart @@ -72,7 +72,7 @@ class DoubleLiteralFormatFix extends ParsedCorrectionProducer { return lexeme; } - final mantissa = lexeme.split('e').first; + final mantissa = lexeme.toLowerCase().split('e').first; return _correctTrailingZeroLexeme( lexeme.replaceFirst( diff --git a/test/lints/double_literal_format/double_literal_format_rule_test.dart b/test/lints/double_literal_format/double_literal_format_rule_test.dart index 3338b090..d3c8c2a7 100644 --- a/test/lints/double_literal_format/double_literal_format_rule_test.dart +++ b/test/lints/double_literal_format/double_literal_format_rule_test.dart @@ -53,12 +53,14 @@ class Test { final badB = -1.20; double get badC => -1.200; double badExpr = 5.23 + 5.230; - + var badD = -0.400e-5; + void someMethod() { var badA = 5.230; double badB = -1.20; double badC = -1.200; double badExpr = 5.23 + 5.230; + var badD = -0.400E-5; } } ''', @@ -67,11 +69,13 @@ class Test { lint(49, 4), lint(77, 5), lint(110, 5), + lint(131, 8), - lint(157, 5), - lint(183, 4), - lint(208, 5), - lint(243, 5), + lint(179, 5), + lint(205, 4), + lint(230, 5), + lint(265, 5), + lint(288, 8), ], ); } @@ -82,21 +86,25 @@ class Test { var badA = .23; double badB = -.2; double badExpr = 5.23 + .23; +var badD = .4e-5; class Test { var badA = .23; double badB = -.2; double get badExpr => 5.23 + .23; + double get badD => -.4E-5; } ''', [ lint(11, 3), lint(31, 2), lint(59, 3), + lint(75, 5), - lint(91, 3), - lint(113, 2), - lint(148, 3), + lint(109, 3), + lint(131, 2), + lint(166, 3), + lint(193, 5), ], ); } @@ -118,6 +126,9 @@ double goodB = -1.2; double goodExpr = 5.23 + 5.23; class DoubleLiteralFormatTest { var goodA = 0.16e+5; + var goodB = 0.16E+5; + double goodC = -0.4E-5; + double goodE = 5.23 + 0.4e-5; void someMethod() { const goodA = -0.25; From e4d17264fb456668375d66602ef498d59a319d89 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Wed, 10 Jun 2026 17:27:02 +0300 Subject: [PATCH 3/3] fix: include unnecessary leading zeroes in lint description --- .../double_literal_format/double_literal_format_rule.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/lints/double_literal_format/double_literal_format_rule.dart b/lib/src/lints/double_literal_format/double_literal_format_rule.dart index 3a1020d1..d90d3726 100644 --- a/lib/src/lints/double_literal_format/double_literal_format_rule.dart +++ b/lib/src/lints/double_literal_format/double_literal_format_rule.dart @@ -109,7 +109,8 @@ class DoubleLiteralFormatRule extends MultiAnalysisRule { name: lintName, description: 'Double literals should begin with `0.` instead of just `.`, ' - 'and should not end with a trailing 0', + 'should not end with a trailing 0 and ' + 'should not start with a leading 0', ); @override