Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

Commit

Permalink
add 3 lints for always specify types
Browse files Browse the repository at this point in the history
  • Loading branch information
a14n committed Sep 4, 2018
1 parent fa57d54 commit 36b8cf3
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 0 deletions.
3 changes: 3 additions & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ linter:
- always_put_control_body_on_new_line
- always_put_required_named_parameters_first
- always_require_non_null_named_parameters
- always_specify_type_annotations
- always_specify_type_arguments_for_classes
- always_specify_type_arguments_for_methods
- always_specify_types
- annotate_overrides
- avoid_annotating_with_dynamic
Expand Down
6 changes: 6 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import 'package:linter/src/rules/always_declare_return_types.dart';
import 'package:linter/src/rules/always_put_control_body_on_new_line.dart';
import 'package:linter/src/rules/always_put_required_named_parameters_first.dart';
import 'package:linter/src/rules/always_require_non_null_named_parameters.dart';
import 'package:linter/src/rules/always_specify_type_annotations.dart';
import 'package:linter/src/rules/always_specify_type_arguments_for_classes.dart';
import 'package:linter/src/rules/always_specify_type_arguments_for_methods.dart';
import 'package:linter/src/rules/always_specify_types.dart';
import 'package:linter/src/rules/annotate_overrides.dart';
import 'package:linter/src/rules/avoid_annotating_with_dynamic.dart';
Expand Down Expand Up @@ -134,6 +137,9 @@ void registerLintRules() {
..register(new AlwaysPutControlBodyOnNewLine())
..register(new AlwaysPutRequiredNamedParametersFirst())
..register(new AlwaysRequireNonNullNamedParameters())
..register(new AlwaysSpecifyTypeAnnotations())
..register(new AlwaysSpecifyTypeArgumentsForClasses())
..register(new AlwaysSpecifyTypeArgumentsForMethods())
..register(new AlwaysSpecifyTypes())
..register(new AnnotateOverrides())
..register(new AvoidAnnotatingWithDynamic())
Expand Down
80 changes: 80 additions & 0 deletions lib/src/rules/always_specify_type_annotations.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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:linter/src/analyzer.dart';
import 'package:linter/src/utils.dart';

const _desc = r'Specify type annotations.';

const _details = r'''
**DO** specify type annotations.
**GOOD:**
```
int foo = 10;
final Bar bar = new Bar();
String baz = 'hello';
const int quux = 20;
```
**BAD:**
```
var foo = 10;
final bar = new Bar();
const quux = 20;
```
''';

class AlwaysSpecifyTypeAnnotations extends LintRule implements NodeLintRule {
AlwaysSpecifyTypeAnnotations()
: super(
name: 'always_specify_type_annotations',
description: _desc,
details: _details,
group: Group.style);

@override
void registerNodeProcessors(NodeLintRegistry registry) {
final visitor = new _Visitor(this);
registry.addDeclaredIdentifier(this, visitor);
registry.addSimpleFormalParameter(this, visitor);
registry.addVariableDeclarationList(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;

_Visitor(this.rule);

@override
void visitDeclaredIdentifier(DeclaredIdentifier node) {
if (node.type == null) {
rule.reportLintForToken(node.keyword);
}
}

@override
void visitSimpleFormalParameter(SimpleFormalParameter param) {
if (param.type == null &&
param.identifier != null &&
!isJustUnderscores(param.identifier.name)) {
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);
}
}
}
131 changes: 131 additions & 0 deletions lib/src/rules/always_specify_type_arguments_for_classes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:linter/src/analyzer.dart';

const _desc = r'Specify type arguments for classes.';

const _details = r'''
**DO** specify type arguments for classes.
**GOOD:**
```
final a = <int>[];
final b = new List<int>();
final c = <int, String>{};
final d = new Map<int, String>();
```
**BAD:**
```
final a = [];
final b = new List();
final c = {};
final d = new Map();
```
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:
```
import 'package:meta/meta.dart';
@optionalTypeArgs
class Key<T> {
...
}
main() {
Key s = new Key(); // OK!
}
```
''';

/// The name of `meta` library, used to define analysis annotations.
String _META_LIB_NAME = 'meta';

/// The name of the top-level variable used to mark a Class as having optional
/// type args.
String _OPTIONAL_TYPE_ARGS_VAR_NAME = 'optionalTypeArgs';

bool _isOptionallyParameterized(ParameterizedType type) {
List<ElementAnnotation> metadata = type.element?.metadata;
if (metadata != null) {
return metadata
.any((ElementAnnotation a) => _isOptionalTypeArgs(a.element));
}
return false;
}

bool _isOptionalTypeArgs(Element element) =>
element is PropertyAccessorElement &&
element.name == _OPTIONAL_TYPE_ARGS_VAR_NAME &&
element.library?.name == _META_LIB_NAME;

class AlwaysSpecifyTypeArgumentsForClasses extends LintRule
implements NodeLintRule {
AlwaysSpecifyTypeArgumentsForClasses()
: super(
name: 'always_specify_type_arguments_for_classes',
description: _desc,
details: _details,
group: Group.style);

@override
void registerNodeProcessors(NodeLintRegistry registry) {
final visitor = new _Visitor(this);
registry.addListLiteral(this, visitor);
registry.addMapLiteral(this, visitor);
registry.addTypeName(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;

_Visitor(this.rule);

@override
void visitListLiteral(ListLiteral literal) {
_checkLiteral(literal);
}

@override
void visitMapLiteral(MapLiteral literal) {
_checkLiteral(literal);
}

void _checkLiteral(TypedLiteral literal) {
if (literal.typeArguments == null) {
rule.reportLintForToken(literal.beginToken);
}
}

// Future kernel API.
void visitNamedType(NamedType namedType) {
DartType type = namedType.type;
if (type is ParameterizedType) {
if (type.typeParameters.isNotEmpty &&
namedType.typeArguments == null &&
namedType.parent is! IsExpression &&
!_isOptionallyParameterized(type)) {
rule.reportLint(namedType);
}
}
}

@override
void visitTypeName(NamedType typeName) {
visitNamedType(typeName);
}
}
91 changes: 91 additions & 0 deletions lib/src/rules/always_specify_type_arguments_for_methods.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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:linter/src/analyzer.dart';

const _desc = r'Specify type arguments for methods.';

const _details = r'''
**DO** specify type arguments for methods.
**GOOD:**
```
void m<T>(T t1, T t2) {}
m<int>(1, 2);
```
**BAD:**
```
void m<T>(T t1, T t2) {}
m(1, 2);
```
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
`m` function whose type parameter you'd like to treat as optional. Using the
`@optionalTypeArgs` would look like this:
```
import 'package:meta/meta.dart';
@optionalTypeArgs
void m<T>(T t1, T t2) {}
main() {
m(1, 2); // OK!
}
```
''';

/// The name of `meta` library, used to define analysis annotations.
String _META_LIB_NAME = 'meta';

/// The name of the top-level variable used to mark a Class as having optional
/// type args.
String _OPTIONAL_TYPE_ARGS_VAR_NAME = 'optionalTypeArgs';

bool _isOptionalTypeArgs(Element element) =>
element is PropertyAccessorElement &&
element.name == _OPTIONAL_TYPE_ARGS_VAR_NAME &&
element.library?.name == _META_LIB_NAME;

class AlwaysSpecifyTypeArgumentsForMethods extends LintRule
implements NodeLintRule {
AlwaysSpecifyTypeArgumentsForMethods()
: super(
name: 'always_specify_type_arguments_for_methods',
description: _desc,
details: _details,
group: Group.style);

@override
void registerNodeProcessors(NodeLintRegistry registry) {
final visitor = new _Visitor(this);
registry.addMethodInvocation(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;

_Visitor(this.rule);

@override
void visitMethodInvocation(MethodInvocation node) {
if (node.typeArguments == null) {
final element = node.methodName.bestElement;
if (element is FunctionTypedElement &&
element.typeParameters.isNotEmpty &&
!element.metadata.any((a) => _isOptionalTypeArgs(a.element))) {
rule.reportLint(node.methodName);
}
}
}
}
55 changes: 55 additions & 0 deletions test/rules/always_specify_type_annotations.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.

// test w/ `pub run test -N always_specify_type_annotations`

final x = 1; //LINT [1:5]
final int xx = 3;
const y = 2; //LINT
const int yy = 3;

a(var x) {} //LINT
b(s) {} //LINT [3:1]
c(int x) {}
d(final x) {} //LINT
e(final int x) {}

main() {
var x = ''; //LINT [3:3]
for (var i = 0; i < 10; ++i) { //LINT [8:3]
print(i);
}
List<String> ls = <String>[];
ls.forEach((s) => print(s)); //LINT [15:1]
for (var l in ls) { //LINT [8:3]
print(l);
}
try {
for (final l in ls) { // LINT [10:5]
print(l);
}
} on Exception catch (ex) {
print(ex);
} catch (e) { // NO warning (https://codereview.chromium.org/1427223002/)
print(e);
}

var __; // LINT

listen((_) { // OK!
// ...
});
}

listen(void onData(Object event)) {}

var z; //LINT

class Foo {
static var bar; //LINT
static final baz = 1; //LINT
static final int bazz = 42;
var foo; //LINT
Foo(var bar); //LINT [7:3]
}
Loading

0 comments on commit 36b8cf3

Please sign in to comment.