public sealed override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction( (compilationContext) => { HashSet <IFieldSymbol> assignedToFields = new HashSet <IFieldSymbol>(); HashSet <IFieldSymbol> mightBecomeReadOnlyFields = new HashSet <IFieldSymbol>(); compilationContext.RegisterOperationBlockStartAction( (operationBlockContext) => { if (operationBlockContext.OwningSymbol is IMethodSymbol containingMethod) { bool inConstructor = containingMethod.MethodKind == MethodKind.Constructor; ITypeSymbol staticConstructorType = containingMethod.MethodKind == MethodKind.StaticConstructor ? containingMethod.ContainingType : null; operationBlockContext.RegisterOperationAction( (operationContext) => { if (operationContext.Operation is IAssignmentOperation assignment) { AssignTo(assignment.Target, inConstructor, staticConstructorType, assignedToFields, mightBecomeReadOnlyFields); } else if (operationContext.Operation is IIncrementOrDecrementOperation increment) { AssignTo(increment.Target, inConstructor, staticConstructorType, assignedToFields, mightBecomeReadOnlyFields); } else { throw TestExceptionUtilities.UnexpectedValue(operationContext.Operation); } }, OperationKind.SimpleAssignment, OperationKind.CompoundAssignment, OperationKind.Increment, OperationKind.Decrement); operationBlockContext.RegisterOperationAction( (operationContext) => { IInvocationOperation invocation = (IInvocationOperation)operationContext.Operation; foreach (IArgumentOperation argument in invocation.Arguments) { if (argument.Parameter.RefKind == RefKind.Out || argument.Parameter.RefKind == RefKind.Ref) { AssignTo(argument.Value, inConstructor, staticConstructorType, assignedToFields, mightBecomeReadOnlyFields); } } }, OperationKind.Invocation); } }); compilationContext.RegisterSymbolAction( (symbolContext) => { IFieldSymbol field = (IFieldSymbol)symbolContext.Symbol; if (!field.IsConst && !field.IsReadOnly && !assignedToFields.Contains(field)) { mightBecomeReadOnlyFields.Add(field); } }, SymbolKind.Field ); compilationContext.RegisterCompilationEndAction( (compilationEndContext) => { foreach (IFieldSymbol couldBeReadOnlyField in mightBecomeReadOnlyFields) { Report(compilationEndContext, couldBeReadOnlyField, FieldCouldBeReadOnlyDescriptor); } }); }); }
public sealed override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction( (compilationContext) => { Dictionary <IFieldSymbol, HashSet <INamedTypeSymbol> > fieldsSourceTypes = new Dictionary <IFieldSymbol, HashSet <INamedTypeSymbol> >(); compilationContext.RegisterOperationBlockStartAction( (operationBlockContext) => { if (operationBlockContext.OwningSymbol is IMethodSymbol containingMethod) { Dictionary <ILocalSymbol, HashSet <INamedTypeSymbol> > localsSourceTypes = new Dictionary <ILocalSymbol, HashSet <INamedTypeSymbol> >(); // Track explicit assignments. operationBlockContext.RegisterOperationAction( (operationContext) => { if (operationContext.Operation is IAssignmentOperation assignment) { AssignTo(assignment.Target, localsSourceTypes, fieldsSourceTypes, assignment.Value); } else if (operationContext.Operation is IIncrementOrDecrementOperation increment) { SyntaxNode syntax = increment.Syntax; ITypeSymbol type = increment.Type; Optional <object> constantValue = new Optional <object>(1); bool isImplicit = increment.IsImplicit; var value = new LiteralExpression(operationContext.Compilation.GetSemanticModel(syntax.SyntaxTree), syntax, type, constantValue, isImplicit); AssignTo(increment.Target, localsSourceTypes, fieldsSourceTypes, value); } else { throw TestExceptionUtilities.UnexpectedValue(operationContext.Operation); } }, OperationKind.SimpleAssignment, OperationKind.CompoundAssignment, OperationKind.Increment); // Track arguments that match out or ref parameters. operationBlockContext.RegisterOperationAction( (operationContext) => { IInvocationOperation invocation = (IInvocationOperation)operationContext.Operation; foreach (IArgumentOperation argument in invocation.Arguments) { if (argument.Parameter.RefKind == RefKind.Out || argument.Parameter.RefKind == RefKind.Ref) { AssignTo(argument.Value, localsSourceTypes, fieldsSourceTypes, argument.Parameter.Type); } } }, OperationKind.Invocation); // Track local variable initializations. operationBlockContext.RegisterOperationAction( (operationContext) => { IVariableInitializerOperation initializer = (IVariableInitializerOperation)operationContext.Operation; if (initializer.Parent is IVariableDeclarationOperation variableDeclaration) { foreach (ILocalSymbol local in variableDeclaration.Variables) { AssignTo(local, local.Type, localsSourceTypes, initializer.Value); } } }, OperationKind.VariableInitializer); // Report locals that could have more specific types. operationBlockContext.RegisterOperationBlockEndAction( (operationBlockEndContext) => { foreach (ILocalSymbol local in localsSourceTypes.Keys) { if (HasMoreSpecificSourceType(local, local.Type, localsSourceTypes, out var mostSpecificSourceType)) { Report(operationBlockEndContext, local, mostSpecificSourceType, LocalCouldHaveMoreSpecificTypeDescriptor); } } }); } }); // Track field initializations. compilationContext.RegisterOperationAction( (operationContext) => { IFieldInitializerOperation initializer = (IFieldInitializerOperation)operationContext.Operation; foreach (IFieldSymbol initializedField in initializer.InitializedFields) { AssignTo(initializedField, initializedField.Type, fieldsSourceTypes, initializer.Value); } }, OperationKind.FieldInitializer); // Report fields that could have more specific types. compilationContext.RegisterCompilationEndAction( (compilationEndContext) => { foreach (IFieldSymbol field in fieldsSourceTypes.Keys) { if (HasMoreSpecificSourceType(field, field.Type, fieldsSourceTypes, out var mostSpecificSourceType)) { Report(compilationEndContext, field, mostSpecificSourceType, FieldCouldHaveMoreSpecificTypeDescriptor); } } }); }); }