blob: e3aba6bb03aa4b5b9d6ca140c8133c859d29f9eb [file] [log] [blame]
// Copyright (c) 2017, 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 '../analyzer.dart';
const _desc = r'Prefer putting asserts in initializer list.';
const _details = r'''
**DO** put asserts in initializer list for constructors with only asserts in
their body.
**GOOD:**
```
class A {
A(int a) : assert(a != null);
}
```
**BAD:**
```
class A {
A(int a) {
assert(a != null);
}
}
```
''';
class PreferAssertsInInitializerLists extends LintRule implements NodeLintRule {
PreferAssertsInInitializerLists()
: super(
name: 'prefer_asserts_in_initializer_lists',
description: _desc,
details: _details,
group: Group.style);
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
final visitor = _Visitor(this);
registry.addClassDeclaration(this, visitor);
registry.addConstructorDeclaration(this, visitor);
}
}
class _AssertVisitor extends RecursiveAstVisitor {
final ConstructorElement constructorElement;
final _ClassAndSuperClasses classAndSuperClasses;
bool needInstance = false;
_AssertVisitor(this.constructorElement, this.classAndSuperClasses);
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
final element = node.staticElement;
// use method
needInstance = needInstance ||
element is MethodElement && !element.isStatic && _hasMethod(element);
// use property accessor not used as field formal parameter
needInstance = needInstance ||
element is PropertyAccessorElement &&
!element.isStatic &&
_hasAccessor(element) &&
!constructorElement.parameters
.whereType<FieldFormalParameterElement>()
.any((p) => p.field.getter == element);
}
@override
void visitThisExpression(ThisExpression node) {
needInstance = true;
}
bool _hasAccessor(PropertyAccessorElement element) =>
classAndSuperClasses.classes.contains(element.enclosingElement);
bool _hasMethod(MethodElement element) =>
classAndSuperClasses.classes.contains(element.enclosingElement);
}
/// Lazy cache of elements.
class _ClassAndSuperClasses {
final ClassElement element;
final Set<ClassElement> _classes = {};
_ClassAndSuperClasses(this.element);
/// The [element] and its super classes, including mixins.
Set<ClassElement> get classes {
if (_classes.isEmpty) {
void addRecursively(ClassElement element) {
if (element != null && _classes.add(element)) {
element.mixins.forEach((t) => addRecursively(t.element));
addRecursively(element.supertype?.element);
}
}
addRecursively(element);
}
return _classes;
}
}
class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;
_ClassAndSuperClasses _classAndSuperClasses;
_Visitor(this.rule);
@override
void visitClassDeclaration(ClassDeclaration node) {
_classAndSuperClasses = _ClassAndSuperClasses(node.declaredElement);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
if (node.declaredElement.isFactory) return;
final body = node.body;
if (body is BlockFunctionBody) {
for (final statement in body.block.statements) {
if (statement is! AssertStatement) break;
final assertVisitor =
_AssertVisitor(node.declaredElement, _classAndSuperClasses);
statement.visitChildren(assertVisitor);
if (!assertVisitor.needInstance) {
rule.reportLintForToken(statement.beginToken);
}
}
}
}
}