Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ Func<int, Location> getArgumentLocation
new ImmutabilityQuery(
ImmutableTypeKind.Total,
argument
),
) { EnforceImmutableTypeParams = false },
getLocation: () => getArgumentLocation( position ),
out Diagnostic diagnostic
) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ out Diagnostic diagnostic
return info.IsImmutableDefinition(
context: this,
definition: namedType,
enforceImmutableTypeParams: query.EnforceImmutableTypeParams,
getLocation: getLocation,
out diagnostic
);
Expand Down
18 changes: 5 additions & 13 deletions src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityQuery.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
#nullable disable

using Microsoft.CodeAnalysis;

namespace D2L.CodeStyle.Analyzers.Immutability {
internal readonly struct ImmutabilityQuery {
public ImmutabilityQuery(
ImmutableTypeKind kind,
ITypeSymbol type
) {
Kind = kind;
Type = type;
}

public ImmutableTypeKind Kind { get; }
public ITypeSymbol Type { get; }
internal readonly record struct ImmutabilityQuery(
ImmutableTypeKind Kind,
ITypeSymbol Type
) {
public bool EnforceImmutableTypeParams { get; init; } = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -523,13 +523,13 @@ .Symbol is not IMethodSymbol methodSymbol
// being anything other than an instance of T.
query = new ImmutabilityQuery(
ImmutableTypeKind.Instance,
type: assignment.AssignedType
Type: assignment.AssignedType
);
} else {
// In general we need to handle subtypes.
query = new ImmutabilityQuery(
ImmutableTypeKind.Total,
type: assignment.AssignedType
Type: assignment.AssignedType
);
}

Expand Down
36 changes: 21 additions & 15 deletions src/D2L.CodeStyle.Analyzers/Immutability/ImmutableTypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,28 @@ namespace D2L.CodeStyle.Analyzers.Immutability {
/// </summary>
internal readonly struct ImmutableTypeInfo {

// a mapping of which type parameters considered necessarily immutable for the
// conditionally immutable type to be immutable
private readonly ImmutableArray<bool> m_conditionalTypeParameters;
private readonly ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> m_typeParameterInfo;

private ImmutableTypeInfo(
ImmutableTypeKind kind,
INamedTypeSymbol type,
ImmutableArray<bool> conditionalTypeParameters
ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> typeParameterInfo
) {
Kind = kind;
Type = type;
m_conditionalTypeParameters = conditionalTypeParameters;
m_typeParameterInfo = typeParameterInfo;
}

public ImmutableTypeKind Kind { get; }

public INamedTypeSymbol Type { get; }

public bool IsConditional => m_conditionalTypeParameters.Length > 0;
public bool IsConditional => m_typeParameterInfo.Any( p => p.IsImmutableCondition );

public bool IsImmutableDefinition(
ImmutabilityContext context,
INamedTypeSymbol definition,
bool enforceImmutableTypeParams,
Func<Location> getLocation,
out Diagnostic diagnostic
) {
Expand All @@ -45,9 +44,10 @@ out Diagnostic diagnostic

var argRelevance = definition
.TypeArguments
.Zip( m_conditionalTypeParameters, ( a, relevant ) => (a, relevant) );
foreach( (ITypeSymbol argument, bool isRelevant) in argRelevance ) {
if( !isRelevant ) {
.Zip( m_typeParameterInfo, ( a, info ) => (a, info) );
foreach( (ITypeSymbol argument, (bool requiresImmutability, bool isImmutableCondition)) in argRelevance ) {
bool relevant = isImmutableCondition || ( requiresImmutability && enforceImmutableTypeParams );
if( !relevant ) {
continue;
}

Expand All @@ -72,31 +72,37 @@ public static ImmutableTypeInfo Create(
ImmutableTypeKind kind,
INamedTypeSymbol type
) {
ImmutableArray<bool> immutableTypeParameters = type
ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> typeParameterInfo = type
.TypeParameters
.Select( p => annotationsContext.Objects.OnlyIf.IsDefined( p ) )
.Select( p => (
annotationsContext.Objects.Immutable.IsDefined( p ),
annotationsContext.Objects.OnlyIf.IsDefined( p )
) )
.ToImmutableArray();

return new ImmutableTypeInfo(
kind: kind,
type: type,
conditionalTypeParameters: immutableTypeParameters
typeParameterInfo: typeParameterInfo
);
}

public static ImmutableTypeInfo CreateWithAllConditionalTypeParameters(
ImmutableTypeKind kind,
INamedTypeSymbol type
) {
ImmutableArray<bool> immutableTypeParameters = type
ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> typeParameterInfo = type
.TypeParameters
.Select( p => true )
.Select( p => (
false,
true
) )
.ToImmutableArray();

return new ImmutableTypeInfo(
kind: kind,
type: type,
conditionalTypeParameters: immutableTypeParameters
typeParameterInfo: typeParameterInfo
);
}
}
Expand Down