Skip to content

Commit 5ceb38a

Browse files
committed
enforce immutable type params during immutability analysis
Decide a type is not immutable if any [Immutable] type parameters to do satisfy that constraint during immutable type analysis. Previously we would assume a different section of the analyzer caught these, as we look at arguments to these type parameters specifically even outside of actual immutable type analysis. Checking during actual immutable type analysis means we don't miss it when it really matters, in case we did otherwise.
1 parent 2f155cd commit 5ceb38a

File tree

5 files changed

+30
-31
lines changed

5 files changed

+30
-31
lines changed

src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ Func<int, Location> getArgumentLocation
294294
new ImmutabilityQuery(
295295
ImmutableTypeKind.Total,
296296
argument
297-
),
297+
) { EnforceImmutableTypeParams = false },
298298
getLocation: () => getArgumentLocation( position ),
299299
out Diagnostic diagnostic
300300
) ) {

src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ out Diagnostic diagnostic
123123
return info.IsImmutableDefinition(
124124
context: this,
125125
definition: namedType,
126+
enforceImmutableTypeParams: query.EnforceImmutableTypeParams,
126127
getLocation: getLocation,
127128
out diagnostic
128129
);
Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
1-
#nullable disable
2-
31
using Microsoft.CodeAnalysis;
42

53
namespace D2L.CodeStyle.Analyzers.Immutability {
6-
internal readonly struct ImmutabilityQuery {
7-
public ImmutabilityQuery(
8-
ImmutableTypeKind kind,
9-
ITypeSymbol type
10-
) {
11-
Kind = kind;
12-
Type = type;
13-
}
14-
15-
public ImmutableTypeKind Kind { get; }
16-
public ITypeSymbol Type { get; }
4+
internal readonly record struct ImmutabilityQuery(
5+
ImmutableTypeKind Kind,
6+
ITypeSymbol Type
7+
) {
8+
public bool EnforceImmutableTypeParams { get; init; } = true;
179
}
1810
}

src/D2L.CodeStyle.Analyzers/Immutability/ImmutableDefinitionChecker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,13 +523,13 @@ .Symbol is not IMethodSymbol methodSymbol
523523
// being anything other than an instance of T.
524524
query = new ImmutabilityQuery(
525525
ImmutableTypeKind.Instance,
526-
type: assignment.AssignedType
526+
Type: assignment.AssignedType
527527
);
528528
} else {
529529
// In general we need to handle subtypes.
530530
query = new ImmutabilityQuery(
531531
ImmutableTypeKind.Total,
532-
type: assignment.AssignedType
532+
Type: assignment.AssignedType
533533
);
534534
}
535535

src/D2L.CodeStyle.Analyzers/Immutability/ImmutableTypeInfo.cs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,28 @@ namespace D2L.CodeStyle.Analyzers.Immutability {
1111
/// </summary>
1212
internal readonly struct ImmutableTypeInfo {
1313

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

1816
private ImmutableTypeInfo(
1917
ImmutableTypeKind kind,
2018
INamedTypeSymbol type,
21-
ImmutableArray<bool> conditionalTypeParameters
19+
ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> typeParameterInfo
2220
) {
2321
Kind = kind;
2422
Type = type;
25-
m_conditionalTypeParameters = conditionalTypeParameters;
23+
m_typeParameterInfo = typeParameterInfo;
2624
}
2725

2826
public ImmutableTypeKind Kind { get; }
2927

3028
public INamedTypeSymbol Type { get; }
3129

32-
public bool IsConditional => m_conditionalTypeParameters.Length > 0;
30+
public bool IsConditional => m_typeParameterInfo.Any( p => p.IsImmutableCondition );
3331

3432
public bool IsImmutableDefinition(
3533
ImmutabilityContext context,
3634
INamedTypeSymbol definition,
35+
bool enforceImmutableTypeParams,
3736
Func<Location> getLocation,
3837
out Diagnostic diagnostic
3938
) {
@@ -45,9 +44,10 @@ out Diagnostic diagnostic
4544

4645
var argRelevance = definition
4746
.TypeArguments
48-
.Zip( m_conditionalTypeParameters, ( a, relevant ) => (a, relevant) );
49-
foreach( (ITypeSymbol argument, bool isRelevant) in argRelevance ) {
50-
if( !isRelevant ) {
47+
.Zip( m_typeParameterInfo, ( a, info ) => (a, info) );
48+
foreach( (ITypeSymbol argument, (bool requiresImmutability, bool isImmutableCondition)) in argRelevance ) {
49+
bool relevant = isImmutableCondition || ( requiresImmutability && enforceImmutableTypeParams );
50+
if( !relevant ) {
5151
continue;
5252
}
5353

@@ -72,31 +72,37 @@ public static ImmutableTypeInfo Create(
7272
ImmutableTypeKind kind,
7373
INamedTypeSymbol type
7474
) {
75-
ImmutableArray<bool> immutableTypeParameters = type
75+
ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> typeParameterInfo = type
7676
.TypeParameters
77-
.Select( p => annotationsContext.Objects.OnlyIf.IsDefined( p ) )
77+
.Select( p => (
78+
annotationsContext.Objects.Immutable.IsDefined( p ),
79+
annotationsContext.Objects.OnlyIf.IsDefined( p )
80+
) )
7881
.ToImmutableArray();
7982

8083
return new ImmutableTypeInfo(
8184
kind: kind,
8285
type: type,
83-
conditionalTypeParameters: immutableTypeParameters
86+
typeParameterInfo: typeParameterInfo
8487
);
8588
}
8689

8790
public static ImmutableTypeInfo CreateWithAllConditionalTypeParameters(
8891
ImmutableTypeKind kind,
8992
INamedTypeSymbol type
9093
) {
91-
ImmutableArray<bool> immutableTypeParameters = type
94+
ImmutableArray<(bool RequiresImmutability, bool IsImmutableCondition)> typeParameterInfo = type
9295
.TypeParameters
93-
.Select( p => true )
96+
.Select( p => (
97+
false,
98+
true
99+
) )
94100
.ToImmutableArray();
95101

96102
return new ImmutableTypeInfo(
97103
kind: kind,
98104
type: type,
99-
conditionalTypeParameters: immutableTypeParameters
105+
typeParameterInfo: typeParameterInfo
100106
);
101107
}
102108
}

0 commit comments

Comments
 (0)