blob: 4ed1fa1eb32638f5958dd6e54315be8dd8a33ae0 [file] [log] [blame] [edit]
// Copyright (c) 2025, 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:kernel/ast.dart';
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/member_builder.dart';
import '../codes/cfe_codes.dart';
import 'compiler_context.dart';
abstract class LookupResult {
/// The [NamedBuilder] used for reading this entity, if any.
NamedBuilder? get getable;
/// The [NamedBuilder] used for writing to this entity, if any.
NamedBuilder? get setable;
/// Returns `true` if the result is invalid.
///
/// For instance because of duplicate declaration or because of an invalid
/// scope origin.
bool get isInvalidLookup;
static LocatedMessage createDuplicateMessage(
LookupResult lookupResult, {
DeclarationBuilder? enclosingDeclaration,
required String name,
required Uri fileUri,
required int fileOffset,
required int length,
}) {
if (name.isEmpty) {
if (enclosingDeclaration != null) {
name = enclosingDeclaration.name;
} else {
name = 'new';
}
length = noLength;
}
Message message = codeDuplicatedDeclarationUse.withArgumentsOld(name);
return message.withLocation(fileUri, fileOffset, length);
}
static InvalidExpression createDuplicateExpression(
LookupResult lookupResult, {
required CompilerContext context,
DeclarationBuilder? enclosingDeclaration,
required String name,
required Uri fileUri,
required int fileOffset,
required int length,
}) {
String text = context
.format(
createDuplicateMessage(
lookupResult,
enclosingDeclaration: enclosingDeclaration,
name: name,
fileUri: fileUri,
fileOffset: fileOffset,
length: length,
),
CfeSeverity.error,
)
.plain;
return new InvalidExpression(text)..fileOffset = fileOffset;
}
static LookupResult? createResult(
NamedBuilder? getable,
NamedBuilder? setable,
) {
return _fromBuilders(getable, setable, assertNoGetterSetterConflict: false);
}
static LookupResult? _fromBuilders(
NamedBuilder? getable,
NamedBuilder? setable, {
required bool assertNoGetterSetterConflict,
}) {
if (getable is LookupResult) {
LookupResult lookupResult = getable as LookupResult;
if (setable == getable) {
return lookupResult;
} else if (setable == null) {
return lookupResult;
} else {
assert(
getable != setable,
"Unexpected getable $getable and setable $setable.",
);
assert(
!assertNoGetterSetterConflict ||
// Coverage-ignore(suite): Not run.
lookupResult.setable == null,
"Unexpected setable ${lookupResult.setable} from "
"getable $getable and setable $setable.",
);
return new GetableSetableResult(getable!, setable);
}
} else if (setable is LookupResult) {
// Coverage-ignore-block(suite): Not run.
LookupResult lookupResult = setable as LookupResult;
if (getable == null) {
return lookupResult;
} else {
assert(
getable != setable,
"Unexpected getable $getable and setable $setable.",
);
assert(
!assertNoGetterSetterConflict || lookupResult.getable == null,
"Unexpected getable ${lookupResult.getable} from "
"setable $setable and getable $getable.",
);
return new GetableSetableResult(getable, setable!);
}
} else {
// Coverage-ignore-block(suite): Not run.
if (getable != null && setable != null) {
return new GetableSetableResult(getable, setable);
} else if (getable != null) {
return new GetableResult(getable);
} else if (setable != null) {
return new SetableResult(setable);
} else {
return null;
}
}
}
}
abstract class InvalidLookupResult implements LookupResult {
factory InvalidLookupResult(LocatedMessage message) =
_InvalidLookupResultImpl;
LocatedMessage get message;
}
// Coverage-ignore(suite): Not run.
class _InvalidLookupResultImpl implements InvalidLookupResult {
@override
final LocatedMessage message;
_InvalidLookupResultImpl(this.message);
@override
bool get isInvalidLookup => true;
@override
NamedBuilder? get getable => null;
@override
NamedBuilder? get setable => null;
}
abstract class MemberLookupResult implements LookupResult {
/// The [MemberBuilder] used for reading this entity, if any.
@override
MemberBuilder? get getable;
/// The [MemberBuilder] used for writing to this entity, if any.
@override
MemberBuilder? get setable;
/// Return `true` if this [MemberBuilder]s of this lookup result are accessed
/// statically.
bool get isStatic;
}
class InvalidMemberLookupResult
implements InvalidLookupResult, MemberLookupResult {
@override
final LocatedMessage message;
InvalidMemberLookupResult(this.message);
@override
bool get isInvalidLookup => true;
@override
// Coverage-ignore(suite): Not run.
MemberBuilder? get getable => null;
@override
// Coverage-ignore(suite): Not run.
MemberBuilder? get setable => null;
@override
// Coverage-ignore(suite): Not run.
bool get isStatic => true;
}
class GetableResult with LookupResultMixin implements LookupResult {
@override
final NamedBuilder getable;
GetableResult(this.getable);
@override
NamedBuilder? get setable => null;
}
// Coverage-ignore(suite): Not run.
class SetableResult with LookupResultMixin implements LookupResult {
@override
final NamedBuilder setable;
SetableResult(this.setable);
@override
NamedBuilder? get getable => null;
}
class GetableSetableResult with LookupResultMixin implements LookupResult {
@override
final NamedBuilder getable;
@override
final NamedBuilder setable;
GetableSetableResult(this.getable, this.setable);
}
class GetableSetableMemberResult
with LookupResultMixin
implements MemberLookupResult {
@override
final MemberBuilder getable;
@override
final MemberBuilder setable;
@override
final bool isStatic;
GetableSetableMemberResult(
this.getable,
this.setable, {
required this.isStatic,
});
}
mixin LookupResultMixin implements LookupResult {
@override
bool get isInvalidLookup =>
(getable?.isDuplicate ?? false) || (setable?.isDuplicate ?? false);
}
class DuplicateMemberLookupResult implements MemberLookupResult {
final List<MemberBuilder> declarations;
DuplicateMemberLookupResult(this.declarations);
@override
MemberBuilder? get getable => null;
@override
bool get isInvalidLookup => true;
@override
MemberBuilder? get setable => null;
/// Return `true` if this [MemberBuilder]s of this lookup result are accessed
/// statically.
///
/// Since this lookup can contain both static and non-static members, we
/// return `true` so that it will not be filtered in static member lookup.
@override
bool get isStatic => true;
}