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()); }
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))); }
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); }
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); }
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); }
private static bool TryCreateNullableEnumParser(FrameworkTypes types, ITypeSymbol forEnum, out Parser?parser) => TryCreateEnumParser(types, true, forEnum, out parser);
private static bool TryCreateDefaultEnumParser(FrameworkTypes types, ITypeSymbol forEnum, out Parser?parser) => TryCreateEnumParser(types, false, forEnum, out parser);
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)); }
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)); }