blob: 36dff58241b1de39a5bf143b8cfa575860a0be26 [file]
// Copyright (c) 2019, 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/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:meta/meta.dart';
/// Helper for checking potentially nullable dereferences.
class NullableDereferenceVerifier {
/// Properties on the object class which are safe to call on nullable types.
///
/// Note that this must include tear-offs.
///
/// TODO(mfairhurst): Calculate these fields rather than hard-code them.
static const _objectPropertyNames = {
'hashCode',
'runtimeType',
'noSuchMethod',
'toString',
};
final TypeSystemImpl _typeSystem;
final ErrorReporter _errorReporter;
NullableDereferenceVerifier({
@required TypeSystemImpl typeSystem,
@required ErrorReporter errorReporter,
}) : _typeSystem = typeSystem,
_errorReporter = errorReporter;
bool expression(Expression expression) {
if (!_typeSystem.isNonNullableByDefault) {
return false;
}
return _check(expression, expression.staticType);
}
void implicitThis(AstNode errorNode, DartType thisType) {
if (!_typeSystem.isNonNullableByDefault) {
return;
}
_check(errorNode, thisType);
}
void methodInvocation(
Expression receiver,
DartType receiverType,
String methodName,
) {
if (!_typeSystem.isNonNullableByDefault) {
return;
}
if (methodName == 'toString' || methodName == 'noSuchMethod') {
return;
}
_check(receiver, receiverType);
}
void propertyAccess(AstNode errorNode, DartType receiverType, String name) {
if (!_typeSystem.isNonNullableByDefault) {
return;
}
if (_objectPropertyNames.contains(name)) {
return;
}
_check(errorNode, receiverType);
}
void report(AstNode errorNode, DartType receiverType) {
var errorCode = receiverType == _typeSystem.typeProvider.nullType
? StaticWarningCode.INVALID_USE_OF_NULL_VALUE
: StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE;
_errorReporter.reportErrorForNode(errorCode, errorNode);
}
/// If the [receiverType] is potentially nullable, report it.
///
/// The [errorNode] is usually the receiver of the invocation, but if the
/// receiver is the implicit `this`, the name of the invocation.
bool _check(AstNode errorNode, DartType receiverType) {
if (identical(receiverType, DynamicTypeImpl.instance) ||
!_typeSystem.isPotentiallyNullable(receiverType)) {
return false;
}
report(errorNode, receiverType);
return true;
}
}