internal void AnalyzeObjectInitializers(SyntaxNodeAnalysisContext ctx) { InitializerExpressionSyntax objectInitializer = (InitializerExpressionSyntax)ctx.Node; // For now, only perform analysis when explicitly enabled by comment. // TODO Support other means to enable, such as static configuration (analyze all/none by default), attributes on types and members if (!_regionsToAnalyze.TextSpans.Any(span => span.IntersectsWith(objectInitializer.Span))) { return; } // Should be direct parent of ObjectInitializerExpression ObjectCreationExpressionSyntax objectCreation = objectInitializer.Parent as ObjectCreationExpressionSyntax; // Only handle initializers immediately following object creation, // not sure what the scenario would be since we are only registered for // object initializers, not things like list/collection initializers. if (objectCreation == null) { return; } INamedTypeSymbol objectCreationNamedType = (INamedTypeSymbol)ctx.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol; if (objectCreationNamedType == null) { return; } ImmutableArray <ISymbol> members = objectCreationNamedType.GetMembers(); List <string> assignedMemberNames = objectInitializer.ChildNodes() .OfType <AssignmentExpressionSyntax>() .Select(assignmentSyntax => ((IdentifierNameSyntax)assignmentSyntax.Left).Identifier.ValueText) .ToList(); // TODO Check if member is assignable using Roslyn data flow analysis instead of these constraints, // as that is the only way to properly determine if it is assignable or not in a context IEnumerable <ISymbol> assignableProperties = members .OfType <IPropertySymbol>() .Where(m => // Exclude indexer properties !m.IsIndexer && // Exclude read-only getter properties !m.IsReadOnly && // Simplification, only care about public members m.DeclaredAccessibility == Accessibility.Public); IEnumerable <ISymbol> assignableFields = members.OfType <IFieldSymbol>() .Where(m => // Exclude readonly fields !m.IsReadOnly && // Exclude const fields !m.HasConstantValue && // Exclude generated backing fields for properties !m.IsImplicitlyDeclared && // Simplification, only care about public members m.DeclaredAccessibility == Accessibility.Public); IEnumerable <string> assignableMemberNames = assignableProperties .Concat(assignableFields) .Select(x => x.Name); ImmutableArray <string> ignoredPropertyNames = GetIgnoredPropertyNames(objectCreation); List <string> unassignedMemberNames = assignableMemberNames .Except(assignedMemberNames) .Except(ignoredPropertyNames) .ToList(); if (unassignedMemberNames.Any()) { string unassignedMembersString = string.Join(", ", unassignedMemberNames); ImmutableDictionary <string, string> properties = new Dictionary <string, string> { { AssignAll_Analyzer.Properties_UnassignedMemberNames, unassignedMembersString } } .ToImmutableDictionary(); Diagnostic diagnostic = Diagnostic.Create(AssignAll_Analyzer.Rule, objectCreation.GetLocation(), //ctx.Node.GetLocation(), properties, objectCreationNamedType.Name, unassignedMembersString); ctx.ReportDiagnostic(diagnostic); } }