Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,83 @@ public async Task TestDeconstructionInTopLevelProgramAsync(string prefix)
FixedCode = fixedCode,
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
public async Task TestRecordInheritanceAsync()
{
const string testCode = @"
public abstract record BaseQuery<T>;
public record MyQuery1 {|#0:(|} ) : BaseQuery<object>;
public record MyQuery2{|#1:(|} ) : BaseQuery<object>;
public record MyQuery3 {|#2:(|}) : BaseQuery<object>;";
const string fixedCode = @"
public abstract record BaseQuery<T>;
public record MyQuery1() : BaseQuery<object>;
public record MyQuery2() : BaseQuery<object>;
public record MyQuery3() : BaseQuery<object>;";

await new CSharpTest()
{
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
ExpectedDiagnostics =
{
// /0/Test0.cs(3,24): warning SA1008: Opening parenthesis should not be preceded by a space.
Diagnostic(DescriptorNotPreceded).WithLocation(0),

// /0/Test0.cs(3,24): warning SA1008: Opening parenthesis should not be followed by a space.
Diagnostic(DescriptorNotFollowed).WithLocation(0),

// /0/Test0.cs(4,23): warning SA1008: Opening parenthesis should not be followed by a space.
Diagnostic(DescriptorNotFollowed).WithLocation(1),

// /0/Test0.cs(5,24): warning SA1008: Opening parenthesis should not be preceded by a space.
Diagnostic(DescriptorNotPreceded).WithLocation(2),
},
TestCode = testCode,
FixedCode = fixedCode,
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
public async Task TestRecordBaseArgumentsWithMultilineSpacingAsync()
{
const string testCode = @"
public abstract record BaseRecord(string Text);

public record Derived1(string Text)
: BaseRecord {|#0:(|}
Text)
{
}

public record Derived2(string Text)
: BaseRecord {|#1:(|}
Text);
";

const string fixedCode = @"
public abstract record BaseRecord(string Text);

public record Derived1(string Text)
: BaseRecord(
Text)
{
}

public record Derived2(string Text)
: BaseRecord(
Text);
";

DiagnosticResult[] expected =
{
Diagnostic(DescriptorNotPreceded).WithLocation(0),
Diagnostic(DescriptorNotPreceded).WithLocation(1),
};

await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,47 @@ public record MyQuery3() : BaseQuery<object>;";
FixedCode = fixedCode,
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(3965, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3965")]
public async Task TestRecordBaseArgumentsWithMultilineSpacingAsync()
{
const string testCode = @"
public abstract record BaseRecord(string Text);

public record Derived1(string Text)
: BaseRecord(
Text {|#0:)|}
{
}

public record Derived2(string Text)
: BaseRecord(
Text {|#1:)|} ;
";

const string fixedCode = @"
public abstract record BaseRecord(string Text);

public record Derived1(string Text)
: BaseRecord(
Text)
{
}

public record Derived2(string Text)
: BaseRecord(
Text);
";

DiagnosticResult[] expected =
{
Diagnostic(DescriptorNotPreceded).WithLocation(0),
Diagnostic(DescriptorNotPreceded).WithLocation(1),
Diagnostic(DescriptorNotFollowed).WithLocation(1),
};

await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}
}
}
5 changes: 5 additions & 0 deletions documentation/SA1202.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ Default interface members (including members with implementations and static int
ordering as class members. Within an interface, `public` members should appear before `internal`, `protected internal`,
`protected`, `private protected`, and `private` members.

### Records and primary constructors

Records (including positional records) and record structs follow the same ordering rules as classes and structs. Primary
constructor parameters that become properties do not change the expected access ordering for the remaining members.

## How to fix violations

To fix an instance of this violation, order the elements in the file in the order described above.
Expand Down
9 changes: 9 additions & 0 deletions documentation/SA1313.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,18 @@ The name of a parameter in C# does not begin with a lower-case letter.

A violation of this rule occurs when the name of a parameter does not begin with a lower-case letter.

### Discards

An exception to this rule is made for lambda parameters named `_` and `__`. These parameters are often used to designate a
placeholder parameter which is not actually used in the body of the lambda expression.

### Records and primary constructors

Positional record parameters become public properties, so PascalCase names are acceptable for those parameters even
though they appear in the primary constructor signature.

### Interop code

If the parameter name is intended to match the name of an item associated with Win32 or COM, and thus needs to begin
with an upper-case letter, place the parameter within a special `NativeMethods` class. A `NativeMethods` class is any
class which contains a name ending in `NativeMethods`, and is intended as a placeholder for Win32 or COM wrappers.
Expand Down
4 changes: 4 additions & 0 deletions documentation/SA1600.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ C# syntax provides a mechanism for inserting documentation for classes and eleme

A violation of this rule occurs if an element is completely missing a documentation header, or if the header is empty. In C# the following types of elements can have documentation headers: classes, constructors, delegates, enums, events, finalizers, indexers, interfaces, methods, properties, records, and structs.

### Records and primary constructors

Record classes and record structs follow the same expectations as classes and structs. Positional parameters in a record declaration become properties; when documenting a public record, include `<param>` tags for those parameters in addition to the `<summary>` text for the record itself.

### Default interface members

Interface members are implicitly `public` unless otherwise specified. Default interface members (methods, properties, indexers, events, etc. that include implementations) follow these rules:
Expand Down
2 changes: 2 additions & 0 deletions documentation/SA1642.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public MyStruct()
}
```

For C# 9 record types, constructors follow the same pattern: record classes use the word 'class' in the summary text, while record structs use 'struct'.

If the class contains generic parameters, these can be annotated within the `cref` link using either of the following
two formats:

Expand Down
Loading