BRO1008: Using directives should be placed correctly¶
Using directives go inside or outside the namespace, as csharp_using_directive_placement says; without that
setting nothing is reported. Replaces StyleCop SA1200, and the SDK's IDE0065, whose fix can break the build.
Example¶
[*.cs]
csharp_using_directive_placement = outside_namespace
Before¶
namespace Shop.Orders
{
using System;
using System.Collections.Generic;
public class Order
{
public List<DateTime> Changes { get; } = new();
}
}
After¶
using System;
using System.Collections.Generic;
namespace Shop.Orders
{
public class Order
{
public List<DateTime> Changes { get; } = new();
}
}
Why¶
One place for usings in every file. Where they are also decides how names are looked up: inside the namespace, a using's names come before the enclosing namespaces' members; outside, after them.
How the fix works¶
All usings of the file move together, and only when no name in the file can be looked up differently afterwards. The
SDK's IDE0065 fix doesn't check that: in eShop it made Order ambiguous (CS0104), in Ocelot a type name turned into a
namespace (CS0118; reported upstream as dotnet/roslyn#86002). The check follows C#'s lookup order instead of compiling the file again:
- Inside
namespace A.B, a name is looked up inA.B, then in the usings there, then inA, the global namespace, and the global usings. Outside, the usings come last, together with the global usings (implicit usings too). - So for every identifier in the file that the moved usings import, the types
Aor the global namespace have under that name must be the same ones, and if they have none, the global usings must not import anything else under it. - Extension methods (names after
., and the onesforeach,await, collection initializers, deconstruction and queries call) that the moved usings bring must not share a name with one from the namespace, its containers or the global usings. - A using's own name (
using X.Y;) must start with a member of the global namespace that no namespace ofA.Bhas.
Names are compared by text, so the check is conservative. When it fails, nothing is reported.
- Comments directly above the first using move with it; the file header stays on top. When usings at the top of the file move in, a comment right below them becomes the header and gets a blank line before the code.
- Outside: the usings go above the namespace (and the comments directly above it), with a blank line before the
namespace and none at the start of the file. Relative names get their full name, like
BRO1126's fix (
using IO;insidenamespace Systembecomesusing System.IO;). - Inside: below
{(indented one level), or belownamespace X;after a blank line, with a blank line before what follows. - In a multi-targeted project every target framework's copy of the file must agree; otherwise the warning stays.
Configuration¶
[*.cs]
# The SDK's setting (StyleCop's usingDirectivesPlacement):
# inside_namespace (StyleCop's default), outside_namespace
# not set, or anything else (preserve): nothing is reported
csharp_using_directive_placement = outside_namespace
The preset sets outside_namespace. stylebro-migrate writes StyleCop's choice from stylecop.json
(usingDirectivesPlacement, inside_namespace when it isn't set, StyleCop's default), so a migrated repository keeps
SA1200's behavior. Without the preset and the setting the rule does nothing: where usings go is a choice it doesn't make
for you.
Not reported¶
- Files where a name could be looked up differently with the usings moved (see above).
- Files with more than one namespace, with types, attributes or top-level statements outside the namespace, with usings on both levels, global usings, or extern aliases.
- A directive (
#if,#pragma,#nullable, ...) anywhere before the namespace's body.#region/#endregionlines are fine when they aren't among the usings and no region is open around the usings or where they go (Newtonsoft.Json's#region Licensearound the header, above or below the usings, which BRO1112 removes in the same run); a region around the usings isn't. - A using sharing its line with other code.
- With
inside_namespace, a file without a namespace (StyleCop reports its usings, but there's nowhere to move them).
Compared with StyleCop¶
Same positions and output where both report (parity sets using-placement and using-placement-outside), except
that StyleCop's fix drops the blank line below namespace X;. StyleCop's fix moves usings without checking what they
bind to: in the parity sets it turned Order into another type, and produced code that doesn't compile (CS0234).