blob: 283204969d479727e70bb084cf44a97246178b8c [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.
library fasta.formal_parameter_builder;
import 'package:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart'
show FormalParameterKind, FormalParameterKindExtension;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token;
import 'package:kernel/ast.dart'
show DartType, DynamicType, Expression, NullLiteral, VariableDeclaration;
import '../constant_context.dart' show ConstantContext;
import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/internal_ast.dart' show VariableDeclarationImpl;
import '../modifier.dart';
import '../scope.dart' show Scope;
import '../source/source_factory_builder.dart';
import '../source/source_field_builder.dart';
import '../source/source_library_builder.dart';
import '../util/helpers.dart' show DelayedActionPerformer;
import 'builder.dart';
import 'class_builder.dart';
import 'constructor_builder.dart';
import 'library_builder.dart';
import 'metadata_builder.dart';
import 'modifier_builder.dart';
import 'named_type_builder.dart';
import 'type_builder.dart';
import 'variable_builder.dart';
abstract class ParameterBuilder {
/// List of metadata builders for the metadata declared on this parameter.
List<MetadataBuilder>? get metadata;
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;
ParameterBuilder clone(
List<NamedTypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration);
}
/// A builder for a formal parameter, i.e. a parameter on a method or
/// constructor.
class FormalParameterBuilder extends ModifierBuilderImpl
implements VariableBuilder, ParameterBuilder {
static const String noNameSentinel = 'no name sentinel';
/// List of metadata builders for the metadata declared on this parameter.
@override
final List<MetadataBuilder>? metadata;
@override
final int modifiers;
@override
final 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.
bool hasDeclaredInitializer = false;
final bool isExtensionThis;
FormalParameterBuilder(this.metadata, this.kind, this.modifiers, this.type,
this.name, LibraryBuilder? compilationUnit, int charOffset,
{Uri? fileUri, this.isExtensionThis: false})
: this.fileUri = fileUri ?? compilationUnit?.fileUri,
super(compilationUnit, charOffset);
@override
String get debugName => "FormalParameterBuilder";
@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;
@override
bool get isLocal => true;
bool get isInitializingFormal => (modifiers & initializingFormalMask) != 0;
bool get isSuperInitializingFormal =>
(modifiers & superInitializingFormalMask) != 0;
bool get isCovariantByDeclaration => (modifiers & covariantMask) != 0;
// An initializing formal parameter might be final without its
// VariableDeclaration being final. See
// [ProcedureBuilder.computeFormalParameterInitializerScope]..
@override
bool get isAssignable =>
variable!.isAssignable &&
!isInitializingFormal &&
!isSuperInitializingFormal;
@override
String get fullNameForErrors => name;
VariableDeclaration build(
SourceLibraryBuilder library, int functionNestingLevel) {
if (variable == null) {
DartType? builtType = type?.build(library, TypeUse.parameterType);
variable = new VariableDeclarationImpl(
name == noNameSentinel ? null : name, functionNestingLevel,
type: builtType,
isFinal: isFinal,
isConst: false,
isInitializingFormal: isInitializingFormal,
isCovariantByDeclaration: isCovariantByDeclaration,
isRequired: isRequiredNamed,
hasDeclaredInitializer: hasDeclaredInitializer,
isLowered: isExtensionThis)
..fileOffset = charOffset;
}
return variable!;
}
@override
ParameterBuilder clone(
List<NamedTypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration) {
// TODO(cstefantsova): It's not clear how [metadata] is used currently,
// and how it should be cloned. Consider cloning it instead of reusing it.
return new FunctionTypeParameterBuilder(metadata, kind,
type?.clone(newTypes, contextLibrary, contextDeclaration), name);
}
FormalParameterBuilder forFormalParameterInitializerScope() {
// ignore: unnecessary_null_comparison
assert(variable != null);
if (isInitializingFormal) {
return new FormalParameterBuilder(
metadata,
kind,
modifiers | finalMask | initializingFormalMask,
type,
name,
null,
charOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis)
..parent = parent
..variable = variable;
} else if (isSuperInitializingFormal) {
return new FormalParameterBuilder(
metadata,
kind,
modifiers | finalMask | superInitializingFormalMask,
type,
name,
null,
charOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis)
..parent = parent
..variable = variable;
} else {
return this;
}
}
void finalizeInitializingFormal(ClassBuilder classBuilder) {
Builder? fieldBuilder = classBuilder.lookupLocalMember(name);
if (fieldBuilder is SourceFieldBuilder) {
variable!.type = fieldBuilder.inferType();
} else {
variable!.type = const DynamicType();
}
}
/// Builds the default value from this [initializerToken] if this is a
/// formal parameter on a const constructor or instance method.
void buildOutlineExpressions(SourceLibraryBuilder library,
List<DelayedActionPerformer> delayedActionPerformers) {
// 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, and
// * for generative constructors to support forwarding constructors
// in mixin applications.
bool needsDefaultValues = false;
if (parent is ConstructorBuilder) {
needsDefaultValues = true;
} else if (parent is SourceFactoryBuilder) {
needsDefaultValues = parent!.isFactory && parent!.isConst;
} else {
needsDefaultValues = parent!.isClassInstanceMember;
}
if (needsDefaultValues) {
if (initializerToken != null) {
final ClassBuilder classBuilder = parent!.parent as ClassBuilder;
Scope scope = classBuilder.scope;
BodyBuilder bodyBuilder = library.loader
.createBodyBuilderForOutlineExpression(
library, classBuilder, this, 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;
library.loader.transformPostInference(
variable!,
bodyBuilder.transformSetLiterals,
bodyBuilder.transformCollections,
library.library);
initializerWasInferred = true;
bodyBuilder.performBacklogComputations(delayedActionPerformers);
} else if (kind != FormalParameterKind.requiredPositional) {
// As done by BodyBuilder.endFormalParameter.
variable!.initializer = new NullLiteral()..parent = variable;
}
}
initializerToken = null;
}
}
class FunctionTypeParameterBuilder implements ParameterBuilder {
@override
final List<MetadataBuilder>? metadata;
@override
final FormalParameterKind kind;
@override
final TypeBuilder? type;
@override
final String? name;
FunctionTypeParameterBuilder(this.metadata, this.kind, this.type, this.name);
@override
ParameterBuilder clone(
List<NamedTypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration) {
// TODO(cstefantsova): It's not clear how [metadata] is used currently,
// and how it should be cloned. Consider cloning it instead of reusing it.
return new FunctionTypeParameterBuilder(metadata, kind,
type?.clone(newTypes, contextLibrary, contextDeclaration), 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;
}