public ImmutableDefinitionChecker( Compilation compilation, DiagnosticSink diagnosticSink, ImmutabilityContext context, AnnotationsContext annotationsContext ) { m_compilation = compilation; m_diagnosticSink = diagnosticSink; m_context = context; m_annotationsContext = annotationsContext; }
private static void AnalyzeTypeArguments( SyntaxNodeAnalysisContext ctx, AnnotationsContext annotationsContext, ImmutabilityContext immutabilityContext, SimpleNameSyntax syntax ) { if (syntax.IsFromDocComment()) { // ignore things in doccomments such as crefs return; } SymbolInfo info = ctx.SemanticModel.GetSymbolInfo(syntax, ctx.CancellationToken); // Ignore anything that cannot have type arguments/parameters if (!GetTypeParamsAndArgs(info.Symbol, out var typeParameters, out var typeArguments)) { return; } 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 (!annotationsContext.Objects.Immutable.IsDefined(parameter)) { continue; } if (!immutabilityContext.IsImmutable( new ImmutabilityQuery( ImmutableTypeKind.Total, argument ), // If the syntax is a GenericName (has explicit type arguments) then the error should be on the argument // Otherwise, it should be on the identifier itself getLocation: () => syntax is GenericNameSyntax genericSyntax ? genericSyntax.TypeArgumentList.Arguments[position].GetLocation() : syntax.Identifier.GetLocation(), out Diagnostic diagnostic )) { // TODO: not necessarily a good diagnostic for this use-case ctx.ReportDiagnostic(diagnostic); } } }
public ImmutableAttributeConsistencyChecker( Compilation compilation, DiagnosticSink diagnosticSink, ImmutabilityContext context, AnnotationsContext annotationsContext ) { m_compilation = compilation; m_diagnosticSink = diagnosticSink; m_context = context; m_annotationsContext = annotationsContext; }
private static void AnalyzeTypeDeclaration( SymbolAnalysisContext ctx, AnnotationsContext annotationsContext, ImmutabilityContext immutabilityContext, INamedTypeSymbol typeSymbol ) { if (typeSymbol.IsImplicitlyDeclared) { return; } ImmutableAttributeConsistencyChecker consistencyChecker = new ImmutableAttributeConsistencyChecker( compilation: ctx.Compilation, diagnosticSink: ctx.ReportDiagnostic, context: immutabilityContext, annotationsContext: annotationsContext ); consistencyChecker.CheckTypeDeclaration(typeSymbol); if (typeSymbol.TypeKind == TypeKind.Interface) { return; } if (!annotationsContext.Objects.Immutable.IsDefined(typeSymbol) && !annotationsContext.Objects.ConditionallyImmutable.IsDefined(typeSymbol) && !annotationsContext.Objects.ImmutableBaseClass.IsDefined(typeSymbol) ) { return; } if (annotationsContext.Objects.ConditionallyImmutable.IsDefined(typeSymbol)) { immutabilityContext = immutabilityContext.WithConditionalTypeParametersAsImmutable(typeSymbol); } ImmutableDefinitionChecker checker = new ImmutableDefinitionChecker( compilation: ctx.Compilation, diagnosticSink: ctx.ReportDiagnostic, context: immutabilityContext, annotationsContext: annotationsContext ); checker.CheckDeclaration(typeSymbol); }
private static void AnalyzeMember( SymbolAnalysisContext ctx, AnnotationsContext annotationsContext, ImmutabilityContext immutabilityContext ) { // We only care about checking static fields/properties. These // are global variables, so we always want them to be immutable. // The fields/properties of [Immutable] types get handled via // AnalyzeTypeDeclaration. if (!ctx.Symbol.IsStatic) { return; } // Ignore const things, which include enum names. if (ctx.Symbol is IFieldSymbol f && f.IsConst) { return; } // We would like this check to run for generated code too, but // there are two problems: // (1) the easy one: we generate some static variables that are // safe in practice but don't analyze well. // (2) the hard one: resx code-gen generates some stuff that's // safe in practice but doesn't analyze well. if (ctx.Symbol.IsFromGeneratedCode()) { return; } var checker = new ImmutableDefinitionChecker( compilation: ctx.Compilation, diagnosticSink: ctx.ReportDiagnostic, context: immutabilityContext, annotationsContext: annotationsContext ); checker.CheckMember(ctx.Symbol); }
public bool IsImmutableDefinition( ImmutabilityContext context, INamedTypeSymbol definition, Func <Location> getLocation, out Diagnostic diagnostic ) { if (!Type.Equals(definition, SymbolEqualityComparer.Default) && !Type.Equals(definition?.OriginalDefinition, SymbolEqualityComparer.Default) ) { throw new InvalidOperationException($"{ nameof( IsImmutableDefinition ) } should only be called with an equivalent type definition"); } var argRelevance = definition .TypeArguments .Zip(m_conditionalTypeParameters, (a, relevant) => (a, relevant)); foreach ((ITypeSymbol argument, bool isRelevant) in argRelevance) { if (!isRelevant) { continue; } if (!context.IsImmutable( new ImmutabilityQuery( ImmutableTypeKind.Total, argument ), getLocation, out diagnostic )) { return(false); } } diagnostic = null; return(true); }
private static void AnalyzeMethodDeclarationConsistency( SymbolAnalysisContext ctx, AnnotationsContext annotationsContext, ImmutabilityContext immutabilityContext, IMethodSymbol methodSymbol ) { // Static methods can't implement interface methods if (methodSymbol.IsStatic) { return; } ImmutableAttributeConsistencyChecker consistencyChecker = new ImmutableAttributeConsistencyChecker( compilation: ctx.Compilation, diagnosticSink: ctx.ReportDiagnostic, context: immutabilityContext, annotationsContext: annotationsContext ); consistencyChecker.CheckMethodDeclaration(methodSymbol); }
public static void CompilationStart( CompilationStartAnalysisContext context ) { if (!AnnotationsContext.TryCreate(context.Compilation, out AnnotationsContext annotationsContext)) { return; } ImmutabilityContext immutabilityContext = ImmutabilityContext.Create(context.Compilation, annotationsContext); context.RegisterSymbolAction( ctx => AnalyzeTypeDeclaration( ctx, annotationsContext, immutabilityContext, (INamedTypeSymbol)ctx.Symbol ), SymbolKind.NamedType ); context.RegisterSymbolAction( ctx => AnalyzeMethodDeclarationConsistency( ctx, annotationsContext, immutabilityContext, (IMethodSymbol)ctx.Symbol ), SymbolKind.Method ); context.RegisterSymbolAction( ctx => AnalyzeMember(ctx, annotationsContext, immutabilityContext), SymbolKind.Field, SymbolKind.Property ); context.RegisterSyntaxNodeAction( ctx => AnalyzeTypeArguments( ctx, annotationsContext, immutabilityContext, (SimpleNameSyntax)ctx.Node ), SyntaxKind.IdentifierName, SyntaxKind.GenericName ); context.RegisterSyntaxNodeAction( ctx => AnalyzeConditionalImmutabilityOnMethodDeclarations( ctx, annotationsContext ), SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement ); context.RegisterSyntaxNodeAction( ctx => AnalyzeConflictingImmutabilityOnTypeParameters( ctx, annotationsContext ), SyntaxKind.TypeParameter ); context.RegisterSyntaxNodeAction( ctx => AnalyzeConflictingImmutabilityOnMember( ctx, annotationsContext ), SyntaxKind.ClassDeclaration, SyntaxKind.InterfaceDeclaration, SyntaxKind.StructDeclaration ); }