From 5b2d0a6fd3ddea453f4d34bf0092897932e49e0b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 15 Jun 2024 15:11:50 -0700 Subject: [PATCH] in progress --- ...bstractPopulateSwitchDiagnosticAnalyzer.cs | 112 +++++++++--------- 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs index 7dc6585af58e6..53d5c1dd8dad7 100644 --- a/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -27,8 +28,6 @@ protected AbstractPopulateSwitchDiagnosticAnalyzer(string diagnosticId, EnforceO { } - #region Interface methods - protected abstract OperationKind OperationKind { get; } protected abstract bool IsSwitchTypeUnknown(TSwitchOperation operation); @@ -54,74 +53,77 @@ private void AnalyzeOperation(OperationAnalysisContext context) if (switchOperation.Syntax is not TSwitchSyntax switchBlock || IsSwitchTypeUnknown(switchOperation)) return; - var tree = switchBlock.SyntaxTree; + if (HasExhaustiveNullAndTypeCheckCases(switchOperation)) + return; - if (SwitchIsIncomplete(switchOperation, out var missingCases, out var missingDefaultCase) && - !tree.OverlapsHiddenPosition(switchBlock.Span, context.CancellationToken)) - { - Debug.Assert(missingCases || missingDefaultCase); - var properties = ImmutableDictionary.Empty - .Add(PopulateSwitchStatementHelpers.MissingCases, missingCases.ToString()) - .Add(PopulateSwitchStatementHelpers.MissingDefaultCase, missingDefaultCase.ToString()); - var diagnostic = Diagnostic.Create( - Descriptor, - GetDiagnosticLocation(switchBlock), - properties: properties, - additionalLocations: [switchBlock.GetLocation()]); - context.ReportDiagnostic(diagnostic); - } - } + var value = GetValueOfSwitchOperation(switchOperation); + var type = value.Type; + if (type is null) + return; - #endregion + var (missingCases, missingDefaultCase) = AnalyzeSwitch(switchOperation, type); + if (!missingCases && !missingDefaultCase) + return; - private bool SwitchIsIncomplete( - TSwitchOperation operation, - out bool missingCases, - out bool missingDefaultCase) - { - missingCases = false; - missingDefaultCase = false; + if (switchBlock.SyntaxTree.OverlapsHiddenPosition(switchBlock.Span, context.CancellationToken)) + return; - if (HasExhaustiveNullAndTypeCheckCases(operation)) - return false; + var properties = ImmutableDictionary.Empty + .Add(PopulateSwitchStatementHelpers.MissingCases, missingCases.ToString()) + .Add(PopulateSwitchStatementHelpers.MissingDefaultCase, missingDefaultCase.ToString()); + var diagnostic = Diagnostic.Create( + Descriptor, + GetDiagnosticLocation(switchBlock), + properties: properties, + additionalLocations: [switchBlock.GetLocation()]); + context.ReportDiagnostic(diagnostic); + } - if (!IsBooleanSwitch(operation, out missingCases, out missingDefaultCase)) - { - var missingEnumMembers = GetMissingEnumMembers(operation); + private (bool missingCases, bool missingDefaultCase) AnalyzeSwitch(TSwitchOperation switchOperation, ITypeSymbol type) + { + var typeWithoutNullable = type.RemoveNullableIfPresent(); - missingCases = missingEnumMembers.Count > 0; - missingDefaultCase = !HasDefaultCase(operation); + if (typeWithoutNullable.SpecialType == SpecialType.System_Boolean) + { + return AnalyzeBooleanSwitch(switchOperation, type); + } + else if (typeWithoutNullable.TypeKind == TypeKind.Enum) + { + return AnalyzeEnumSwitch(switchOperation, type); + } + else + { + return (missingCases: false, missingDefaultCase: !HasDefaultCase(switchOperation)); } - - return missingCases || missingDefaultCase; } - private bool IsBooleanSwitch(TSwitchOperation operation, out bool missingCases, out bool missingDefaultCase) + private (bool missingCases, bool missingDefaultCase) AnalyzeBooleanSwitch(TSwitchOperation operation, ITypeSymbol type) { - missingCases = false; - missingDefaultCase = false; - - var value = GetValueOfSwitchOperation(operation); - var type = value.Type.RemoveNullableIfPresent(); - if (type is not { SpecialType: SpecialType.System_Boolean }) - return false; + if (type.RemoveNullableIfPresent() is not { SpecialType: SpecialType.System_Boolean }) + return default; // If the switch already has a default case, then we don't have to offer the user anything. if (HasDefaultCase(operation)) - { - missingDefaultCase = false; - } - else - { - // Doesn't have a default. We don't want to offer that if they're already complete. - var hasAllCases = HasConstantCase(operation, true) && HasConstantCase(operation, false); - if (value.Type.IsNullable()) - hasAllCases = hasAllCases && HasConstantCase(operation, null); + return default; - missingDefaultCase = !hasAllCases; - } + // Doesn't have a default. We don't want to offer that if they're already complete. + var hasAllCases = HasConstantCase(operation, true) && HasConstantCase(operation, false); + if (type.IsNullable()) + hasAllCases = hasAllCases && HasConstantCase(operation, null); + + return (missingCases: !hasAllCases, missingDefaultCase: false); + } + + private (bool missingCases, bool missingDefaultCase) AnalyzeEnumSwitch(TSwitchOperation operation, ITypeSymbol type) + { + if (type.RemoveNullableIfPresent()?.TypeKind != TypeKind.Enum) + return default; + + var missingEnumMembers = GetMissingEnumMembers(operation); - return true; + var missingCases = missingEnumMembers.Count > 0; + var missingDefaultCase = !HasDefaultCase(operation); + return (missingCases, missingDefaultCase); } protected static bool ConstantValueEquals(Optional constantValue, object? value)