blob: 69ebd81fd3d802d79f1c06df8a20208110373738 [file] [log] [blame]
// 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/element.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 [style guide for the flutter repo](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`.
**BAD:**
```dart
var foo = 10;
final bar = Bar();
const quux = 20;
```
**GOOD:**
```dart
int foo = 10;
final Bar bar = Bar();
String baz = 'hello';
const int 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 {
static const LintCode code = LintCode(
'always_specify_types', 'Missing type annotation.',
correctionMessage: 'Try adding a type annotation.');
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
LintCode get lintCode => code;
@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 = namedType.name.staticElement;
if (element is TypeParameterizedElement &&
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 name = param.name;
if (name != null && param.type == null && !name.lexeme.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);
}
}
}