Skip to content
Merged
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 @@ -6,7 +6,7 @@
<Title>D2L.CodeStyle.Analyzers</Title>
<Product>D2L.CodeStyle</Product>
<Description>D2L.CodeStyle analyzers</Description>
<Version>0.228.0</Version>
<Version>0.229.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Brightspace/D2L.CodeStyle</PackageProjectUrl>
<Authors>D2L</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ public sealed partial class ImmutabilityAnalyzer : DiagnosticAnalyzer {
Diagnostics.PrimaryClassConstructorIntroducesMutability
);

private readonly ImmutableHashSet<string> m_additionalImmutableTypes;

public ImmutabilityAnalyzer() : this( ImmutableHashSet<string>.Empty ) { }

public ImmutabilityAnalyzer( ImmutableHashSet<string> additionalImmutableTypes ) {
m_additionalImmutableTypes = additionalImmutableTypes;
}

public override void Initialize( AnalysisContext context ) {
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis( GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics );
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -94,71 +96,54 @@ internal partial class ImmutabilityContext {

internal static ImmutabilityContext Create(
Compilation compilation,
AnnotationsContext annotationsContext,
ImmutableHashSet<string> additionalImmutableTypes = default
AnnotationsContext annotationsContext
) {
if( additionalImmutableTypes == default ) {
additionalImmutableTypes = ImmutableHashSet<string>.Empty;
}

// Generate a dictionary of types that we have specifically determined
// should be considered Immutable by the Analyzer.
var extraImmutableTypesBuilder = ImmutableDictionary.CreateBuilder<INamedTypeSymbol, ImmutableTypeInfo>( SymbolEqualityComparer.Default );
foreach( ( string typeName, string qualifiedAssembly ) in DefaultExtraTypes ) {
INamedTypeSymbol type = GetTypeSymbol( compilation, qualifiedAssembly, typeName );
ImmutableArray<INamedTypeSymbol> 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<IMethodSymbol>( SymbolEqualityComparer.Default );
foreach( ( string typeName, string methodName, string qualifiedAssembly ) in KnownImmutableReturningMethods ) {
INamedTypeSymbol type = GetTypeSymbol( compilation, qualifiedAssembly, typeName );
ImmutableArray<INamedTypeSymbol> types = GetTypeSymbols( compilation, qualifiedAssembly, typeName );

if( type == null ) {
if( types.IsDefaultOrEmpty ) {
continue;
}

IMethodSymbol[] methodSymbols = type
.GetMembers( methodName )
.OfType<IMethodSymbol>()
.Where( m => m.Parameters.Length == 0 )
.ToArray();
foreach( INamedTypeSymbol type in types ) {

if( methodSymbols.Length != 1 ) {
continue;
}
IMethodSymbol[] methodSymbols = type
.GetMembers( methodName )
.OfType<IMethodSymbol>()
.Where( m => m.Parameters.Length == 0 )
.ToArray();

knownImmutableReturnsBuilder.Add( methodSymbols[0] );
if( methodSymbols.Length != 1 ) {
continue;
}

knownImmutableReturnsBuilder.Add( methodSymbols[ 0 ] );
}
}

return new ImmutabilityContext(
Expand All @@ -167,13 +152,13 @@ internal static ImmutabilityContext Create(
knownImmutableReturns: knownImmutableReturnsBuilder.ToImmutable(),
conditionalTypeParamemters: ImmutableHashSet<ITypeParameterSymbol>.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<INamedTypeSymbol> GetTypeSymbols(
Compilation compilation,
string qualifiedAssembly,
string typeName
Expand All @@ -182,7 +167,7 @@ string typeName
ImmutableArray<INamedTypeSymbol> types = compilation.GetTypesByMetadataName( typeName );

if( types.IsEmpty ) {
return null;
return default;
}

if( qualifiedAssembly == default ) {
Expand All @@ -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;
}

}
Expand Down
Loading