From Package Manager Console:
Install-Package RG.CodeAnalyzer
Best used together with Microsoft.CodeAnalysis.NetAnalyzers and Roslynator
You can either:
- Set the diagnostic severity to None, or
- Suppress the warning and write your justification
Q: I think one of the warnings is important and I don't want my developers to get away with it. How do I do it?
You can set the diagnostic severity to Error. This will prevent build from succeeding.
To learn more about changing diagnostic severity and suppressing warnings, visit: https://docs.microsoft.com/en-us/visualstudio/code-quality/use-roslyn-analyzers?view=vs-2019
for (;;) {
await Task.Delay(1000); // RG0001: Asynchronous operation awaited inside for loop.
}
foreach (var x in y) {
await Task.Delay(1000); // RG0001: Asynchronous operation awaited inside foreach loop.
}
while (true) {
await Task.Delay(1000); // RG0001: Asynchronous operation awaited inside while loop.
}
do {
await Task.Delay(1000); // RG0001: Asynchronous operation awaited inside do..while loop.
} while (true);
Task Foo() { // RG0002: Method 'Foo' disposes an object and shouldn't return Task.
using var cts = new CancellationTokenSource();
return Task.Delay(1000, cts.Token);
}
namespace Foo.Internal.Models {
public class Bar { // RG0003: Identifier 'Bar' is declared in 'Foo.Internal.Models' namespace, and thus must be declared internal.
}
}
class Foo {
private int x;
public void SetX(Foo obj, int value) {
obj.x = value; // RG0004: Private field 'x' should not be accessed directly.
}
}
class Foo : IDisposable {
private static readonly HttpClient _client = new HttpClient();_
public void Dispose() {
_client.Dispose(); // RG0005: Field '_client' is marked 'static readonly' and should not be disposed.
}
}
class Program {
static void Main() {
Foo.ListenAsync().Wait(); // RG0006: Calling Task.Wait() blocks current thread and is not recommended. Use await instead.
}
}
class Foo {
private async Task<int> GetValueAsync() {
return await Task.FromResult(0);
}
public int GetValue() {
return GetValueAsync().Result; // RG0007: Accessing Task<>.Result blocks current thread and is not recommended. Use await instead.
}
}
(int firstItem, string secondItem) tuple = (1, "2"); // RG0008: 'firstItem' is not a proper name of a tuple element.
// RG0008: 'secondItem' is not a proper name of a tuple element.
(int a, string b) = (tuple.firstItem, tuple.secondItem);
public async Task<List<Product>> GetAllAsync(int id, CancellationToken cancellationToken) {
return await _dbContext.Products.ToListAsync(); // RG0009: This method has an overload that accepts CancellationToken.
}
var obj = GetObjOfObsoleteClass(); // RG0010: 'ObsoleteClass' is obsolete.
interface I : IDisposable { } // RG0011: 'I' derives from IDisposable.
// TODO: implement this method please, Bob
// RG0012: Unresolved TODO: implement this method please, Bob
public int Foo() {
// HACK: throw this exception until implemented
// RG0012: Unresolved HACK: throw this exception until implemented
throw new NotImplementedException();
}
record Foo(int X);
Foo f = new(0);
f = f with { X = 1 }; // RG0013: 'with' used outside 'MyApp.Foo'
int i = Convert.ToInt32("100"); // RG0014: Parsing 'int' using 'Convert.ToInt32'
// Code fix: Change to 'int.Parse'
record Foo {
public int X { get; init; }
public int Y { get; set; } // RG0015: 'Y' should not have set accessor because it's declared in a record
}
MUTABLE RECORDS
You can annotate records with [Mutable]
attribute from RG.Annotations to skip all immutability checks (RG0015 to RG0020).
record Foo {
public readonly int A, B;
public const int C;
public int D; // RG0016: 'D' should not be mutable because it's declared in a record
}
record Foo(
ImmutableArray<int> A,
int[] B // RG0017: 'B' is a mutable collection and should not be used in a record
);
class C { }
struct S { }
record R { }
record Foo(
C C, // RG0018: 'C' is class type and should not be used in a record
S S, // RG0018: 'S' is struct type and should not be used in a record
R R
);
class
(See "Allowed classes")struct
(See "Allowed structs")interface
dynamic
object
Tuple
record
enum
delegate
System.Uri
System.Type
System.Reflection.Module
System.Reflection.Assembly
System.Reflection.TypeInfo
System.Reflection.MethodInfo
System.Reflection.PropertyInfo
System.Reflection.FieldInfo
System.Reflection.ConstructorInfo
System.Reflection.ParameterInfo
System.Reflection.EventInfo
System.Reflection.LocalVariableInfo
System.Reflection.MemberInfo
System.Reflection.ManifestResourceInfo
System.Reflection.MethodBase
System.Reflection.MethodBody
System.Net.IPAddress
- Primitive value types
struct
s inSystem
namespacestruct
s inUnitsNet
namespace (3rd party library)
record Foo {
public int X { get; init; }
public int Y { get; init; }
[Required]
public int Z { get; init; }
}
Foo foo = new() { // RG0019: 'Z' is a required property and should be initialized
X = 0
};
You can also put an @
prefix to record property name to mark it as a required property
record Foo {
public int X { get; init; }
public int Y { get; init; }
public int @Z { get; init; }
}
Foo foo = new() { // RG0019: 'Z' is a required property and should be initialized
X = 0
};
20. (Reserved for #43)
Put an @
prefix to local name to mark it as a readonly local
int @max = 100;
max = 50; // RG0021: 'max' is a readonly local variable
NOTE
This is a work in progress and does not currently support ref
locals, ref readonly
locals, and ref
expressions.
void Foo(int @x) {
x = 0; // RG0022: 'x' is a readonly parameter
}
void Foo(out int @x) { // RG0023: 'out' parameter 'x' cannot be readonly
}
enum X { A, B, C }
enum Y { A, B, C, D }
X x = (X)Y.A; // RG0025: Casting to an incompatible enum; Value 3 is missing from 'X'
Y y = (Y)X.A; // no warning
enum X { A, B, C }
enum Y { A, C, B }
X x = (X)Y.A; // Info: RG0026: Possibly casting to an incompatible enum; 'B' doesn't have the same value in 'X' and in 'Y'
enum X { A, BB, C }
enum Y { A, B, C }
X x = (X)Y.A; // Info: RG0027: Possibly casting to an incompatible enum; Value 1 doesn't have a same name in 'X' and in 'Y'
// .proto
message X {
int32 foo = 1;
string bar = 2;
}
// .cs
X x = new() { // RG0028: 'Bar' is a required protobuf property and should be initialized
Foo = 1
};
// .proto
message X {
oneof which {
int32 foo = 1;
string bar = 2;
}
}
// .cs
X x = new() {
Foo = 1,
Bar = "Hello" // RG0029: 'Bar' cannot be initialized because 'Foo' has been initialized
};
dynamic x = 1; // RG0031: Do not use dynamic type