blob: 7d6e8abd7e56311cfb0c7a214f5137e86f88b4e9 [file] [log] [blame]
// Copyright (c) 2014, 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 'dart:collection';
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/class_hierarchy.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/error/constructor_fields_verifier.dart';
import 'package:analyzer/src/error/correct_override.dart';
import 'package:analyzer/src/error/duplicate_definition_verifier.dart';
import 'package:analyzer/src/error/getter_setter_types_verifier.dart';
import 'package:analyzer/src/error/literal_element_verifier.dart';
import 'package:analyzer/src/error/required_parameters_verifier.dart';
import 'package:analyzer/src/error/return_type_verifier.dart';
import 'package:analyzer/src/error/type_arguments_verifier.dart';
import 'package:analyzer/src/error/use_result_verifier.dart';
import 'package:analyzer/src/generated/element_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error_detection_helpers.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:analyzer/src/generated/this_access_tracker.dart';
import 'package:collection/collection.dart';
class EnclosingExecutableContext {
final ExecutableElement? element;
final bool isAsynchronous;
final bool isConstConstructor;
final bool isGenerativeConstructor;
final bool isGenerator;
final bool inFactoryConstructor;
final bool inStaticMethod;
/// If this [EnclosingExecutableContext] is the first argument in a method
/// invocation of [Future.catchError], returns the return type expected for
/// `Future<T>.catchError`'s `onError` parameter, which is `FutureOr<T>`,
/// otherwise `null`.
final InterfaceType? catchErrorOnErrorReturnType;
/// The return statements that have a value.
final List<ReturnStatement> _returnsWith = [];
/// The return statements that do not have a value.
final List<ReturnStatement> _returnsWithout = [];
/// This flag is set to `false` when the declared return type is not legal
/// for the kind of the function body, e.g. not `Future` for `async`.
bool hasLegalReturnType = true;
EnclosingExecutableContext(this.element,
{bool? isAsynchronous, this.catchErrorOnErrorReturnType})
: isAsynchronous =
isAsynchronous ?? (element != null && element.isAsynchronous),
isConstConstructor = element is ConstructorElement && element.isConst,
isGenerativeConstructor =
element is ConstructorElement && !element.isFactory,
isGenerator = element != null && element.isGenerator,
inFactoryConstructor = _inFactoryConstructor(element),
inStaticMethod = _inStaticMethod(element);
EnclosingExecutableContext.empty() : this(null);
String? get displayName {
final element = this.element;
if (element is ConstructorElement) {
var className = element.enclosingElement.displayName;
var constructorName = element.displayName;
return constructorName.isEmpty
? className
: '$className.$constructorName';
} else {
return element?.displayName;
}
}
bool get isClosure {
return element is FunctionElement && element!.displayName.isEmpty;
}
bool get isConstructor => element is ConstructorElement;
bool get isFunction {
if (element is FunctionElement) {
return element!.displayName.isNotEmpty;
}
return element is PropertyAccessorElement;
}
bool get isMethod => element is MethodElement;
bool get isSynchronous => !isAsynchronous;
DartType get returnType {
return catchErrorOnErrorReturnType ?? element!.returnType;
}
static bool _inFactoryConstructor(Element? element) {
var enclosing = element?.enclosingElement;
if (enclosing == null) {
return false;
}
if (element is ConstructorElement) {
return element.isFactory;
}
return _inFactoryConstructor(enclosing);
}
static bool _inStaticMethod(Element? element) {
var enclosing = element?.enclosingElement;
if (enclosing == null) {
return false;
}
if (enclosing is ClassElement || enclosing is ExtensionElement) {
if (element is ExecutableElement) {
return element.isStatic;
}
}
return _inStaticMethod(enclosing);
}
}
/// A visitor used to traverse an AST structure looking for additional errors
/// and warnings not covered by the parser and resolver.
class ErrorVerifier extends RecursiveAstVisitor<void>
with ErrorDetectionHelpers {
/// The error reporter by which errors will be reported.
@override
final ErrorReporter errorReporter;
/// The current library that is being analyzed.
final LibraryElementImpl _currentLibrary;
/// The type representing the type 'int'.
late final InterfaceType _intType;
/// The options for verification.
late final AnalysisOptionsImpl _options;
/// The object providing access to the types defined by the language.
final TypeProvider _typeProvider;
/// The type system primitives
@override
late final TypeSystemImpl typeSystem;
/// The manager for the inheritance mappings.
final InheritanceManager3 _inheritanceManager;
/// A flag indicating whether the visitor is currently within a catch clause.
///
/// See [visitCatchClause].
bool _isInCatchClause = false;
/// A flag indicating whether the visitor is currently within a comment.
bool _isInComment = false;
/// The stack of flags, where `true` at the top (last) of the stack indicates
/// that the visitor is in the initializer of a lazy local variable. When the
/// top is `false`, we might be not in a local variable, or it is not `lazy`,
/// etc.
final List<bool> _isInLateLocalVariable = [false];
/// A flag indicating whether the visitor is currently within a native class
/// declaration.
bool _isInNativeClass = false;
/// A flag indicating whether the visitor is currently within a static
/// variable declaration.
bool _isInStaticVariableDeclaration = false;
/// A flag indicating whether the visitor is currently within an instance
/// variable declaration, which is not `late`.
bool _isInInstanceNotLateVariableDeclaration = false;
/// A flag indicating whether the visitor is currently within a constructor
/// initializer.
bool _isInConstructorInitializer = false;
/// This is set to `true` iff the visitor is currently within a function typed
/// formal parameter.
bool _isInFunctionTypedFormalParameter = false;
/// A flag indicating whether the visitor is currently within code in the SDK.
bool _isInSystemLibrary = false;
/// A flag indicating whether the current library contains at least one import
/// directive with a URI that uses the "dart-ext" scheme.
bool _hasExtUri = false;
/// The class containing the AST nodes being visited, or `null` if we are not
/// in the scope of a class.
ClassElementImpl? _enclosingClass;
/// The enum containing the AST nodes being visited, or `null` if we are not
/// in the scope of an enum.
ClassElement? _enclosingEnum;
/// The element of the extension being visited, or `null` if we are not
/// in the scope of an extension.
ExtensionElement? _enclosingExtension;
/// The helper for tracking if the current location has access to `this`.
final ThisAccessTracker _thisAccessTracker = ThisAccessTracker.unit();
/// The context of the method or function that we are currently visiting, or
/// `null` if we are not inside a method or function.
EnclosingExecutableContext _enclosingExecutable =
EnclosingExecutableContext.empty();
/// A table mapping names to the exported elements.
final Map<String, Element> _exportedElements = HashMap<String, Element>();
/// A set of the names of the variable initializers we are visiting now.
final HashSet<String> _namesForReferenceToDeclaredVariableInInitializer =
HashSet<String>();
/// The elements that will be defined later in the current scope, but right
/// now are not declared.
HiddenElements? _hiddenElements;
final _UninstantiatedBoundChecker _uninstantiatedBoundChecker;
/// The features enabled in the unit currently being checked for errors.
FeatureSet? _featureSet;
final RequiredParametersVerifier _requiredParametersVerifier;
final DuplicateDefinitionVerifier _duplicateDefinitionVerifier;
final UseResultVerifier _checkUseVerifier;
late final TypeArgumentsVerifier _typeArgumentsVerifier;
late final ConstructorFieldsVerifier _constructorFieldsVerifier;
late final ReturnTypeVerifier _returnTypeVerifier;
/// Initialize a newly created error verifier.
ErrorVerifier(this.errorReporter, this._currentLibrary, this._typeProvider,
this._inheritanceManager)
: _uninstantiatedBoundChecker =
_UninstantiatedBoundChecker(errorReporter),
_checkUseVerifier = UseResultVerifier(errorReporter),
_requiredParametersVerifier = RequiredParametersVerifier(errorReporter),
_duplicateDefinitionVerifier =
DuplicateDefinitionVerifier(_currentLibrary, errorReporter) {
_isInSystemLibrary = _currentLibrary.source.isInSystemLibrary;
_hasExtUri = _currentLibrary.hasExtUri;
_isInCatchClause = false;
_isInStaticVariableDeclaration = false;
_isInConstructorInitializer = false;
_intType = _typeProvider.intType;
typeSystem = _currentLibrary.typeSystem;
_options = _currentLibrary.context.analysisOptions as AnalysisOptionsImpl;
_typeArgumentsVerifier =
TypeArgumentsVerifier(_options, _currentLibrary, errorReporter);
_constructorFieldsVerifier = ConstructorFieldsVerifier(
typeSystem: typeSystem,
errorReporter: errorReporter,
);
_returnTypeVerifier = ReturnTypeVerifier(
typeProvider: _typeProvider as TypeProviderImpl,
typeSystem: typeSystem,
errorReporter: errorReporter,
);
}
ClassElement? get enclosingClass => _enclosingClass;
/// For consumers of error verification as a library, (currently just the
/// angular plugin), expose a setter that can make the errors reported more
/// accurate when dangling code snippets are being resolved from a class
/// context. Note that this setter is very defensive for potential misuse; it
/// should not be modified in the middle of visiting a tree and requires an
/// analyzer-provided Impl instance to work.
set enclosingClass(ClassElement? classElement) {
assert(classElement is ClassElementImpl);
assert(_enclosingClass == null);
assert(_enclosingEnum == null);
assert(_enclosingExecutable.element == null);
_enclosingClass = classElement as ClassElementImpl;
}
/// The language team is thinking about adding abstract fields, or external
/// fields. But for now we will ignore such fields in `Struct` subtypes.
bool get _isEnclosingClassFfiStruct {
var superClass = _enclosingClass?.supertype?.element;
return superClass != null &&
superClass.library.name == 'dart.ffi' &&
superClass.name == 'Struct';
}
bool get _isNonNullableByDefault =>
_featureSet?.isEnabled(Feature.non_nullable) ?? false;
@override
List<DiagnosticMessage> computeWhyNotPromotedMessages(
SyntacticEntity errorEntity,
Map<DartType, NonPromotionReason>? whyNotPromoted) {
return [];
}
@override
void visitAnnotation(Annotation node) {
_checkForInvalidAnnotationFromDeferredLibrary(node);
_checkForMissingJSLibAnnotation(node);
super.visitAnnotation(node);
}
@override
void visitAsExpression(AsExpression node) {
_checkForTypeAnnotationDeferredClass(node.type);
super.visitAsExpression(node);
}
@override
void visitAssertInitializer(AssertInitializer node) {
_isInConstructorInitializer = true;
try {
super.visitAssertInitializer(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
void visitAssignmentExpression(AssignmentExpression node) {
TokenType operatorType = node.operator.type;
Expression lhs = node.leftHandSide;
if (operatorType == TokenType.QUESTION_QUESTION_EQ) {
_checkForDeadNullCoalesce(node.readType as TypeImpl, node.rightHandSide);
}
_checkForAssignmentToFinal(lhs);
super.visitAssignmentExpression(node);
}
@override
void visitAwaitExpression(AwaitExpression node) {
if (!_enclosingExecutable.isAsynchronous) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, node.awaitKeyword);
}
if (_isNonNullableByDefault) {
checkForUseOfVoidResult(node.expression);
}
_checkForAwaitInLateLocalVariableInitializer(node);
super.visitAwaitExpression(node);
}
@override
void visitBinaryExpression(BinaryExpression node) {
Token operator = node.operator;
TokenType type = operator.type;
if (type == TokenType.AMPERSAND_AMPERSAND || type == TokenType.BAR_BAR) {
checkForUseOfVoidResult(node.rightOperand);
} else {
// Assignability checking is done by the resolver.
}
if (type == TokenType.QUESTION_QUESTION) {
_checkForDeadNullCoalesce(
node.leftOperand.staticType as TypeImpl, node.rightOperand);
}
checkForUseOfVoidResult(node.leftOperand);
super.visitBinaryExpression(node);
}
@override
void visitBlock(Block node) {
_withHiddenElements(node.statements, () {
_duplicateDefinitionVerifier.checkStatements(node.statements);
super.visitBlock(node);
});
}
@override
void visitBlockFunctionBody(BlockFunctionBody node) {
_thisAccessTracker.enterFunctionBody(node);
try {
super.visitBlockFunctionBody(node);
} finally {
_thisAccessTracker.exitFunctionBody(node);
}
}
@override
void visitBreakStatement(BreakStatement node) {
var labelNode = node.label;
if (labelNode != null) {
var labelElement = labelNode.staticElement;
if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, labelNode);
}
}
}
@override
void visitCatchClause(CatchClause node) {
_duplicateDefinitionVerifier.checkCatchClause(node);
bool previousIsInCatchClause = _isInCatchClause;
try {
_isInCatchClause = true;
_checkForTypeAnnotationDeferredClass(node.exceptionType);
super.visitCatchClause(node);
} finally {
_isInCatchClause = previousIsInCatchClause;
}
}
@override
void visitClassDeclaration(ClassDeclaration node) {
var outerClass = _enclosingClass;
try {
_isInNativeClass = node.nativeClause != null;
_enclosingClass = node.declaredElement as ClassElementImpl;
List<ClassMember> members = node.members;
_duplicateDefinitionVerifier.checkClass(node);
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME);
_checkForConflictingClassTypeVariableErrorCodes();
var superclass = node.extendsClause?.superclass;
var implementsClause = node.implementsClause;
var withClause = node.withClause;
// Only do error checks on the clause nodes if there is a non-null clause
if (implementsClause != null ||
superclass != null ||
withClause != null) {
_checkClassInheritance(node, superclass, withClause, implementsClause);
}
_checkForConflictingClassMembers();
_constructorFieldsVerifier.enterClass(node);
_checkForFinalNotInitializedInClass(members);
_checkForBadFunctionUse(node);
_checkForWrongTypeParameterVarianceInSuperinterfaces();
_checkForMainFunction(node.name);
super.visitClassDeclaration(node);
} finally {
_isInNativeClass = false;
_constructorFieldsVerifier.leaveClass();
_enclosingClass = outerClass;
}
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
var outerClassElement = _enclosingClass;
try {
_enclosingClass = node.declaredElement as ClassElementImpl;
_checkClassInheritance(
node, node.superclass, node.withClause, node.implementsClause);
_checkForMainFunction(node.name);
_checkForWrongTypeParameterVarianceInSuperinterfaces();
} finally {
_enclosingClass = outerClassElement;
}
super.visitClassTypeAlias(node);
}
@override
void visitComment(Comment node) {
_isInComment = true;
try {
super.visitComment(node);
} finally {
_isInComment = false;
}
}
@override
void visitCompilationUnit(CompilationUnit node) {
_featureSet = node.featureSet;
_duplicateDefinitionVerifier.checkUnit(node);
_checkForDeferredPrefixCollisions(node);
super.visitCompilationUnit(node);
_featureSet = null;
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
var element = node.declaredElement!;
_withEnclosingExecutable(element, () {
_checkForInvalidModifierOnBody(
node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR);
_checkForConstConstructorWithNonFinalField(node, element);
_checkForConstConstructorWithNonConstSuper(node);
_constructorFieldsVerifier.verify(node);
_checkForRedirectingConstructorErrorCodes(node);
_checkForMultipleSuperInitializers(node);
_checkForRecursiveConstructorRedirect(node, element);
if (!_checkForRecursiveFactoryRedirect(node, element)) {
_checkForAllRedirectConstructorErrorCodes(node);
}
_checkForUndefinedConstructorInInitializerImplicit(node);
_checkForReturnInGenerativeConstructor(node);
super.visitConstructorDeclaration(node);
});
}
@override
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
_isInConstructorInitializer = true;
try {
SimpleIdentifier fieldName = node.fieldName;
var staticElement = fieldName.staticElement;
_checkForInvalidField(node, fieldName, staticElement);
if (staticElement is FieldElement) {
_checkForAbstractOrExternalFieldConstructorInitializer(
node.fieldName, staticElement);
}
super.visitConstructorFieldInitializer(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
void visitContinueStatement(ContinueStatement node) {
var labelNode = node.label;
if (labelNode != null) {
var labelElement = labelNode.staticElement;
if (labelElement is LabelElementImpl &&
labelElement.isOnSwitchStatement) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONTINUE_LABEL_ON_SWITCH, labelNode);
}
}
}
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
checkForInvalidAssignment(node.identifier, node.defaultValue);
super.visitDefaultFormalParameter(node);
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
var outerEnum = _enclosingEnum;
try {
_enclosingEnum = node.declaredElement;
_duplicateDefinitionVerifier.checkEnum(node);
super.visitEnumDeclaration(node);
} finally {
_enclosingEnum = outerEnum;
}
}
@override
void visitExportDirective(ExportDirective node) {
var exportElement = node.element;
if (exportElement != null) {
var exportedLibrary = exportElement.exportedLibrary;
_checkForAmbiguousExport(node, exportElement, exportedLibrary);
_checkForExportInternalLibrary(node, exportElement);
_checkForExportLegacySymbol(node);
}
super.visitExportDirective(node);
}
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
_thisAccessTracker.enterFunctionBody(node);
try {
_returnTypeVerifier.verifyExpressionFunctionBody(node);
super.visitExpressionFunctionBody(node);
} finally {
_thisAccessTracker.exitFunctionBody(node);
}
}
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
_enclosingExtension = node.declaredElement;
_duplicateDefinitionVerifier.checkExtension(node);
_checkForConflictingExtensionTypeVariableErrorCodes();
_checkForFinalNotInitializedInClass(node.members);
GetterSetterTypesVerifier(
typeSystem: typeSystem,
errorReporter: errorReporter,
).checkExtension(node);
final name = node.name;
if (name != null) {
_checkForBuiltInIdentifierAsName(
name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_EXTENSION_NAME);
}
super.visitExtensionDeclaration(node);
_enclosingExtension = null;
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
var fields = node.fields;
_thisAccessTracker.enterFieldDeclaration(node);
_isInStaticVariableDeclaration = node.isStatic;
_isInInstanceNotLateVariableDeclaration =
!node.isStatic && !node.fields.isLate;
if (!_isInStaticVariableDeclaration) {
if (fields.isConst) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.CONST_INSTANCE_FIELD, fields.keyword!);
}
}
try {
_checkForNotInitializedNonNullableStaticField(node);
_checkForWrongTypeParameterVarianceInField(node);
_checkForLateFinalFieldWithConstConstructor(node);
super.visitFieldDeclaration(node);
} finally {
_isInStaticVariableDeclaration = false;
_isInInstanceNotLateVariableDeclaration = false;
_thisAccessTracker.exitFieldDeclaration(node);
}
}
@override
void visitFieldFormalParameter(FieldFormalParameter node) {
_checkForValidField(node);
_checkForPrivateOptionalParameter(node);
_checkForFieldInitializingFormalRedirectingConstructor(node);
_checkForTypeAnnotationDeferredClass(node.type);
ParameterElement element = node.declaredElement!;
if (element is FieldFormalParameterElement) {
var fieldElement = element.field;
if (fieldElement != null) {
_checkForAbstractOrExternalFieldConstructorInitializer(
node.identifier, fieldElement);
}
}
super.visitFieldFormalParameter(node);
}
@override
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
DeclaredIdentifier loopVariable = node.loopVariable;
if (_checkForEachParts(node, loopVariable.identifier)) {
if (loopVariable.isConst) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.FOR_IN_WITH_CONST_VARIABLE,
loopVariable.keyword!);
}
}
super.visitForEachPartsWithDeclaration(node);
}
@override
void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
SimpleIdentifier identifier = node.identifier;
if (_checkForEachParts(node, identifier)) {
_checkForAssignmentToFinal(identifier);
}
super.visitForEachPartsWithIdentifier(node);
}
@override
void visitFormalParameterList(FormalParameterList node) {
_duplicateDefinitionVerifier.checkParameters(node);
_checkUseOfCovariantInParameters(node);
_checkUseOfDefaultValuesInParameters(node);
super.visitFormalParameterList(node);
}
@override
void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
_duplicateDefinitionVerifier.checkForVariables(node.variables);
super.visitForPartsWithDeclarations(node);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
ExecutableElement functionElement = node.declaredElement!;
if (functionElement.enclosingElement is! CompilationUnitElement) {
_hiddenElements!.declare(functionElement);
}
_withEnclosingExecutable(functionElement, () {
SimpleIdentifier identifier = node.name;
TypeAnnotation? returnType = node.returnType;
if (node.isGetter) {
GetterSetterTypesVerifier(
typeSystem: typeSystem,
errorReporter: errorReporter,
).checkGetter(
node.name, node.declaredElement as PropertyAccessorElement);
}
if (node.isSetter) {
FunctionExpression functionExpression = node.functionExpression;
_checkForWrongNumberOfParametersForSetter(
identifier, functionExpression.parameters);
_checkForNonVoidReturnTypeForSetter(returnType);
}
_checkForTypeAnnotationDeferredClass(returnType);
_returnTypeVerifier.verifyReturnType(returnType);
_checkForImplicitDynamicReturn(node.name, node.declaredElement!);
_checkForMainFunction(node.name);
super.visitFunctionDeclaration(node);
});
}
@override
void visitFunctionExpression(FunctionExpression node) {
_isInLateLocalVariable.add(false);
if (node.parent is FunctionDeclaration) {
super.visitFunctionExpression(node);
} else {
_withEnclosingExecutable(node.declaredElement!, () {
super.visitFunctionExpression(node);
});
}
_isInLateLocalVariable.removeLast();
}
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
Expression functionExpression = node.function;
if (functionExpression is ExtensionOverride) {
return super.visitFunctionExpressionInvocation(node);
}
DartType expressionType = functionExpression.typeOrThrow;
if (expressionType is FunctionType) {
_typeArgumentsVerifier.checkFunctionExpressionInvocation(node);
}
_requiredParametersVerifier.visitFunctionExpressionInvocation(node);
super.visitFunctionExpressionInvocation(node);
}
@override
void visitFunctionReference(FunctionReference node) {
_typeArgumentsVerifier.checkFunctionReference(node);
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
_checkForMainFunction(node.name);
_checkForTypeAliasCannotReferenceItself(
node.name, node.declaredElement as TypeAliasElementImpl);
super.visitFunctionTypeAlias(node);
}
@override
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
bool old = _isInFunctionTypedFormalParameter;
_isInFunctionTypedFormalParameter = true;
try {
_checkForTypeAnnotationDeferredClass(node.returnType);
// TODO(jmesserly): ideally we'd use _checkForImplicitDynamicReturn, and
// we can get the function element via `node?.element?.type?.element` but
// it doesn't have hasImplicitReturnType set correctly.
if (!_options.implicitDynamic && node.returnType == null) {
DartType parameterType = node.declaredElement!.type;
if (parameterType is FunctionType &&
parameterType.returnType.isDynamic) {
errorReporter.reportErrorForNode(LanguageCode.IMPLICIT_DYNAMIC_RETURN,
node.identifier, [node.identifier]);
}
}
super.visitFunctionTypedFormalParameter(node);
} finally {
_isInFunctionTypedFormalParameter = old;
}
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
_checkForMainFunction(node.name);
_checkForTypeAliasCannotReferenceItself(
node.name, node.declaredElement as TypeAliasElementImpl);
super.visitGenericTypeAlias(node);
}
@override
void visitImplementsClause(ImplementsClause node) {
node.interfaces.forEach(_checkForImplicitDynamicType);
super.visitImplementsClause(node);
}
@override
void visitImportDirective(ImportDirective node) {
var importElement = node.element;
if (node.prefix != null) {
_checkForBuiltInIdentifierAsName(node.prefix!,
CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME);
}
if (importElement != null) {
_checkForImportInternalLibrary(node, importElement);
if (importElement.isDeferred) {
_checkForDeferredImportOfExtensions(node, importElement);
}
}
super.visitImportDirective(node);
}
@override
void visitIndexExpression(IndexExpression node) {
if (node.isNullAware) {
_checkForUnnecessaryNullAware(
node.realTarget,
node.question ?? node.period ?? node.leftBracket,
);
}
super.visitIndexExpression(node);
}
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
ConstructorName constructorName = node.constructorName;
TypeName typeName = constructorName.type;
DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
_checkForConstOrNewWithAbstractClass(node, typeName, type);
_checkForConstOrNewWithEnum(node, typeName, type);
_checkForConstOrNewWithMixin(node, typeName, type);
_requiredParametersVerifier.visitInstanceCreationExpression(node);
if (node.isConst) {
_checkForConstWithNonConst(node);
_checkForConstWithUndefinedConstructor(node, constructorName, typeName);
_checkForConstDeferredClass(node, constructorName, typeName);
} else {
_checkForNewWithUndefinedConstructor(node, constructorName, typeName);
}
_checkForListConstructor(node, type);
}
_checkForImplicitDynamicType(typeName);
super.visitInstanceCreationExpression(node);
}
@override
void visitIntegerLiteral(IntegerLiteral node) {
_checkForOutOfRange(node);
super.visitIntegerLiteral(node);
}
@override
void visitInterpolationExpression(InterpolationExpression node) {
checkForUseOfVoidResult(node.expression);
super.visitInterpolationExpression(node);
}
@override
void visitIsExpression(IsExpression node) {
_checkForTypeAnnotationDeferredClass(node.type);
checkForUseOfVoidResult(node.expression);
super.visitIsExpression(node);
}
@override
void visitListLiteral(ListLiteral node) {
_typeArgumentsVerifier.checkListLiteral(node);
_checkForListElementTypeNotAssignable(node);
super.visitListLiteral(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
_withEnclosingExecutable(node.declaredElement!, () {
var returnType = node.returnType;
if (node.isStatic && node.isGetter) {
GetterSetterTypesVerifier(
typeSystem: typeSystem,
errorReporter: errorReporter,
).checkGetter(
node.name, node.declaredElement as PropertyAccessorElement);
}
if (node.isSetter) {
_checkForWrongNumberOfParametersForSetter(node.name, node.parameters);
_checkForNonVoidReturnTypeForSetter(returnType);
} else if (node.isOperator) {
var hasWrongNumberOfParameters =
_checkForWrongNumberOfParametersForOperator(node);
if (!hasWrongNumberOfParameters) {
// If the operator has too many parameters including one or more
// optional parameters, only report one error.
_checkForOptionalParameterInOperator(node);
}
_checkForNonVoidReturnTypeForOperator(node);
}
_checkForExtensionDeclaresMemberOfObject(node);
_checkForTypeAnnotationDeferredClass(returnType);
_returnTypeVerifier.verifyReturnType(returnType);
_checkForImplicitDynamicReturn(node, node.declaredElement!);
_checkForWrongTypeParameterVarianceInMethod(node);
super.visitMethodDeclaration(node);
});
}
@override
void visitMethodInvocation(MethodInvocation node) {
var target = node.realTarget;
SimpleIdentifier methodName = node.methodName;
if (target != null) {
var typeReference = ElementResolver.getTypeReference(target);
_checkForStaticAccessToInstanceMember(typeReference, methodName);
_checkForInstanceAccessToStaticMember(
typeReference, node.target, methodName);
_checkForUnnecessaryNullAware(target, node.operator!);
} else {
_checkForUnqualifiedReferenceToNonLocalStaticMember(methodName);
}
_typeArgumentsVerifier.checkMethodInvocation(node);
_requiredParametersVerifier.visitMethodInvocation(node);
_checkUseVerifier.checkMethodInvocation(node);
super.visitMethodInvocation(node);
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
// TODO(scheglov) Verify for all mixin errors.
var outerClass = _enclosingClass;
try {
_enclosingClass = node.declaredElement as ClassElementImpl;
List<ClassMember> members = node.members;
_duplicateDefinitionVerifier.checkMixin(node);
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME);
_checkForConflictingClassTypeVariableErrorCodes();
var onClause = node.onClause;
var implementsClause = node.implementsClause;
// Only do error checks only if there is a non-null clause.
if (onClause != null || implementsClause != null) {
_checkMixinInheritance(node, onClause, implementsClause);
}
_checkForConflictingClassMembers();
_checkForFinalNotInitializedInClass(members);
_checkForMainFunction(node.name);
_checkForWrongTypeParameterVarianceInSuperinterfaces();
// _checkForBadFunctionUse(node);
super.visitMixinDeclaration(node);
} finally {
_enclosingClass = outerClass;
}
}
@override
void visitNativeClause(NativeClause node) {
// TODO(brianwilkerson) Figure out the right rule for when 'native' is
// allowed.
if (!_isInSystemLibrary) {
errorReporter.reportErrorForNode(
ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK_CODE, node);
}
super.visitNativeClause(node);
}
@override
void visitNativeFunctionBody(NativeFunctionBody node) {
_checkForNativeFunctionBodyInNonSdkCode(node);
super.visitNativeFunctionBody(node);
}
@override
void visitPostfixExpression(PostfixExpression node) {
var operand = node.operand;
if (node.operator.type == TokenType.BANG) {
checkForUseOfVoidResult(node);
_checkForUnnecessaryNullAware(operand, node.operator);
} else {
_checkForAssignmentToFinal(operand);
_checkForIntNotAssignable(operand);
}
super.visitPostfixExpression(node);
}
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
if (node.parent is! Annotation) {
var typeReference = ElementResolver.getTypeReference(node.prefix);
SimpleIdentifier name = node.identifier;
_checkForStaticAccessToInstanceMember(typeReference, name);
_checkForInstanceAccessToStaticMember(typeReference, node.prefix, name);
}
super.visitPrefixedIdentifier(node);
}
@override
void visitPrefixExpression(PrefixExpression node) {
TokenType operatorType = node.operator.type;
Expression operand = node.operand;
if (operatorType != TokenType.BANG) {
if (operatorType.isIncrementOperator) {
_checkForAssignmentToFinal(operand);
}
checkForUseOfVoidResult(operand);
_checkForIntNotAssignable(operand);
}
super.visitPrefixExpression(node);
}
@override
void visitPropertyAccess(PropertyAccess node) {
var target = node.realTarget;
var typeReference = ElementResolver.getTypeReference(target);
SimpleIdentifier propertyName = node.propertyName;
_checkForStaticAccessToInstanceMember(typeReference, propertyName);
_checkForInstanceAccessToStaticMember(
typeReference, node.target, propertyName);
_checkForUnnecessaryNullAware(target, node.operator);
_checkUseVerifier.checkPropertyAccess(node);
super.visitPropertyAccess(node);
}
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
_requiredParametersVerifier.visitRedirectingConstructorInvocation(node);
_isInConstructorInitializer = true;
try {
super.visitRedirectingConstructorInvocation(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
void visitRethrowExpression(RethrowExpression node) {
_checkForRethrowOutsideCatch(node);
super.visitRethrowExpression(node);
}
@override
void visitReturnStatement(ReturnStatement node) {
if (node.expression == null) {
_enclosingExecutable._returnsWithout.add(node);
} else {
_enclosingExecutable._returnsWith.add(node);
}
_returnTypeVerifier.verifyReturnStatement(node);
super.visitReturnStatement(node);
}
@override
void visitSetOrMapLiteral(SetOrMapLiteral node) {
if (node.isMap) {
_typeArgumentsVerifier.checkMapLiteral(node);
_checkForMapTypeNotAssignable(node);
_checkForNonConstMapAsExpressionStatement3(node);
} else if (node.isSet) {
_typeArgumentsVerifier.checkSetLiteral(node);
_checkForSetElementTypeNotAssignable3(node);
}
super.visitSetOrMapLiteral(node);
}
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
_checkForPrivateOptionalParameter(node);
_checkForTypeAnnotationDeferredClass(node.type);
// Checks for an implicit dynamic parameter type.
//
// We can skip other parameter kinds besides simple formal, because:
// - DefaultFormalParameter contains a simple one, so it gets here,
// - FieldFormalParameter error should be reported on the field,
// - FunctionTypedFormalParameter is a function type, not dynamic.
_checkForImplicitDynamicIdentifier(node, node.identifier);
super.visitSimpleFormalParameter(node);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
_checkForAmbiguousImport(node);
_checkForReferenceBeforeDeclaration(node);
_checkForInvalidInstanceMemberAccess(node);
_checkForTypeParameterReferencedByStatic(node);
if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) {
_checkForUnqualifiedReferenceToNonLocalStaticMember(node);
}
_checkUseVerifier.checkSimpleIdentifier(node);
super.visitSimpleIdentifier(node);
}
@override
void visitSpreadElement(SpreadElement node) {
if (node.isNullAware) {
_checkForUnnecessaryNullAware(node.expression, node.spreadOperator);
}
super.visitSpreadElement(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
_requiredParametersVerifier.visitSuperConstructorInvocation(node);
_isInConstructorInitializer = true;
try {
super.visitSuperConstructorInvocation(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
void visitSwitchCase(SwitchCase node) {
_withHiddenElements(node.statements, () {
_duplicateDefinitionVerifier.checkStatements(node.statements);
super.visitSwitchCase(node);
});
}
@override
void visitSwitchDefault(SwitchDefault node) {
_withHiddenElements(node.statements, () {
_duplicateDefinitionVerifier.checkStatements(node.statements);
super.visitSwitchDefault(node);
});
}
@override
void visitSwitchStatement(SwitchStatement node) {
_checkForSwitchExpressionNotAssignable(node);
_checkForCaseBlocksNotTerminated(node);
_checkForMissingEnumConstantInSwitch(node);
super.visitSwitchStatement(node);
}
@override
void visitThisExpression(ThisExpression node) {
_checkForInvalidReferenceToThis(node);
super.visitThisExpression(node);
}
@override
void visitThrowExpression(ThrowExpression node) {
_checkForConstEvalThrowsException(node);
checkForUseOfVoidResult(node.expression);
_checkForThrowOfInvalidType(node);
super.visitThrowExpression(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
_checkForFinalNotInitialized(node.variables);
_checkForNotInitializedNonNullableVariable(node.variables);
for (var declaration in node.variables.variables) {
_checkForMainFunction(declaration.name);
}
super.visitTopLevelVariableDeclaration(node);
}
@override
void visitTypeArgumentList(TypeArgumentList node) {
NodeList<TypeAnnotation> list = node.arguments;
for (TypeAnnotation type in list) {
_checkForTypeAnnotationDeferredClass(type);
}
super.visitTypeArgumentList(node);
}
@override
void visitTypeName(TypeName node) {
_typeArgumentsVerifier.checkTypeName(node);
super.visitTypeName(node);
}
@override
void visitTypeParameter(TypeParameter node) {
_checkForBuiltInIdentifierAsName(node.name,
CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME);
_checkForTypeAnnotationDeferredClass(node.bound);
_checkForImplicitDynamicType(node.bound);
_checkForGenericFunctionType(node.bound);
node.bound?.accept(_uninstantiatedBoundChecker);
super.visitTypeParameter(node);
}
@override
void visitTypeParameterList(TypeParameterList node) {
_duplicateDefinitionVerifier.checkTypeParameters(node);
_checkForTypeParameterBoundRecursion(node.typeParameters);
super.visitTypeParameterList(node);
}
@override
void visitVariableDeclaration(VariableDeclaration node) {
SimpleIdentifier nameNode = node.name;
var initializerNode = node.initializer;
// do checks
_checkForImplicitDynamicIdentifier(node, nameNode);
_checkForAbstractOrExternalVariableInitializer(node);
// visit name
nameNode.accept(this);
// visit initializer
String name = nameNode.name;
_namesForReferenceToDeclaredVariableInInitializer.add(name);
try {
if (initializerNode != null) {
initializerNode.accept(this);
}
} finally {
_namesForReferenceToDeclaredVariableInInitializer.remove(name);
}
// declare the variable
AstNode grandparent = node.parent!.parent!;
if (grandparent is! TopLevelVariableDeclaration &&
grandparent is! FieldDeclaration) {
VariableElement element = node.declaredElement!;
// There is no hidden elements if we are outside of a function body,
// which will happen for variables declared in control flow elements.
_hiddenElements?.declare(element);
}
}
@override
void visitVariableDeclarationList(VariableDeclarationList node) {
_checkForTypeAnnotationDeferredClass(node.type);
super.visitVariableDeclarationList(node);
}
@override
void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
_isInLateLocalVariable.add(node.variables.isLate);
_checkForFinalNotInitialized(node.variables);
super.visitVariableDeclarationStatement(node);
_isInLateLocalVariable.removeLast();
}
@override
void visitWithClause(WithClause node) {
node.mixinTypes.forEach(_checkForImplicitDynamicType);
super.visitWithClause(node);
}
/// Checks the class for problems with the superclass, mixins, or implemented
/// interfaces.
void _checkClassInheritance(
NamedCompilationUnitMember node,
TypeName? superclass,
WithClause? withClause,
ImplementsClause? implementsClause) {
// Only check for all of the inheritance logic around clauses if there
// isn't an error code such as "Cannot extend double" already on the
// class.
if (!_checkForExtendsDisallowedClass(superclass) &&
!_checkForImplementsClauseErrorCodes(implementsClause) &&
!_checkForAllMixinErrorCodes(withClause) &&
!_checkForNoGenerativeConstructorsInSuperclass(superclass)) {
_checkForImplicitDynamicType(superclass);
_checkForExtendsDeferredClass(superclass);
_checkForRepeatedType(implementsClause?.interfaces,
CompileTimeErrorCode.IMPLEMENTS_REPEATED);
_checkImplementsSuperClass(implementsClause);
_checkMixinsSuperClass(withClause);
_checkMixinInference(node, withClause);
_checkForMixinWithConflictingPrivateMember(withClause, superclass);
_checkForConflictingGenerics(node);
if (node is ClassDeclaration) {
_checkForNoDefaultSuperConstructorImplicit(node);
}
}
}
/// Given a list of [directives] that have the same prefix, generate an error
/// if there is more than one import and any of those imports is deferred.
///
/// See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX].
void _checkDeferredPrefixCollision(List<ImportDirective> directives) {
int count = directives.length;
if (count > 1) {
for (int i = 0; i < count; i++) {
var deferredToken = directives[i].deferredKeyword;
if (deferredToken != null) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, deferredToken);
}
}
}
}
void _checkForAbstractOrExternalFieldConstructorInitializer(
AstNode node, FieldElement fieldElement) {
if (fieldElement.isAbstract) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ABSTRACT_FIELD_CONSTRUCTOR_INITIALIZER, node);
}
if (fieldElement.isExternal) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTERNAL_FIELD_CONSTRUCTOR_INITIALIZER, node);
}
}
void _checkForAbstractOrExternalVariableInitializer(
VariableDeclaration node) {
var declaredElement = node.declaredElement;
if (node.initializer != null) {
if (declaredElement is FieldElement) {
if (declaredElement.isAbstract) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ABSTRACT_FIELD_INITIALIZER, node.name);
}
if (declaredElement.isExternal) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTERNAL_FIELD_INITIALIZER, node.name);
}
} else if (declaredElement is TopLevelVariableElement) {
if (declaredElement.isExternal) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTERNAL_VARIABLE_INITIALIZER, node.name);
}
}
}
}
/// Verify that all classes of the given [withClause] are valid.
///
/// See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
/// [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT].
bool _checkForAllMixinErrorCodes(WithClause? withClause) {
if (withClause == null) {
return false;
}
bool problemReported = false;
int mixinTypeIndex = -1;
for (int mixinNameIndex = 0;
mixinNameIndex < withClause.mixinTypes.length;
mixinNameIndex++) {
TypeName mixinName = withClause.mixinTypes[mixinNameIndex];
DartType mixinType = mixinName.typeOrThrow;
if (mixinType is InterfaceType) {
mixinTypeIndex++;
if (_checkForExtendsOrImplementsDisallowedClass(
mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) {
problemReported = true;
} else {
ClassElement mixinElement = mixinType.element;
if (_checkForExtendsOrImplementsDeferredClass(
mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) {
problemReported = true;
}
if (mixinElement.isMixin) {
if (_checkForMixinSuperclassConstraints(
mixinNameIndex, mixinName)) {
problemReported = true;
} else if (_checkForMixinSuperInvokedMembers(
mixinTypeIndex, mixinName, mixinElement, mixinType)) {
problemReported = true;
}
} else {
if (_checkForMixinClassDeclaresConstructor(
mixinName, mixinElement)) {
problemReported = true;
}
if (_checkForMixinInheritsNotFromObject(mixinName, mixinElement)) {
problemReported = true;
}
}
}
}
}
return problemReported;
}
/// Check for errors related to the redirected constructors.
void _checkForAllRedirectConstructorErrorCodes(
ConstructorDeclaration declaration) {
// Prepare redirected constructor node
var redirectedConstructor = declaration.redirectedConstructor;
if (redirectedConstructor == null) {
return;
}
// Prepare redirected constructor type
var redirectedElement = redirectedConstructor.staticElement;
if (redirectedElement == null) {
// If the element is null, we check for the
// REDIRECT_TO_MISSING_CONSTRUCTOR case
TypeName constructorTypeName = redirectedConstructor.type;
DartType redirectedType = constructorTypeName.typeOrThrow;
if (redirectedType.element != null && !redirectedType.isDynamic) {
// Prepare the constructor name
String constructorStrName = constructorTypeName.name.name;
if (redirectedConstructor.name != null) {
constructorStrName += ".${redirectedConstructor.name!.name}";
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR,
redirectedConstructor,
[constructorStrName, redirectedType]);
}
return;
}
FunctionType redirectedType = redirectedElement.type;
DartType redirectedReturnType = redirectedType.returnType;
// Report specific problem when return type is incompatible
FunctionType constructorType = declaration.declaredElement!.type;
DartType constructorReturnType = constructorType.returnType;
if (!typeSystem.isAssignableTo(
redirectedReturnType, constructorReturnType)) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.REDIRECT_TO_INVALID_RETURN_TYPE,
redirectedConstructor,
[redirectedReturnType, constructorReturnType]);
return;
} else if (!typeSystem.isSubtypeOf(redirectedType, constructorType)) {
// Check parameters.
errorReporter.reportErrorForNode(
CompileTimeErrorCode.REDIRECT_TO_INVALID_FUNCTION_TYPE,
redirectedConstructor,
[redirectedType, constructorType]);
}
}
/// Verify that the export namespace of the given export [directive] does not
/// export any name already exported by another export directive. The
/// [exportElement] is the [ExportElement] retrieved from the node. If the
/// element in the node was `null`, then this method is not called. The
/// [exportedLibrary] is the library element containing the exported element.
///
/// See [CompileTimeErrorCode.AMBIGUOUS_EXPORT].
void _checkForAmbiguousExport(ExportDirective directive,
ExportElement exportElement, LibraryElement? exportedLibrary) {
if (exportedLibrary == null) {
return;
}
// check exported names
Namespace namespace =
NamespaceBuilder().createExportNamespaceForDirective(exportElement);
Map<String, Element> definedNames = namespace.definedNames;
for (String name in definedNames.keys) {
var element = definedNames[name]!;
var prevElement = _exportedElements[name];
if (prevElement != null && prevElement != element) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.AMBIGUOUS_EXPORT, directive.uri, [
name,
prevElement.library!.definingCompilationUnit.source.uri,
element.library!.definingCompilationUnit.source.uri
]);
return;
} else {
_exportedElements[name] = element;
}
}
}
/// Check the given node to see whether it was ambiguous because the name was
/// imported from two or more imports.
void _checkForAmbiguousImport(SimpleIdentifier node) {
var element = node.writeOrReadElement;
if (element is MultiplyDefinedElementImpl) {
String name = element.displayName;
List<Element> conflictingMembers = element.conflictingElements;
var libraryNames =
conflictingMembers.map((e) => _getLibraryName(e)).toList();
libraryNames.sort();
errorReporter.reportErrorForNode(CompileTimeErrorCode.AMBIGUOUS_IMPORT,
node, [name, StringUtilities.printListOfQuotedNames(libraryNames)]);
}
}
/// Verify that the given [expression] is not final.
///
/// See [StaticWarningCode.ASSIGNMENT_TO_CONST],
/// [StaticWarningCode.ASSIGNMENT_TO_FINAL], and
/// [StaticWarningCode.ASSIGNMENT_TO_METHOD].
void _checkForAssignmentToFinal(Expression expression) {
// TODO(scheglov) Check SimpleIdentifier(s) as all other nodes.
if (expression is! SimpleIdentifier) return;
// Already handled in the assignment resolver.
if (expression.parent is AssignmentExpression) {
return;
}
// prepare element
var highlightedNode = expression;
var element = expression.staticElement;
if (expression is PrefixedIdentifier) {
var prefixedIdentifier = expression as PrefixedIdentifier;
highlightedNode = prefixedIdentifier.identifier;
}
// check if element is assignable
if (element is VariableElement) {
if (element.isConst) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
expression,
);
} else if (element.isFinal) {
if (_isNonNullableByDefault) {
// Handled during resolution, with flow analysis.
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
expression,
[element.name],
);
}
}
} else if (element is PropertyAccessorElement && element.isGetter) {
var variable = element.variable;
if (variable.isConst) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
expression,
);
} else if (variable is FieldElement && variable.isSynthetic) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
highlightedNode,
[variable.name, variable.enclosingElement.displayName],
);
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
highlightedNode,
[variable.name],
);
}
} else if (element is FunctionElement) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FUNCTION, expression);
} else if (element is MethodElement) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_METHOD, expression);
} else if (element is ClassElement ||
element is DynamicElementImpl ||
element is TypeParameterElement) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_TYPE, expression);
}
}
void _checkForAwaitInLateLocalVariableInitializer(AwaitExpression node) {
if (_isInLateLocalVariable.last) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.AWAIT_IN_LATE_LOCAL_VARIABLE_INITIALIZER,
node.awaitKeyword,
);
}
}
/// Verifies that the class is not named `Function` and that it doesn't
/// extends/implements/mixes in `Function`.
void _checkForBadFunctionUse(ClassDeclaration node) {
var extendsClause = node.extendsClause;
var implementsClause = node.implementsClause;
var withClause = node.withClause;
if (node.name.name == "Function") {
errorReporter.reportErrorForNode(
HintCode.DEPRECATED_FUNCTION_CLASS_DECLARATION, node.name);
}
if (extendsClause != null) {
var superElement = extendsClause.superclass.name.staticElement;
if (superElement != null && superElement.name == "Function") {
errorReporter.reportErrorForNode(
HintCode.DEPRECATED_EXTENDS_FUNCTION, extendsClause.superclass);
}
}
if (implementsClause != null) {
for (var interface in implementsClause.interfaces) {
var type = interface.type;
if (type != null && type.isDartCoreFunction) {
errorReporter.reportErrorForNode(
HintCode.DEPRECATED_IMPLEMENTS_FUNCTION,
interface,
);
break;
}
}
}
if (withClause != null) {
for (TypeName type in withClause.mixinTypes) {
var mixinElement = type.name.staticElement;
if (mixinElement != null && mixinElement.name == "Function") {
errorReporter.reportErrorForNode(
HintCode.DEPRECATED_MIXIN_FUNCTION, type);
}
}
}
}
/// Verify that the given [identifier] is not a keyword, and generates the
/// given [errorCode] on the identifier if it is a keyword.
///
/// See [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_EXTENSION_NAME],
/// [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME],
/// [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME], and
/// [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME].
void _checkForBuiltInIdentifierAsName(
SimpleIdentifier identifier, ErrorCode errorCode) {
Token token = identifier.token;
if (token.type.isKeyword && token.keyword?.isPseudo != true) {
errorReporter
.reportErrorForNode(errorCode, identifier, [identifier.name]);
}
}
/// Verify that the given [switchCase] is terminated with 'break', 'continue',
/// 'return' or 'throw'.
///
/// see [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
void _checkForCaseBlockNotTerminated(SwitchCase switchCase) {
NodeList<Statement> statements = switchCase.statements;
if (statements.isEmpty) {
// fall-through without statements at all
var parent = switchCase.parent;
if (parent is SwitchStatement) {
NodeList<SwitchMember> members = parent.members;
int index = members.indexOf(switchCase);
if (index != -1 && index < members.length - 1) {
return;
}
}
// no other switch member after this one
} else {
Statement statement = statements.last;
if (statement is Block && statement.statements.isNotEmpty) {
Block block = statement;
statement = block.statements.last;
}
// terminated with statement
if (statement is BreakStatement ||
statement is ContinueStatement ||
statement is ReturnStatement) {
return;
}
// terminated with 'throw' expression
if (statement is ExpressionStatement) {
Expression expression = statement.expression;
if (expression is ThrowExpression || expression is RethrowExpression) {
return;
}
}
}
errorReporter.reportErrorForToken(
CompileTimeErrorCode.CASE_BLOCK_NOT_TERMINATED, switchCase.keyword);
}
/// Verify that the switch cases in the given switch [statement] are
/// terminated with 'break', 'continue', 'rethrow', 'return' or 'throw'.
///
/// See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
void _checkForCaseBlocksNotTerminated(SwitchStatement statement) {
if (_isNonNullableByDefault) return;
NodeList<SwitchMember> members = statement.members;
int lastMember = members.length - 1;
for (int i = 0; i < lastMember; i++) {
SwitchMember member = members[i];
if (member is SwitchCase) {
_checkForCaseBlockNotTerminated(member);
}
}
}
/// Verify that the [_enclosingClass] does not have a method and getter pair
/// with the same name, via inheritance.
///
/// See [CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE],
/// [CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD], and
/// [CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD].
void _checkForConflictingClassMembers() {
if (_enclosingClass == null) {
return;
}
Uri libraryUri = _currentLibrary.source.uri;
// method declared in the enclosing class vs. inherited getter/setter
for (MethodElement method in _enclosingClass!.methods) {
String name = method.name;
// find inherited property accessor
var inherited = _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, name));
inherited ??= _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, '$name='));
if (method.isStatic && inherited != null) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, method, [
_enclosingClass!.displayName,
name,
inherited.enclosingElement.displayName,
]);
} else if (inherited is PropertyAccessorElement) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD, method, [
_enclosingClass!.displayName,
name,
inherited.enclosingElement.displayName
]);
}
}
// getter declared in the enclosing class vs. inherited method
for (PropertyAccessorElement accessor in _enclosingClass!.accessors) {
String name = accessor.displayName;
// find inherited method or property accessor
var inherited = _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, name));
inherited ??= _inheritanceManager.getInherited2(
_enclosingClass!, Name(libraryUri, '$name='));
if (accessor.isStatic && inherited != null) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, accessor, [
_enclosingClass!.displayName,
name,
inherited.enclosingElement.displayName,
]);
} else if (inherited is MethodElement) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD, accessor, [
_enclosingClass!.displayName,
name,
inherited.enclosingElement.displayName
]);
}
}
}
/// Verify all conflicts between type variable and enclosing class.
/// TODO(scheglov)
void _checkForConflictingClassTypeVariableErrorCodes() {
for (TypeParameterElement typeParameter
in _enclosingClass!.typeParameters) {
String name = typeParameter.name;
// name is same as the name of the enclosing class
if (_enclosingClass!.name == name) {
var code = _enclosingClass!.isMixin
? CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MIXIN
: CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS;
errorReporter.reportErrorForElement(code, typeParameter, [name]);
}
// check members
if (_enclosingClass!.getMethod(name) != null ||
_enclosingClass!.getGetter(name) != null ||
_enclosingClass!.getSetter(name) != null) {
var code = _enclosingClass!.isMixin
? CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_MIXIN
: CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_CLASS;
errorReporter.reportErrorForElement(code, typeParameter, [name]);
}
}
}
/// Verify all conflicts between type variable and enclosing extension.
///
/// See [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION], and
/// [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION_MEMBER].
void _checkForConflictingExtensionTypeVariableErrorCodes() {
for (TypeParameterElement typeParameter
in _enclosingExtension!.typeParameters) {
String name = typeParameter.name;
// name is same as the name of the enclosing class
if (_enclosingExtension!.name == name) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION,
typeParameter,
[name]);
}
// check members
if (_enclosingExtension!.getMethod(name) != null ||
_enclosingExtension!.getGetter(name) != null ||
_enclosingExtension!.getSetter(name) != null) {
errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_EXTENSION,
typeParameter,
[name]);
}
}
}
void _checkForConflictingGenerics(NamedCompilationUnitMember node) {
var element = node.declaredElement as ClassElement;
var analysisSession = _currentLibrary.session as AnalysisSessionImpl;
var errors = analysisSession.classHierarchy.errors(element);
for (var error in errors) {
if (error is IncompatibleInterfacesClassHierarchyError) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES,
node.name,
[
_enclosingClass!.name,
error.first.getDisplayString(withNullability: true),
error.second.getDisplayString(withNullability: true),
],
);
} else {
throw UnimplementedError('${error.runtimeType}');
}
}
}
/// Verify that if the given [constructor] declaration is 'const' then there
/// are no invocations of non-'const' super constructors, and that there are
/// no instance variables mixed in.
///
/// See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER], and
/// [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD].
void _checkForConstConstructorWithNonConstSuper(
ConstructorDeclaration constructor) {
if (!_enclosingExecutable.isConstConstructor) {
return;
}
// OK, const factory, checked elsewhere
if (constructor.factoryKeyword != null) {
return;
}
// check for mixins
var instanceFields = <FieldElement>[];
for (var mixin in _enclosingClass!.mixins) {
instanceFields.addAll(mixin.element.fields
.where((field) => !field.isStatic && !field.isSynthetic));
}
if (instanceFields.length == 1) {
var field = instanceFields.single;
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD,
constructor.returnType,
["'${field.enclosingElement.name}.${field.name}'"]);
return;
} else if (instanceFields.length > 1) {
var fieldNames = instanceFields
.map((field) => "'${field.enclosingElement.name}.${field.name}'")
.join(', ');
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELDS,
constructor.returnType,
[fieldNames]);
return;
}
// try to find and check super constructor invocation
for (ConstructorInitializer initializer in constructor.initializers) {
if (initializer is SuperConstructorInvocation) {
var element = initializer.staticElement;
if (element == null || element.isConst) {
return;
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER,
initializer,
[element.enclosingElement.displayName]);
return;
}
}
// no explicit super constructor invocation, check default constructor
var supertype = _enclosingClass!.supertype;
if (supertype == null) {
return;
}
if (supertype.isDartCoreObject) {
return;
}
var unnamedConstructor = supertype.element.unnamedConstructor;
if (unnamedConstructor == null || unnamedConstructor.isConst) {
return;
}
// default constructor is not 'const', report problem
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER,
constructor.returnType,
[supertype]);
}
/// Verify that if the given [constructor] declaration is 'const' then there
/// are no non-final instance variable. The [constructorElement] is the
/// constructor element.
void _checkForConstConstructorWithNonFinalField(
ConstructorDeclaration constructor,
ConstructorElement constructorElement) {
if (!_enclosingExecutable.isConstConstructor) {
return;
}
// check if there is non-final field
ClassElement classElement = constructorElement.enclosingElement;
if (!classElement.hasNonFinalField) {
return;
}
// TODO(brianwilkerson) Stop generating
// CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD when either
// CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER or
// CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD is also generated.
errorReporter.reportErrorForName(
CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD,
constructor);
}
/// Verify that the given 'const' instance creation [expression] is not
/// creating a deferred type. The [constructorName] is the constructor name,
/// always non-`null`. The [typeName] is the name of the type defining the
/// constructor, always non-`null`.
///
/// See [CompileTimeErrorCode.CONST_DEFERRED_CLASS].
void _checkForConstDeferredClass(InstanceCreationExpression expression,
ConstructorName constructorName, TypeName typeName) {
if (typeName.isDeferred) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_DEFERRED_CLASS,
constructorName,
[typeName.name.name]);
}
}
/// Verify that the given throw [expression] is not enclosed in a 'const'
/// constructor declaration.
///
/// See [CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION].
void _checkForConstEvalThrowsException(ThrowExpression expression) {
if (_enclosingExecutable.isConstConstructor) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, expression);
}
}
/// Verify that the given instance creation [expression] is not being invoked
/// on an abstract class. The [typeName] is the [TypeName] of the
/// [ConstructorName] from the [InstanceCreationExpression], this is the AST
/// node that the error is attached to. The [type] is the type being
/// constructed with this [InstanceCreationExpression].
void _checkForConstOrNewWithAbstractClass(
InstanceCreationExpression expression,
TypeName typeName,
InterfaceType type) {
if (type.element.isAbstract && !type.element.isMixin) {
var element = expression.constructorName.staticElement;
if (element != null && !element.isFactory) {
bool isImplicit =
(expression as InstanceCreationExpressionImpl).isImplicit;
if (!isImplicit) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS, typeName);
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS, typeName);
}
}
}
}
/// Verify that the given instance creation [expression] is not being invoked
/// on an enum. The [typeName] is the [TypeName] of the [ConstructorName] from
/// the [InstanceCreationExpression], this is the AST node that the error is
/// attached to. The [type] is the type being constructed with this
/// [InstanceCreationExpression].
///
/// See [CompileTimeErrorCode.INSTANTIATE_ENUM].
void _checkForConstOrNewWithEnum(InstanceCreationExpression expression,
TypeName typeName, InterfaceType type) {
if (type.element.isEnum) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANTIATE_ENUM, typeName);
}
}
/// Verify that the given [expression] is not a mixin instantiation.
void _checkForConstOrNewWithMixin(InstanceCreationExpression expression,
TypeName typeName, InterfaceType type) {
if (type.element.isMixin) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MIXIN_INSTANTIATE, typeName);
}
}
/// Verify that the given 'const' instance creation [expression] is not being
/// invoked on a constructor that is not 'const'.
///
/// This method assumes that the instance creation was tested to be 'const'
/// before being called.
///
/// See [CompileTimeErrorCode.CONST_WITH_NON_CONST].
void _checkForConstWithNonConst(InstanceCreationExpression expression) {
var constructorElement = expression.constructorName.staticElement;
if (constructorElement != null && !constructorElement.isConst) {
if (expression.keyword != null) {
errorReporter.reportErrorForToken(
CompileTimeErrorCode.CONST_WITH_NON_CONST, expression.keyword!);
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_WITH_NON_CONST, expression);
}
}
}
/// Verify that if the given 'const' instance creation [expression] is being
/// invoked on the resolved constructor. The [constructorName] is the
/// constructor name, always non-`null`. The [typeName] is the name of the
/// type defining the constructor, always non-`null`.
///
/// This method assumes that the instance creation was tested to be 'const'
/// before being called.
///
/// See [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR], and
/// [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT].
void _checkForConstWithUndefinedConstructor(
InstanceCreationExpression expression,
ConstructorName constructorName,
TypeName typeName) {
// OK if resolved
if (constructorName.staticElement != null) {
return;
}
DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
ClassElement element = type.element;
if (element.isEnum) {
// We have already reported the error.
return;
}
}
Identifier className = typeName.name;
// report as named or default constructor absence
var name = constructorName.name;
if (name != null) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR,
name,
[className, name]);
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT,
constructorName,
[className]);
}
}
void _checkForDeadNullCoalesce(TypeImpl lhsType, Expression rhs) {
if (!_isNonNullableByDefault) return;
if (typeSystem.isStrictlyNonNullable(lhsType)) {
errorReporter.reportErrorForNode(
StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION,
rhs,
);
}
}
/// Report a diagnostic if there are any extensions in the imported library
/// that are not hidden.
void _checkForDeferredImportOfExtensions(
ImportDirective directive, ImportElement importElement) {
for (var element in importElement.namespace.definedNames.values) {
if (element is ExtensionElement) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.DEFERRED_IMPORT_OF_EXTENSION,
directive.uri,
);
return;
}
}
}
/// Verify that any deferred imports in the given compilation [unit] have a
/// unique prefix.
///
/// See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX].
void _checkForDeferredPrefixCollisions(CompilationUnit unit) {
NodeList<Directive> directives = unit.directives;
int count = directives.length;
if (count > 0) {
Map<PrefixElement, List<ImportDirective>> prefixToDirectivesMap =
HashMap<PrefixElement, List<ImportDirective>>();
for (int i = 0; i < count; i++) {
Directive directive = directives[i];
if (directive is ImportDirective) {
var prefix = directive.prefix;
if (prefix != null) {
var element = prefix.staticElement;
if (element is PrefixElement) {
var elements = prefixToDirectivesMap[element];
if (elements == null) {
elements = <ImportDirective>[];
prefixToDirectivesMap[element] = elements;
}
elements.add(directive);
}
}
}
}
for (List<ImportDirective> imports in prefixToDirectivesMap.values) {
_checkDeferredPrefixCollision(imports);
}
}
}
/// Return `true` if the caller should continue checking the rest of the
/// information in the for-each part.
bool _checkForEachParts(ForEachParts node, SimpleIdentifier variable) {
if (checkForUseOfVoidResult(node.iterable)) {
return false;
}
DartType iterableType = node.iterable.typeOrThrow;
// TODO(scheglov) use NullableDereferenceVerifier
if (_isNonNullableByDefault) {
if (typeSystem.isNullable(iterableType)) {
return false;
}
}
// The type of the loop variable.
DartType variableType;
var variableElement = variable.staticElement;
if (variableElement is VariableElement) {
variableType = variableElement.type;
} else {
return false;
}
Token? awaitKeyword;
var parent = node.parent;
if (parent is ForStatement) {
awaitKeyword = parent.awaitKeyword;
} else if (parent is ForElement) {
awaitKeyword = parent.awaitKeyword;
}
// Use an explicit string instead of [loopType] to remove the "<E>".
String loopTypeName = awaitKeyword != null ? "Stream" : "Iterable";
// The object being iterated has to implement Iterable<T> for some T that
// is assignable to the variable's type.
// TODO(rnystrom): Move this into mostSpecificTypeArgument()?
iterableType = iterableType.resolveToBound(_typeProvider.objectType);
var requiredSequenceType = awaitKeyword != null
? _typeProvider.streamDynamicType
: _typeProvider.iterableDynamicType;
if (typeSystem.isTop(iterableType)) {
iterableType = requiredSequenceType;
}
if (!typeSystem.isAssignableTo(iterableType, requiredSequenceType)) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FOR_IN_OF_INVALID_TYPE,
node.iterable,
[iterableType, loopTypeName],
);
return false;
}
DartType? sequenceElementType;
{
var sequenceElement = awaitKeyword != null
? _typeProvider.streamElement
: _typeProvider.iterableElement;
var sequenceType = iterableType.asInstanceOf(sequenceElement);
if (sequenceType != null) {
sequenceElementType = sequenceType.typeArguments[0];
}
}
if (sequenceElementType == null) {
return true;
}
if (!typeSystem.isAssignableTo(sequenceElementType, variableType)) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE,
node.iterable,
[iterableType, loopTypeName, variableType],
);
}
return true;
}
/// Check that if the visiting library is not system, then any given library
/// should not be SDK internal library. The [exportElement] is the
/// [ExportElement] retrieved from the node, if the element in the node was
/// `null`, then this method is not called.
///
/// See [CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY].
void _checkForExportInternalLibrary(
ExportDirective directive, ExportElement exportElement) {
if (_isInSystemLibrary) {
return;
}
var exportedLibrary = exportElement.exportedLibrary;
if (exportedLibrary == null) {
return;
}
// should be private
var sdk = _currentLibrary.context.sourceFactory.dartSdk!;
var uri = exportedLibrary.source.uri.toString();
var sdkLibrary = sdk.getSdkLibrary(uri);
if (sdkLibrary == null) {
return;
}
if (!sdkLibrary.isInternal) {
return;
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY,
directive,
[directive.uri]);
}
/// See [CompileTimeErrorCode.EXPORT_LEGACY_SYMBOL].
void _checkForExportLegacySymbol(ExportDirective node) {
if (!_isNonNullableByDefault) {
return;
}
var element = node.element!;
// TODO(scheglov) Expose from ExportElement.
var namespace =
NamespaceBuilder().createExportNamespaceForDirective(element);
for (var element in namespace.definedNames.values) {
if (element == DynamicElementImpl.instance ||
element == NeverElementImpl.instance) {
continue;
}
if (!element.library!.isNonNullableByDefault) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXPORT_LEGACY_SYMBOL,
node.uri,
[element.displayName],
);
// Stop after the first symbol.
// We don't want to list them all.
break;
}
}
}
/// Verify that the given extends [clause] does not extend a deferred class.
///
/// See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS].
void _checkForExtendsDeferredClass(TypeName? superclass) {
if (superclass == null) {
return;
}
_checkForExtendsOrImplementsDeferredClass(
superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS);
}
/// Verify that the given extends [clause] does not extend classes such as
/// 'num' or 'String'.
///
/// See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS].
bool _checkForExtendsDisallowedClass(TypeName? superclass) {
if (superclass == null) {
return false;
}
return _checkForExtendsOrImplementsDisallowedClass(
superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS);
}
/// Verify that the given [typeName] does not extend, implement or mixin
/// classes that are deferred.
///
/// See [_checkForExtendsDeferredClass],
/// [_checkForExtendsDeferredClassInTypeAlias],
/// [_checkForImplementsDeferredClass],
/// [_checkForAllMixinErrorCodes],
/// [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS],
/// [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS], and
/// [CompileTimeErrorCode.MIXIN_DEFERRED_CLASS].
bool _checkForExtendsOrImplementsDeferredClass(
TypeName typeName, ErrorCode errorCode) {
if (typeName.isSynthetic) {
return false;
}
if (typeName.isDeferred) {
errorReporter.reportErrorForNode(errorCode, typeName);
return true;
}
return false;
}
/// Verify that the given [typeName] does not extend, implement or mixin
/// classes such as 'num' or 'String'.
///
/// TODO(scheglov) Remove this method, when all inheritance / override
/// is concentrated. We keep it for now only because we need to know when
/// inheritance is completely wrong, so that we don't need to check anything
/// else.
bool _checkForExtendsOrImplementsDisallowedClass(
TypeName typeName, ErrorCode errorCode) {
if (typeName.isSynthetic) {
return false;
}
// The SDK implementation may implement disallowed types. For example,
// JSNumber in dart2js and _Smi in Dart VM both implement int.
if (_currentLibrary.source.isInSystemLibrary) {
return false;
}
var type = typeName.type;
return type is InterfaceType &&
_typeProvider.isNonSubtypableClass(type.element);
}
void _checkForExtensionDeclaresMemberOfObject(MethodDeclaration node) {
if (_enclosingExtension == null) return;
var name = node.name.name;
if (name == '==' ||
name == 'hashCode' ||
name == 'toString' ||
name == 'runtimeType' ||
name == 'noSuchMethod') {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT,
node.name,
);
}
}
/// Verify that the given field formal [parameter] is in a constructor
/// declaration.
///
/// See [CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR].
void _checkForFieldInitializingFormalRedirectingConstructor(
FieldFormalParameter parameter) {
// prepare the node that should be a ConstructorDeclaration
var formalParameterList = parameter.parent;
if (formalParameterList is! FormalParameterList) {
formalParameterList = formalParameterList?.parent;
}
var constructor = formalParameterList?.parent;
// now check whether the node is actually a ConstructorDeclaration
if (constructor is ConstructorDeclaration) {
// constructor cannot be a factory
if (constructor.factoryKeyword != null) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR,
parameter);
return;
}
// constructor cannot have a redirection
for (ConstructorInitializer initializer in constructor.initializers) {
if (initializer is RedirectingConstructorInvocation) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR,
parameter);
return;
}
}
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR,
parameter);
}
}
/// Verify that the given variable declaration [list] has only initialized
/// variables if the list is final or const.
///
/// See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and
/// [StaticWarningCode.FINAL_NOT_INITIALIZED].
void _checkForFinalNotInitialized(VariableDeclarationList list) {
if (_isInNativeClass || list.isSynthetic) {
return;
}
// Handled during resolution, with flow analysis.
if (_isNonNullableByDefault &&
list.isFinal &&
list.parent is VariableDeclarationStatement) {
return;
}
bool isConst = list.isConst;
if (!(isConst || list.isFinal)) {
return;
}
NodeList<VariableDeclaration> variables = list.variables;
for (VariableDeclaration variable in variables) {
if (variable.initializer == null) {
if (isConst) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_NOT_INITIALIZED,
variable.name,
[variable.name.name]);
} else {
var variableElement = variable.declaredElement;
if (variableElement is FieldElement &&
(variableElement.isAbstract || variableElement.isExternal)) {
// Abstract and external fields can't be initialized, so no error.
} else if (variableElement is TopLevelVariableElement &&
variableElement.isExternal) {
// External top level variables can't be initialized, so no error.
} else if (!_isNonNullableByDefault || !variable.isLate) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.FINAL_NOT_INITIALIZED,
variable.name,
[variable.name.name]);
}
}
}
}
}
/// If there are no constructors in the given [members], verify that all
/// final fields are initialized. Cases in which there is at least one
/// constructor are handled in [_checkForAllFinalInitializedErrorCodes].
///
/// See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and
/// [StaticWarningCode.FINAL_NOT_INITIALIZED].
void _checkForFinalNotInitializedInClass(List<ClassMember> members) {
for (ClassMember classMember in members) {
if (classMember is ConstructorDeclaration) {
if (_isNonNullableByDefault) {
if (classMember.factoryKeyword == null) {
return;
}
} else {
return;
}
}
}
for (ClassMember classMember in members) {
if (classMember is FieldDeclaration) {
var fields = classMember.fields;
_checkForFinalNotInitialized(fields);
_checkForNotInitializedNonNullableInstanceFields(classMember);
}
}
}
void _checkForGenericFunctionType(TypeAnnotation? node) {
if (node == null) {
return;
}
if (_featureSet?.isEnabled(Feature.generic_metadata) ?? false) {
return;
}
DartType type = node.typeOrThrow;
if (type is FunctionType && type.typeFormals.isNotEmpty) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
node,
[type]);
}
}
/// Verify that the given implements [clause] does not implement classes such
/// as 'num' or 'String'.
///
/// See [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS],
/// [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS].
bool _checkForImplementsClauseErrorCodes(ImplementsClause? clause) {
if (clause == null) {
return false;
}
bool foundError = false;
for (TypeName type in clause.interfaces) {
if (_checkForExtendsOrImplementsDisallowedClass(
type, CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS)) {
foundError = true;
} else if (_checkForExtendsOrImplementsDeferredClass(
type, CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS)) {
foundError = true;
}
}
return foundError;
}
void _checkForImplicitDynamicIdentifier(AstNode node, Identifier? id) {
if (_options.implicitDynamic) {
return;
}
var variable = getVariableElement(id);
if (variable != null &&
variable.hasImplicitType &&
variable.type.isDynamic) {
ErrorCode errorCode;
if (variable is FieldElement) {
errorCode = LanguageCode.IMPLICIT_DYNAMIC_FIELD;
} else if (variable is ParameterElement) {
errorCode = LanguageCode.IMPLICIT_DYNAMIC_PARAMETER;
} else {
errorCode = LanguageCode.IMPLICIT_DYNAMIC_VARIABLE;
}
errorReporter.reportErrorForNode(errorCode, node, [id]);
}
}
void _checkForImplicitDynamicReturn(
AstNode functionName, ExecutableElement element) {
if (_options.implicitDynamic) {
return;
}
if (element is PropertyAccessorElement && element.isSetter) {
return;
}
if (element.hasImplicitReturnType && element.returnType.isDynamic) {
errorReporter.reportErrorForNode(LanguageCode.IMPLICIT_DYNAMIC_RETURN,
functionName, [element.displayName]);
}
}
void _checkForImplicitDynamicType(TypeAnnotation? node) {
if (_options.implicitDynamic ||
node == null ||
(node is TypeName && node.typeArguments != null)) {
return;
}
DartType type = node.typeOrThrow;
if (type is ParameterizedType &&
type.typeArguments.isNotEmpty &&
type.typeArguments.any((t) => t.isDynamic)) {
errorReporter
.reportErrorForNode(LanguageCode.IMPLICIT_DYNAMIC_TYPE, node, [type]);
}
}
/// Check that if the visiting library is not system, then any given library
/// should not be SDK internal library. The [importElement] is the
/// [ImportElement] retrieved from the node, if the element in the node was
/// `null`, then this method is not called
///
/// See [CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY].
void _checkForImportInternalLibrary(
ImportDirective directive, ImportElement importElement) {
if (_isInSystemLibrary) {
return;
}
var importedLibrary = importElement.importedLibrary;
if (importedLibrary == null) {
return;
}
// should be private
var sdk = _currentLibrary.context.sourceFactory.dartSdk!;
var uri = importedLibrary.source.uri.toString();
var sdkLibrary = sdk.getSdkLibrary(uri);
if (sdkLibrary == null || !sdkLibrary.isInternal) {
return;
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY,
directive.uri,
[directive.uri.stringValue]);
}
/// Check that the given [typeReference] is not a type reference and that then
/// the [name] is reference to an instance member.
///
/// See [CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER].
void _checkForInstanceAccessToStaticMember(
ClassElement? typeReference, Expression? target, SimpleIdentifier name) {
if (_isInComment) {
// OK, in comment
return;
}
// prepare member Element
var element = name.writeOrReadElement;
if (element is ExecutableElement) {
if (!element.isStatic) {
// OK, instance member
return;
}
Element enclosingElement = element.enclosingElement;
if (enclosingElement is ExtensionElement) {
if (target is ExtensionOverride) {
// OK, target is an extension override
return;
} else if (target is SimpleIdentifier &&
target.staticElement is ExtensionElement) {
return;
} else if (target is PrefixedIdentifier &&
target.staticElement is ExtensionElement) {
return;
}
} else {
if (typeReference != null) {
// OK, target is a type
return;
}
if (enclosingElement is! ClassElement) {
// OK, top-level element
return;
}
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
name,
[name.name, _getKind(element), element.enclosingElement.name]);
}
}
/// Verify that an 'int' can be assigned to the parameter corresponding to the
/// given [argument]. This is used for prefix and postfix expressions where
/// the argument value is implicit.
///
/// See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
void _checkForIntNotAssignable(Expression argument) {
var staticParameterElement = argument.staticParameterElement;
var staticParameterType = staticParameterElement?.type;
checkForArgumentTypeNotAssignable(argument, staticParameterType, _intType,
CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
}
/// Verify that the given [annotation] isn't defined in a deferred library.
///
/// See [CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY].
void _checkForInvalidAnnotationFromDeferredLibrary(Annotation annotation) {
Identifier nameIdentifier = annotation.name;
if (nameIdentifier is PrefixedIdentifier && nameIdentifier.isDeferred) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY,
annotation.name);
}
}
/// Check the given [initializer] to ensure that the field being initialized
/// is a valid field. The [fieldName] is the field name from the
/// [ConstructorFieldInitializer]. The [staticElement] is the static element
/// from the name in the [ConstructorFieldInitializer].
void _checkForInvalidField(ConstructorFieldInitializer initializer,
SimpleIdentifier fieldName, Element? staticElement) {
if (staticElement is FieldElement) {
if (staticElement.isSynthetic) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD,
initializer,
[fieldName]);
} else if (staticElement.isStatic) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD,
initializer,
[fieldName]);
}
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD,
initializer,
[fieldName]);
return;
}
}
/// Verify that if the given [identifier] is part of a constructor
/// initializer, then it does not implicitly reference 'this' expression.
///
/// See [CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER],
/// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY], and
/// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC].
void _checkForInvalidInstanceMemberAccess(SimpleIdentifier identifier) {
if (_isInComment) {
return;
}
if (!_isInConstructorInitializer &&
!_enclosingExecutable.inStaticMethod &&
!_enclosingExecutable.inFactoryConstructor &&
!_isInInstanceNotLateVariableDeclaration &&
!_isInStaticVariableDeclaration) {
return;
}
// prepare element
var element = identifier.writeOrReadElement;
if (!(element is MethodElement || element is PropertyAccessorElement)) {
return;
}
// static element
ExecutableElement executableElement = element as ExecutableElement;
if (executableElement.isStatic) {
return;
}
// not a class member
Element enclosingElement = element.enclosingElement;
if (enclosingElement is! ClassElement &&
enclosingElement is! ExtensionElement) {
return;
}
// qualified method invocation
var parent = identifier.parent;
if (parent is MethodInvocation) {
if (identical(parent.methodName, identifier) &&
parent.realTarget != null) {
return;
}
}
// qualified property access
if (parent is PropertyAccess) {
if (identical(parent.propertyName, identifier)) {
return;
}
}
if (parent is PrefixedIdentifier) {
if (identical(parent.identifier, identifier)) {
return;
}
}
if (_enclosingExecutable.inStaticMethod) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, identifier);
} else if (_enclosingExecutable.inFactoryConstructor) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, identifier);
} else {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER,
identifier,
[identifier.name]);
}
}
/// Check to see whether the given function [body] has a modifier associated
/// with it, and report it as an error if it does.
void _checkForInvalidModifierOnBody(
FunctionBody body, CompileTimeErrorCode errorCode) {
var keyword = body.keyword;
if (keyword != null) {
errorReporter.reportErrorForToken(errorCode, keyword, [keyword.lexeme]);
}
}
/// Verify that the usage of the given 'this' is valid.
///
/// See [CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS].
void _checkForInvalidReferenceToThis(ThisExpression expression) {
if (!_thisAccessTracker.hasAccess) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, expression);
}
}
void _checkForLateFinalFieldWithConstConstructor(FieldDeclaration node) {
if (node.isStatic) return;
var variableList = node.fields;
if (!variableList.isFinal) return;
var lateKeyword = variableList.lateKeyword;
if (lateKeyword == null) return;
var hasConstConstructor =
_enclosingClass!.constructors.any((c) => c.isConst);
if (!hasConstConstructor) return;
errorReporter.reportErrorForToken(
CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR,
lateKeyword,
);
}
void _checkForListConstructor(
InstanceCreationExpression node, InterfaceType type) {
if (!_isNonNullableByDefault) return;
if (node.constructorName.name == null && type.isDartCoreList) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.DEFAULT_LIST_CONSTRUCTOR,
node.constructorName,
);
}
}
/// Verify that the elements of the given list [literal] are subtypes of the
/// list's static type.
///
/// See [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE], and
/// [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE].
void _checkForListElementTypeNotAssignable(ListLiteral literal) {
// Determine the list's element type. We base this on the static type and
// not the literal's type arguments because in strong mode, the type
// arguments may be inferred.
DartType listType = literal.typeOrThrow;
assert(listType is InterfaceTypeImpl);
List<DartType> typeArguments =
(listType as InterfaceTypeImpl).typeArguments;
assert(typeArguments.length == 1);
DartType listElementType = typeArguments[0];
// Check every list element.
var verifier = LiteralElementVerifier(
_typeProvider,
typeSystem,
errorReporter,
checkForUseOfVoidResult,
forList: true,
elementType: listElementType,
featureSet: _featureSet!,
);
for (CollectionElement element in literal.elements) {
verifier.verify(element);
}
}
void _checkForMainFunction(SimpleIdentifier nameNode) {
if (!_currentLibrary.isNonNullableByDefault) {
return;
}
var element = nameNode.staticElement!;
// We should only check exported declarations, i.e. top-level.
if (element.enclosingElement is! CompilationUnitElement) {
return;
}
if (element.displayName != 'main') {
return;
}
if (element is! FunctionElement) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MAIN_IS_NOT_FUNCTION,
nameNode,
);
return;
}
var functionDeclaration = nameNode.parent as FunctionDeclaration;
var functionExpression = functionDeclaration.functionExpression;
var parameters = functionExpression.parameters!.parameters;
var positional = parameters.where((e) => e.isPositional).toList();
var requiredPositional =
parameters.where((e) => e.isRequiredPositional).toList();
if (requiredPositional.length > 2) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
nameNode,
);
}
if (parameters.any((e) => e.isRequiredNamed)) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS,
nameNode,
);
}
if (positional.isNotEmpty) {
var first = positional.first;
var type = first.declaredElement!.type;
var listOfString = _typeProvider.listType(_typeProvider.stringType);
if (!typeSystem.isSubtypeOf(listOfString, type)) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE,
first.notDefault.typeOrSelf,
);
}
}
}
void _checkForMapTypeNotAssignable(SetOrMapLiteral literal) {
// Determine the map's key and value types. We base this on the static type
// and not the literal's type arguments because in strong mode, the type
// arguments may be inferred.
DartType mapType = literal.typeOrThrow;
assert(mapType is InterfaceTypeImpl);
List<DartType> typeArguments = (mapType as InterfaceTypeImpl).typeArguments;
// It is possible for the number of type arguments to be inconsistent when
// the literal is ambiguous and a non-map type was selected.
// TODO(brianwilkerson) Unify this and _checkForSetElementTypeNotAssignable3
// to better handle recovery situations.
if (typeArguments.length == 2) {
DartType keyType = typeArguments[0];
DartType valueType = typeArguments[1];
var verifier = LiteralElementVerifier(
_typeProvider,
typeSystem,
errorReporter,
checkForUseOfVoidResult,
forMap: true,
mapKeyType: keyType,
mapValueType: valueType,
featureSet: _featureSet!,
);
for (CollectionElement element in literal.elements) {
verifier.verify(element);
}
}
}
/// Check to make sure that the given switch [statement] whose static type is
/// an enum type either have a default case or include all of the enum
/// constants.
void _checkForMissingEnumConstantInSwitch(SwitchStatement statement) {
// TODO(brianwilkerson) This needs to be checked after constant values have
// been computed.
var expressionType = statement.expression.staticType;
var hasCaseNull = false;
if (expressionType is InterfaceType) {
var enumElement = expressionType.element;
if (enumElement.isEnum) {
var constantNames = enumElement.fields
.where((field) => field.isStatic && !field.isSynthetic)
.map((field) => field.name)
.toSet();
for (var member in statement.members) {
if (member is SwitchCase) {
var expression = member.expression.unParenthesized;
if (expression is NullLiteral) {
hasCaseNull = true;
} else {
var constantName = _getConstantName(expression);
constantNames.remove(constantName);
}
}
if (member is SwitchDefault) {
return;
}
}
for (var constantName in constantNames) {
int offset = statement.offset;
int end = statement.rightParenthesis.end;
errorReporter.reportErrorForOffset(
StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH,
offset,
end - offset,
[constantName],
);
}
if (typeSystem.isNullable(expressionType) && !hasCaseNull) {
int offset = statement.offset;
int end = statement.rightParenthesis.end;
errorReporter.reportErrorForOffset(
StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH,
offset,
end - offset,
['null'],
);
}
}
}
}
void _checkForMissingJSLibAnnotation(Annotation node) {
if (node.elementAnnotation?.isJS ?? false) {
if (_currentLibrary.hasJS != true) {
errorReporter.reportErrorForNode(
HintCode.MISSING_JS_LIB_ANNOTATION, node);
}
}
}
/// Verify that the given mixin does not have an explicitly declared
/// constructor. The [mixinName] is the node to report problem on. The
/// [mixinElement] is the mixing to evaluate.
///
/// See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR].
bool _checkForMixinClassDeclaresConstructor(
TypeName mixinName, ClassElement mixinElement) {
for (ConstructorElement constructor in mixinElement.constructors) {
if (!constructor.isSynthetic && !constructor.isFactory) {
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR,
mixinName,
[mixinElement.name]);
return true;
}
}
return false;
}
/// Verify that the given mixin has the 'Object' superclass.
///
/// The [mixinName] is the node to report problem on. The [mixinElement] is
/// the mixing to evaluate.
///
/// See [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT].
bool _checkForMixinInheritsNotFromObject(
TypeName mixinName, ClassElement mixinElement) {
var mixinSupertype = mixinElement.supertype;
if (mixinSupertype == null || mixinSupertype.isDartCoreObject) {
var mixins = mixinElement.mixins;
if (mixins.isEmpty ||
mixinElement.isMixinApplication && mixins.length < 2) {
return false;
}
}
errorReporter.reportErrorForNode(
CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT,
mixinName,
[mixinElement.name],
);
return true;
}
/// Check that superclass constrains for the mixin type of [mixinName] at
/// the [mixinIndex] position in the mixins list are satisfied by the
/// [_enclosingClass], or a previous mixin.
bool _checkForMixinSuperclassConstraints(int mixinIndex, TypeName mixinName) {
InterfaceType mixinType = mixinName.type as InterfaceType;
for (var constraint in mixinType.superclassConstraints) {
var superType = _enclosingClass!.supertype as InterfaceTypeImpl;
if (_currentLibrary.isNonNullableByDefault) {
superType = superType.withNullability(NullabilitySuffix.none);
}
bool isSatisfied = typeSystem.isSubtypeOf(superType, constraint);
if (!isSatisfied) {
for (int i = 0; i < mixinIndex && !isSatisfied; i++) {
isSatisfied =
typeSystem.isSubtypeOf(_enclosingClass!.mixins[i], constraint);
}
}
if (!