Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
}
}
145 changes: 86 additions & 59 deletions lib/src/lints/double_literal_format/double_literal_format_rule.dart
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -25,75 +22,105 @@ 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<DiagnosticCode> 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 `.`, '
'should not end with a trailing 0 and '
'should not start with a leading 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<Fix> getFixes() => [_DoubleLiteralFormatFix()];
}
Original file line number Diff line number Diff line change
@@ -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] != '.';

Expand All @@ -10,7 +11,7 @@ extension _StringDoubleEx 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') &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,84 @@
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<Diagnostic> 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<void> 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,
);
});
}

String _correctLeadingZeroLexeme(String lexeme) => !lexeme.hasLeadingZero
? 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.toLowerCase().split('e').first;

return _correctTrailingZeroLexeme(
lexeme.replaceFirst(
mantissa,
mantissa.substring(0, mantissa.length - 1),
),
);
}
Comment thread
andrew-bekhiet-solid marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -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<void> {
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;
}
}
}
Loading
Loading