What's wrong
MetadataFile.Deserialize<T> (SourceGeneratorToolkit/MetadataFile.cs:119-148) catches only JsonException, NotSupportedException and InvalidOperationException. System.Text.Json does not wrap exceptions thrown by the model's own constructor. A model with an ordinary guard clause therefore throws straight through to the Roslyn driver when a property is missing from the JSON. That is exactly the outcome the method's remarks, and CLAUDE.md, say the catch exists to prevent: a generic CS8785 that names neither the file nor the reason, with every other file in the invocation abandoned.
Repro
public record GuardedThing
{
public GuardedThing(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
public string Name { get; }
}
public class ThingsModel { public List<GuardedThing> Things { get; set; } = []; }
Use a two-file generator shaped like the test project's ResilientPairGenerator (things.json + others.json). Supply things.json = {"things":[{"kind":"x"}]} (the name is missing) and a valid others.json.
- Observed:
result.Exception is ArgumentNullException, with warning CS8785: Generator '…' failed to generate source. It emits 0 sources, so the valid others.json output is lost too.
- Expected: a parse-failed diagnostic naming
things.json, with others.json still generated. A bad Version value is already handled this way (control case).
Suggested fix
Add catch (ArgumentException ex) { return ParseFailed(ex); }, which also covers ArgumentNullException and ArgumentOutOfRangeException. The stated aim is that nothing escapes to the driver, so a stronger option is a catch-all with a filter that excludes OperationCanceledException. Add a guarded-constructor fixture next to AmbiguousMetadata so this path stays covered.
What's wrong
MetadataFile.Deserialize<T>(SourceGeneratorToolkit/MetadataFile.cs:119-148) catches onlyJsonException,NotSupportedExceptionandInvalidOperationException. System.Text.Json does not wrap exceptions thrown by the model's own constructor. A model with an ordinary guard clause therefore throws straight through to the Roslyn driver when a property is missing from the JSON. That is exactly the outcome the method's remarks, and CLAUDE.md, say the catch exists to prevent: a generic CS8785 that names neither the file nor the reason, with every other file in the invocation abandoned.Repro
Use a two-file generator shaped like the test project's
ResilientPairGenerator(things.json+others.json). Supplythings.json={"things":[{"kind":"x"}]}(thenameis missing) and a validothers.json.result.ExceptionisArgumentNullException, withwarning CS8785: Generator '…' failed to generate source. It emits 0 sources, so the validothers.jsonoutput is lost too.things.json, withothers.jsonstill generated. A badVersionvalue is already handled this way (control case).Suggested fix
Add
catch (ArgumentException ex) { return ParseFailed(ex); }, which also coversArgumentNullExceptionandArgumentOutOfRangeException. The stated aim is that nothing escapes to the driver, so a stronger option is a catch-all with a filter that excludesOperationCanceledException. Add a guarded-constructor fixture next toAmbiguousMetadataso this path stays covered.