Skip to content

Commit

Permalink
in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi committed Jun 15, 2024
1 parent df30535 commit 5b2d0a6
Showing 1 changed file with 57 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,8 +28,6 @@ protected AbstractPopulateSwitchDiagnosticAnalyzer(string diagnosticId, EnforceO
{
}

#region Interface methods

protected abstract OperationKind OperationKind { get; }

protected abstract bool IsSwitchTypeUnknown(TSwitchOperation operation);
Expand All @@ -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<string, string?>.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<string, string?>.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<object?> constantValue, object? value)
Expand Down

0 comments on commit 5b2d0a6

Please sign in to comment.