-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Omsmith/conditional parameters fix #668
Conversation
@@ -116,6 +116,10 @@ INamedTypeSymbol typeSymbol | |||
return; | |||
} | |||
|
|||
if( Attributes.Objects.ConditionallyImmutable.IsDefined( typeSymbol ) ) { | |||
immutabilityContext = immutabilityContext.WithConditionalTypeParametersAsImmutable( typeSymbol ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be some problem here with say...
[ConditionallyImmutable]
public sealed class Foo<[ConditionallyImmutable.OnlyIf] T> {
[ConditionallyImmutable]
public sealed class Bar<[ConditionallyImmutable.OnlyIf] U> {
// something in here....
}
}
But I haven't thought about that much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open an issue to add test cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// <summary> | ||
/// This function is intended to provide a new immutability context that considers the conditional type parameters | ||
/// of the ConditionallyImmutable type being checked as immutable. It is important this is only used in those cases, | ||
/// and not for instance while validating type arguments to an [Immutable] type parameter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not used for the linked function, which keeps conditional type parameters from being passed to strictly immutable ones as arguments.
D2L.CodeStyle/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityAnalyzer.cs
Lines 128 to 162 in 8baddd9
private static void AnalyzeGenericMethodTypeArguments( | |
SyntaxNodeAnalysisContext ctx, | |
ImmutabilityContext immutabilityContext, | |
GenericNameSyntax syntax | |
) { | |
if( syntax.IsFromDocComment() ) { | |
// ignore things in doccomments such as crefs | |
return; | |
} | |
SymbolInfo info = ctx.SemanticModel.GetSymbolInfo( syntax, ctx.CancellationToken ); | |
var (typeParameters, typeArguments) = GetTypeParamsAndArgs( info.Symbol ); | |
int i = 0; | |
var paramArgPairs = typeParameters.Zip( typeArguments, ( p, a ) => (p, a, i++) ); | |
foreach( var (parameter, argument, position) in paramArgPairs ) { | |
// TODO: this should eventually use information from ImmutableTypeInfo | |
// however the current information about immutable type parameters | |
// includes [Immutable] filling for what will instead be the upcoming | |
// [OnlyIf] (e.g. it would be broken for IEnumerable<>) | |
if( !Attributes.Objects.Immutable.IsDefined( parameter ) ) { | |
continue; | |
} | |
if( !immutabilityContext.IsImmutable( | |
type: argument, | |
kind: ImmutableTypeKind.Total, | |
getLocation: () => syntax.TypeArgumentList.Arguments[position].GetLocation(), | |
out Diagnostic diagnostic | |
) ) { | |
// TODO: not necessarily a good diagnostic for this use-case | |
ctx.ReportDiagnostic( diagnostic ); | |
} | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be "nice" if this wasn't sound only because of two different parts of analysis covering for each other, but this is pretty straight forward and doesn't require a concept like "Conditional" vs "Definite" immutability
@@ -17,6 +17,7 @@ namespace D2L.CodeStyle.Analyzers.Immutability { | |||
/// </summary> | |||
internal sealed partial class ImmutabilityContext { | |||
private readonly ImmutableDictionary<INamedTypeSymbol, ImmutableTypeInfo> m_extraImmutableTypes; | |||
private readonly ImmutableHashSet<ITypeParameterSymbol> m_conditionalTypeParameters; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could just be ITypeSymbol
and not parameter specific,, but 🤷 can be adjusted later if needed.
@@ -769,17 +769,17 @@ public sealed class AnalyzedImmutableGenericClassGivenT<[ConditionallyImmutable. | |||
|
|||
|
|||
|
|||
static T /* MemberIsNotReadOnly(Field, m_field, AnalyzedImmutableGenericClassGivenT) */ m_field /**/; | |||
static readonly T m_field; | |||
static /* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/ /* MemberIsNotReadOnly(Field, m_field, AnalyzedImmutableGenericClassGivenT) */ m_field /**/; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional parameters aren't added as immutable when verifying statics. They aren't safe to hold as static, only definitely immutable things are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that these updates are in the context of
[ConditionallyImmutable]
public sealed class AnalyzedImmutableGenericClassGivenT<[ConditionallyImmutable.OnlyIf] T, U> {
static /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field, AnalyzedImmutableGenericClassGivenT) */ m_field /**/; | ||
static readonly /* TypeParameterIsNotKnownToBeImmutable(U) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ m_field; | ||
static /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ /* MemberIsNotReadOnly(Field, m_field, AnalyzedImmutableGenericClassGivenT) */ m_field /**/; | ||
static readonly /* TypeParameterIsNotKnownToBeImmutable(T) */ Types.SomeImmutableGenericInterfaceGivenTU<T, U> /**/ m_field; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expected this to be TypeParameterIsNotKnownToBeImmutable(T), TypeParameterIsNotKnownToBeImmutable(U)
, but these diagnostics come from immutableTypeInfo.IsImmutableDefinition
, and the result and diagnostic come from the first parameter that's an issue (i.e. it's out Diagnostic
, not out IEnumerable<Diagnostic>
)
readonly Types.SomeImmutableGenericInterfaceRestrictingT<T, U> m_field; | ||
Types.SomeImmutableGenericInterfaceRestrictingT<T, U> Property { get; } | ||
Types.SomeImmutableGenericInterfaceRestrictingT<T, U> Property { get { return default; } } | ||
static Types.SomeImmutableGenericInterfaceRestrictingT</* TypeParameterIsNotKnownToBeImmutable(T) */ T /**/, U> /* MemberIsNotReadOnly(Field, m_field, AnalyzedImmutableGenericClassGivenT) */ m_field /**/; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
conditional type parameters aren't considered immutable when checking arguments to immutable type parameters (<[Immutable] T>
). These parameters are expecting definitely immutable things.
It may be a little confusing to be told "T" isn't considered immutable for a static, for instance, but not for trying to hold it directly, but... we'll see how it plays out and address via #650 |
Done commenting |
src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Jacob Parker <[email protected]>
No description provided.