diff --git a/src/Analyzers/CSharp/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.cs b/src/Analyzers/CSharp/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.cs index cb5fad721755a..d2502c782931d 100644 --- a/src/Analyzers/CSharp/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.cs @@ -2,8 +2,10 @@ // 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.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Analyzers.MakeFieldReadonly; using Microsoft.CodeAnalysis.CSharp.MakeFieldReadonly; using Microsoft.CodeAnalysis.Diagnostics; @@ -18,6 +20,8 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeFieldReadonly [Trait(Traits.Feature, Traits.Features.CodeActionsMakeFieldReadonly)] public class MakeFieldReadonlyTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { + private static readonly ParseOptions s_strictFeatureFlag = CSharpParseOptions.Default.WithFeatures(new[] { new KeyValuePair("strict", "true") }); + public MakeFieldReadonlyTests(ITestOutputHelper logger) : base(logger) { @@ -2188,5 +2192,79 @@ public class C } """); } + + [Fact, WorkItem(47197, "https://github.com/dotnet/roslyn/issues/47197")] + public async Task StrictFeatureFlagAssignment1() + { + await TestInRegularAndScriptAsync( + """ + using System; + using System.Collections.Generic; + + class C + { + private static IEqualityComparer [|s_value|]; + + static C() + { + C.s_value = null; + } + } + """, + """ + using System; + using System.Collections.Generic; + + class C + { + private static readonly IEqualityComparer s_value; + + static C() + { + C.s_value = null; + } + } + """, parseOptions: s_strictFeatureFlag); + } + + [Fact, WorkItem(47197, "https://github.com/dotnet/roslyn/issues/47197")] + public async Task StrictFeatureFlagAssignment2() + { + await TestMissingInRegularAndScriptAsync( + """ + using System; + using System.Collections.Generic; + + class C + { + private static IEqualityComparer [|s_value|]; + + static C() + { + C.s_value = null; + } + } + """, new TestParameters(parseOptions: s_strictFeatureFlag)); + } + + [Fact, WorkItem(47197, "https://github.com/dotnet/roslyn/issues/47197")] + public async Task StrictFeatureFlagAssignment3() + { + await TestMissingInRegularAndScriptAsync( + """ + using System; + using System.Collections.Generic; + + class C + { + private static IEqualityComparer [|s_value|]; + + static C() + { + C.s_value = null; + } + } + """); + } } } diff --git a/src/Analyzers/Core/Analyzers/MakeFieldReadonly/AbstractMakeFieldReadonlyDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/MakeFieldReadonly/AbstractMakeFieldReadonlyDiagnosticAnalyzer.cs index 1dea82317b603..7e5d7af549400 100644 --- a/src/Analyzers/Core/Analyzers/MakeFieldReadonly/AbstractMakeFieldReadonlyDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/MakeFieldReadonly/AbstractMakeFieldReadonlyDiagnosticAnalyzer.cs @@ -96,15 +96,13 @@ void AnalyzeOperation(OperationAnalysisContext operationContext) // Ignore fields that are not candidates or have already been written outside the constructor/field initializer. if (!isCandidate || written) - { return; - } // Check if this is a field write outside constructor and field initializer, and update field state accordingly. - if (IsFieldWrite(fieldReference, operationContext.ContainingSymbol)) - { - UpdateFieldStateOnWrite(fieldReference.Field); - } + if (!IsFieldWrite(fieldReference, operationContext.ContainingSymbol)) + return; + + UpdateFieldStateOnWrite(fieldReference.Field); } void OnSymbolEnd(SymbolAnalysisContext symbolEndContext) @@ -113,7 +111,7 @@ void OnSymbolEnd(SymbolAnalysisContext symbolEndContext) var members = ((INamedTypeSymbol)symbolEndContext.Symbol).GetMembers(); foreach (var member in members) { - if (member is IFieldSymbol field && fieldStateMap.TryRemove(field, out var value)) + if (member is IFieldSymbol field && fieldStateMap.TryRemove(field.OriginalDefinition, out var value)) { var (isCandidate, written) = value; if (isCandidate && !written) @@ -160,9 +158,9 @@ static bool IsDataContractSerializable(IFieldSymbol symbol, INamedTypeSymbol? da void UpdateFieldStateOnWrite(IFieldSymbol field) { Debug.Assert(IsCandidateField(field, threadStaticAttribute, dataContractAttribute, dataMemberAttribute)); - Debug.Assert(fieldStateMap.ContainsKey(field)); + Debug.Assert(fieldStateMap.ContainsKey(field.OriginalDefinition)); - fieldStateMap[field] = (isCandidate: true, written: true); + fieldStateMap[field.OriginalDefinition] = (isCandidate: true, written: true); } // Method to get or initialize the field state. @@ -173,13 +171,13 @@ void UpdateFieldStateOnWrite(IFieldSymbol field) return default; } - if (fieldStateMap.TryGetValue(fieldSymbol, out var result)) + if (fieldStateMap.TryGetValue(fieldSymbol.OriginalDefinition, out var result)) { return result; } result = ComputeInitialFieldState(fieldSymbol, options, threadStaticAttribute, dataContractAttribute, dataMemberAttribute, cancellationToken); - return fieldStateMap.GetOrAdd(fieldSymbol, result); + return fieldStateMap.GetOrAdd(fieldSymbol.OriginalDefinition, result); } // Method to compute the initial field state. @@ -209,41 +207,55 @@ private static bool IsFieldWrite(IFieldReferenceOperation fieldReference, ISymbo // Check if the underlying member is being written or a writable reference to the member is taken. var valueUsageInfo = fieldReference.GetValueUsageInfo(owningSymbol); if (!valueUsageInfo.IsWrittenTo()) - { return false; - } // Writes to fields inside constructor are ignored, except for the below cases: // 1. Instance reference of an instance field being written is not the instance being initialized by the constructor. // 2. Field is being written inside a lambda or local function. // Check if we are in the constructor of the containing type of the written field. - var isInConstructor = owningSymbol.IsConstructor(); + var isInInstanceConstructor = owningSymbol.IsConstructor(); var isInStaticConstructor = owningSymbol.IsStaticConstructor(); + + // If we're not in a constructor, this is definitely a write to the field that would prevent it from + // becoming readonly. + if (!isInInstanceConstructor && !isInStaticConstructor) + return true; + var field = fieldReference.Field; - if ((isInConstructor || isInStaticConstructor) && - field.ContainingType == owningSymbol.ContainingType) - { - // For instance fields, ensure that the instance reference is being initialized by the constructor. - var instanceFieldWrittenInCtor = isInConstructor && - fieldReference.Instance?.Kind == OperationKind.InstanceReference && - !fieldReference.IsTargetOfObjectMemberInitializer(); + // Follow 'strict' C#/VB behavior. Has to be opted into by user with feature-flag "strict", but is + // actually what the languages specify as correct. Specifically the actual types of the containing + // type and field-reference-containing type must be the same (not just their OriginalDefinition + // types). + // + // https://github.com/dotnet/roslyn/blob/8770fb62a36157ed4ca38a16a0283d27321a01a7/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs#L1201-L1203 + // https//github.com/dotnet/roslyn/blob/93d3aa1a2cf1790b1a0fe2d120f00987d50445c0/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb#L1868-L1871 + if (!field.ContainingType.Equals(owningSymbol.ContainingType)) + return true; + + if (isInStaticConstructor) + { // For static fields, ensure that we are in the static constructor. - var staticFieldWrittenInStaticCtor = isInStaticConstructor && field.IsStatic; - - if (instanceFieldWrittenInCtor || staticFieldWrittenInStaticCtor) + if (!field.IsStatic) + return true; + } + else + { + // For instance fields, ensure that the instance reference is being initialized by the constructor. + Debug.Assert(isInInstanceConstructor); + if (fieldReference.Instance?.Kind != OperationKind.InstanceReference || + fieldReference.IsTargetOfObjectMemberInitializer()) { - // Finally, ensure that the write is not inside a lambda or local function. - if (fieldReference.TryGetContainingAnonymousFunctionOrLocalFunction() is null) - { - // It is safe to ignore this write. - return false; - } + return true; } } - return true; + // Finally, ensure that the write is not inside a lambda or local function. + if (fieldReference.TryGetContainingAnonymousFunctionOrLocalFunction() is not null) + return true; + + return false; } private static CodeStyleOption2 GetCodeStyleOption(IFieldSymbol field, AnalyzerOptions options) diff --git a/src/Analyzers/VisualBasic/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.vb b/src/Analyzers/VisualBasic/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.vb index f344d27c9398c..0769b136ebc95 100644 --- a/src/Analyzers/VisualBasic/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.vb +++ b/src/Analyzers/VisualBasic/Tests/MakeFieldReadonly/MakeFieldReadonlyTests.vb @@ -12,6 +12,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.MakeFieldReadonly Public Class MakeFieldReadonlyTests Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest + Private Shared ReadOnly s_strictFeatureFlag As ParseOptions = VisualBasicParseOptions.Default.WithFeatures({New KeyValuePair(Of String, String)("strict", "true")}) + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) Return (New VisualBasicMakeFieldReadonlyDiagnosticAnalyzer(), New VisualBasicMakeFieldReadonlyCodeFixProvider()) End Function @@ -1123,5 +1125,67 @@ End Module Await TestInRegularAndScript1Async(initialMarkup, expectedMarkup) End Function + + Public Async Function StrictFeatureFlagAssignment1() As Task + Await TestInRegularAndScriptAsync( +" +imports System +imports System.Collections.Generic + +class C(Of T) + private shared [|s_value|] as IEqualityComparer(Of T) + + shared sub new() + C(Of T).s_value = nothing + end sub +end class +", +" +imports System +imports System.Collections.Generic + +class C(Of T) + private shared ReadOnly s_value as IEqualityComparer(Of T) + + shared sub new() + C(Of T).s_value = nothing + end sub +end class +", parseOptions:=s_strictFeatureFlag) + End Function + + + Public Async Function StrictFeatureFlagAssignment2() As Task + Await TestMissingInRegularAndScriptAsync( +" +imports System +imports System.Collections.Generic + +class C(Of T) + private shared [|s_value|] as IEqualityComparer(Of T) + + shared sub new() + C(Of string).s_value = nothing + end sub +end class +", New TestParameters(parseOptions:=s_strictFeatureFlag)) + End Function + + + Public Async Function StrictFeatureFlagAssignment3() As Task + Await TestMissingAsync( +" +imports System +imports System.Collections.Generic + +class C(Of T) + private shared [|s_value|] as IEqualityComparer(Of T) + + shared sub new() + C(Of string).s_value = nothing + end sub +end class +") + End Function End Class End Namespace diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index 24533e61624bf..68fa8d01fbfec 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -107,7 +107,7 @@ where symbol.Equals(impl) public static ImmutableArray ImplicitInterfaceImplementations(this ISymbol symbol) => symbol.ExplicitOrImplicitInterfaceImplementations().Except(symbol.ExplicitInterfaceImplementations()).ToImmutableArray(); - public static bool IsOverridable([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsOverridable([NotNullWhen(true)] this ISymbol? symbol) { // Members can only have overrides if they are virtual, abstract or override and is not // sealed. @@ -116,7 +116,7 @@ public static bool IsOverridable([NotNullWhen(returnValue: true)] this ISymbol? !symbol.IsSealed; } - public static bool IsImplementableMember([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsImplementableMember([NotNullWhen(true)] this ISymbol? symbol) { if (symbol != null && symbol.ContainingType != null && @@ -159,109 +159,109 @@ MethodKind.UserDefinedOperator or return symbol.ContainingType; } - public static bool IsErrorType([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as ITypeSymbol)?.TypeKind == TypeKind.Error; + public static bool IsErrorType([NotNullWhen(true)] this ISymbol? symbol) + => symbol is ITypeSymbol { TypeKind: TypeKind.Error }; - public static bool IsModuleType([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as ITypeSymbol)?.IsModuleType() == true; + public static bool IsModuleType([NotNullWhen(true)] this ISymbol? symbol) + => symbol is ITypeSymbol { TypeKind: TypeKind.Module }; - public static bool IsInterfaceType([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as ITypeSymbol)?.IsInterfaceType() == true; + public static bool IsInterfaceType([NotNullWhen(true)] this ISymbol? symbol) + => symbol is ITypeSymbol { TypeKind: TypeKind.Interface }; - public static bool IsArrayType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsArrayType([NotNullWhen(true)] this ISymbol? symbol) => symbol?.Kind == SymbolKind.ArrayType; - public static bool IsTupleType([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as ITypeSymbol)?.IsTupleType ?? false; + public static bool IsTupleType([NotNullWhen(true)] this ISymbol? symbol) + => symbol is ITypeSymbol { IsTupleType: true }; - public static bool IsAnonymousFunction([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.AnonymousFunction; + public static bool IsAnonymousFunction([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.AnonymousFunction }; - public static bool IsKind([NotNullWhen(returnValue: true)] this ISymbol? symbol, SymbolKind kind) + public static bool IsKind([NotNullWhen(true)] this ISymbol? symbol, SymbolKind kind) => symbol.MatchesKind(kind); - public static bool MatchesKind([NotNullWhen(returnValue: true)] this ISymbol? symbol, SymbolKind kind) + public static bool MatchesKind([NotNullWhen(true)] this ISymbol? symbol, SymbolKind kind) => symbol?.Kind == kind; - public static bool MatchesKind([NotNullWhen(returnValue: true)] this ISymbol? symbol, SymbolKind kind1, SymbolKind kind2) + public static bool MatchesKind([NotNullWhen(true)] this ISymbol? symbol, SymbolKind kind1, SymbolKind kind2) { return symbol != null && (symbol.Kind == kind1 || symbol.Kind == kind2); } - public static bool MatchesKind([NotNullWhen(returnValue: true)] this ISymbol? symbol, SymbolKind kind1, SymbolKind kind2, SymbolKind kind3) + public static bool MatchesKind([NotNullWhen(true)] this ISymbol? symbol, SymbolKind kind1, SymbolKind kind2, SymbolKind kind3) { return symbol != null && (symbol.Kind == kind1 || symbol.Kind == kind2 || symbol.Kind == kind3); } - public static bool MatchesKind([NotNullWhen(returnValue: true)] this ISymbol? symbol, params SymbolKind[] kinds) + public static bool MatchesKind([NotNullWhen(true)] this ISymbol? symbol, params SymbolKind[] kinds) { return symbol != null && kinds.Contains(symbol.Kind); } - public static bool IsReducedExtension([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsReducedExtension([NotNullWhen(true)] this ISymbol? symbol) => symbol is IMethodSymbol { MethodKind: MethodKind.ReducedExtension }; - public static bool IsEnumMember([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsEnumMember([NotNullWhen(true)] this ISymbol? symbol) => symbol?.Kind == SymbolKind.Field && symbol.ContainingType.IsEnumType(); public static bool IsExtensionMethod(this ISymbol symbol) - => symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).IsExtensionMethod; + => symbol is IMethodSymbol { IsExtensionMethod: true }; - public static bool IsLocalFunction([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => symbol != null && symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).MethodKind == MethodKind.LocalFunction; + public static bool IsLocalFunction([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.LocalFunction }; - public static bool IsAnonymousOrLocalFunction([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsAnonymousOrLocalFunction([NotNullWhen(true)] this ISymbol? symbol) => symbol.IsAnonymousFunction() || symbol.IsLocalFunction(); - public static bool IsModuleMember([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => symbol != null && symbol.ContainingSymbol is INamedTypeSymbol && symbol.ContainingType.TypeKind == TypeKind.Module; + public static bool IsModuleMember([NotNullWhen(true)] this ISymbol? symbol) + => symbol is { ContainingType.TypeKind: TypeKind.Module }; - public static bool IsConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.Constructor; + public static bool IsConstructor([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.Constructor }; - public static bool IsStaticConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.StaticConstructor; + public static bool IsStaticConstructor([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.StaticConstructor }; - public static bool IsDestructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.Destructor; + public static bool IsDestructor([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.Destructor }; - public static bool IsUserDefinedOperator([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.UserDefinedOperator; + public static bool IsUserDefinedOperator([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.UserDefinedOperator }; - public static bool IsConversion([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.Conversion; + public static bool IsConversion([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.Conversion }; - public static bool IsOrdinaryMethod([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IMethodSymbol)?.MethodKind == MethodKind.Ordinary; + public static bool IsOrdinaryMethod([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IMethodSymbol { MethodKind: MethodKind.Ordinary }; public static bool IsOrdinaryMethodOrLocalFunction([NotNullWhen(true)] this ISymbol? symbol) => symbol is IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.LocalFunction }; - public static bool IsDelegateType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsDelegateType([NotNullWhen(true)] this ISymbol? symbol) => symbol is ITypeSymbol { TypeKind: TypeKind.Delegate }; - public static bool IsAnonymousType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsAnonymousType([NotNullWhen(true)] this ISymbol? symbol) => symbol is INamedTypeSymbol { IsAnonymousType: true }; - public static bool IsNormalAnonymousType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsNormalAnonymousType([NotNullWhen(true)] this ISymbol? symbol) => symbol.IsAnonymousType() && !symbol.IsDelegateType(); - public static bool IsAnonymousDelegateType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsAnonymousDelegateType([NotNullWhen(true)] this ISymbol? symbol) => symbol.IsAnonymousType() && symbol.IsDelegateType(); - public static bool IsAnonymousTypeProperty([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsAnonymousTypeProperty([NotNullWhen(true)] this ISymbol? symbol) => symbol is IPropertySymbol && symbol.ContainingType.IsNormalAnonymousType(); - public static bool IsTupleField([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => symbol is IFieldSymbol && symbol.ContainingType.IsTupleType; + public static bool IsTupleField([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IFieldSymbol { ContainingType.IsTupleType: true }; - public static bool IsIndexer([NotNullWhen(returnValue: true)] this ISymbol? symbol) - => (symbol as IPropertySymbol)?.IsIndexer == true; + public static bool IsIndexer([NotNullWhen(true)] this ISymbol? symbol) + => symbol is IPropertySymbol { IsIndexer: true }; - public static bool IsWriteableFieldOrProperty([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsWriteableFieldOrProperty([NotNullWhen(true)] this ISymbol? symbol) => symbol switch { IFieldSymbol fieldSymbol => !fieldSymbol.IsReadOnly && !fieldSymbol.IsConst, @@ -269,7 +269,7 @@ public static bool IsWriteableFieldOrProperty([NotNullWhen(returnValue: true)] t _ => false, }; - public static bool IsRequired([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsRequired([NotNullWhen(true)] this ISymbol? symbol) => symbol is IFieldSymbol { IsRequired: true } or IPropertySymbol { IsRequired: true }; public static ITypeSymbol? GetMemberType(this ISymbol? symbol) @@ -406,7 +406,7 @@ public static ImmutableArray GetAllTypeArguments(this ISymbol symbo return results.ToImmutableAndFree(); } - public static bool IsAttribute([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsAttribute([NotNullWhen(true)] this ISymbol? symbol) => (symbol as ITypeSymbol)?.IsAttribute() == true; /// @@ -415,7 +415,7 @@ public static bool IsAttribute([NotNullWhen(returnValue: true)] this ISymbol? sy /// is unsafe, as is int* Goo { get; }. This will return for /// symbols that cannot have the modifier on them. /// - public static bool RequiresUnsafeModifier([NotNullWhen(returnValue: true)] this ISymbol? member) + public static bool RequiresUnsafeModifier([NotNullWhen(true)] this ISymbol? member) { // TODO(cyrusn): Defer to compiler code to handle this once it can. return member?.Accept(new RequiresUnsafeModifierVisitor()) == true; @@ -465,14 +465,14 @@ static string WithArity(string typeName, int arity) => arity > 0 ? typeName + '`' + arity : typeName; } - public static bool IsStaticType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsStaticType([NotNullWhen(true)] this ISymbol? symbol) => symbol != null && symbol.Kind == SymbolKind.NamedType && symbol.IsStatic; - public static bool IsNamespace([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsNamespace([NotNullWhen(true)] this ISymbol? symbol) => symbol?.Kind == SymbolKind.Namespace; public static bool IsOrContainsAccessibleAttribute( - [NotNullWhen(returnValue: true)] this ISymbol? symbol, ISymbol withinType, IAssemblySymbol withinAssembly, CancellationToken cancellationToken) + [NotNullWhen(true)] this ISymbol? symbol, ISymbol withinType, IAssemblySymbol withinAssembly, CancellationToken cancellationToken) { var namespaceOrType = symbol is IAliasSymbol alias ? alias.Target : symbol as INamespaceOrTypeSymbol; if (namespaceOrType == null) @@ -542,13 +542,13 @@ public static bool IsInaccessibleLocal(this ISymbol symbol, int position) return declarationSyntax != null && position < declarationSyntax.SpanStart; } - public static bool IsAccessor([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsAccessor([NotNullWhen(true)] this ISymbol? symbol) => symbol.IsPropertyAccessor() || symbol.IsEventAccessor(); - public static bool IsPropertyAccessor([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsPropertyAccessor([NotNullWhen(true)] this ISymbol? symbol) => (symbol as IMethodSymbol)?.MethodKind.IsPropertyAccessor() == true; - public static bool IsEventAccessor([NotNullWhen(returnValue: true)] this ISymbol? symbol) + public static bool IsEventAccessor([NotNullWhen(true)] this ISymbol? symbol) => symbol is IMethodSymbol { MethodKind: MethodKind.EventAdd or MethodKind.EventRaise or MethodKind.EventRemove }; public static bool IsFromSource(this ISymbol symbol) @@ -573,7 +573,7 @@ public static bool IsNonImplicitAndFromSource(this ISymbol symbol) /// If the is a type symbol, returns if that type is "awaitable". /// An "awaitable" is any type that exposes a GetAwaiter method which returns a valid "awaiter". This GetAwaiter method may be an instance method or an extension method. /// - public static bool IsAwaitableNonDynamic([NotNullWhen(returnValue: true)] this ISymbol? symbol, SemanticModel semanticModel, int position) + public static bool IsAwaitableNonDynamic([NotNullWhen(true)] this ISymbol? symbol, SemanticModel semanticModel, int position) { var methodSymbol = symbol as IMethodSymbol; ITypeSymbol? typeSymbol = null;