예제 #1
0
 public ImmutableDefinitionChecker(
     Compilation compilation,
     DiagnosticSink diagnosticSink,
     ImmutabilityContext context,
     AnnotationsContext annotationsContext
     )
 {
     m_compilation        = compilation;
     m_diagnosticSink     = diagnosticSink;
     m_context            = context;
     m_annotationsContext = annotationsContext;
 }
예제 #2
0
        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);
                }
            }
        }
예제 #3
0
 public ImmutableAttributeConsistencyChecker(
     Compilation compilation,
     DiagnosticSink diagnosticSink,
     ImmutabilityContext context,
     AnnotationsContext annotationsContext
     )
 {
     m_compilation        = compilation;
     m_diagnosticSink     = diagnosticSink;
     m_context            = context;
     m_annotationsContext = annotationsContext;
 }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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
                );
        }