Skip to content

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 its restore), 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_rule that covers the symbol is enforced (IDE1006, or the rule's own severity, at suggestion or above) and asks for another style, like _camelCase for 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 with InternalsVisibleTo.
  • 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's ParamName, which callers and tests compare (Newtonsoft.Json's JObject.ContainsKey(null) test expects propertyName). 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).