Skip to content

BRO1301: Variable names should begin with a lower-case letter

Local variables and query range variables are camelCase: total, not Total or _total. Replaces StyleCop SA1312.

Example

Before

using System.Collections.Generic;
using System.Linq;

public class Basket
{
    public decimal Total(IEnumerable<decimal> prices)
    {
        decimal Sum = 0;
        foreach (var Price in prices)
        {
            Sum += Price;
        }

        var _discounted = from P in prices where P > 100 select P * 0.9m;
        return Sum - prices.Where(x => x > 100).Sum() + _discounted.Sum();
    }
}

After

using System.Collections.Generic;
using System.Linq;

public class Basket
{
    public decimal Total(IEnumerable<decimal> prices)
    {
        decimal sum = 0;
        foreach (var price in prices)
        {
            sum += price;
        }

        var discounted = from p in prices where p > 100 select p * 0.9m;
        return sum - prices.Where(x => x > 100).Sum() + discounted.Sum();
    }
}

Why

C# names locals and parameters in camelCase and everything else in PascalCase, so the case tells you at a glance whether a name is local to the method. _name is the convention for private fields, not locals.

How the fix works

The variable and every use of it are renamed. The new name drops leading underscores and lowers the leading capitals: Total becomes total, _total becomes total, URL becomes url, and HTMLParser becomes htmlParser (the capital that starts the next word stays). Words separated by underscores are joined like BRO1308 joins a field's: RANGE_REP_REG becomes rangeRepReg, Max_Value becomes maxValue.

Checked: local variables (including using, for, foreach, catch, out var, pattern and deconstruction variables) and query range variables (from, let, join, into). Local constants are PascalCase and aren't reported.

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). The fix only renames when the new name can't change what any code refers to. Checked by syntax over the member that contains the variable, so it errs on the side of skipping:

  • A name that nameof(...) produces: the rename would change the string (Ocelot built an HTTP header name with nameof(X_RateLimit_Limit), and a test read a private field with GetField(nameof(_components))). Looked for in the member for variables and parameters, in the type for fields; the fix also looks in the rest of the solution and leaves the warning when it finds one.

  • The new name is already used in the member, for anything: another variable, a field, a property of another object (var Value = value; in a setter).
  • Two names in the member would get the same new name (Total and _total).
  • The variable names an anonymous type member or tuple element by inference (new { Total }, (Total, 2)). The rename would rename the member or element too.
  • The member contains #if. Code that isn't compiled for the current target can't be renamed reliably.
  • Names without a camelCase form: only underscores (_, ___), a digit after the underscores (_1), a keyword (Class would become class), or field (Field): in C# 14 field is a keyword inside property accessors, where a local named field doesn't compile (CS9273).
  • Names in an expression tree: range variables of a query, and parameters of a lambda, that end up in a System.Linq.Expressions tree (a query over IQueryable, an Expression<Func<...>> lambda, or a lambda nested in one). The names are data at run time: they show in the expression's ToString() and query providers may use them (Mapster's tests compare a query's text, and from Param_0 in ... -> param0 failed one). StyleCop's SA1312/SA1313 report them.
  • value inside a property, indexer or event (var Value = 2; in a setter): the accessor's implicit value parameter already has that name.

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, a name that keeps IDE0042 (deconstruct variable declaration, a warning or an error in your settings) from reporting a tuple variable next to it. 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

Same results as StyleCop, except for the skipped cases above (names in expression trees among them). StyleCop's fix for SA1312 only lowers the first letter, can't fix names that start with _, and under dotnet format applies only some of the renames per run.