コード例 #1
0
        static void Analyze(SyntaxNodeAnalysisContext context)
        {
            var model = context.SemanticModel;

            var typeDeclaration = context.Node as TypeDeclarationSyntax;

            if (typeDeclaration == null)
            {
                return;
            }

            var declaredSymbol = model.GetDeclaredSymbol(typeDeclaration);

            if (declaredSymbol == null)
            {
                return;
            }

            // Analysing Target is only [ZeroFormattable] class, allows ShortName
            if (declaredSymbol.GetAttributes().FindAttributeShortName(ZeroFormattableAttributeShortName) == null)
            {
                return;
            }

            var reportContext = new DiagnosticsReportContext(context);

            // Start RootType
            VerifyType(reportContext, typeDeclaration.GetLocation(), declaredSymbol, new HashSet <ITypeSymbol>(), null);

            reportContext.ReportAll();
        }
コード例 #2
0
        static void VerifyField(DiagnosticsReportContext context, IFieldSymbol field, HashSet <ITypeSymbol> alreadyAnalyzed, HashSet <int> definedIndexes)
        {
            if (field.IsStatic)
            {
                return;
            }

            if (field.DeclaredAccessibility != Accessibility.Public)
            {
                return;
            }

            var attributes = field.GetAttributes();

            if (attributes.FindAttributeShortName(IgnoreShortName) != null)
            {
                return;
            }

            if (!field.ContainingType.IsValueType)
            {
                context.Add(Diagnostic.Create(ClassNotSupportPublicField, field.Locations[0], field.ContainingType?.Name, field.Name));
                return;
            }

            var indexAttr = attributes.FindAttributeShortName(IndexAttributeShortName);

            if (indexAttr == null || indexAttr.ConstructorArguments.Length == 0)
            {
                context.Add(Diagnostic.Create(PublicPropertyNeedsIndex, field.Locations[0], field.ContainingType?.Name, field.Name));
                return;
            }

            var index = indexAttr.ConstructorArguments[0];

            if (index.IsNull)
            {
                return; // null is normal compiler error.
            }

            if (!definedIndexes.Add((int)index.Value))
            {
                context.Add(Diagnostic.Create(IndexAttributeDuplicate, field.Locations[0], field.ContainingType?.Name, field.Name, index.Value));
                return;
            }

            if ((int)index.Value >= 100)
            {
                context.Add(Diagnostic.Create(IndexIsTooLarge, field.Locations[0], index.Value, field.Name));
            }

            var namedType = field.Type as INamedTypeSymbol;

            if (namedType != null) // if <T> is unnamed type, it can't analyze.
            {
                VerifyType(context, field.Locations[0], field.Type, alreadyAnalyzed, field);
            }
        }
コード例 #3
0
        static void Analyze(SyntaxNodeAnalysisContext context)
        {
            var model = context.SemanticModel;

            var typeDeclaration = context.Node as TypeDeclarationSyntax;

            if (typeDeclaration == null)
            {
                return;
            }

            var declaredSymbol = model.GetDeclaredSymbol(typeDeclaration);

            if (declaredSymbol == null)
            {
                return;
            }

            var isUnion = declaredSymbol.GetAttributes().FindAttributeShortName(UnionAttributeShortName) != null;
            var zeroFormattableAttribute = declaredSymbol.GetAttributes().FindAttributeShortName(ZeroFormattableAttributeShortName);

            if (!isUnion && zeroFormattableAttribute == null)
            {
                return;
            }

            var reportContext = new DiagnosticsReportContext(context);

            if (isUnion)
            {
                VerifyUnion(reportContext, typeDeclaration.GetLocation(), declaredSymbol);
            }
            if (zeroFormattableAttribute != null)
            {
                var explicitFormatter =
                    zeroFormattableAttribute.NamedArguments.Where(
                        x => x.Key == ZeroFormattableFormatterProperty);
                if (explicitFormatter.Any())
                {
                    if (explicitFormatter.First().Value.Type.BaseType.Name != FormatterTypeName)
                    {
                        reportContext.Add(Diagnostic.Create(ExplicitFormatterMustInheritFormatter,
                                                            typeDeclaration.GetLocation(), typeDeclaration.GetLocation(),
                                                            explicitFormatter.First().Value.Type));
                    }

                    return;
                }

                VerifyType(reportContext, typeDeclaration.GetLocation(), declaredSymbol, new HashSet <ITypeSymbol>(), null);
            }

            reportContext.ReportAll();
        }
コード例 #4
0
        static void VerifyUnion(DiagnosticsReportContext context, Location callerLocation, ITypeSymbol type)
        {
            var unionKeys = type.GetMembers().OfType <IPropertySymbol>().Where(x => x.GetAttributes().FindAttributeShortName(UnionKeyAttributeShortName) != null).ToArray();

            if (unionKeys.Length == 0)
            {
                context.Add(Diagnostic.Create(UnionTypeRequiresUnionKey, callerLocation, type.Locations, type.Name));
                return;
            }
            else if (unionKeys.Length != 1)
            {
                context.Add(Diagnostic.Create(UnionKeyDoesNotAllowMultipleKey, callerLocation, type.Locations, type.Name));
                return;
            }

            var unionKeyProperty = unionKeys[0];

            if (!unionKeyProperty.GetMethod.IsAbstract)
            {
                context.Add(Diagnostic.Create(UnionTypeRequiresUnionKey, callerLocation, type.Locations, type.Name));
                return;
            }

            var constructorArguments = type.GetAttributes().FindAttributeShortName(UnionAttributeShortName)?.ConstructorArguments.FirstOrDefault();

            if (constructorArguments == null)
            {
                return;
            }


            foreach (var item in constructorArguments.Value.Values.Select(x => x.Value).OfType <ITypeSymbol>())
            {
                var found = item.FindBaseTargetType(type.ToDisplayString());

                if (found == null)
                {
                    context.Add(Diagnostic.Create(AllUnionSubTypesMustBeInheritedType, callerLocation, type.Locations, type.Name, item.Name));
                    return;
                }
            }
        }
コード例 #5
0
        static void Analyze(SyntaxNodeAnalysisContext context)
        {
            var model = context.SemanticModel;

            var typeDeclaration = context.Node as TypeDeclarationSyntax;

            if (typeDeclaration == null)
            {
                return;
            }

            var declaredSymbol = model.GetDeclaredSymbol(typeDeclaration);

            if (declaredSymbol == null)
            {
                return;
            }

            var isUnion           = declaredSymbol.GetAttributes().FindAttributeShortName(UnionAttributeShortName) != null;
            var isZeroFormattable = declaredSymbol.GetAttributes().FindAttributeShortName(ZeroFormattableAttributeShortName) != null;

            if (!isUnion && !isZeroFormattable)
            {
                return;
            }

            var reportContext = new DiagnosticsReportContext(context);

            if (isUnion)
            {
                VerifyUnion(reportContext, typeDeclaration.GetLocation(), declaredSymbol);
            }
            if (isZeroFormattable)
            {
                VerifyType(reportContext, typeDeclaration.GetLocation(), declaredSymbol, new HashSet <ITypeSymbol>(), null);
            }

            reportContext.ReportAll();
        }
コード例 #6
0
        static void VerifyProperty(DiagnosticsReportContext context, IPropertySymbol property, HashSet <ITypeSymbol> alreadyAnalyzed, HashSet <int> definedIndexes)
        {
            if (property.DeclaredAccessibility != Accessibility.Public)
            {
                return;
            }

            var attributes = property.GetAttributes();

            if (attributes.FindAttributeShortName(IgnoreShortName) != null)
            {
                return;
            }

            if (property.FindAttributeIncludeBasePropertyShortName(UnionKeyAttributeShortName) != null)
            {
                return;
            }

            if (!property.IsVirtual && !property.ContainingType.IsValueType)
            {
                if (property.IsOverride && !property.IsSealed)
                {
                    // ok, base type's override property.
                }
                else
                {
                    context.Add(Diagnostic.Create(PublicPropertyMustBeVirtual, property.Locations[0], property.ContainingType?.Name, property.Name));
                    return;
                }
            }

            var indexAttr = attributes.FindAttributeShortName(IndexAttributeShortName);

            if (indexAttr == null || indexAttr.ConstructorArguments.Length == 0)
            {
                context.Add(Diagnostic.Create(PublicPropertyNeedsIndex, property.Locations[0], property.ContainingType?.Name, property.Name));
                return;
            }

            var index = indexAttr.ConstructorArguments[0];

            if (index.IsNull)
            {
                return; // null is normal compiler error.
            }

            if (!definedIndexes.Add((int)index.Value))
            {
                context.Add(Diagnostic.Create(IndexAttributeDuplicate, property.Locations[0], property.ContainingType?.Name, property.Name, index.Value));
                return;
            }

            if ((int)index.Value >= 100)
            {
                context.Add(Diagnostic.Create(IndexIsTooLarge, property.Locations[0], index.Value, property.Name));
            }

            if (!property.ContainingType.IsValueType)
            {
                if (property.GetMethod == null || property.SetMethod == null ||
                    property.GetMethod.DeclaredAccessibility == Accessibility.Private ||
                    property.SetMethod.DeclaredAccessibility == Accessibility.Private)
                {
                    context.Add(Diagnostic.Create(PublicPropertyNeedsGetAndSetAccessor, property.Locations[0], property.ContainingType?.Name, property.Name));
                    return;
                }
            }

            var namedType = property.Type as INamedTypeSymbol;

            if (namedType != null) // if <T> is unnamed type, it can't analyze.
            {
                VerifyType(context, property.Locations[0], property.Type, alreadyAnalyzed, property);
            }
        }
コード例 #7
0
        static void VerifyType(DiagnosticsReportContext context, Location callerLocation, ITypeSymbol type, HashSet <ITypeSymbol> alreadyAnalyzed, ISymbol callFromProperty)
        {
            if (!alreadyAnalyzed.Add(type))
            {
                return;
            }

            var displayString = type.ToDisplayString();

            if (AllowTypes.Contains(displayString))
            {
                return; // it is primitive...
            }
            else if (context.AdditionalAllowTypes.Contains(displayString))
            {
                return;
            }

            if (type.TypeKind == TypeKind.Enum)
            {
                return;
            }
            if (type.TypeKind == TypeKind.Array)
            {
                var array = type as IArrayTypeSymbol;
                var t     = array.ElementType;
                VerifyType(context, callerLocation, t, alreadyAnalyzed, callFromProperty);
                return;
            }

            var namedType = type as INamedTypeSymbol;

            if (namedType != null && namedType.IsGenericType && callFromProperty != null)
            {
                var genericType       = namedType.ConstructUnboundGenericType();
                var genericTypeString = genericType.ToDisplayString();

                if (genericTypeString == "T?")
                {
                    VerifyType(context, callerLocation, namedType.TypeArguments[0], alreadyAnalyzed, callFromProperty);
                    return;
                }
                else if (genericTypeString == "System.Collections.Generic.IList<>" ||
                         genericTypeString == "System.Collections.Generic.IDictionary<,>" ||
                         genericTypeString == "System.Collections.Generic.Dictionary<,>" ||
                         genericTypeString == "ZeroFormatter.ILazyDictionary<,>" ||
                         genericTypeString == "System.Collections.Generic.IReadOnlyList<>" ||
                         genericTypeString == "System.Collections.Generic.IReadOnlyCollection<>" ||
                         genericTypeString == "System.Collections.Generic.IReadOnlyDictionary<,>" ||
                         genericTypeString == "System.Collections.Generic.ICollection<>" ||
                         genericTypeString == "System.Collections.Generic.IEnumerable<>" ||
                         genericTypeString == "System.Collections.Generic.ISet<>" ||
                         genericTypeString == "System.Collections.ObjectModel.ReadOnlyCollection<>" ||
                         genericTypeString == "System.Collections.ObjectModel.ReadOnlyDictionary<,>" ||
                         genericTypeString == "ZeroFormatter.ILazyReadOnlyDictionary<,>" ||
                         genericTypeString == "System.Linq.ILookup<,>" ||
                         genericTypeString == "ZeroFormatter.ILazyLookup<,>" ||
                         genericTypeString.StartsWith("System.Collections.Generic.KeyValuePair") ||
                         genericTypeString.StartsWith("System.Tuple") ||
                         genericTypeString.StartsWith("ZeroFormatter.KeyTuple") ||
                         context.AdditionalAllowTypes.Contains(genericTypeString)
                         )
                {
                    foreach (var t in namedType.TypeArguments)
                    {
                        VerifyType(context, callerLocation, t, alreadyAnalyzed, callFromProperty);
                    }
                    return;
                }
                else
                {
                    if (namedType.AllInterfaces.Any(x => (x.IsGenericType ? x.ConstructUnboundGenericType().ToDisplayString() : "") == "System.Collections.Generic.ICollection<>"))
                    {
                        foreach (var t in namedType.TypeArguments)
                        {
                            VerifyType(context, callerLocation, t, alreadyAnalyzed, callFromProperty);
                        }
                        return;
                    }
                }
            }

            if (type.GetAttributes().FindAttributeShortName(ZeroFormattableAttributeShortName) == null)
            {
                context.Add(Diagnostic.Create(TypeMustBeZeroFormattable, callerLocation, type.Locations, type.Name));
                return;
            }

            if (namedType != null && !namedType.IsValueType)
            {
                if (!namedType.Constructors.Any(x => x.Parameters.Length == 0))
                {
                    context.Add(Diagnostic.Create(TypeMustNeedsParameterlessConstructor, callerLocation, type.Locations, type.Name));
                    return;
                }
            }

            // If in another project, we can not report so stop analyze.
            if (callFromProperty == null)
            {
                var definedIndexes = new HashSet <int>();
                foreach (var member in type.GetAllMembers().OfType <IPropertySymbol>())
                {
                    VerifyProperty(context, member, alreadyAnalyzed, definedIndexes);
                }
                foreach (var member in type.GetAllMembers().OfType <IFieldSymbol>())
                {
                    VerifyField(context, member, alreadyAnalyzed, definedIndexes);
                }

                if (type.IsValueType && context.Diagnostics.Count == 0)
                {
                    var indexes = new List <Tuple <int, ITypeSymbol> >();
                    foreach (var item in type.GetMembers())
                    {
                        var propSymbol  = item as IPropertySymbol;
                        var fieldSymbol = item as IFieldSymbol;
                        if ((propSymbol == null && fieldSymbol == null))
                        {
                            continue;
                        }

                        var indexAttr = item.GetAttributes().FindAttributeShortName(IndexAttributeShortName);
                        if (indexAttr != null)
                        {
                            var index = (int)indexAttr.ConstructorArguments[0].Value;
                            indexes.Add(Tuple.Create(index, (propSymbol != null) ? propSymbol.Type : fieldSymbol.Type));
                        }
                    }
                    indexes = indexes.OrderBy(x => x.Item1).ToList();

                    var expected = 0;
                    foreach (var item in indexes)
                    {
                        if (item.Item1 != expected)
                        {
                            context.Add(Diagnostic.Create(StructIndexMustBeStartedWithZeroAndSequential, callerLocation, type.Locations, type.Name, item.Item1));
                            return;
                        }
                        expected++;
                    }

                    var foundConstructor = false;
                    var ctors            = (type as INamedTypeSymbol)?.Constructors;
                    foreach (var ctor in ctors)
                    {
                        var isMatch = indexes.Select(x => x.Item2.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
                                      .SequenceEqual(ctor.Parameters.Select(x => x.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
                        if (isMatch)
                        {
                            foundConstructor = true;
                        }
                    }
                    if (!foundConstructor && indexes.Count != 0)
                    {
                        context.Add(Diagnostic.Create(StructMustNeedsSameConstructorWithIndexes, callerLocation, type.Locations, type.Name));
                        return;
                    }

                    return;
                }
            }
        }
コード例 #8
0
        static void VerifyProperty(DiagnosticsReportContext context, IPropertySymbol property, HashSet <ITypeSymbol> alreadyAnalyzed, HashSet <int> definedIndexes)
        {
            if (property.DeclaredAccessibility != Accessibility.Public)
            {
                return;
            }

            var attributes = property.GetAttributes();

            if (attributes.FindAttributeShortName(IgnoreShortName) != null)
            {
                return;
            }

            if (!property.IsVirtual && !property.ContainingType.IsValueType)
            {
                context.Add(Diagnostic.Create(PublicPropertyMustBeVirtual, property.Locations[0], property.ContainingType?.Name, property.Name));
                return;
            }

            var indexAttr = attributes.FindAttributeShortName(IndexAttributeShortName);

            if (indexAttr == null || indexAttr.ConstructorArguments.Length == 0)
            {
                context.Add(Diagnostic.Create(PublicPropertyNeedsIndex, property.Locations[0], property.ContainingType?.Name, property.Name));
                return;
            }

            var index = indexAttr.ConstructorArguments[0];

            if (index.IsNull)
            {
                return; // null is normal compiler error.
            }

            if (!definedIndexes.Add((int)index.Value))
            {
                context.Add(Diagnostic.Create(IndexAttributeDuplicate, property.Locations[0], property.ContainingType?.Name, property.Name, index.Value));
                return;
            }

            if ((int)index.Value >= 100)
            {
                context.Add(Diagnostic.Create(IndexIsTooLarge, property.Locations[0], index.Value, property.Name));
            }

            if (!property.ContainingType.IsValueType)
            {
                if (property.GetMethod == null || property.SetMethod == null ||
                    property.GetMethod.DeclaredAccessibility == Accessibility.Private ||
                    property.SetMethod.DeclaredAccessibility == Accessibility.Private)
                {
                    context.Add(Diagnostic.Create(PublicPropertyNeedsGetAndSetAccessor, property.Locations[0], property.ContainingType?.Name, property.Name));
                    return;
                }
            }

            if (property.Type.TypeKind == TypeKind.Array)
            {
                var array = property.Type as IArrayTypeSymbol;
                var t     = array.ElementType;
                if (t.SpecialType == SpecialType.System_Byte) // allows byte[]
                {
                    return;
                }

                context.Add(Diagnostic.Create(ArrayNotSupport, property.Locations[0], property.ContainingType?.Name, property.Name));
                return;
            }

            var namedType = property.Type as INamedTypeSymbol;

            if (namedType != null) // if <T> is unnamed type, it can't analyze.
            {
                VerifyType(context, property.Locations[0], property.Type, alreadyAnalyzed, property);
            }
        }
コード例 #9
0
        static void VerifyUnion(DiagnosticsReportContext context, Location callerLocation, ITypeSymbol type)
        {
            var unionKeys = type.GetMembers().OfType <IPropertySymbol>().Where(x => x.GetAttributes().FindAttributeShortName(UnionKeyAttributeShortName) != null).ToArray();

            if (unionKeys.Length == 0)
            {
                context.Add(Diagnostic.Create(UnionTypeRequiresUnionKey, callerLocation, type.Locations, type.Name));
                return;
            }
            else if (unionKeys.Length != 1)
            {
                context.Add(Diagnostic.Create(UnionKeyDoesNotAllowMultipleKey, callerLocation, type.Locations, type.Name));
                return;
            }

            var unionKeyProperty = unionKeys[0];

            if (type.TypeKind != TypeKind.Interface && !unionKeyProperty.GetMethod.IsAbstract)
            {
                context.Add(Diagnostic.Create(UnionTypeRequiresUnionKey, callerLocation, type.Locations, type.Name));
                return;
            }

            var ctorArguments  = type.GetAttributes().FindAttributeShortName(UnionAttributeShortName)?.ConstructorArguments;
            var firstArguments = ctorArguments?.FirstOrDefault();

            if (firstArguments == null)
            {
                return;
            }

            TypedConstant fallbackType = default(TypedConstant);

            if (ctorArguments.Value.Length == 2)
            {
                fallbackType = ctorArguments.Value[1];
            }

            if (type.TypeKind != TypeKind.Interface)
            {
                foreach (var item in firstArguments.Value.Values.Concat(new[] { fallbackType }).Where(x => !x.IsNull).Select(x => x.Value).OfType <ITypeSymbol>())
                {
                    var found = item.FindBaseTargetType(type.ToDisplayString());

                    if (found == null)
                    {
                        context.Add(Diagnostic.Create(AllUnionSubTypesMustBeInheritedType, callerLocation, type.Locations, type.Name, item.Name));
                        return;
                    }
                }
            }
            else
            {
                foreach (var item in firstArguments.Value.Values.Concat(new[] { fallbackType }).Where(x => !x.IsNull).Select(x => x.Value).OfType <ITypeSymbol>())
                {
                    var typeString = type.ToDisplayString();
                    if (!(item as INamedTypeSymbol).AllInterfaces.Any(x => x.OriginalDefinition?.ToDisplayString() == typeString))
                    {
                        context.Add(Diagnostic.Create(AllUnionSubTypesMustBeInheritedType, callerLocation, type.Locations, type.Name, item.Name));
                        return;
                    }
                }
            }
        }