예제 #1
0
        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);
            }
        }