Example #1
0
        internal static ImmutableArray <AttributeSyntax> GetConfigurationAttributes(
            AttributedMembers attrMembers,
            ITypeSymbol attributeType,
            FrameworkTypes frameworkTypes,
            ISymbol member
            )
        {
            if (!attrMembers.AttributedSymbolsToAttributes.TryGetValue(member, out var attrs))
            {
                return(ImmutableArray <AttributeSyntax> .Empty);
            }

            var relevantAttributes = ImmutableArray.CreateBuilder <AttributeSyntax>();

            foreach (var(attr, attrType) in attrs)
            {
                if (attributeType.Equals(attrType, SymbolEqualityComparer.Default))
                {
                    relevantAttributes.Add(attr);
                }

                if (frameworkTypes.DataMemberAttribute != null && frameworkTypes.DataMemberAttribute.Equals(attrType, SymbolEqualityComparer.Default))
                {
                    relevantAttributes.Add(attr);
                }
            }

            return(relevantAttributes.ToImmutable());
        }
Example #2
0
        internal static bool IsIgnored(ISymbol member, FrameworkTypes types)
        {
            var ignoreDataMember = types.IgnoreDataMemberAttribute;

            if (ignoreDataMember == null)
            {
                return(false);
            }

            var attrs = member.GetAttributes();

            return(attrs.Any(a => ignoreDataMember.Equals(a.AttributeClass, SymbolEqualityComparer.Default)));
        }
Example #3
0
        internal static bool IsFlagsEnum(FrameworkTypes types, ITypeSymbol forEnum)
        {
            var attrs = forEnum.GetAttributes();

            foreach (var i in attrs)
            {
                var attrClass = i.AttributeClass;

                if (types.FlagsAttribute.Equals(attrClass, SymbolEqualityComparer.Default))
                {
                    return(true);
                }
            }

            return(false);
        }
Example #4
0
        internal static bool TryCreate(Compilation compilation, BuiltInTypes builtIns, out FrameworkTypes?types)
        {
            var iBufferWriter = compilation.GetTypeByMetadataName("System.Buffers.IBufferWriter`1");

            if (iBufferWriter == null)
            {
                types = null;
                return(false);
            }

            var flagsAttribute = compilation.GetTypeByMetadataName("System.FlagsAttribute");

            if (flagsAttribute == null)
            {
                types = null;
                return(false);
            }

            var readOnlySpan = compilation.GetTypeByMetadataName("System.ReadOnlySpan`1");

            if (readOnlySpan == null)
            {
                types = null;
                return(false);
            }

            var iBufferWriterOfChar = iBufferWriter.Construct(builtIns.Char);

            var readOnlySpanOfChar = readOnlySpan.Construct(builtIns.Char);

            var dataMember       = compilation.GetTypeByMetadataName("System.Runtime.Serialization.DataMemberAttribute");
            var ignoreDataMember = compilation.GetTypeByMetadataName("System.Runtime.Serialization.IgnoreDataMemberAttribute");

            types = new FrameworkTypes(iBufferWriterOfChar, flagsAttribute, readOnlySpanOfChar, dataMember, ignoreDataMember);
            return(true);
        }
Example #5
0
        private static bool TryCreateEnumParser(FrameworkTypes types, bool nullable, ITypeSymbol forEnum, out Parser?parser)
        {
            var isFlags = Utils.IsFlagsEnum(types, forEnum);
            var forEnumFullyQualified = forEnum.ToFullyQualifiedName();

            // strip out type parameters
            var nonGeneric = Utils.MakeNonGenericType(EnumTypeParserTemplate, "T", forEnumFullyQualified);

            // remove all the stuff we _don't_ need
            var toRemoveMtd =
                nonGeneric
                .DescendantNodesAndSelf()
                .OfType <MethodDeclarationSyntax>()
                .Where(
                    t =>
            {
                var val = t.Identifier.ValueText;

                if (RemoveEnumParserMethods.Contains(val))
                {
                    return(true);
                }

                if (!isFlags && RemoveEnumParserMethodsWhenNotFlags.Contains(val))
                {
                    return(true);
                }

                return(false);
            }
                    )
                .ToList();


            var toRemoveFields =
                nonGeneric
                .DescendantNodesAndSelf()
                .OfType <FieldDeclarationSyntax>()
                .Where(
                    t =>
                    t.Declaration.Variables.Any(
                        x =>
            {
                var val = x.Identifier.ValueText;
                if (RemoveEnumParserFields.Contains(val))
                {
                    return(true);
                }

                if (!isFlags && RemoveEnumParserFieldsWhenNotFlags.Contains(val))
                {
                    return(true);
                }

                return(false);
            }
                        )
                    )
                .ToList();

            var toRemove = toRemoveMtd.Cast <SyntaxNode>().Concat(toRemoveFields);
            var trimmed  = Utils.NonNull(nonGeneric.RemoveNodes(toRemove, SyntaxRemoveOptions.KeepNoTrivia));

            // now grab the right method
            string expectedMethodName;

            if (isFlags)
            {
                if (nullable)
                {
                    expectedMethodName = "TryParseNullableFlagsEnum";
                }
                else
                {
                    expectedMethodName = "TryParseFlagsEnum";
                }
            }
            else
            {
                if (nullable)
                {
                    expectedMethodName = "TryParseNullableEnum";
                }
                else
                {
                    expectedMethodName = "TryParseEnum";
                }
            }

            var mtd =
                trimmed
                .DescendantNodesAndSelf()
                .OfType <MethodDeclarationSyntax>()
                .Single(m => m.Identifier.ValueText == expectedMethodName);

            // construct final class
            var publicFormatMethod =
                mtd
                .WithIdentifier(SyntaxFactory.ParseToken("__TryParse"))
                .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)));

            var retClass = trimmed.ReplaceNode(mtd, publicFormatMethod);

            retClass = retClass
                       .WithIdentifier(SyntaxFactory.ParseToken("__Class"))
                       .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)));

            if (!nullable)
            {
                // remove the other TryXXX method, but only if we're not going to call it
                //   have to do this because we rename the method it'd call
                var extraMethods = retClass.DescendantNodesAndSelf().OfType <MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText.StartsWith("Try"));
                retClass = Utils.NonNull(retClass.RemoveNodes(extraMethods, SyntaxRemoveOptions.KeepNoTrivia));
            }

            // replace IsFlagsEnum
            var isFlagsEnumCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "IsFlagsEnum")
                .ToImmutableArray();

            var isFlagsEnumReplacement = SyntaxFactory.LiteralExpression(isFlags ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression);

            retClass =
                retClass.ReplaceNodes(
                    isFlagsEnumCalls,
                    (_, __) => isFlagsEnumReplacement
                    );

            // replace calls to Utils.NonNull
            var nonNullCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "NonNull")
                .ToImmutableArray();

            retClass =
                retClass.ReplaceNodes(
                    nonNullCalls,
                    (_, old) =>
            {
                var i   = old;
                var ps  = i.ArgumentList.Arguments;
                var val = ps[0].Expression;

                var eq       = SyntaxFactory.BinaryExpression(SyntaxKind.NotEqualsExpression, val, SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression));
                var throwExp = SyntaxFactory.ParseExpression("throw new InvalidOperationException(\"Expected non-null value, but found null\")");

                var cond    = SyntaxFactory.ConditionalExpression(eq, val, throwExp);
                var parened = SyntaxFactory.ParenthesizedExpression(cond);

                return(parened);
            }
                    );

            // replace Utils.ULongToEnum calls with direct casts (since we've removed all the generic Ts, this will compile now)
            var ulongToEnumCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "ULongToEnum")
                .ToImmutableArray();

            var enumTypeName = SyntaxFactory.ParseTypeName(forEnumFullyQualified);

            retClass =
                retClass.ReplaceNodes(
                    ulongToEnumCalls,
                    (_, old) =>
            {
                var i    = old;
                var ps   = i.ArgumentList.Arguments;
                var cast = SyntaxFactory.CastExpression(enumTypeName, ps[0].Expression);

                return(cast);
            }
                    );

            // replace calls to Utils.EnumToULong
            var enumToULongCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "EnumToULong")
                .ToImmutableArray();

            var ulongType = SyntaxFactory.ParseTypeName("ulong");

            retClass =
                retClass.ReplaceNodes(
                    enumToULongCalls,
                    (_, old) =>
            {
                var i    = old;
                var ps   = i.ArgumentList.Arguments;
                var cast = SyntaxFactory.CastExpression(ulongType, ps[0].Expression);

                return(cast);
            }
                    );

            // make TryParseFlagsEnum private
            var tryParseFlagsEnumMtd = retClass.DescendantNodesAndSelf().OfType <MethodDeclarationSyntax>().SingleOrDefault(s => s.Identifier.ValueText == "TryParseFlagsEnum");

            if (tryParseFlagsEnumMtd != null)
            {
                var privateTryParseFlagsEnumMtd =
                    tryParseFlagsEnumMtd.WithModifiers(
                        SyntaxFactory.TokenList(
                            SyntaxFactory.Token(SyntaxKind.PrivateKeyword),
                            SyntaxFactory.Token(SyntaxKind.StaticKeyword)
                            )
                        );

                retClass =
                    retClass.ReplaceNode(
                        tryParseFlagsEnumMtd,
                        privateTryParseFlagsEnumMtd
                        );
            }

            // get the string we're using
            retClass = retClass.NormalizeWhitespace();
            var code = retClass.ToFullString();

            var forType = nullable ? forEnumFullyQualified + "?" : forEnumFullyQualified;

            parser = new Parser(false, forType, code);
            return(true);
        }
Example #6
0
 private static bool TryCreateNullableEnumParser(FrameworkTypes types, ITypeSymbol forEnum, out Parser?parser)
 => TryCreateEnumParser(types, true, forEnum, out parser);
Example #7
0
 private static bool TryCreateDefaultEnumParser(FrameworkTypes types, ITypeSymbol forEnum, out Parser?parser)
 => TryCreateEnumParser(types, false, forEnum, out parser);
Example #8
0
        private static Formatter CreateEnumDefaultFormatter(FrameworkTypes types, ITypeSymbol forEnum)
        {
            var isFlags = Utils.IsFlagsEnum(types, forEnum);
            var forEnumFullyQualified = forEnum.ToFullyQualifiedName();

            // strip out type parameters
            var nonGeneric = Utils.MakeNonGenericType(EnumTypeFormatterTemplate, "T", forEnumFullyQualified);

            // remove all the stuff we _don't_ need
            var toRemoveMtd =
                nonGeneric
                .DescendantNodesAndSelf()
                .OfType <MethodDeclarationSyntax>()
                .Where(
                    t =>
            {
                var val = t.Identifier.ValueText;

                if (RemoveEnumFormatterMethods.Contains(val))
                {
                    return(true);
                }

                if (!isFlags && RemoveEnumFormatterMethodsWhenNotFlags.Contains(val))
                {
                    return(true);
                }

                return(false);
            }
                    )
                .ToList();


            var toRemoveFields =
                nonGeneric
                .DescendantNodesAndSelf()
                .OfType <FieldDeclarationSyntax>()
                .Where(
                    t =>
                    t.Declaration.Variables.Any(
                        x =>
            {
                var val = x.Identifier.ValueText;
                if (RemoveEnumFormatterFields.Contains(val))
                {
                    return(true);
                }

                if (!isFlags && RemoveEnumFormatterFieldsWhenNotFlags.Contains(val))
                {
                    return(true);
                }

                return(false);
            }
                        )
                    )
                .ToList();

            var toRemove = toRemoveMtd.Cast <SyntaxNode>().Concat(toRemoveFields);
            var trimmed  = Utils.NonNull(nonGeneric.RemoveNodes(toRemove, SyntaxRemoveOptions.KeepNoTrivia));

            // now grab the right method
            var mtd =
                trimmed
                .DescendantNodesAndSelf()
                .OfType <MethodDeclarationSyntax>()
                .Single(m => isFlags ? m.Identifier.ValueText == "TryFormatFlagsEnum" : m.Identifier.ValueText == "TryFormatBasicEnum");

            // inline anything that needs inlining
            var updatedMtd = Utils.InlineTailCalls(mtd, trimmed);

            // construct final class
            var publicFormatMethod =
                updatedMtd
                .WithIdentifier(SyntaxFactory.ParseToken("__TryFormat"))
                .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)));

            var retClass = trimmed.ReplaceNode(mtd, publicFormatMethod);

            retClass = retClass
                       .WithIdentifier(SyntaxFactory.ParseToken("__Class"))
                       .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)));
            retClass = Utils.NonNull(retClass.RemoveNodes(retClass.Members.OfType <MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText.StartsWith("Try")), SyntaxRemoveOptions.KeepNoTrivia));

            // replace calls to Utils.EnumToULong
            var enumToULongCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "EnumToULong")
                .ToImmutableArray();

            var ulongType = SyntaxFactory.ParseTypeName("ulong");

            retClass =
                retClass.ReplaceNodes(
                    enumToULongCalls,
                    (_, old) =>
            {
                var i    = old;
                var ps   = i.ArgumentList.Arguments;
                var cast = SyntaxFactory.CastExpression(ulongType, ps[0].Expression);

                return(cast);
            }
                    );

            // replace calls to Utils.NonNull
            var nonNullCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "NonNull")
                .ToImmutableArray();

            retClass =
                retClass.ReplaceNodes(
                    nonNullCalls,
                    (_, old) =>
            {
                var i   = old;
                var ps  = i.ArgumentList.Arguments;
                var val = ps[0].Expression;

                var eq       = SyntaxFactory.BinaryExpression(SyntaxKind.NotEqualsExpression, val, SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression));
                var throwExp = SyntaxFactory.ParseExpression("throw new InvalidOperationException(\"Expected non-null value, but found null\")");

                var cond    = SyntaxFactory.ConditionalExpression(eq, val, throwExp);
                var parened = SyntaxFactory.ParenthesizedExpression(cond);

                return(parened);
            }
                    );

            // replace IsFlagsEnum
            var isFlagsEnumCalls =
                retClass
                .DescendantNodesAndSelf()
                .OfType <InvocationExpressionSyntax>()
                .Where(i => i.Expression is MemberAccessExpressionSyntax)
                .Where(i => ((MemberAccessExpressionSyntax)i.Expression).Name.Identifier.ValueText == "IsFlagsEnum")
                .ToImmutableArray();

            var isFlagsEnumReplacement = SyntaxFactory.LiteralExpression(isFlags ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression);

            retClass =
                retClass.ReplaceNodes(
                    isFlagsEnumCalls,
                    (_, __) => isFlagsEnumReplacement
                    );

            // make FormatFlagsEnumImpl private
            var formatFlagsEnumImpl =
                retClass
                .Members
                .OfType <MethodDeclarationSyntax>()
                .SingleOrDefault(s => s.Identifier.ValueText == "FormatFlagsEnumImpl");

            if (formatFlagsEnumImpl != null)
            {
                var privateFormatFlagsEnumImpl =
                    formatFlagsEnumImpl.WithModifiers(
                        SyntaxFactory.TokenList(
                            SyntaxFactory.Token(SyntaxKind.PrivateKeyword),
                            SyntaxFactory.Token(SyntaxKind.StaticKeyword)
                            )
                        );

                retClass =
                    retClass.ReplaceNode(
                        formatFlagsEnumImpl,
                        privateFormatFlagsEnumImpl
                        );
            }

            // get the string we're using
            retClass = retClass.NormalizeWhitespace();

            bool   isMethod;
            string code;

            if (retClass.Members.Count == 1)
            {
                isMethod = true;
                code     = retClass.Members.Single().ToFullString();
            }
            else
            {
                // only include the full class if we're actually using fields or other members
                isMethod = false;
                code     = retClass.ToFullString();
            }

            return(new Formatter(isMethod, forEnumFullyQualified, code));
        }
Example #9
0
        private static Formatter CreateNullableEnumDefaultFormatter(FrameworkTypes types, ITypeSymbol forNullableEnum)
        {
            var forEnum          = ((INamedTypeSymbol)forNullableEnum).TypeArguments.Single();
            var nonNullFormatter = CreateEnumDefaultFormatter(types, forEnum);

            var isFlags = Utils.IsFlagsEnum(types, forEnum);
            var forEnumFullyQualified         = forEnum.ToFullyQualifiedName();
            var forNullableEnumFullyQualified = forNullableEnum.ToFullyQualifiedName();

            // get the formatter method for the nullable enum
            var nonGeneric  = Utils.MakeNonGenericType(EnumTypeFormatterTemplate, "T", forEnumFullyQualified);
            var nullableMtd =
                nonGeneric
                .Members
                .OfType <MethodDeclarationSyntax>()
                .Single(m => isFlags ? m.Identifier.ValueText == "TryFormatNullableFlagsEnum" : m.Identifier.ValueText == "TryFormatNullableBasicEnum");

            // get the formatter method for the NON-NULLABLE enum
            var nonNullFormatterParsed =
                Utils.NonNull(SyntaxFactory.ParseMemberDeclaration(Utils.NonNull(nonNullFormatter.DefaultCode)));
            var nonNullFormatterMtd = nonNullFormatterParsed.DescendantNodesAndSelf().OfType <MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "__TryFormat");
            var nonNullMtdBody      = Utils.NonNull(nonNullFormatterMtd.Body);

            // rewrite things to produce final code
            var replaceRet = nullableMtd.DescendantNodesAndSelf().OfType <ReturnStatementSyntax>().Last();
            var toReplace  = ImmutableDictionary.CreateBuilder <ReturnStatementSyntax, (ParameterListSyntax Parameters, BlockSyntax Statements)>();

            toReplace.Add(replaceRet, (nonNullFormatterMtd.ParameterList, nonNullMtdBody));

            bool       isMethod;
            SyntaxNode nullableFinal;

            if (nonNullFormatter.DefaultIsMethod)
            {
                // just a method, simple inlining is sufficient
                isMethod = true;

                nullableFinal = Utils.ReplaceIn(nullableMtd, toReplace.ToImmutable());
            }
            else
            {
                // whole class, so we need to inline and remove the old method
                isMethod = false;

                var nonNullClass = (ClassDeclarationSyntax)nonNullFormatterParsed;
                var nonNullClassWithoutTryFormat = Utils.NonNull(nonNullClass.RemoveNode(nonNullFormatterMtd, SyntaxRemoveOptions.KeepNoTrivia));

                var newNullableMethod =
                    Utils.ReplaceIn(nullableMtd, toReplace.ToImmutable())
                    .WithIdentifier(SyntaxFactory.ParseToken("__TryFormat"))
                    .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)));

                nullableFinal = nonNullClassWithoutTryFormat.AddMembers(newNullableMethod);
            }

            nullableFinal = nullableFinal.NormalizeWhitespace();

            var code = nullableFinal.ToFullString();

            return(new Formatter(isMethod, forNullableEnumFullyQualified, code));
        }