private void Process(ComponentExtensionNode node)
            {
                // First collect all of the information we have about each type parameter
                //
                // Listing all type parameters that exist
                var bindings = new Dictionary <string, GenericTypeNameRewriter.Binding>();

                foreach (var attribute in node.Component.GetTypeParameters())
                {
                    bindings.Add(attribute.Name, new GenericTypeNameRewriter.Binding()
                    {
                        Attribute = attribute,
                    });
                }

                // Listing all type arguments that have been specified.
                var hasTypeArgumentSpecified = false;

                foreach (var typeArgumentNode in node.TypeArguments)
                {
                    hasTypeArgumentSpecified = true;

                    var binding = bindings[typeArgumentNode.TypeParameterName];
                    binding.Node    = typeArgumentNode;
                    binding.Content = GetContent(typeArgumentNode);
                }

                if (hasTypeArgumentSpecified)
                {
                    // OK this means that the developer has specified at least one type parameter.
                    // Either they specified everything and its OK to rewrite, or its an error.
                    if (ValidateTypeArguments(node, bindings))
                    {
                        RewriteTypeNames(new GenericTypeNameRewriter(bindings), node);
                    }

                    return;
                }

                // OK if we get here that means that no type arguments were specified, so we will try to infer
                // the type.
                //
                // The actual inference is done by the C# compiler, we just emit an a method that represents the
                // use of this component.

                // Since we're generating code in a different namespace, we need to 'global qualify' all of the types
                // to avoid clashes with our generated code.
                RewriteTypeNames(new GlobalQualifiedTypeNameRewriter(bindings.Keys), node);

                //
                // We need to verify that an argument was provided that 'covers' each type parameter.
                //
                // For example, consider a repeater where the generic type is the 'item' type, but the developer has
                // not set the items. We won't be able to do type inference on this and so it will just be nonsense.
                var attributes = node.Attributes.Select(a => a.BoundAttribute).Concat(node.ChildContents.Select(c => c.BoundAttribute));

                foreach (var attribute in attributes)
                {
                    if (attribute == null)
                    {
                        // Will be null for attributes set on the component that don't match a declared component parameter
                        continue;
                    }

                    // Now we need to parse the type name and extract the generic parameters.
                    //
                    // Two cases;
                    // 1. name is a simple identifier like TItem
                    // 2. name contains type parameters like Dictionary<string, TItem>
                    if (!attribute.IsGenericTypedProperty())
                    {
                        continue;
                    }

                    var parsed = SyntaxFactory.ParseTypeName(attribute.TypeName);
                    if (parsed is IdentifierNameSyntax identifier)
                    {
                        bindings.Remove(identifier.ToString());
                    }
                    else
                    {
                        var typeParameters = parsed.DescendantNodesAndSelf().OfType <TypeArgumentListSyntax>().SelectMany(arg => arg.Arguments);
                        foreach (var typeParameter in typeParameters)
                        {
                            bindings.Remove(typeParameter.ToString());
                        }
                    }
                }

                // If any bindings remain then this means we would never be able to infer the arguments of this
                // component usage because the user hasn't set properties that include all of the types.
                if (bindings.Count > 0)
                {
                    // However we still want to generate 'type inference' code because we want the errors to be as
                    // helpful as possible. So let's substitute 'object' for all of those type parameters, and add
                    // an error.
                    RewriteTypeNames(new GenericTypeNameRewriter(bindings), node);

                    node.Diagnostics.Add(BlazorDiagnosticFactory.Create_GenericComponentTypeInferenceUnderspecified(node.Source, node, node.Component.GetTypeParameters()));
                }

                // Next we need to generate a type inference 'method' node. This represents a method that we will codegen that
                // contains all of the operations on the render tree building. Calling a method to operate on the builder
                // will allow the C# compiler to perform type inference.
                var documentNode = (DocumentIntermediateNode)Ancestors[Ancestors.Count - 1];

                CreateTypeInferenceMethod(documentNode, node, bindings);
            }