BRO1313: Parameter names should match the base member¶
A parameter of an override or an interface implementation has the name of the parameter it overrides or implements.
Proposed for StyleCop as SA1315 (#1949, never
shipped); the SDK's CA1725 reports the same thing but has no fix dotnet format can apply. Off by default.
Example¶
root = true
[*.cs]
dotnet_diagnostic.BRO1313.severity = warning
Before¶
internal interface IStore
{
void Save(int id, string name);
}
internal class Store : IStore
{
public void Save(int key, string name) => System.Console.WriteLine(key + name);
public override bool Equals(object other) => other is Store;
public override int GetHashCode() => 0;
}
internal static class Caller
{
public static void Run(Store store) => store.Save(key: 1, name: "a");
}
After¶
internal interface IStore
{
void Save(int id, string name);
}
internal class Store : IStore
{
public void Save(int id, string name) => System.Console.WriteLine(id + name);
public override bool Equals(object obj) => obj is Store;
public override int GetHashCode() => 0;
}
internal static class Caller
{
public static void Run(Store store) => store.Save(id: 1, name: "a");
}
Why¶
A caller using named arguments sees different names depending on the type it calls through (store.Save(key: 1) but
((IStore)store).Save(id: 1)), and the documentation of the base member describes parameters the override doesn't
have.
Configuration¶
stylebro_rename_public_api = true also renames names other assemblies see (default false, see Not reported; the owner's decision in decisions.md).
stylebro_match_base_parameter_names = non_generic_methods limits the rule to what Sonar's S927 reports (stylebro-migrate
writes it for a repository that enforces S927): methods only (not indexers), and not a parameter whose type in the base is a
type parameter the member fills in, or a generic type whose type arguments differ in kind. MediatR's
Handle(TNotification notification, ...) implemented as Handle(OrderPlaced @event, ...) keeps @event, and
All(IEnumerable<T> items) implemented as All(IEnumerable<Order> orders) keeps orders. Default all.
How the fix works¶
The parameter is renamed like BRO1302's renames: the declaration, every use in the method, named
arguments anywhere in the solution and <param>/<paramref> tags. The rename continues to overrides of the method
that had the same old name. Bases in libraries count too (Equals(object obj)): matching them is the point.
A base parameter in your code counts with the name it is about to get: when the base's own parameter is renamed by
BRO1302 (Count -> count) or matches its own base, the override takes that name in the same run, so a chain of
overrides converges in one dotnet format run. With BRO1313 on, it decides the name of such a parameter instead of
BRO1302 and BRO1310 (one rename per parameter).
Named arguments outside the solution break: a caller in another repository using key: no longer compiles. That
is why the rule is off by default.
Not reported¶
- Where IDE1006 is suppressed: after
#pragma warning disable IDE1006(up to itsrestore), or with[SuppressMessage("Style", "IDE1006")]on the declaration, its member or type, or a global one targeting them. Code that opts out of the SDK's naming rules (a file copied from another project) keeps its names. - A new name your SDK naming rule rejects: when the first
dotnet_naming_rulethat covers the symbol is enforced (IDE1006, or the rule's own severity, at suggestion or above) and asks for another style, like_camelCasefor private properties, the name stays (renaming it would only trade this warning for IDE1006). - Public API (unless
stylebro_rename_public_api = true): Parameters that other assemblies see, that is public, protected or protected internal in types that are visible too. Renaming them breaks code outside the solution (named arguments use parameter names). Internal code is renamed, also withInternalsVisibleTo. - Discards (
_,__): an unused parameter named on purpose. - Bases that disagree: a method implementing two interfaces whose parameters have different names.
- A name that would clash in the method (a local or another parameter already has it), like BRO1302.
- A name that reaches run time:
nameof(key)in the method, or the parameter passed to a[CallerArgumentExpression]parameter (ArgumentNullException.ThrowIfNull(key)). Both usually become an exception'sParamName, which callers and tests compare (Newtonsoft.Json'sJObject.ContainsKey(null)test expectspropertyName). An override renamed along with its base keeps its name for the same reason. - Partial methods, constructors, operators, local functions, lambdas and record parameters (none of them override).
- A base name that is a C# keyword, or
field(a keyword inside C# 14 property accessors).
Reported but not fixed¶
If dotnet format leaves a finding, StyleBro kept it on purpose. The fix looks at the whole solution, other
projects included, which the analyzer can't see, and leaves a change that would break something there: the name in a
string or a nameof (reflection, serialization), a member that would hide it, a use in generated code. It offers no
code action for such a finding, and stylebro-migrate format lists each one with its reason
(Findings kept on purpose); its --verify-no-changes doesn't fail on
them. Change it by hand only after checking those uses, or suppress it or record it in the baseline.
Compared with StyleCop¶
StyleCop never shipped the rule. Its name checks (SA1313) skip a parameter that has its base's name, and so does BRO1302.
Real world (2026-10-06)¶
Run with every other rule and the repositories' tests (Invoke-RealWorld.ps1 -Enable BRO1313 -Tests): Newtonsoft.Json 13
renames (JObject's IDictionary members, converters, Equals(object obj)), FFMpegCore 6, CsvHelper 3, Polly 2,
Serilog 2, FluentValidation 1, Jellyfin 1. All compile, converge in one run, and no test that passed before fails. The
first run renamed JObject.ContainsKey(propertyName) to key, which changed its ArgumentNullException's ParamName
and failed a test; such names are now kept (see Not reported).