diff --git a/checker/internal/type_checker_builder_impl.cc b/checker/internal/type_checker_builder_impl.cc index ea869056d..ace6afd92 100644 --- a/checker/internal/type_checker_builder_impl.cc +++ b/checker/internal/type_checker_builder_impl.cc @@ -175,6 +175,14 @@ absl::Status ValidateType(const Type& t, bool check_type_param_name, return ValidateType(value_type, check_type_param_name, depth_limit, remaining_depth); } + case TypeKind::kStruct: { + auto message_type = t.AsMessage(); + if (message_type.has_value() && !static_cast(*message_type)) { + return absl::InvalidArgumentError( + "an empty message type cannot be used in a type declaration"); + } + return absl::OkStatus(); + } case TypeKind::kOpaque: { for (Type type_param : t.AsOpaque()->GetParameters()) { CEL_RETURN_IF_ERROR(ValidateType(type_param, check_type_param_name, diff --git a/checker/internal/type_checker_builder_impl_test.cc b/checker/internal/type_checker_builder_impl_test.cc index 6b3e7a890..5e4054128 100644 --- a/checker/internal/type_checker_builder_impl_test.cc +++ b/checker/internal/type_checker_builder_impl_test.cc @@ -207,7 +207,8 @@ TEST(ContextDeclsTest, ErrorOnOverlappingVariableDeclaration) { "variable 'single_int64' declared multiple times")); } -TEST(ContextDeclsTest, InvalidTypeParamNameVariableValidationDisabled) { +TEST(TypeCheckerBuilderImplTest, + InvalidTypeParamNameVariableValidationDisabled) { CheckerOptions options; options.enable_type_parameter_name_validation = false; TypeCheckerBuilderImpl builder(internal::GetSharedTestingDescriptorPool(), @@ -219,7 +220,18 @@ TEST(ContextDeclsTest, InvalidTypeParamNameVariableValidationDisabled) { IsOk()); } -TEST(ContextDeclsTest, ErrorOnInvalidTypeParamNameVariable) { +TEST(TypeCheckerBuilderImplTest, ErrorOnUnspecifiedMessageType) { + CheckerOptions options; + options.enable_type_parameter_name_validation = true; + TypeCheckerBuilderImpl builder(internal::GetSharedTestingDescriptorPool(), + options); + ASSERT_THAT( + builder.AddVariable(MakeVariableDecl("x", MessageType())), + StatusIs(absl::StatusCode::kInvalidArgument, + "an empty message type cannot be used in a type declaration")); +} + +TEST(TypeCheckerBuilderImplTest, ErrorOnInvalidTypeParamNameVariable) { CheckerOptions options; options.enable_type_parameter_name_validation = true; TypeCheckerBuilderImpl builder(internal::GetSharedTestingDescriptorPool(), @@ -234,7 +246,7 @@ TEST(ContextDeclsTest, ErrorOnInvalidTypeParamNameVariable) { "type parameter name 'T% foo' is not a valid identifier")); } -TEST(ContextDeclsTest, ErrorOnTooDeepTypeNestingVariable) { +TEST(TypeCheckerBuilderImplTest, ErrorOnTooDeepTypeNestingVariable) { CheckerOptions options; options.max_type_decl_nesting = 2; google::protobuf::Arena arena; @@ -251,7 +263,7 @@ TEST(ContextDeclsTest, ErrorOnTooDeepTypeNestingVariable) { "type nesting limit of 2 exceeded")); } -TEST(ContextDeclsTest, ErrorOnInvalidTypeParamNameFunction) { +TEST(TypeCheckerBuilderImplTest, ErrorOnInvalidTypeParamNameFunction) { CheckerOptions options; options.enable_type_parameter_name_validation = true; TypeCheckerBuilderImpl builder(internal::GetSharedTestingDescriptorPool(), @@ -269,7 +281,7 @@ TEST(ContextDeclsTest, ErrorOnInvalidTypeParamNameFunction) { "type parameter name '' is not a valid identifier")); } -TEST(ContextDeclsTest, ErrorOnTooDeepTypeNestingFunction) { +TEST(TypeCheckerBuilderImplTest, ErrorOnTooDeepTypeNestingFunction) { CheckerOptions options; options.max_type_decl_nesting = 2; google::protobuf::Arena arena; @@ -294,7 +306,7 @@ TEST(ContextDeclsTest, ErrorOnTooDeepTypeNestingFunction) { "type nesting limit of 2 exceeded")); } -TEST(ContextDeclsTest, ReplaceVariable) { +TEST(TypeCheckerBuilderImplTest, ReplaceVariable) { TypeCheckerBuilderImpl builder(internal::GetSharedTestingDescriptorPool(), {}); ASSERT_THAT( diff --git a/common/types/message_type.h b/common/types/message_type.h index 232258170..782af87aa 100644 --- a/common/types/message_type.h +++ b/common/types/message_type.h @@ -57,6 +57,10 @@ class MessageType final { << descriptor->full_name(); } + // Constructs a `MessageType` in an empty state. + // + // Most operations on an empty `MessageType` result in undefined behavior. Use + // `operator bool` to test if a `MessageType` is empty. MessageType() = default; MessageType(const MessageType&) = default; MessageType(MessageType&&) = default;