Skip to content

Stop the picker from building a converter that throws - #263

Draft
VPDPersonal wants to merge 1 commit into
feat/generate-binders-attributefrom
fix/converter-picker-double-region
Draft

Stop the picker from building a converter that throws#263
VPDPersonal wants to merge 1 commit into
feat/generate-binders-attributefrom
fix/converter-picker-double-region

Conversation

@VPDPersonal

Copy link
Copy Markdown
Owner

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.

NumberConverterSpecificExtensions marks its delegate-backed adapters [TypeSelectorDisplay(Hidden = true)] so the picker never offers what an inspector cannot build — except the Double region, 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 through FormatterServices with 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 the params constructor, 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 found SequenceConverters<T> on its first run.

The same review found formatEmptyValues no longer reaching a null value, though the field's tooltip promises it does — a leftover from folding StringFormatConverter.Convert into the base. GenericToString<TFrom> now decides in two hooks: ShouldFormat picks which values the format applies to, Format applies it. The base rejects null, StringFormatConverter accepts 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

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant