diff --git a/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj b/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj index b111776f..b98ce3a9 100644 --- a/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj +++ b/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj @@ -6,7 +6,7 @@ D2L.CodeStyle.Analyzers D2L.CodeStyle D2L.CodeStyle analyzers - 0.228.0 + 0.229.0 Apache-2.0 https://github.com/Brightspace/D2L.CodeStyle D2L diff --git a/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityAnalyzer.cs b/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityAnalyzer.cs index f0cbf3fc..684f9555 100644 --- a/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityAnalyzer.cs +++ b/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityAnalyzer.cs @@ -37,14 +37,6 @@ public sealed partial class ImmutabilityAnalyzer : DiagnosticAnalyzer { Diagnostics.PrimaryClassConstructorIntroducesMutability ); - private readonly ImmutableHashSet m_additionalImmutableTypes; - - public ImmutabilityAnalyzer() : this( ImmutableHashSet.Empty ) { } - - public ImmutabilityAnalyzer( ImmutableHashSet additionalImmutableTypes ) { - m_additionalImmutableTypes = additionalImmutableTypes; - } - public override void Initialize( AnalysisContext context ) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics ); @@ -57,7 +49,7 @@ CompilationStartAnalysisContext context if( !AnnotationsContext.TryCreate( context.Compilation, out AnnotationsContext annotationsContext ) ) { return; } - ImmutabilityContext immutabilityContext = ImmutabilityContext.Create( context.Compilation, annotationsContext, m_additionalImmutableTypes ); + ImmutabilityContext immutabilityContext = ImmutabilityContext.Create( context.Compilation, annotationsContext ); ImmutableTypeParameterArgumentAnalysis.Register( context, diff --git a/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.Factory.cs b/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.Factory.cs index 06b715e0..0049c966 100644 --- a/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.Factory.cs +++ b/src/D2L.CodeStyle.Analyzers/Immutability/ImmutabilityContext.Factory.cs @@ -7,6 +7,8 @@ namespace D2L.CodeStyle.Analyzers.Immutability { internal partial class ImmutabilityContext { + private const string ALL_TYPE_INSTANCES_QUALIFIER = "*"; + internal static readonly ImmutableArray<(string TypeName, string AssmeblyName)> DefaultExtraTypes = ImmutableArray.Create( // Framework Container Types (not that the distinction matters) ("System.Collections.Frozen.FrozenDictionary`2", default), @@ -38,10 +40,10 @@ internal partial class ImmutabilityContext { ("System.Drawing.Imaging.ImageFormat", default), ("System.Drawing.Size", default), // only safe because it's a struct with primitive fields ("System.Guid", default), - ("System.Index", default), + ("System.Index", ALL_TYPE_INSTANCES_QUALIFIER), ("System.Net.IPNetwork", default), ("System.Net.Http.HttpMethod", default), - ("System.Range", default), + ("System.Range", ALL_TYPE_INSTANCES_QUALIFIER), ("System.Reflection.ConstructorInfo", default), ("System.Reflection.FieldInfo", default), ("System.Reflection.MemberInfo", default), @@ -94,71 +96,54 @@ internal partial class ImmutabilityContext { internal static ImmutabilityContext Create( Compilation compilation, - AnnotationsContext annotationsContext, - ImmutableHashSet additionalImmutableTypes = default + AnnotationsContext annotationsContext ) { - if( additionalImmutableTypes == default ) { - additionalImmutableTypes = ImmutableHashSet.Empty; - } // Generate a dictionary of types that we have specifically determined // should be considered Immutable by the Analyzer. var extraImmutableTypesBuilder = ImmutableDictionary.CreateBuilder( SymbolEqualityComparer.Default ); foreach( ( string typeName, string qualifiedAssembly ) in DefaultExtraTypes ) { - INamedTypeSymbol type = GetTypeSymbol( compilation, qualifiedAssembly, typeName ); + ImmutableArray types = GetTypeSymbols( compilation, qualifiedAssembly, typeName ); - if( type == null ) { + if( types.IsDefaultOrEmpty ) { continue; } - ImmutableTypeInfo info = ImmutableTypeInfo.CreateWithAllConditionalTypeParameters( - ImmutableTypeKind.Total, - type - ); - - extraImmutableTypesBuilder.Add( type, info ); - } - - foreach( string typeName in additionalImmutableTypes ) { - INamedTypeSymbol type = GetTypeSymbol( compilation, qualifiedAssembly: default, typeName ); + foreach( INamedTypeSymbol type in types ) { - if( type == null ) { - continue; - } + ImmutableTypeInfo info = ImmutableTypeInfo.CreateWithAllConditionalTypeParameters( + ImmutableTypeKind.Total, + type + ); - if( extraImmutableTypesBuilder.ContainsKey( type ) ) { - continue; + extraImmutableTypesBuilder.Add( type, info ); } - - ImmutableTypeInfo info = ImmutableTypeInfo.CreateWithAllConditionalTypeParameters( - ImmutableTypeKind.Total, - type - ); - - extraImmutableTypesBuilder.Add( type, info ); } // Generate a set of methods that we have specifically determined // have a return value which should be considered Immutable by the Analyzer. var knownImmutableReturnsBuilder = ImmutableHashSet.CreateBuilder( SymbolEqualityComparer.Default ); foreach( ( string typeName, string methodName, string qualifiedAssembly ) in KnownImmutableReturningMethods ) { - INamedTypeSymbol type = GetTypeSymbol( compilation, qualifiedAssembly, typeName ); + ImmutableArray types = GetTypeSymbols( compilation, qualifiedAssembly, typeName ); - if( type == null ) { + if( types.IsDefaultOrEmpty ) { continue; } - IMethodSymbol[] methodSymbols = type - .GetMembers( methodName ) - .OfType() - .Where( m => m.Parameters.Length == 0 ) - .ToArray(); + foreach( INamedTypeSymbol type in types ) { - if( methodSymbols.Length != 1 ) { - continue; - } + IMethodSymbol[] methodSymbols = type + .GetMembers( methodName ) + .OfType() + .Where( m => m.Parameters.Length == 0 ) + .ToArray(); - knownImmutableReturnsBuilder.Add( methodSymbols[0] ); + if( methodSymbols.Length != 1 ) { + continue; + } + + knownImmutableReturnsBuilder.Add( methodSymbols[ 0 ] ); + } } return new ImmutabilityContext( @@ -167,13 +152,13 @@ internal static ImmutabilityContext Create( knownImmutableReturns: knownImmutableReturnsBuilder.ToImmutable(), conditionalTypeParamemters: ImmutableHashSet.Empty, regexInfo: ( - GetTypeSymbol( compilation, default, "System.Text.RegularExpressions.Regex" ), - GetTypeSymbol( compilation, default, "System.CodeDom.Compiler.GeneratedCodeAttribute" ) + GetTypeSymbols( compilation, default, "System.Text.RegularExpressions.Regex" ).FirstOrDefault(), + GetTypeSymbols( compilation, default, "System.CodeDom.Compiler.GeneratedCodeAttribute" ).FirstOrDefault() ) ); } - private static INamedTypeSymbol GetTypeSymbol( + private static ImmutableArray GetTypeSymbols( Compilation compilation, string qualifiedAssembly, string typeName @@ -182,7 +167,7 @@ string typeName ImmutableArray types = compilation.GetTypesByMetadataName( typeName ); if( types.IsEmpty ) { - return null; + return default; } if( qualifiedAssembly == default ) { @@ -192,16 +177,20 @@ string typeName ); } - return types[ 0 ]; + return types; + } + + if( qualifiedAssembly == ALL_TYPE_INSTANCES_QUALIFIER ) { + return types; } foreach( INamedTypeSymbol type in types ) { if( type.ContainingAssembly.Name.Equals( qualifiedAssembly, StringComparison.Ordinal ) ) { - return type; + return ImmutableArray.Create( type ); } } - return null; + return default; } }