Skip to content
Merged
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
346 changes: 53 additions & 293 deletions ReadMe.md

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions docs/Rules/EPC11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# EPC11 - Suspicious equality implementation

This analyzer detects suspicious implementations of `Equals` methods that may not be working as intended.

## Description

The analyzer warns when an `Equals` method implementation appears suspicious, typically when the method doesn't use any instance members or compares against other instances properly.

## Code that triggers the analyzer

```csharp
public class MyClass
{
public override bool Equals(object obj)
{
// Suspicious: only using static members or not using 'this' instance
return SomeStaticProperty == 42;
}
}
```

```csharp
public class Person
{
public string Name { get; set; }

public override bool Equals(object obj)
{
// Suspicious: parameter 'obj' is never used
return this.Name == "test";
}
}
```

## How to fix

Implement `Equals` method properly by:

1. Using the parameter that's passed in
2. Checking instance members against the other object's members
3. Following the standard equality pattern

```csharp
public class Person
{
public string Name { get; set; }

public override bool Equals(object obj)
{
if (obj is Person other)
{
return this.Name == other.Name;
}
return false;
}
}
```
41 changes: 41 additions & 0 deletions docs/Rules/EPC12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# EPC12 - Suspicious exception handling: only the 'Message' property is observed in the catch block

This analyzer detects catch blocks where only the `Message` property of an exception is used, which often indicates incomplete exception handling.

## Description

The analyzer warns when a catch block only accesses the `Message` property of an exception without observing the exception object itself or its other important properties like `InnerException`. This is suspicious because the `Message` property often contains generic information, while the actual useful data is in the exception object or its inner exceptions.

## Code that triggers the analyzer

```csharp
try
{
// Some risky operation
SomeMethod();
}
catch (Exception ex)
{
// Only using ex.Message - this triggers the warning
Console.WriteLine(ex.Message);
}
```


## How to fix

Use the full exception object or access additional properties like `InnerException`:

```csharp
try
{
SomeMethod();
}
catch (Exception ex)
{
// Log the full exception object
Console.WriteLine(ex.ToString());
// Or access the exception object directly
LogException(ex);
}
```
64 changes: 64 additions & 0 deletions docs/Rules/EPC13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# EPC13 - Suspiciously unobserved result

This analyzer detects when method return values that should typically be observed are being ignored.

## Description

The analyzer warns when the result of a method call is not being used, particularly for methods that return important values that should be observed. This helps catch cases where developers might have forgotten to use the return value of a method.

## Code that triggers the analyzer

```csharp
public class Example
{
public void ProcessData()
{
// Return value is ignored - this triggers the warning
GetImportantValue();

// String methods return new strings but result is ignored
"hello".ToUpper();

// Collection methods that return new collections
list.Where(x => x > 0);
}

public string GetImportantValue()
{
return "important data";
}
}
```

## How to fix

Observe (use) the return values:

```csharp
public class Example
{
public void ProcessData()
{
// Store the result in a variable
var importantValue = GetImportantValue();

// Use the result directly
var upperText = "hello".ToUpper();
Console.WriteLine(upperText);

// Chain operations or store result
var filteredList = list.Where(x => x > 0).ToList();
}

public string GetImportantValue()
{
return "important data";
}
}
```

If you intentionally want to ignore the result, you can use discard:

```csharp
_ = GetImportantValue(); // Explicitly ignore the result
```
36 changes: 36 additions & 0 deletions docs/Rules/EPC14.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# EPC14 - ConfigureAwait(false) call is redundant

This analyzer detects redundant calls to `ConfigureAwait(false)` when the assembly is configured not to require them.

## Description

The analyzer provides information (not a warning) when `ConfigureAwait(false)` calls are redundant because the assembly has been configured to not capture the synchronization context automatically. This helps clean up unnecessary code.

## Code that triggers the analyzer

```csharp
public async Task ProcessAsync()
{
// ConfigureAwait(false) is redundant when assembly is configured for it
await SomeAsyncMethod().ConfigureAwait(false);

// This is also redundant
var result = await GetDataAsync().ConfigureAwait(false);
}
```

## How to fix

Remove the redundant `ConfigureAwait(false)` calls:

```csharp
public async Task ProcessAsync()
{
// ConfigureAwait(false) behavior is already configured at assembly level
await SomeAsyncMethod();

var result = await GetDataAsync();
}
```

Note: This rule only applies when your assembly is configured to not capture synchronization context by default. The configuration is typically done through project settings or attributes.
46 changes: 46 additions & 0 deletions docs/Rules/EPC15.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# EPC15 - ConfigureAwait(false) must be used

This analyzer detects missing `ConfigureAwait(false)` calls when the assembly is configured to require them.

## Description

The analyzer warns when `ConfigureAwait(false)` should be used but is missing. This typically applies to library code where you want to avoid deadlocks by not capturing the synchronization context.

## Code that triggers the analyzer

```csharp
public async Task ProcessAsync()
{
// Missing ConfigureAwait(false) - this triggers the warning
await SomeAsyncMethod();

// Also missing ConfigureAwait(false)
var result = await GetDataAsync();
}
```

## How to fix

Add `ConfigureAwait(false)` to all await expressions:

```csharp
public async Task ProcessAsync()
{
// Add ConfigureAwait(false) to avoid capturing sync context
await SomeAsyncMethod().ConfigureAwait(false);

var result = await GetDataAsync().ConfigureAwait(false);
}
```

## When to use ConfigureAwait(false)

Use `ConfigureAwait(false)` in:
- Library code that doesn't need to return to the original synchronization context
- Background processing code
- Code that doesn't interact with UI elements

Don't use `ConfigureAwait(false)` in:
- UI event handlers
- Controller actions that need to return to the original context
- Code that accesses UI elements after the await
64 changes: 64 additions & 0 deletions docs/Rules/EPC16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# EPC16 - Awaiting a result of a null-conditional expression will cause NullReferenceException

This analyzer detects dangerous patterns where awaiting the result of a null-conditional operator can cause a `NullReferenceException`.

## Description

The analyzer warns when code awaits the result of a null-conditional expression. When the left-hand side of the null-conditional operator is null, the expression returns null, and awaiting null will throw a `NullReferenceException`.

## Code that triggers the analyzer

```csharp
public async Task ProcessAsync()
{
SomeService service = GetService(); // might return null

// Dangerous: if service is null, this returns null, and awaiting null throws NRE
await service?.ProcessAsync();
}
```

```csharp
public async Task<string> GetDataAsync()
{
var client = GetHttpClient(); // might return null

// This will throw NRE if client is null
return await client?.GetStringAsync("http://example.com");
}
```

## How to fix

Check for null before awaiting, or use proper null handling:

```csharp
public async Task ProcessAsync()
{
SomeService service = GetService();

// Option 1: Check for null first
if (service != null)
{
await service.ProcessAsync();
}

// Option 2: Use null-coalescing with Task.CompletedTask
await (service?.ProcessAsync() ?? Task.CompletedTask);
}
```

```csharp
public async Task<string> GetDataAsync()
{
var client = GetHttpClient();

// Check for null before using
if (client == null)
{
return null; // or throw, or return default value
}

return await client.GetStringAsync("http://example.com");
}
```
61 changes: 61 additions & 0 deletions docs/Rules/EPC17.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# EPC17 - Avoid async-void delegates

This analyzer detects the use of async void delegates, which can cause application crashes due to unhandled exceptions.

## Description

The analyzer warns against using async void delegates. Unlike async void methods (which should only be used for event handlers), async void delegates are particularly dangerous because exceptions thrown in them cannot be caught and will crash the application.

## Code that triggers the analyzer

```csharp
public void SetupHandlers()
{
// Dangerous: async void delegate
SomeEvent += async () =>
{
await SomeAsyncMethod();
// If this throws, it will crash the app
};

// Another dangerous pattern
Task.Run(async () =>
{
await ProcessAsync();
// Exceptions here are unhandled
});
}
```

```csharp
public void RegisterCallback()
{
// Async void delegate in callback
RegisterCallback(async () =>
{
await DoWorkAsync();
});
}
```

## How to fix

Use regular void-return method and make a blocking call to async method.

```csharp
public void SetupHandlers()
{
// Safe: async Task delegate
SomeEvent += (sender, e) =>
{
try
{
SomeAsyncMethod().GetAwaiter().GetResult();
}
catch (Exception ex)
{
// Handle exception properly
LogError(ex);
}
};}
```
Loading