S3L0002: Create, then return¶
An object built across many lines is created into a named variable first, then returned.
| Category | Snail |
| Severity | warning — an error under TreatWarningsAsErrors, which every Snail project builds with |
| Analyzer | ReturnShape in Snail.Sdk.Analyzers |
| Code fix | Create '<name>', then return it |
What it reports¶
An object built inside a return or yield return that spans more than 15 lines: new T { … }, a target-typed
new(…) { … }, an anonymous new { … }, or a collection expression [ … ]. The message gives the length:
This object spans 22 lines inside the return: assign it to a named variable first.
What it leaves alone¶
An expression-bodied member that only builds an object, Theme Theme() => new(…); — its name already names what it
builds — and objects of 15 lines or fewer.
Why¶
The answer of a method hides behind its construction: the reader sees return new and twenty lines of initialisers
before learning what comes back. A named variable, then return name;, shows it.
Measured across the fleet on 2026-10-12: an object built inside a return spans 7 lines at the median, and
7.5 % pass fifteen.
How to fix¶
// Holds
var settings = new Settings
{
// … twenty lines …
};
return settings;
The code fix¶
Inside a block, the fix declares a variable before the return and returns it after a blank line. The variable is named
after the type in camelCase (Point gives point), or result for an anonymous object or a collection expression,
with 2, 3… added when the name is taken. It is declared with var when the expression names its type; for a collection
expression or a target-typed new(…) with the type it converts to, written as briefly as the file's usings allow
(IReadOnlyList<int> result = [ … ];). No fix is offered when that type cannot be written (an anonymous or unresolved
type) or for an expression body. The fix keeps the document's line endings. Fix All is not offered.
Suppressing it¶
Fix the code rather than silence the rule: the rule is part of the standard every Snail repository keeps. Where one place truly must differ, suppress that place and say why:
[SuppressMessage("Snail", "S3L0002:Create, then return", Justification = "<why this place differs>")]
A NoWarn in a project or props file would hide the rule from every new line too; the
conformance check reports one as migration debt (S3LC0017).