Skip to content

Instantly share code, notes, and snippets.

@MarcinusX
Last active March 6, 2023 09:48
Show Gist options
  • Save MarcinusX/9548f66251c4784aeb2de5eeaa7c9da5 to your computer and use it in GitHub Desktop.
Save MarcinusX/9548f66251c4784aeb2de5eeaa7c9da5 to your computer and use it in GitHub Desktop.

Revisions

  1. MarcinusX revised this gist Mar 6, 2023. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion timesamp_to_local_datetimes_linter.dart
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,10 @@ extension on MethodInvocation {

    bool get isFromTimestamp =>
    targetType != null &&
    TypeChecker.fromName('Timestamp').isAssignableFromType(targetType!);
    TypeChecker.fromName(
    'Timestamp',
    packageName: 'cheddar_api',
    ).isAssignableFromType(targetType!);

    bool get isToDateTime => methodName.name == 'toDateTime';

  2. MarcinusX created this gist Feb 28, 2023.
    106 changes: 106 additions & 0 deletions timesamp_to_local_datetimes_linter.dart
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    import 'package:analyzer/dart/ast/ast.dart';
    import 'package:analyzer/dart/element/type.dart';
    import 'package:analyzer/error/error.dart';
    import 'package:analyzer/error/listener.dart';
    import 'package:analyzer/source/source_range.dart';
    import 'package:custom_lint_builder/custom_lint_builder.dart';

    class PreferTimestampsToLocalDateTimes extends DartLintRule {
    PreferTimestampsToLocalDateTimes() : super(code: _code);

    static const _code = LintCode(
    name: 'timestamp_to_date_time_is_local',
    problemMessage:
    'Parsing Timestamp to DateTime should always be to local time',
    );

    @override
    void run(
    CustomLintResolver resolver,
    ErrorReporter reporter,
    CustomLintContext context,
    ) {
    context.registry.addMethodInvocation((node) {
    if (node.isFromTimestamp &&
    node.isToDateTime &&
    !node.hasArgumentToLocalSetToTrue) {
    reporter.reportErrorForNode(_code, node);
    }
    });
    }

    @override
    List<Fix> getFixes() => [_AddIsLocalFix()];
    }

    class _AddIsLocalFix extends DartFix {
    @override
    void run(
    CustomLintResolver resolver,
    ChangeReporter reporter,
    CustomLintContext context,
    AnalysisError analysisError,
    List<AnalysisError> others,
    ) {
    context.registry.addMethodInvocation((node) {
    if (!analysisError.sourceRange.intersects(node.sourceRange) ||
    !node.isFromTimestamp && !node.isToDateTime) {
    return;
    }
    final leftParenthesisOffset = node.argumentList.leftParenthesis.offset;
    final rightParenthesisOffset = node.argumentList.rightParenthesis.offset;
    final fix = 'toLocal: true';
    if (node.hasArgumentToLocalSetToFalse) {
    final changeBuilder = reporter.createChangeBuilder(
    message: 'Replace with "toLocal: true"',
    priority: 1,
    );
    changeBuilder.addDartFileEdit((builder) {
    builder.addSimpleReplacement(
    SourceRange(
    leftParenthesisOffset + 1,
    rightParenthesisOffset - leftParenthesisOffset - 1,
    ),
    fix,
    );
    });
    } else {
    final changeBuilder = reporter.createChangeBuilder(
    message: 'Add "toLocal: true"',
    priority: 1,
    );
    changeBuilder.addDartFileEdit((builder) {
    builder.addSimpleInsertion(
    leftParenthesisOffset + 1,
    fix,
    );
    });
    }
    });
    }
    }

    extension on MethodInvocation {
    DartType? get targetType => target?.staticType;

    bool get isFromTimestamp =>
    targetType != null &&
    TypeChecker.fromName('Timestamp').isAssignableFromType(targetType!);

    bool get isToDateTime => methodName.name == 'toDateTime';

    bool get hasArgumentToLocalSetToTrue => hasArgumentToLocalSetToValue(true);

    bool get hasArgumentToLocalSetToFalse => hasArgumentToLocalSetToValue(false);

    bool hasArgumentToLocalSetToValue(bool value) =>
    argumentList.arguments.any((argument) =>
    argument is NamedExpression &&
    argument.name.label.name == 'toLocal' &&
    argument.expression.isBooleanWithValue(value));
    }

    extension on Expression {
    bool isBooleanWithValue(bool value) =>
    this is BooleanLiteral && (this as BooleanLiteral).value == value;
    }