blob: caf698d1d40462a91a71c710ebde706ab7c804e8 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart'
show FormalParameterKind;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token;
import 'package:kernel/ast.dart'
show
DartType,
DynamicType,
Expression,
InvalidExpression,
NullLiteral,
VariableDeclaration;
import 'package:kernel/class_hierarchy.dart';
import '../base/constant_context.dart' show ConstantContext;
import '../base/lookup_result.dart';
import '../base/modifiers.dart';
import '../base/scope.dart' show LookupScope;
import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/body_builder_context.dart';
import '../kernel/internal_ast.dart' show VariableDeclarationImpl;
import '../kernel/wildcard_lowering.dart';
import '../source/fragment_factory.dart';
import '../source/source_constructor_builder.dart';
import '../source/source_factory_builder.dart';
import '../source/source_library_builder.dart';
import '../source/source_property_builder.dart';
import 'builder.dart';
import 'constructor_builder.dart';
import 'declaration_builders.dart';
import 'member_builder.dart';
import 'omitted_type_builder.dart';
import 'property_builder.dart';
import 'type_builder.dart';
import 'variable_builder.dart';
abstract class ParameterBuilder {
TypeBuilder get type;
/// The kind of this parameter, i.e. if it's required, positional optional,
/// or named optional.
FormalParameterKind get kind;
bool get isPositional;
bool get isRequiredPositional;
bool get isNamed;
bool get isRequiredNamed;
String? get name;
}
/// A builder for a formal parameter, i.e. a parameter on a method or
/// constructor.
class FormalParameterBuilder extends NamedBuilderImpl
with LookupResultMixin
implements VariableBuilder, ParameterBuilder, InferredTypeListener {
static const String noNameSentinel = 'no name sentinel';
@override
final int fileOffset;
final Modifiers modifiers;
@override
TypeBuilder type;
@override
final String name;
@override
final Uri fileUri;
@override
final FormalParameterKind kind;
/// The variable declaration created for this formal parameter.
@override
VariableDeclaration? variable;
/// The first token of the default value, if any.
///
/// This is stored until outlines have been built through
/// [buildOutlineExpressions].
Token? initializerToken;
bool initializerWasInferred = false;
/// True if the initializer was declared by the programmer.
final bool hasImmediatelyDeclaredInitializer;
/// True if the initializer was declared by the programmer, either directly
/// or inferred from a super parameter.
bool hasDeclaredInitializer;
final bool isExtensionThis;
/// Whether this formal parameter is a wildcard variable.
final bool isWildcard;
FormalParameterBuilder(
this.kind,
this.modifiers,
this.type,
this.name,
this.fileOffset, {
required this.fileUri,
this.isExtensionThis = false,
required this.hasImmediatelyDeclaredInitializer,
this.isWildcard = false,
}) : this.hasDeclaredInitializer = hasImmediatelyDeclaredInitializer {
type.registerInferredTypeListener(this);
}
@override
// Coverage-ignore(suite): Not run.
Builder? get parent => null;
@override
bool get isRequiredPositional => kind.isRequiredPositional;
// TODO(johnniwinther): This was previously named `isOptional` so we might
// have some uses that intended to use the now existing `isOptional` method.
bool get isOptionalPositional => !isRequiredPositional;
@override
bool get isRequiredNamed => kind.isRequiredNamed;
@override
bool get isPositional => kind.isPositional;
@override
bool get isNamed => kind.isNamed;
bool get isOptional => kind.isOptional;
bool get isInitializingFormal => modifiers.isInitializingFormal;
bool get isSuperInitializingFormal => modifiers.isSuperInitializingFormal;
bool get isCovariantByDeclaration => modifiers.isCovariant;
@override
bool get isConst => modifiers.isConst;
// An initializing formal parameter might be final without its
// VariableDeclaration being final. See
// [ProcedureBuilder.computeFormalParameterInitializerScope]..
@override
bool get isAssignable =>
variable!.isAssignable &&
!isInitializingFormal &&
!isSuperInitializingFormal;
@override
NamedBuilder get getable => this;
@override
NamedBuilder? get setable => isAssignable ? this : null;
@override
// Coverage-ignore(suite): Not run.
String get fullNameForErrors => name;
VariableDeclaration build(SourceLibraryBuilder library) {
if (variable == null) {
bool isTypeOmitted = type is OmittedTypeBuilder;
DartType? builtType = type.build(library, TypeUse.parameterType);
variable = new VariableDeclarationImpl(
name == noNameSentinel ? null : name,
// `null` is used in [VariableDeclarationImpl] to signal an omitted
// type.
type: isTypeOmitted ? null : builtType,
isFinal: modifiers.isFinal,
isConst: false,
isInitializingFormal: isInitializingFormal,
isSuperInitializingFormal: isSuperInitializingFormal,
isCovariantByDeclaration: isCovariantByDeclaration,
isRequired: isRequiredNamed,
hasDeclaredInitializer: hasDeclaredInitializer,
isLowered: isExtensionThis,
isSynthesized: name == noNameSentinel,
isWildcard: isWildcard,
)..fileOffset = fileOffset;
}
return variable!;
}
@override
void onInferredType(DartType type) {
if (variable != null) {
variable!.type = type;
}
}
FormalParameterBuilder forPrimaryConstructor(FragmentFactory builderFactory) {
return new FormalParameterBuilder(
kind,
modifiers | Modifiers.InitializingFormal,
builderFactory.addInferableType(),
name,
fileOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis,
hasImmediatelyDeclaredInitializer: hasImmediatelyDeclaredInitializer,
)..variable = variable;
}
FormalParameterBuilder forFormalParameterInitializerScope() {
if (isInitializingFormal) {
return new FormalParameterBuilder(
kind,
modifiers | Modifiers.Final | Modifiers.InitializingFormal,
type,
name,
fileOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis,
hasImmediatelyDeclaredInitializer: hasImmediatelyDeclaredInitializer,
)..variable = variable;
} else if (isSuperInitializingFormal) {
return new FormalParameterBuilder(
kind,
modifiers | Modifiers.Final | Modifiers.SuperInitializingFormal,
type,
name,
fileOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis,
hasImmediatelyDeclaredInitializer: hasImmediatelyDeclaredInitializer,
)..variable = variable;
} else {
return this;
}
}
void finalizeInitializingFormal(
DeclarationBuilder declarationBuilder,
SourceConstructorBuilder constructorBuilder,
ClassHierarchyBase hierarchy,
) {
String fieldName = isWildcardLoweredFormalParameter(name) ? '_' : name;
LookupResult? result = declarationBuilder.lookupLocalMember(fieldName);
Builder? fieldBuilder = result?.getable;
if (result is DuplicateMemberLookupResult) {
fieldBuilder = result.declarations.first;
}
if (fieldBuilder is SourcePropertyBuilder && fieldBuilder.hasField) {
DartType fieldType = fieldBuilder.inferFieldType(hierarchy);
fieldType = constructorBuilder.substituteFieldType(fieldType);
type.registerInferredType(fieldType);
} else {
type.registerInferredType(const DynamicType());
}
}
static bool needsDefaultValuesBuiltAsOutlineExpressions(
MemberBuilder memberBuilder,
) {
// For modular compilation we need to include default values for optional
// and named parameters in several cases:
// * for const constructors to enable constant evaluation,
// * for instance methods because these might be needed to generated
// noSuchMethod forwarders,
// * for generative constructors to support forwarding constructors
// in mixin applications, and
// * for factories, to uphold the invariant that optional parameters always
// have default values, even during modular compilation.
if (memberBuilder is ConstructorBuilder) {
return true;
} else if (memberBuilder is SourceFactoryBuilder) {
return true;
} else {
// Coverage-ignore-block(suite): Not run.
return memberBuilder.isClassInstanceMember;
}
}
/// Builds the default value from this [initializerToken] if this is a
/// formal parameter on a const constructor or instance method.
void buildOutlineExpressions(
SourceLibraryBuilder libraryBuilder,
DeclarationBuilder? declarationBuilder, {
required LookupScope scope,
required bool buildDefaultValue,
}) {
if (buildDefaultValue) {
if (initializerToken != null) {
BodyBuilderContext bodyBuilderContext = new ParameterBodyBuilderContext(
libraryBuilder,
declarationBuilder,
this,
);
BodyBuilder bodyBuilder = libraryBuilder.loader
.createBodyBuilderForOutlineExpression(
libraryBuilder,
bodyBuilderContext,
scope,
fileUri,
);
bodyBuilder.constantContext = ConstantContext.required;
assert(!initializerWasInferred);
Expression initializer = bodyBuilder.parseFieldInitializer(
initializerToken!,
);
initializer = bodyBuilder.typeInferrer.inferParameterInitializer(
bodyBuilder,
initializer,
variable!.type,
hasDeclaredInitializer,
);
variable!.initializer = initializer..parent = variable;
if (initializer is InvalidExpression) {
variable!.isErroneouslyInitialized = true;
}
initializerWasInferred = true;
bodyBuilder.performBacklogComputations();
} else if (kind.isOptional) {
// As done by BodyBuilder.endFormalParameter.
variable!.initializer = new NullLiteral()..parent = variable;
}
}
initializerToken = null;
}
@override
String toString() => '$runtimeType($name)';
}
class FunctionTypeParameterBuilder implements ParameterBuilder {
@override
final FormalParameterKind kind;
@override
final TypeBuilder type;
@override
final String? name;
FunctionTypeParameterBuilder(this.kind, this.type, this.name);
@override
bool get isNamed => kind.isNamed;
@override
bool get isRequiredNamed => kind.isRequiredNamed;
@override
bool get isPositional => kind.isPositional;
@override
bool get isRequiredPositional => kind.isRequiredPositional;
}