diff --git a/README.md b/README.md
index 8450848e0..077c58fba 100755
--- a/README.md
+++ b/README.md
@@ -205,6 +205,7 @@ If you are already using other analyzers, you can check [which rules are duplica
|[MA0188](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0188.md)|Design|Use System.TimeProvider instead of a custom time abstraction|ℹ️|✔️|❌|
|[MA0189](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0189.md)|Design|Use InlineArray instead of fixed-size buffers|ℹ️|✔️|✔️|
|[MA0190](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0190.md)|Design|Use partial property instead of partial method for GeneratedRegex|ℹ️|✔️|✔️|
+|[MA0191](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0191.md)|Design|Do not use the null-forgiving operator|⚠️|❌|❌|
diff --git a/docs/README.md b/docs/README.md
index 711afa5e9..08d5ae0c4 100755
--- a/docs/README.md
+++ b/docs/README.md
@@ -189,6 +189,7 @@
|[MA0188](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0188.md)|Design|Use System.TimeProvider instead of a custom time abstraction|ℹ️|✔️|❌|
|[MA0189](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0189.md)|Design|Use InlineArray instead of fixed-size buffers|ℹ️|✔️|✔️|
|[MA0190](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0190.md)|Design|Use partial property instead of partial method for GeneratedRegex|ℹ️|✔️|✔️|
+|[MA0191](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0191.md)|Design|Do not use the null-forgiving operator|⚠️|❌|❌|
|Id|Suppressed rule|Justification|
|--|---------------|-------------|
@@ -771,6 +772,9 @@ dotnet_diagnostic.MA0189.severity = suggestion
# MA0190: Use partial property instead of partial method for GeneratedRegex
dotnet_diagnostic.MA0190.severity = suggestion
+
+# MA0191: Do not use the null-forgiving operator
+dotnet_diagnostic.MA0191.severity = none
```
# .editorconfig - all rules disabled
@@ -1339,4 +1343,7 @@ dotnet_diagnostic.MA0189.severity = none
# MA0190: Use partial property instead of partial method for GeneratedRegex
dotnet_diagnostic.MA0190.severity = none
+
+# MA0191: Do not use the null-forgiving operator
+dotnet_diagnostic.MA0191.severity = none
```
diff --git a/docs/Rules/MA0191.md b/docs/Rules/MA0191.md
new file mode 100644
index 000000000..1f11d5728
--- /dev/null
+++ b/docs/Rules/MA0191.md
@@ -0,0 +1,64 @@
+# MA0191 - Do not use the null-forgiving operator
+
+Source: [DoNotUseNullForgivenessAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseNullForgivenessAnalyzer.cs)
+
+
+The null-forgiving operator (`!`) suppresses nullable warnings from the compiler. Using `null!` or `default!` to assign or initialize a value is a code smell that hides potential null reference issues by telling the compiler to ignore a null assignment.
+
+This rule reports usages of the null-forgiving operator where the operand is `null`, `default`, or `default(T)`.
+
+## Non-compliant code
+
+````csharp
+#nullable enable
+class Sample
+{
+ // Field initialized with null!
+ HttpClient _httpClient = null!;
+
+ // Property initialized with null!
+ string TempDir { get; set; } = null!;
+
+ // default!
+ string _value = default!;
+
+ // default(T)!
+ string _value2 = default(string)!;
+}
+````
+
+## Compliant code
+
+````csharp
+#nullable enable
+class Sample
+{
+ // Initialized with a real value
+ HttpClient _httpClient = new HttpClient();
+
+ // Property with a non-null default
+ string TempDir { get; set; } = string.Empty;
+
+ // Using ! on non-null/default expressions is allowed
+ string? _nullable = GetNullable();
+ string _value = _nullable!; // allowed – not null! or default!
+}
+````
+
+## When it's necessary
+
+In some cases the null-forgiving operator is unavoidable, such as when integrating with dependency injection frameworks, deserializers, or model binders that initialize properties after construction. In these situations, suppress the warning with `#pragma warning disable` and include a comment explaining why the null-forgiving operator is justified.
+
+````csharp
+#pragma warning disable MA0191 // The DI container will inject this before use
+HttpClient _httpClient = default!;
+#pragma warning restore MA0191
+````
+
+## Configuration
+
+This rule is disabled by default. To enable it, add the following to your `.editorconfig` file:
+
+````editorconfig
+dotnet_diagnostic.MA0191.severity = warning
+````
diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig
index d49f3766c..c1f0232be 100644
--- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig
+++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig
@@ -565,3 +565,6 @@ dotnet_diagnostic.MA0189.severity = suggestion
# MA0190: Use partial property instead of partial method for GeneratedRegex
dotnet_diagnostic.MA0190.severity = suggestion
+
+# MA0191: Do not use the null-forgiving operator
+dotnet_diagnostic.MA0191.severity = none
diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig
index d9307299a..3cc8e7a39 100644
--- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig
+++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig
@@ -565,3 +565,6 @@ dotnet_diagnostic.MA0189.severity = none
# MA0190: Use partial property instead of partial method for GeneratedRegex
dotnet_diagnostic.MA0190.severity = none
+
+# MA0191: Do not use the null-forgiving operator
+dotnet_diagnostic.MA0191.severity = none
diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs
index 08a659b40..7d2445750 100755
--- a/src/Meziantou.Analyzer/RuleIdentifiers.cs
+++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs
@@ -190,6 +190,7 @@ internal static class RuleIdentifiers
public const string UseTimeProviderInsteadOfInterface = "MA0188";
public const string UseInlineArrayInsteadOfFixedBuffer = "MA0189";
public const string UsePartialPropertyInsteadOfPartialMethodForGeneratedRegex = "MA0190";
+ public const string DoNotUseNullForgiveness = "MA0191";
public static string GetHelpUri(string identifier)
{
diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseNullForgivenessAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseNullForgivenessAnalyzer.cs
new file mode 100644
index 000000000..b1d9a6814
--- /dev/null
+++ b/src/Meziantou.Analyzer/Rules/DoNotUseNullForgivenessAnalyzer.cs
@@ -0,0 +1,43 @@
+using System.Collections.Immutable;
+using Meziantou.Analyzer.Internals;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Meziantou.Analyzer.Rules;
+
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class DoNotUseNullForgivenessAnalyzer : DiagnosticAnalyzer
+{
+ private static readonly DiagnosticDescriptor Rule = new(
+ RuleIdentifiers.DoNotUseNullForgiveness,
+ title: "Do not use the null-forgiving operator",
+ messageFormat: "Do not use the null-forgiving operator",
+ RuleCategories.Design,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: false,
+ description: "",
+ helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.DoNotUseNullForgiveness));
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+
+ context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.SuppressNullableWarningExpression);
+ }
+
+ private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
+ {
+ var node = (PostfixUnaryExpressionSyntax)context.Node;
+ if (!node.Operand.IsKind(SyntaxKind.NullLiteralExpression) &&
+ !node.Operand.IsKind(SyntaxKind.DefaultLiteralExpression) &&
+ !node.Operand.IsKind(SyntaxKind.DefaultExpression))
+ return;
+
+ context.ReportDiagnostic(Rule, node);
+ }
+}
diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseNullForgivenessAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseNullForgivenessAnalyzerTests.cs
new file mode 100644
index 000000000..21690731c
--- /dev/null
+++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseNullForgivenessAnalyzerTests.cs
@@ -0,0 +1,124 @@
+using Meziantou.Analyzer.Rules;
+using TestHelper;
+using Xunit;
+
+namespace Meziantou.Analyzer.Test.Rules;
+
+public sealed class DoNotUseNullForgivenessAnalyzerTests
+{
+ private static ProjectBuilder CreateProjectBuilder()
+ {
+ return new ProjectBuilder()
+ .WithTargetFramework(Helpers.TargetFramework.Net9_0)
+ .WithAnalyzer();
+ }
+
+ [Fact]
+ public async Task NullForgiveness_NullLiteral_ReportsDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Sample
+ {
+ string _field = [|null!|];
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task NullForgiveness_DefaultLiteral_ReportsDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Sample
+ {
+ string _field = [|default!|];
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task NullForgiveness_DefaultExpression_ReportsDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Sample
+ {
+ string _field = [|default(string)!|];
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task NullForgiveness_Property_ReportsDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Sample
+ {
+ string Prop { get; set; } = [|null!|];
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task NullForgiveness_VariableAssignment_ReportsDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Sample
+ {
+ void M()
+ {
+ string s = [|null!|];
+ }
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task NullForgiveness_MemberAccess_NoDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Model
+ {
+ public string? Value { get; set; }
+ }
+ class Sample
+ {
+ void M(Model model)
+ {
+ _ = model.Value!.Length;
+ }
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task NoNullForgiveness_NoDiagnostic()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ #nullable enable
+ class Sample
+ {
+ string _field = "value";
+ string Prop { get; set; } = "value";
+ }
+ """)
+ .ValidateAsync();
+ }
+}