Stop the picker from building a converter that throws - #263
Draft
VPDPersonal wants to merge 1 commit into
Draft
Conversation
The Double region of NumberConverterSpecificExtensions was missed when the family was marked, so four delegate-backed adapters stayed pickable and came back uninitialized. SequenceConverters<T> had the same shape for a different reason: only a params constructor, so the picker fell back to an instance whose array was null. Both now behave — the adapters are hidden, the sequence starts empty — and a contract test asserts the rule over both StarterKit assemblies instead of leaving it to review. GenericToString gained a ShouldFormat hook, which puts formatEmptyValues back in reach of a null value as StringFormatConverter's tooltip promises, and the CHANGELOG no longer claims an empty format used to produce an empty string. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Picking a converter from the Inspector could hand back an object that throws on the first value it is given. Two types did it, for two different reasons.
NumberConverterSpecificExtensionsmarks its delegate-backed adapters[TypeSelectorDisplay(Hidden = true)]so the picker never offers what an inspector cannot build — except theDoubleregion, missed when the family was marked. Four adapters (ConverterDouble,ConverterDoubleToInt,ConverterDoubleToLong,ConverterDoubleToFloat) stayed in the dropdown next to the real converters; choosing one produced an instance throughFormatterServiceswith a null delegate, unrecoverable and not serialized, and the first pushed value threw.SequenceConverters<T>reached the same state from the other direction: it only declared theparamsconstructor, so the picker had no parameterless one to call. It belongs in the picker — the array is serialized and meant to be filled in the Inspector — so it gets a parameterless constructor that starts the chain empty, plus the tooltip its serialized field never had.A contract test now asserts the rule over both StarterKit assemblies: a non-abstract type assignable to
IConverter<,>with no parameterless constructor must be hidden. It foundSequenceConverters<T>on its first run.The same review found
formatEmptyValuesno longer reaching a null value, though the field's tooltip promises it does — a leftover from foldingStringFormatConverter.Convertinto the base.GenericToString<TFrom>now decides in two hooks:ShouldFormatpicks which values the format applies to,Formatapplies it. The base rejects null,StringFormatConverteraccepts it when the flag is set, and the matrix of null / empty / whitespace / value × format × flag is covered by tests. Undocumented constructors and both hooks got their XML docs, and two CHANGELOG claims were corrected: an empty format never produced an empty string, and the picker fix covered four types fewer than it said.EditMode: 806 tests, all green (799 before, 7 added).
По-русски
Конвертер, выбранный в инспекторе, мог оказаться объектом, который падает на первом же значении. Так делали два типа — по разным причинам.
В
NumberConverterSpecificExtensionsадаптеры на делегатах помечены[TypeSelectorDisplay(Hidden = true)], чтобы пикер не предлагал то, что инспектор создать не может, — кроме регионаDouble, пропущенного при разметке семейства. Четыре адаптера (ConverterDouble,ConverterDoubleToInt,ConverterDoubleToLong,ConverterDoubleToFloat) оставались в дропдауне рядом с настоящими конвертерами; выбор давал экземпляр черезFormatterServicesсnullвместо делегата — невосстановимый и не сериализуемый, — и первое же значение приводило к падению.SequenceConverters<T>приходил в то же состояние с другой стороны: у него был объявлен только конструктор сparams, и безпараметрического пикеру взять было негде. Он в пикере нужен — массив сериализуется и заполняется в инспекторе, — поэтому получает безпараметрический конструктор, начинающий цепочку пустой, и тултип, которого у его сериализуемого поля не было.Контрактный тест закрепляет правило по обеим сборкам StarterKit: неабстрактный тип, назначаемый
IConverter<,>, без безпараметрического конструктора обязан быть скрыт. На первом же прогоне он нашёлSequenceConverters<T>.Тем же разбором найдено, что
formatEmptyValuesбольше не доходит до значенияnull, хотя тултип поля это обещает — след схлопыванияStringFormatConverter.Convertв базовый класс. ТеперьGenericToString<TFrom>решает в двух хуках:ShouldFormatвыбирает, к каким значениям применять формат,Formatего применяет. База отвергаетnull,StringFormatConverterпринимает его при взведённом флаге, а матрица null / пусто / пробелы / значение × формат × флаг покрыта тестами. Недокументированные конструкторы и оба хука получили XML-документацию, и исправлены два утверждения в CHANGELOG: пустой формат никогда не давал пустую строку, а фикс пикера покрывал на четыре типа меньше, чем заявлено.EditMode: 806 тестов, все зелёные (было 799, добавлено 7).
🤖 Generated with Claude Code