static void BindEnumerableAndEnumerator(EnumerableDetails details, TypeSyntax newOutType, out TypeSyntax updatedEnumerable, out TypeSyntax updatedEnumerator) { var baseEnumerable = details.BridgeEnumerable; var baseEnumerator = details.BridgeEnumerator; var enumerableMentionsOfOutType = baseEnumerable.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == details.OutItem).ToList(); var enumeratorMentionsOfOutType = baseEnumerator.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == details.OutItem).ToList(); updatedEnumerable = baseEnumerable.ReplaceNodes(enumerableMentionsOfOutType, (old, _) => newOutType.WithTriviaFrom(old)); updatedEnumerator = baseEnumerator.ReplaceNodes(enumeratorMentionsOfOutType, (old, _) => newOutType.WithTriviaFrom(old)); }
static IEnumerable <MethodDeclarationSyntax> ExpandDoubleParameterizedFromPlaceholders( MethodDeclarationSyntax mtd, TypeSyntax[] outTypes, EnumerableDetails builtIn, IEnumerable <EnumerableDetails> allBuiltIns, IEnumerable <EnumerableDetails> allEnumerables ) { List <SyntaxNode> firstEnumerables, firstEnumerators, secondEnumerables, secondEnumerators, firstParameterEnumerables, secondParameterEnumerables; ExtractEnumerablesAndEnumeratorsInFirstAndSecondPosition( mtd, out firstEnumerables, out secondEnumerables, out firstEnumerators, out secondEnumerators, out firstParameterEnumerables, out secondParameterEnumerables ); Func <SyntaxNode, bool, EnumerableDetails, SyntaxNode> makeReplacementEnumerable = (node, isFirst, e) => { TypeSyntax outType; if (outTypes.Length == 1) { outType = outTypes[0]; } else { outType = outTypes[isFirst ? 0 : 1]; } var bridgeEnumerable = e.BridgeEnumerable ?? e.Enumerable; var defaultOutType = e.OutItem; var uses = bridgeEnumerable.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == defaultOutType).ToList(); var updatedEnumerable = bridgeEnumerable.ReplaceNodes(uses, (old, _) => outType.WithTriviaFrom(old)); return(updatedEnumerable.WithTriviaFrom(node)); }; Func <SyntaxNode, bool, EnumerableDetails, SyntaxNode> makeReplacementEnumerator = (node, isFirst, e) => { TypeSyntax outType; if (outTypes.Length == 1) { outType = outTypes[0]; } else { outType = outTypes[isFirst ? 0 : 1]; } var bridgeEnumerator = e.BridgeEnumerator ?? e.Enumerator; var defaultOutType = e.OutItem; var uses = bridgeEnumerator.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == defaultOutType).ToList(); var updatedEnumerator = bridgeEnumerator.ReplaceNodes(uses, (old, _) => outType.WithTriviaFrom(old)); return(updatedEnumerator.WithTriviaFrom(node)); }; Func <SyntaxNode, bool, EnumerableDetails, SyntaxNode> makeReplacementSystemEnumerable = (node, isFirst, e) => { TypeSyntax outType; if (outTypes.Length == 1) { outType = outTypes[0]; } else { outType = outTypes[isFirst ? 0 : 1]; } var enumerable = e.Enumerable; var defaultOutType = e.OutItem; var uses = enumerable.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == defaultOutType).ToList(); var updatedEnumerable = enumerable.ReplaceNodes(uses, (old, _) => outType.WithTriviaFrom(old)); return(updatedEnumerable.WithTriviaFrom(node)); }; var firstReplacements = new Dictionary <SyntaxNode, SyntaxNode>(); foreach (var enumerable in firstEnumerables) { firstReplacements[enumerable] = makeReplacementEnumerable(enumerable, true, builtIn); } foreach (var enumerator in firstEnumerators) { firstReplacements[enumerator] = makeReplacementEnumerator(enumerator, true, builtIn); } foreach (var enumerable in firstParameterEnumerables) { firstReplacements[enumerable] = makeReplacementSystemEnumerable(enumerable, true, builtIn); } var ret = new List <MethodDeclarationSyntax>(); var overIntroducedEnumerables = secondEnumerables.All(t => (t as SimpleNameSyntax)?.Identifier.ValueText == PLACEHOLDER_ENUMERABLE_NAME); var toExpandTo = overIntroducedEnumerables ? allEnumerables : allBuiltIns; foreach (var otherBuiltInUnmodified in toExpandTo) { var otherBuiltIn = otherBuiltInUnmodified; var replacements = new Dictionary <SyntaxNode, SyntaxNode>(firstReplacements); var additionalGenericTerms = new List <string>(builtIn.GenericArgs); var genericArgsInConflict = otherBuiltIn.GenericArgs.Where(t => additionalGenericTerms.Contains(t)).ToList(); var oldGenArgtoNew = new Dictionary <string, string>(); var outType = outTypes.Length == 1 ? outTypes[0] : outTypes[1]; oldGenArgtoNew[otherBuiltIn.OutItem] = outType.ToFullString().Trim(); foreach (var inConflict in genericArgsInConflict) { var i = 1; var updated = inConflict + i; while (otherBuiltIn.GenericArgs.Contains(updated) || oldGenArgtoNew.ContainsValue(updated)) { i++; updated = inConflict + i; } oldGenArgtoNew[inConflict] = updated; } otherBuiltIn = otherBuiltIn.MapGenericTypeParams(oldGenArgtoNew); additionalGenericTerms.AddRange(otherBuiltIn.GenericArgs); foreach (var enumerable in secondEnumerables) { replacements[enumerable] = makeReplacementEnumerable(enumerable, false, otherBuiltIn); } foreach (var enumerator in secondEnumerators) { replacements[enumerator] = makeReplacementEnumerator(enumerator, false, otherBuiltIn); } foreach (var enumerable in secondParameterEnumerables) { replacements[enumerable] = makeReplacementSystemEnumerable(enumerable, false, otherBuiltIn); } var pairUpdated = mtd.ReplaceNodes(replacements.Keys, (old, _) => replacements[old]); if (additionalGenericTerms.Count > 0) { var additionalTypeParams = additionalGenericTerms.Select(t => SyntaxFactory.TypeParameter(t)).ToArray(); pairUpdated = pairUpdated.AddTypeParameterListParameters(additionalTypeParams); } if (otherBuiltIn.Constraints.Count > 0) { var constraintList = SyntaxFactory.List(otherBuiltIn.Constraints); pairUpdated = pairUpdated.WithConstraintClauses(constraintList); } ret.Add(pairUpdated); } return(ret); }
void WriteExtensionsMethodClassFor( Projects projects, EnumerableDetails builtIn, IEnumerable <EnumerableDetails> allBuiltIns, IEnumerable <EnumerableDetails> allEnumerables ) { string name; if (builtIn.Enumerable is GenericNameSyntax) { name = ((GenericNameSyntax)builtIn.Enumerable).Identifier.ValueText; } else { if (builtIn.Enumerable is QualifiedNameSyntax) { var asQualified = (QualifiedNameSyntax)builtIn.Enumerable; var left = asQualified.Left; var right = asQualified.Right; if (left is GenericNameSyntax) { name = ((GenericNameSyntax)left).Identifier.ValueText; } else { name = ((SimpleNameSyntax)left).Identifier.ValueText; } if (right is GenericNameSyntax) { name += ((GenericNameSyntax)right).Identifier.ValueText; } else { name += right.Identifier.ValueText; } } else { if (builtIn.Enumerable is ArrayTypeSyntax) { name = "Array"; } else { throw new InvalidOperationException(); } } } var templateFileNameStarts = "BuiltInExtensionMethodsBase."; var templateFileNameEnd = ".cs"; var templateDocs = projects.Template.Documents .Where(d => d.Name.StartsWith(templateFileNameStarts) && d.Name.EndsWith(templateFileNameEnd)) .ToList(); var parsedRoots = templateDocs.Select(d => d.GetSyntaxRootAsync().Result).ToList(); var templateMtds = parsedRoots.SelectMany(r => r.DescendantNodesAndSelf().OfType <MethodDeclarationSyntax>()).ToList(); // create the initial template CompilationUnitSyntax root; ClassDeclarationSyntax extensionDecl; CreateInitialTemplate(name, out root, out extensionDecl); // create the methods for each enumerable var updatedDecl = extensionDecl; foreach (var mtd in templateMtds) { // figure out what the enumerable and enumerator should _really_ be bound to var mentionsOfPlaceholderEnumerable = mtd .DescendantNodesAndSelf() .OfType <SimpleNameSyntax>() .Where( i => i.Identifier.ValueText == BUILTIN_ENUMERABLE_NAME || i.Identifier.ValueText == PLACEHOLDER_ENUMERABLE_NAME || i.Identifier.ValueText == CONSTRAINED_BUILTIN_ENUMERABLE_NAME ) .ToList(); var mentionsOfPlaceholderEnumerator = mtd .DescendantNodesAndSelf() .OfType <SimpleNameSyntax>() .Where( i => i.Identifier.ValueText == BUILTIN_ENUMERATOR_NAME || i.Identifier.ValueText == PLACEHOLDER_ENUMERATOR_NAME || i.Identifier.ValueText == CONSTRAINED_BUILTIN_ENUMERATOR_NAME ) .ToList(); var outTypes = mentionsOfPlaceholderEnumerable .OfType <GenericNameSyntax>() .Concat(mentionsOfPlaceholderEnumerator.OfType <GenericNameSyntax>()) .Select(g => g.TypeArgumentList.Arguments.ElementAt(0)) .OfType <TypeSyntax>() .Select(t => t.ToString()) .Distinct() .ToList(); var outTypeStr = outTypes.ToArray(); var enumerableParamCount = mtd.ParameterList.Parameters .Count( c => c.DescendantNodesAndSelf() .OfType <SimpleNameSyntax>() .Any( x => x.Identifier.ValueText == BUILTIN_ENUMERABLE_NAME || x.Identifier.ValueText == PLACEHOLDER_ENUMERABLE_NAME || x.Identifier.ValueText == CONSTRAINED_BUILTIN_ENUMERABLE_NAME ) ); var updatedMtd = mtd; if (enumerableParamCount <= 1) { HandleSinglePlaceholderTemplateMethod(ref updatedDecl, updatedMtd, builtIn, outTypeStr.Single()); } else { HandleDoublePlaceholderTemplateMethod(ref updatedDecl, updatedMtd, builtIn, allBuiltIns, allEnumerables, outTypeStr); } } // write the extension methods document var newFileName = name + "ExtensionMethods.cs"; var updatdRoot = root.ReplaceNode(extensionDecl, updatedDecl.WithTriviaFrom(extensionDecl)); var updatedCode = updatdRoot.ToFullString(); projects.ModifyOutput( p => { var updatedDoc = p.AddDocument(newFileName, updatedCode); return(updatedDoc.Project); } ); }
static void HandleDoublePlaceholderTemplateMethod( ref ClassDeclarationSyntax updatedDecl, MethodDeclarationSyntax mtd, EnumerableDetails builtIn, IEnumerable <EnumerableDetails> allBuiltIns, IEnumerable <EnumerableDetails> allEnumerables, string[] outTypeStrs ) { var outTypes = outTypeStrs.Select(t => SyntaxFactory.ParseTypeName(t)).ToArray(); var expanded = ExpandDoubleParameterizedFromPlaceholders(mtd, outTypes, builtIn, allBuiltIns, allEnumerables); var extensions = new List <MethodDeclarationSyntax>(); foreach (var bound in expanded) { var firstParam = bound.ParameterList.Parameters.First(); var withThisStr = THIS.ToFullString() + firstParam.ToFullString(); var withThis = SyntaxFactory.ParseParameterList(withThisStr).Parameters.ElementAt(0); var updated = bound.ReplaceNode(firstParam, withThis); var modifiers = new SyntaxToken[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword).WithTriviaFrom(updated.Modifiers.First()), SyntaxFactory.Token(SyntaxKind.StaticKeyword).WithTrailingTrivia(SyntaxFactory.Whitespace(" ")) }; var modifiersList = SyntaxFactory.TokenList(modifiers); updated = updated.WithModifiers(modifiersList); extensions.Add(updated); } var bridgesReplaced = new List <MethodDeclarationSyntax>(); foreach (var extension in extensions) { var updated = ReplaceBridgeCalls(extension); bridgesReplaced.Add(updated); } var refLocalsReplaced = new List <MethodDeclarationSyntax>(); foreach (var bridged in bridgesReplaced) { var updated = bridged; ReplaceRefLocalCalls(ref updated); refLocalsReplaced.Add(updated); } var refParamsReplaced = new List <MethodDeclarationSyntax>(); foreach (var deLocaled in refLocalsReplaced) { var updated = deLocaled; ReplaceRefParamCalls(ref updated); refParamsReplaced.Add(updated); } updatedDecl = updatedDecl.AddMembers(refParamsReplaced.ToArray()); }
static MethodDeclarationSyntax ExpandConstrainedBuiltIns(MethodDeclarationSyntax mtd, EnumerableDetails builtIn, string outTypeStr) { var constraintEnumerablesMentions = mtd .DescendantNodesAndSelf() .OfType <SimpleNameSyntax>() .Where(w => w.Identifier.ValueText == CONSTRAINED_BUILTIN_ENUMERABLE_NAME) .ToList(); var constraintEnumeratorsMentions = mtd .DescendantNodesAndSelf() .OfType <SimpleNameSyntax>() .Where(w => w.Identifier.ValueText == CONSTRAINED_BUILTIN_ENUMERATOR_NAME) .ToList(); if (constraintEnumerablesMentions.Count == 0 || constraintEnumeratorsMentions.Count == 0) { return(mtd); } var enumerable = builtIn.BridgeEnumerable; var enumerator = builtIn.BridgeEnumerator; var outType = SyntaxFactory.ParseTypeName(outTypeStr); var enumerableOutMentions = enumerable.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == builtIn.OutItem).ToList(); var enumeratorOutMentions = enumerator.DescendantNodesAndSelf().OfType <SimpleNameSyntax>().Where(i => i.Identifier.ValueText == builtIn.OutItem).ToList(); var boundEnumerable = enumerable.ReplaceNodes(enumerableOutMentions, (old, _) => outType.WithTriviaFrom(old)); var boundEnumerator = enumerator.ReplaceNodes(enumeratorOutMentions, (old, _) => outType.WithTriviaFrom(old)); var replacements = new Dictionary <SyntaxNode, SyntaxNode>(); constraintEnumerablesMentions.ForEach(e => replacements[e] = boundEnumerable); constraintEnumeratorsMentions.ForEach(e => replacements[e] = boundEnumerator); var updatedMtd = mtd.ReplaceNodes(replacements.Keys, (old, _) => replacements[old].WithTriviaFrom(old)); updatedMtd = updatedMtd.WithAdditionalAnnotations(DO_NOT_PARAMETERIZE); return(updatedMtd); }
static void HandleSinglePlaceholderTemplateMethod(ref ClassDeclarationSyntax updatedDecl, MethodDeclarationSyntax mtd, EnumerableDetails builtIn, string outTypeStr) { var expanded = ExpandMethodFromPlaceholders(mtd, new[] { builtIn }, BUILTIN_ENUMERABLE_NAME, BUILTIN_ENUMERATOR_NAME, false); var constrainedExpansions = new List <MethodDeclarationSyntax>(); foreach (var expandedMtd in expanded) { var updated = ExpandConstrainedBuiltIns(expandedMtd, builtIn, outTypeStr); constrainedExpansions.Add(updated); } foreach (var expandedMtd in constrainedExpansions) { // slap the type on so we can find it when rewritting later var withOutTypeAnnotation = expandedMtd.WithAdditionalAnnotations(new SyntaxAnnotation(OUT_TYPE_ANNOTATION, outTypeStr)); updatedDecl = updatedDecl.AddMembers(new[] { withOutTypeAnnotation }); } // change methods to be extension methods, and replace placeholders var replaces = new Dictionary <SyntaxNode, SyntaxNode>(); foreach (var workingMtd in updatedDecl.DescendantNodesAndSelf().OfType <MethodDeclarationSyntax>().Where(m => !m.HasAnnotation(METHOD_REWRITTEN))) { // grab the type we need for the enumerable and enumerator var outTypeAnnotation = workingMtd.GetAnnotations(OUT_TYPE_ANNOTATION).SingleOrDefault(); // did we already handle this elsewhere? if (outTypeAnnotation == null) { var skipped = workingMtd.WithAdditionalAnnotations(METHOD_REWRITTEN); replaces[workingMtd] = skipped.WithTriviaFrom(workingMtd); continue; } var workingOutTypeStr = outTypeAnnotation.Data; var outType = SyntaxFactory.ParseTypeName(workingOutTypeStr); TypeSyntax enumerable, enumerator; BindEnumerableAndEnumerator(builtIn, outType, out enumerable, out enumerator); // make the method an extension method var extension = MakeExtensionMethod(workingMtd); // make the dynamic bridge calls call CommonImplementation.Bridge var bridged = ReplaceBridgeCalls(extension); // slap the appropriate enumerable and enumerator into the various CommonImplementation calls MethodDeclarationSyntax parameterized; if (!bridged.HasAnnotation(DO_NOT_PARAMETERIZE)) { parameterized = InjectIdentityAndEnumerator(bridged, enumerable, enumerator); } else { parameterized = bridged; } parameterized = parameterized.WithAdditionalAnnotations(METHOD_REWRITTEN); replaces[workingMtd] = parameterized.WithTriviaFrom(workingMtd); } updatedDecl = updatedDecl.ReplaceNodes(replaces.Keys, (old, _) => replaces[old]); // change methods to be extension methods, and replace placeholders //while (true) //{ // var workingMtd = updatedDecl.DescendantNodesAndSelf().OfType<MethodDeclarationSyntax>().Where(m => !m.HasAnnotation(METHOD_REWRITTEN)).FirstOrDefault(); // if (workingMtd == null) break; // // grab the type we need for the enumerable and enumerator // var outTypeAnnotation = workingMtd.GetAnnotations(OUT_TYPE_ANNOTATION).SingleOrDefault(); // // did we already handle this elsewhere? // if (outTypeAnnotation == null) // { // var skipped = workingMtd.WithAdditionalAnnotations(METHOD_REWRITTEN); // updatedDecl = updatedDecl.ReplaceNode(workingMtd, skipped.WithTriviaFrom(workingMtd)); // continue; // } // var workingOutTypeStr = outTypeAnnotation.Data; // var outType = SyntaxFactory.ParseTypeName(workingOutTypeStr); // TypeSyntax enumerable, enumerator; // BindEnumerableAndEnumerator(builtIn, outType, out enumerable, out enumerator); // // make the method an extension method // var extension = MakeExtensionMethod(workingMtd); // // make the dynamic bridge calls call CommonImplementation.Bridge // var bridged = ReplaceBridgeCalls(extension); // // slap the appropriate enumerable and enumerator into the various CommonImplementation calls // MethodDeclarationSyntax parameterized; // if (!bridged.HasAnnotation(DO_NOT_PARAMETERIZE)) // { // parameterized = InjectIdentityAndEnumerator(bridged, enumerable, enumerator); // } // else // { // parameterized = bridged; // } // parameterized = parameterized.WithAdditionalAnnotations(METHOD_REWRITTEN); // updatedDecl = updatedDecl.ReplaceNode(workingMtd, parameterized.WithTriviaFrom(workingMtd)); //} }