mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[dart2js] Disallow default clauses on switches over enums and enum-like classes.
In a recent change I added a new value to an enum and had no easy way to statically find all the switches where I might need to handle that new enum value. Instead I had to wait for runtime errors (or worse-yet incorrect generated code) to find what I had missed. Though it may be add a little inconvience to now allow "default" cases, the static warnings/checks it provides are worth it. Change-Id: I2347bcec77ee8bc0a5618ea4dbc6e0840bdf2f6c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/343321 Commit-Queue: Nate Biggs <natebiggs@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
334ac53602
commit
acbbfd60fb
|
@ -17,3 +17,5 @@ linter:
|
|||
- prefer_if_null_operators
|
||||
- prefer_null_aware_operators
|
||||
- use_super_parameters
|
||||
- exhaustive_cases
|
||||
- no_default_cases
|
||||
|
|
|
@ -2263,9 +2263,6 @@ abstract class DartTypes {
|
|||
if (!_isSubtype(sArgs[i], tArgs[i], env) ||
|
||||
!_isSubtype(tArgs[i], sArgs[i], env)) return false;
|
||||
break;
|
||||
default:
|
||||
throw StateError(
|
||||
"Invalid variance ${variances[i]} used for subtype check.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -492,7 +492,6 @@ class FlatTypeMask extends TypeMask {
|
|||
return other.withFlags(
|
||||
isNullable: includeNull, hasLateSentinel: includeLateSentinel);
|
||||
case SubclassResultKind.SET:
|
||||
default:
|
||||
if (result.classes.isEmpty) {
|
||||
return includeNull
|
||||
? TypeMask.empty(hasLateSentinel: includeLateSentinel)
|
||||
|
|
|
@ -69,7 +69,13 @@ String? computeKernelElementNameForSourceMaps(
|
|||
String enclosingMemberName =
|
||||
computeElementNameForSourceMaps(enclosingMember, callStructure)!;
|
||||
return '$enclosingMemberName.$name';
|
||||
default:
|
||||
case MemberKind.constructor:
|
||||
case MemberKind.constructorBody:
|
||||
case MemberKind.recordGetter:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.parameterStub:
|
||||
return computeElementNameForSourceMaps(member, callStructure);
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +196,7 @@ class KernelSourceInformationBuilder implements SourceInformationBuilder {
|
|||
case MemberKind.closureField:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.generatorBody:
|
||||
// TODO(sra): Should we target the generator itself for generatorBody
|
||||
// TODO(sra): Should we target the generator itself for generatorBody?
|
||||
break;
|
||||
}
|
||||
return _buildTreeNode(base ?? definition.node as ir.TreeNode, name: name);
|
||||
|
@ -295,7 +301,12 @@ class KernelSourceInformationBuilder implements SourceInformationBuilder {
|
|||
case MemberKind.closureCall:
|
||||
final node = definition.node as ir.LocalFunction;
|
||||
return _buildFunctionExit(node, node.function);
|
||||
default:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.recordGetter:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.parameterStub:
|
||||
break;
|
||||
}
|
||||
return _buildTreeNode(definition.node as ir.TreeNode);
|
||||
}
|
||||
|
|
|
@ -1568,8 +1568,6 @@ class EvaluationComplexity {
|
|||
case ComplexityLevel.definitelyLazy:
|
||||
sb.write('lazy');
|
||||
break;
|
||||
default:
|
||||
throw UnsupportedError("Unexpected complexity level $level");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
@ -77,9 +77,6 @@ AsyncMarker getAsyncMarker(ir.FunctionNode node) {
|
|||
return AsyncMarker.SYNC;
|
||||
case ir.AsyncMarker.SyncStar:
|
||||
return AsyncMarker.SYNC_STAR;
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
"Async marker ${node.asyncMarker} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,24 @@ class CodegenEnqueuer extends Enqueuer {
|
|||
// TODO(johnniwinther): Should this be tracked with _MemberUsage ?
|
||||
listener.registerUsedElement(staticUse.element as MemberEntity);
|
||||
break;
|
||||
default:
|
||||
case StaticUseKind.CALL_METHOD:
|
||||
case StaticUseKind.CLOSURE:
|
||||
case StaticUseKind.CLOSURE_CALL:
|
||||
case StaticUseKind.DIRECT_INVOKE:
|
||||
case StaticUseKind.FIELD_CONSTANT_INIT:
|
||||
case StaticUseKind.FIELD_INIT:
|
||||
case StaticUseKind.INSTANCE_FIELD_GET:
|
||||
case StaticUseKind.INSTANCE_FIELD_SET:
|
||||
case StaticUseKind.STATIC_GET:
|
||||
case StaticUseKind.STATIC_INVOKE:
|
||||
case StaticUseKind.STATIC_SET:
|
||||
case StaticUseKind.STATIC_TEAR_OFF:
|
||||
case StaticUseKind.SUPER_FIELD_SET:
|
||||
case StaticUseKind.SUPER_GET:
|
||||
case StaticUseKind.SUPER_INVOKE:
|
||||
case StaticUseKind.SUPER_SETTER_SET:
|
||||
case StaticUseKind.SUPER_TEAR_OFF:
|
||||
case StaticUseKind.WEAK_STATIC_TEAR_OFF:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -104,7 +104,20 @@ class CodegenImpactTransformer {
|
|||
_closedWorld.outputUnitData.registerConstantDeferredUse(
|
||||
constantUse.value as DeferredGlobalConstantValue);
|
||||
break;
|
||||
default:
|
||||
case ConstantValueKind.BOOL:
|
||||
case ConstantValueKind.DOUBLE:
|
||||
case ConstantValueKind.DUMMY_INTERCEPTOR:
|
||||
case ConstantValueKind.FUNCTION:
|
||||
case ConstantValueKind.INT:
|
||||
case ConstantValueKind.INTERCEPTOR:
|
||||
case ConstantValueKind.JAVASCRIPT_OBJECT:
|
||||
case ConstantValueKind.JS_NAME:
|
||||
case ConstantValueKind.LATE_SENTINEL:
|
||||
case ConstantValueKind.NULL:
|
||||
case ConstantValueKind.RECORD:
|
||||
case ConstantValueKind.STRING:
|
||||
case ConstantValueKind.TYPE:
|
||||
case ConstantValueKind.UNREACHABLE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -175,6 +188,9 @@ class CodegenImpactTransformer {
|
|||
_impacts.asyncStarBody
|
||||
.registerImpact(transformed, _elementEnvironment);
|
||||
break;
|
||||
case AsyncMarker.SYNC:
|
||||
// No implicit impacts.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1988,9 +1988,6 @@ abstract class ModularNamer {
|
|||
return asName(fixedNames.recordShapeTag);
|
||||
case JsGetName.RECORD_SHAPE_TYPE_PROPERTY:
|
||||
return asName(fixedNames.recordShapeRecipe);
|
||||
default:
|
||||
throw failedAt(spannable ?? CURRENT_ELEMENT_SPANNABLE,
|
||||
'Error: Namer has no name for "$name".');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import '../js_backend/interceptor_data.dart' show InterceptorData;
|
|||
import '../js_model/js_world.dart' show JClosedWorld;
|
||||
import '../universe/class_hierarchy.dart' show ClassHierarchy;
|
||||
|
||||
enum _Kind {
|
||||
enum IsTestSpecializationKind {
|
||||
isNull,
|
||||
isNotNull,
|
||||
isString,
|
||||
|
@ -22,27 +22,30 @@ enum _Kind {
|
|||
}
|
||||
|
||||
class IsTestSpecialization {
|
||||
static const isNull = IsTestSpecialization._(_Kind.isNull);
|
||||
static const isNotNull = IsTestSpecialization._(_Kind.isNotNull);
|
||||
static const isString = IsTestSpecialization._(_Kind.isString);
|
||||
static const isBool = IsTestSpecialization._(_Kind.isBool);
|
||||
static const isNum = IsTestSpecialization._(_Kind.isNum);
|
||||
static const isInt = IsTestSpecialization._(_Kind.isInt);
|
||||
static const isArrayTop = IsTestSpecialization._(_Kind.isArrayTop);
|
||||
static const isNull = IsTestSpecialization._(IsTestSpecializationKind.isNull);
|
||||
static const isNotNull =
|
||||
IsTestSpecialization._(IsTestSpecializationKind.isNotNull);
|
||||
static const isString =
|
||||
IsTestSpecialization._(IsTestSpecializationKind.isString);
|
||||
static const isBool = IsTestSpecialization._(IsTestSpecializationKind.isBool);
|
||||
static const isNum = IsTestSpecialization._(IsTestSpecializationKind.isNum);
|
||||
static const isInt = IsTestSpecialization._(IsTestSpecializationKind.isInt);
|
||||
static const isArrayTop =
|
||||
IsTestSpecialization._(IsTestSpecializationKind.isArrayTop);
|
||||
|
||||
final _Kind _kind;
|
||||
final IsTestSpecializationKind kind;
|
||||
final InterfaceType? _type;
|
||||
|
||||
const IsTestSpecialization._(this._kind) : _type = null;
|
||||
const IsTestSpecialization._(this.kind) : _type = null;
|
||||
|
||||
const IsTestSpecialization._instanceof(InterfaceType type)
|
||||
: _kind = _Kind.isInstanceof,
|
||||
: kind = IsTestSpecializationKind.isInstanceof,
|
||||
_type = type;
|
||||
|
||||
bool get isInstanceof => _kind == _Kind.isInstanceof;
|
||||
bool get isInstanceof => kind == IsTestSpecializationKind.isInstanceof;
|
||||
|
||||
InterfaceType get interfaceType {
|
||||
assert(_kind == _Kind.isInstanceof);
|
||||
assert(kind == IsTestSpecializationKind.isInstanceof);
|
||||
return _type!;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,7 +167,9 @@ class ClosureDataImpl implements ClosureData {
|
|||
const CapturedScope();
|
||||
case MemberKind.parameterStub:
|
||||
return const CapturedScope();
|
||||
default:
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.recordGetter:
|
||||
throw failedAt(entity, "Unexpected member definition $definition");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -642,7 +642,12 @@ void forEachOrderedParameter(JsToElementMap elementMap, FunctionEntity function,
|
|||
forEachOrderedParameterByFunctionNode(
|
||||
node.function, parameterStructure, handleParameter);
|
||||
return;
|
||||
default:
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.recordGetter:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.parameterStub:
|
||||
break;
|
||||
}
|
||||
failedAt(function, "Unexpected function definition $definition.");
|
||||
}
|
||||
|
|
|
@ -301,7 +301,14 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
|
|||
methodMap[node as ir.Procedure] = member as JFunction;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case MemberKind.closureCall:
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.constructorBody:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.recordGetter:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.parameterStub:
|
||||
break;
|
||||
}
|
||||
}
|
||||
source.end(memberTag);
|
||||
|
|
|
@ -123,7 +123,8 @@ void reportFrontEndMessage(
|
|||
case fe.Severity.info:
|
||||
reporter.reportInfo(mainMessage, infos);
|
||||
break;
|
||||
default:
|
||||
case fe.Severity.context:
|
||||
case fe.Severity.ignored:
|
||||
throw UnimplementedError('unhandled severity ${message.severity}');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,7 +158,25 @@ class ResolutionEnqueuer extends Enqueuer {
|
|||
constructor: staticUse.element as ConstructorEntity?,
|
||||
globalDependency: false);
|
||||
break;
|
||||
default:
|
||||
case StaticUseKind.STATIC_TEAR_OFF:
|
||||
case StaticUseKind.SUPER_TEAR_OFF:
|
||||
case StaticUseKind.SUPER_FIELD_SET:
|
||||
case StaticUseKind.SUPER_GET:
|
||||
case StaticUseKind.SUPER_SETTER_SET:
|
||||
case StaticUseKind.SUPER_INVOKE:
|
||||
case StaticUseKind.INSTANCE_FIELD_GET:
|
||||
case StaticUseKind.INSTANCE_FIELD_SET:
|
||||
case StaticUseKind.CLOSURE:
|
||||
case StaticUseKind.CLOSURE_CALL:
|
||||
case StaticUseKind.CALL_METHOD:
|
||||
case StaticUseKind.DIRECT_INVOKE:
|
||||
case StaticUseKind.INLINING:
|
||||
case StaticUseKind.STATIC_INVOKE:
|
||||
case StaticUseKind.STATIC_GET:
|
||||
case StaticUseKind.STATIC_SET:
|
||||
case StaticUseKind.FIELD_INIT:
|
||||
case StaticUseKind.FIELD_CONSTANT_INIT:
|
||||
case StaticUseKind.WEAK_STATIC_TEAR_OFF:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -380,9 +380,6 @@ class RandomAccessFileOutputProvider implements api.CompilerOutput {
|
|||
}
|
||||
uri = out!.resolve('$name.$extension');
|
||||
break;
|
||||
default:
|
||||
onFailure('Unknown output type: $type');
|
||||
throw StateError('unreachable');
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
|
|
@ -971,12 +971,19 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
|||
constructorData, field.enclosingClass!);
|
||||
|
||||
MemberDefinition definition = _elementMap.getMemberDefinition(field);
|
||||
late final ir.Field node;
|
||||
ir.Field node;
|
||||
switch (definition.kind) {
|
||||
case MemberKind.regular:
|
||||
node = definition.node as ir.Field;
|
||||
break;
|
||||
default:
|
||||
case MemberKind.constructor:
|
||||
case MemberKind.constructorBody:
|
||||
case MemberKind.closureCall:
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.recordGetter:
|
||||
case MemberKind.parameterStub:
|
||||
failedAt(field, "Unexpected member definition $definition.");
|
||||
}
|
||||
|
||||
|
@ -5118,7 +5125,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
|||
String typesAccess = _emitter.generateEmbeddedGlobalAccessString(TYPES);
|
||||
return js.js.expressionTemplateFor("$typesAccess[#]");
|
||||
|
||||
default:
|
||||
case JsBuiltin.isJsInteropTypeArgument:
|
||||
reporter.internalError(
|
||||
NO_LOCATION_SPANNABLE, "Unhandled Builtin: $builtin");
|
||||
return null;
|
||||
|
@ -6857,8 +6864,11 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
|||
final node = definition.node as ir.LocalFunction;
|
||||
node.function.body!.accept(this);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
case MemberKind.closureField:
|
||||
case MemberKind.generatorBody:
|
||||
case MemberKind.recordGetter:
|
||||
case MemberKind.signature:
|
||||
case MemberKind.parameterStub:
|
||||
}
|
||||
failedAt(function, "Unexpected inlined function: $definition");
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import '../js_backend/namer.dart' show ModularNamer;
|
|||
import '../js_backend/runtime_types_codegen.dart';
|
||||
import '../js_backend/runtime_types_new.dart'
|
||||
show RecipeEncoder, RecipeEncoding, indexTypeVariable;
|
||||
import '../js_backend/specialized_checks.dart' show IsTestSpecialization;
|
||||
import '../js_backend/specialized_checks.dart' show IsTestSpecializationKind;
|
||||
import '../js_backend/type_reference.dart' show TypeReference;
|
||||
import '../js_emitter/js_emitter.dart' show ModularEmitter;
|
||||
import '../js_model/elements.dart' show JGeneratorBody;
|
||||
|
@ -3192,41 +3192,37 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
}
|
||||
|
||||
late js.Expression test;
|
||||
switch (node.specialization) {
|
||||
case IsTestSpecialization.isNull:
|
||||
case IsTestSpecialization.isNotNull:
|
||||
switch (node.specialization.kind) {
|
||||
case IsTestSpecializationKind.isNull:
|
||||
case IsTestSpecializationKind.isNotNull:
|
||||
// These cases should be lowered using [HIdentity] during optimization.
|
||||
failedAt(node, 'Missing lowering');
|
||||
|
||||
case IsTestSpecialization.isString:
|
||||
case IsTestSpecializationKind.isString:
|
||||
test = typeof("string");
|
||||
break;
|
||||
|
||||
case IsTestSpecialization.isBool:
|
||||
case IsTestSpecializationKind.isBool:
|
||||
test = isTest(_commonElements.specializedIsBool);
|
||||
break;
|
||||
|
||||
case IsTestSpecialization.isNum:
|
||||
case IsTestSpecializationKind.isNum:
|
||||
test = typeof("number");
|
||||
break;
|
||||
|
||||
case IsTestSpecialization.isInt:
|
||||
case IsTestSpecializationKind.isInt:
|
||||
test = isTest(_commonElements.specializedIsInt);
|
||||
break;
|
||||
|
||||
case IsTestSpecialization.isArrayTop:
|
||||
case IsTestSpecializationKind.isArrayTop:
|
||||
test = handleNegative(js.js('Array.isArray(#)', [value]));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (node.specialization.isInstanceof) {
|
||||
InterfaceType type = node.specialization.interfaceType;
|
||||
_registry.registerTypeUse(TypeUse.constructorReference(type));
|
||||
test = handleNegative(js.js('# instanceof #',
|
||||
[value, _emitter.constructorAccess(type.element)]));
|
||||
} else {
|
||||
failedAt(node, 'Unknown specialization: ${node.specialization}');
|
||||
}
|
||||
case IsTestSpecializationKind.isInstanceof:
|
||||
InterfaceType type = node.specialization.interfaceType;
|
||||
_registry.registerTypeUse(TypeUse.constructorReference(type));
|
||||
test = handleNegative(js.js('# instanceof #',
|
||||
[value, _emitter.constructorAccess(type.element)]));
|
||||
}
|
||||
push(test.withSourceInformation(node.sourceInformation));
|
||||
}
|
||||
|
|
|
@ -190,6 +190,8 @@ class SsaFunctionCompiler implements FunctionCompiler {
|
|||
asyncTypeParameter,
|
||||
name);
|
||||
break;
|
||||
case AsyncMarker.SYNC:
|
||||
throw StateError('Cannot rewrite sync method as async.');
|
||||
}
|
||||
return rewriter.rewrite(
|
||||
code as js.Fun, bodySourceInformation, exitSourceInformation);
|
||||
|
|
|
@ -236,7 +236,7 @@ abstract class TypeBuilder {
|
|||
case ClassTypeVariableAccess.property:
|
||||
usesInstanceParameters = true;
|
||||
return;
|
||||
default:
|
||||
case ClassTypeVariableAccess.none:
|
||||
builder.reporter.internalError(
|
||||
type.element, 'Unexpected type variable in static context.');
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ class InstantiationInfo {
|
|||
break;
|
||||
case Instantiation.UNINSTANTIATED:
|
||||
break;
|
||||
default:
|
||||
case Instantiation.INDIRECTLY_INSTANTIATED:
|
||||
throw StateError("Instantiation $kind is not allowed.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -334,7 +334,22 @@ class StaticUse {
|
|||
case StaticUseKind.CLOSURE:
|
||||
sb.write('def:');
|
||||
break;
|
||||
default:
|
||||
case StaticUseKind.STATIC_TEAR_OFF:
|
||||
case StaticUseKind.SUPER_TEAR_OFF:
|
||||
case StaticUseKind.SUPER_GET:
|
||||
case StaticUseKind.SUPER_INVOKE:
|
||||
case StaticUseKind.INSTANCE_FIELD_GET:
|
||||
case StaticUseKind.CLOSURE_CALL:
|
||||
case StaticUseKind.CALL_METHOD:
|
||||
case StaticUseKind.CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.DIRECT_INVOKE:
|
||||
case StaticUseKind.INLINING:
|
||||
case StaticUseKind.STATIC_INVOKE:
|
||||
case StaticUseKind.STATIC_GET:
|
||||
case StaticUseKind.FIELD_CONSTANT_INIT:
|
||||
case StaticUseKind.WEAK_STATIC_TEAR_OFF:
|
||||
break;
|
||||
}
|
||||
final member = element;
|
||||
if (member is MemberEntity) {
|
||||
|
|
Loading…
Reference in a new issue