blob: 20267a9e100d7b2ea0a6259e965b456fdf93e087 [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';
import '../ast.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:**
```dart
class A {
A(int a) : assert(a != null);
}
```
**BAD:**
```dart
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) {
var 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) {
var element = getWriteOrReadElement(node);
// 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) {
var classes = classAndSuperClasses?.classes;
return classes != null && classes.contains(element.enclosingElement);
}
bool _hasMethod(MethodElement element) {
var classes = classAndSuperClasses?.classes;
return classes != null && 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)) {
for (var t in element.mixins) {
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) {
var declaredElement = node.declaredElement;
if (declaredElement == null || declaredElement.isFactory) return;
var body = node.body;
if (body is BlockFunctionBody) {
for (var statement in body.block.statements) {
if (statement is! AssertStatement) break;
var assertVisitor =
_AssertVisitor(declaredElement, _classAndSuperClasses);
statement.visitChildren(assertVisitor);
if (!assertVisitor.needInstance) {
rule.reportLintForToken(statement.beginToken);
}
}
}
}
}