diff --git a/src/Creedengo.Core/Analyzers/GCI2334.TrueForAllInsteadOfAll.Fixer.cs b/src/Creedengo.Core/Analyzers/GCI2334.TrueForAllInsteadOfAll.Fixer.cs
new file mode 100644
index 00000000..fc697562
--- /dev/null
+++ b/src/Creedengo.Core/Analyzers/GCI2334.TrueForAllInsteadOfAll.Fixer.cs
@@ -0,0 +1,49 @@
+namespace Creedengo.Core.Analyzers;
+
+/// GCI2334: Use 'TrueForAll' instead of 'All' on a List.
+[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(TrueForAllInsteadOfAllFixer)), Shared]
+public sealed class TrueForAllInsteadOfAllFixer : CodeFixProvider
+{
+ ///
+ public override ImmutableArray FixableDiagnosticIds => _fixableDiagnosticIds;
+ private static readonly ImmutableArray _fixableDiagnosticIds = ImmutableArray.Create(TrueForAllInsteadOfAll.Descriptor.Id);
+
+ ///
+ [ExcludeFromCodeCoverage]
+ public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
+
+ ///
+ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ if (context.Diagnostics.Length == 0)
+ return;
+
+ var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+ if (root is null) return;
+
+ var nodeToFix = root.FindNode(context.Span, getInnermostNodeForTie: true);
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ title: "Use 'TrueForAll' instead of 'All'",
+ createChangedDocument: token => RefactorAsync(context.Document, nodeToFix, token),
+ equivalenceKey: "Use 'TrueForAll' instead of 'All'"),
+ context.Diagnostics);
+ }
+
+ private static async Task RefactorAsync(Document document, SyntaxNode nodeToFix, CancellationToken token)
+ {
+ // nodeToFix is the IdentifierNameSyntax "All"; climb up to the InvocationExpressionSyntax
+ if (nodeToFix.Parent is not MemberAccessExpressionSyntax memberAccess ||
+ memberAccess.Parent is not InvocationExpressionSyntax invocation)
+ {
+ return document;
+ }
+
+ var editor = await DocumentEditor.CreateAsync(document, token).ConfigureAwait(false);
+
+ var newName = SyntaxFactory.IdentifierName("TrueForAll").WithTriviaFrom(memberAccess.Name);
+ editor.ReplaceNode(invocation, invocation.WithExpression(memberAccess.WithName(newName)));
+
+ return editor.GetChangedDocument();
+ }
+}
diff --git a/src/Creedengo.Core/Analyzers/GCI2334.TrueForAllInsteadOfAll.cs b/src/Creedengo.Core/Analyzers/GCI2334.TrueForAllInsteadOfAll.cs
new file mode 100644
index 00000000..7e00abe5
--- /dev/null
+++ b/src/Creedengo.Core/Analyzers/GCI2334.TrueForAllInsteadOfAll.cs
@@ -0,0 +1,61 @@
+namespace Creedengo.Core.Analyzers;
+
+/// GCI2334: Use 'TrueForAll' instead of 'All' on a List.
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class TrueForAllInsteadOfAll : DiagnosticAnalyzer
+{
+ private static readonly ImmutableArray SyntaxKinds = ImmutableArray.Create(SyntaxKind.InvocationExpression);
+
+ /// The diagnostic descriptor.
+ public static DiagnosticDescriptor Descriptor { get; } = Rule.CreateDescriptor(
+ id: Rule.Ids.GCI2334_TrueForAllInsteadOfAll,
+ title: "Use 'TrueForAll' instead of 'All'",
+ message: "Use 'List.TrueForAll' instead of 'Enumerable.All' for better performance",
+ category: Rule.Categories.Performance,
+ severity: DiagnosticSeverity.Warning,
+ description: "Prefer 'List.TrueForAll' over the LINQ 'Enumerable.All' extension method when the source is a 'List', as it avoids LINQ overhead.");
+
+ ///
+ public override ImmutableArray SupportedDiagnostics => _supportedDiagnostics;
+ private static readonly ImmutableArray _supportedDiagnostics = ImmutableArray.Create(Descriptor);
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.RegisterCompilationStartAction(static context =>
+ {
+ var enumerableType = context.Compilation.GetTypeByMetadataName("System.Linq.Enumerable");
+ var listType = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
+ if (enumerableType is null || listType is null) return;
+
+ context.RegisterSyntaxNodeAction(
+ nodeContext => AnalyzeInvocation(nodeContext, enumerableType, listType),
+ SyntaxKinds);
+ });
+ }
+
+ private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedTypeSymbol enumerableType, INamedTypeSymbol listType)
+ {
+ var invocationExpr = (InvocationExpressionSyntax)context.Node;
+
+ if (invocationExpr.Expression is not MemberAccessExpressionSyntax memberAccess)
+ return;
+
+ if (memberAccess.Name.Identifier.Text != nameof(Enumerable.All))
+ return;
+
+ if (context.SemanticModel.GetSymbolInfo(invocationExpr).Symbol is not IMethodSymbol method)
+ return;
+
+ if (!method.IsExtensionMethod || !SymbolEqualityComparer.Default.Equals(method.ContainingType, enumerableType))
+ return;
+
+ var receiverType = context.SemanticModel.GetTypeInfo(memberAccess.Expression).Type;
+ if (receiverType is null || !SymbolEqualityComparer.Default.Equals(receiverType.OriginalDefinition, listType))
+ return;
+
+ context.ReportDiagnostic(Diagnostic.Create(Descriptor, memberAccess.Name.GetLocation()));
+ }
+}
diff --git a/src/Creedengo.Core/Models/Rule.cs b/src/Creedengo.Core/Models/Rule.cs
index 6cc5d153..9fd497a3 100644
--- a/src/Creedengo.Core/Models/Rule.cs
+++ b/src/Creedengo.Core/Models/Rule.cs
@@ -33,6 +33,7 @@ public static class Ids
public const string GCI96_UseEventArgsDotEmpty = "GCI96";
public const string GCI2508_RemoveUselessToStringCall = "GCI2508";
public const string GCI2333_RemoveRedundantToCharArrayCall = "GCI2333";
+ public const string GCI2334_TrueForAllInsteadOfAll = "GCI2334";
public const string GCI98_UseThenByInsteadOfOrderBy = "GCI98";
}
diff --git a/src/Creedengo.Tests/Tests/GCI2334.TrueForAllInsteadOfAll.Tests.cs b/src/Creedengo.Tests/Tests/GCI2334.TrueForAllInsteadOfAll.Tests.cs
new file mode 100644
index 00000000..3270ad33
--- /dev/null
+++ b/src/Creedengo.Tests/Tests/GCI2334.TrueForAllInsteadOfAll.Tests.cs
@@ -0,0 +1,205 @@
+namespace Creedengo.Tests.Tests;
+
+[TestClass]
+public sealed class TrueForAllInsteadOfAllTests
+{
+ private static readonly CodeFixerDlg VerifyAsync = TestRunner.VerifyAsync;
+
+ [TestMethod]
+ public Task EmptyCodeAsync() => VerifyAsync("");
+
+ // --- No-diagnostic cases ---
+
+ [TestMethod] // .All() on an array — source type is not List
+ public Task AllOnArrayNoDiagnosticAsync() => VerifyAsync("""
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run()
+ {
+ int[] arr = { 1, 2, 3 };
+ bool result = arr.All(x => x > 0);
+ }
+ }
+ """);
+
+ [TestMethod] // .All() on IEnumerable — source type is not List
+ public Task AllOnIEnumerableNoDiagnosticAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run(IEnumerable values)
+ {
+ bool result = values.All(x => x > 0);
+ }
+ }
+ """);
+
+ [TestMethod] // .All() on IList — source type is not List
+ public Task AllOnIListNoDiagnosticAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run(IList values)
+ {
+ bool result = values.All(x => x > 0);
+ }
+ }
+ """);
+
+ [TestMethod] // .TrueForAll() already used — no LINQ call to flag
+ public Task TrueForAllAlreadyUsedNoDiagnosticAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+
+ public class Test
+ {
+ public void Run()
+ {
+ var list = new List { 1, 2, 3 };
+ bool result = list.TrueForAll(x => x > 0);
+ }
+ }
+ """);
+
+ [TestMethod] // custom type with an All method — not from Enumerable
+ public Task AllOnCustomTypeWithAllMethodNoDiagnosticAsync() => VerifyAsync("""
+ public class MyCollection
+ {
+ public bool All(System.Func predicate) => true;
+ }
+
+ public class Test
+ {
+ public void Run()
+ {
+ var col = new MyCollection();
+ bool result = col.All(x => x > 0);
+ }
+ }
+ """);
+
+ // --- Positive cases (diagnostic + fix) ---
+
+ [TestMethod]
+ public Task AllOnListWithLambdaShouldUseTrueForAllAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run()
+ {
+ var list = new List { 1, 2, 3 };
+ bool result = list.[|All|](x => x > 0);
+ }
+ }
+ """,
+ """
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run()
+ {
+ var list = new List { 1, 2, 3 };
+ bool result = list.TrueForAll(x => x > 0);
+ }
+ }
+ """);
+
+ [TestMethod]
+ public Task AllOnListWithMethodGroupShouldUseTrueForAllAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ private static bool IsPositive(int x) => x > 0;
+
+ public void Run()
+ {
+ var list = new List { 1, 2, 3 };
+ bool result = list.[|All|](IsPositive);
+ }
+ }
+ """,
+ """
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ private static bool IsPositive(int x) => x > 0;
+
+ public void Run()
+ {
+ var list = new List { 1, 2, 3 };
+ bool result = list.TrueForAll(IsPositive);
+ }
+ }
+ """);
+
+ [TestMethod]
+ public Task AllOnListParameterShouldUseTrueForAllAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public bool Run(List items)
+ {
+ return items.[|All|](s => s.Length > 0);
+ }
+ }
+ """,
+ """
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public bool Run(List items)
+ {
+ return items.TrueForAll(s => s.Length > 0);
+ }
+ }
+ """);
+
+ [TestMethod]
+ public Task AllOnMultipleListsShouldUseTrueForAllAsync() => VerifyAsync("""
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run()
+ {
+ var list1 = new List { 1, 2, 3 };
+ var list2 = new List { "a", "b" };
+ bool a = list1.[|All|](x => x > 0);
+ bool b = list2.[|All|](s => s.Length > 0);
+ }
+ }
+ """,
+ """
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class Test
+ {
+ public void Run()
+ {
+ var list1 = new List { 1, 2, 3 };
+ var list2 = new List { "a", "b" };
+ bool a = list1.TrueForAll(x => x > 0);
+ bool b = list2.TrueForAll(s => s.Length > 0);
+ }
+ }
+ """);
+}