| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| |
| import '../analyzer.dart'; |
| |
| const _desc = 'Prefer int literals over double literals.'; |
| |
| class PreferIntLiterals extends LintRule { |
| PreferIntLiterals() |
| : super(name: LintNames.prefer_int_literals, description: _desc); |
| |
| @override |
| LintCode get lintCode => LinterLintCode.prefer_int_literals; |
| |
| @override |
| void registerNodeProcessors( |
| NodeLintRegistry registry, |
| LinterContext context, |
| ) { |
| registry.addDoubleLiteral(this, _Visitor(this)); |
| } |
| } |
| |
| class _Visitor extends SimpleAstVisitor<void> { |
| final LintRule rule; |
| |
| _Visitor(this.rule); |
| |
| /// Determine if the given literal can be replaced by an int literal. |
| bool canReplaceWithIntLiteral(DoubleLiteral literal) { |
| var parent = literal.parent; |
| if (parent is PrefixExpression) { |
| if (parent.operator.lexeme == '-') { |
| return hasTypeDouble(parent); |
| } else { |
| return false; |
| } |
| } |
| return hasTypeDouble(literal); |
| } |
| |
| bool hasReturnTypeDouble(AstNode? node) { |
| if (node is FunctionExpression) { |
| var functionDeclaration = node.parent; |
| if (functionDeclaration is FunctionDeclaration) { |
| return _isDartCoreDoubleTypeAnnotation(functionDeclaration.returnType); |
| } |
| } else if (node is MethodDeclaration) { |
| return _isDartCoreDoubleTypeAnnotation(node.returnType); |
| } |
| return false; |
| } |
| |
| bool hasTypeDouble(Expression expression) { |
| var parent = expression.parent; |
| if (parent is ArgumentList) { |
| return _isDartCoreDouble(expression.correspondingParameter?.type); |
| } else if (parent is ListLiteral) { |
| var typeArguments = parent.typeArguments?.arguments; |
| return typeArguments?.length == 1 && |
| _isDartCoreDoubleTypeAnnotation(typeArguments!.first); |
| } else if (parent is NamedExpression) { |
| var argList = parent.parent; |
| if (argList is ArgumentList) { |
| return _isDartCoreDouble(parent.correspondingParameter?.type); |
| } |
| } else if (parent is ExpressionFunctionBody) { |
| return hasReturnTypeDouble(parent.parent); |
| } else if (parent is ReturnStatement) { |
| var body = parent.thisOrAncestorOfType<BlockFunctionBody>(); |
| return body != null && hasReturnTypeDouble(body.parent); |
| } else if (parent is VariableDeclaration) { |
| var varList = parent.parent; |
| if (varList is VariableDeclarationList) { |
| return _isDartCoreDoubleTypeAnnotation(varList.type); |
| } |
| } |
| return false; |
| } |
| |
| @override |
| void visitDoubleLiteral(DoubleLiteral node) { |
| // Check if the double can be represented as an int |
| try { |
| var value = node.value; |
| if (value != value.truncate()) { |
| return; |
| } |
| // ignore: avoid_catching_errors |
| } on UnsupportedError catch (_) { |
| // The double cannot be represented as an int |
| return; |
| } |
| |
| // Ensure that replacing the double would not change the semantics |
| if (canReplaceWithIntLiteral(node)) { |
| rule.reportAtNode(node); |
| } |
| } |
| |
| bool _isDartCoreDouble(DartType? type) => type?.isDartCoreDouble ?? false; |
| |
| bool _isDartCoreDoubleTypeAnnotation(TypeAnnotation? annotation) => |
| _isDartCoreDouble(annotation?.type); |
| } |