| // Copyright (c) 2015, 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'; |
| import '../util/ascii_utils.dart'; |
| |
| const _desc = r'Specify type annotations.'; |
| |
| const _details = r''' |
| |
| From the [flutter style guide](https://flutter.dev/style-guide/): |
| |
| **DO** specify type annotations. |
| |
| Avoid `var` when specifying that a type is unknown and short-hands that elide |
| type annotations. Use `dynamic` if you are being explicit that the type is |
| unknown. Use `Object` if you are being explicit that you want an object that |
| implements `==` and `hashCode`. |
| |
| **GOOD:** |
| ```dart |
| int foo = 10; |
| final Bar bar = Bar(); |
| String baz = 'hello'; |
| const int quux = 20; |
| ``` |
| |
| **BAD:** |
| ```dart |
| var foo = 10; |
| final bar = Bar(); |
| const quux = 20; |
| ``` |
| |
| NOTE: Using the the `@optionalTypeArgs` annotation in the `meta` package, API |
| authors can special-case type variables whose type needs to by dynamic but whose |
| declaration should be treated as optional. For example, suppose you have a |
| `Key` object whose type parameter you'd like to treat as optional. Using the |
| `@optionalTypeArgs` would look like this: |
| |
| ```dart |
| import 'package:meta/meta.dart'; |
| |
| @optionalTypeArgs |
| class Key<T> { |
| ... |
| } |
| |
| main() { |
| Key s = Key(); // OK! |
| } |
| ``` |
| |
| '''; |
| |
| class AlwaysSpecifyTypes extends LintRule { |
| AlwaysSpecifyTypes() |
| : super( |
| name: 'always_specify_types', |
| description: _desc, |
| details: _details, |
| group: Group.style); |
| |
| @override |
| List<String> get incompatibleRules => |
| const ['avoid_types_on_closure_parameters', 'omit_local_variable_types']; |
| |
| @override |
| void registerNodeProcessors( |
| NodeLintRegistry registry, LinterContext context) { |
| var visitor = _Visitor(this); |
| registry.addDeclaredIdentifier(this, visitor); |
| registry.addListLiteral(this, visitor); |
| registry.addSetOrMapLiteral(this, visitor); |
| registry.addSimpleFormalParameter(this, visitor); |
| registry.addNamedType(this, visitor); |
| registry.addVariableDeclarationList(this, visitor); |
| } |
| } |
| |
| class _Visitor extends SimpleAstVisitor<void> { |
| final LintRule rule; |
| |
| _Visitor(this.rule); |
| |
| void checkLiteral(TypedLiteral literal) { |
| if (literal.typeArguments == null) { |
| rule.reportLintForToken(literal.beginToken); |
| } |
| } |
| |
| @override |
| void visitDeclaredIdentifier(DeclaredIdentifier node) { |
| if (node.type == null) { |
| rule.reportLintForToken(node.keyword); |
| } |
| } |
| |
| @override |
| void visitListLiteral(ListLiteral literal) { |
| checkLiteral(literal); |
| } |
| |
| @override |
| void visitNamedType(NamedType namedType) { |
| var type = namedType.type; |
| if (type is InterfaceType) { |
| var element = type.alias?.element ?? type.element; |
| if (element.typeParameters.isNotEmpty && |
| namedType.typeArguments == null && |
| namedType.parent is! IsExpression && |
| !element.hasOptionalTypeArgs) { |
| rule.reportLint(namedType); |
| } |
| } |
| } |
| |
| @override |
| void visitSetOrMapLiteral(SetOrMapLiteral literal) { |
| checkLiteral(literal); |
| } |
| |
| @override |
| void visitSimpleFormalParameter(SimpleFormalParameter param) { |
| var identifier = param.identifier; |
| if (identifier != null && |
| param.type == null && |
| !identifier.name.isJustUnderscores) { |
| if (param.keyword != null) { |
| rule.reportLintForToken(param.keyword); |
| } else { |
| rule.reportLint(param); |
| } |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationList(VariableDeclarationList list) { |
| if (list.type == null) { |
| rule.reportLintForToken(list.keyword); |
| } |
| } |
| } |