Beispiel #1
0
    public static bool IsParameterWithCaptureUnmatchedValues(ComponentSymbols symbols, IPropertySymbol property)
    {
        if (symbols == null)
        {
            throw new ArgumentNullException(nameof(symbols));
        }

        if (property == null)
        {
            throw new ArgumentNullException(nameof(property));
        }

        var attribute = property.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.ParameterAttribute));

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

        foreach (var kvp in attribute.NamedArguments)
        {
            if (string.Equals(kvp.Key, ComponentsApi.ParameterAttribute.CaptureUnmatchedValues, StringComparison.Ordinal))
            {
                return(kvp.Value.Value as bool? ?? false);
            }
        }

        return(false);
    }
        private TagHelperDescriptor CreateDescriptor(ComponentSymbols symbols, INamedTypeSymbol type)
        {
            var typeName     = type.ToDisplayString(FullNameTypeDisplayFormat);
            var assemblyName = type.ContainingAssembly.Identity.Name;

            var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, typeName, assemblyName);

            builder.SetTypeName(typeName);

            // This opts out this 'component' tag helper for any processing that's specific to the default
            // Razor ITagHelper runtime.
            builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Component.RuntimeName;

            if (type.IsGenericType)
            {
                builder.Metadata[ComponentMetadata.Component.GenericTypedKey] = bool.TrueString;

                for (var i = 0; i < type.TypeArguments.Length; i++)
                {
                    var typeParameter = type.TypeArguments[i] as ITypeParameterSymbol;
                    if (typeParameter != null)
                    {
                        CreateTypeParameterProperty(builder, typeParameter);
                    }
                }
            }

            var xml = type.GetDocumentationCommentXml();

            if (!string.IsNullOrEmpty(xml))
            {
                builder.Documentation = xml;
            }

            // Components have very simple matching rules. The type name (short) matches the tag name.
            builder.TagMatchingRule(r => r.TagName = type.Name);

            foreach (var property in GetProperties(symbols, type))
            {
                if (property.kind == PropertyKind.Ignored)
                {
                    continue;
                }

                CreateProperty(builder, property.property, property.kind);
            }

            if (builder.BoundAttributes.Any(a => a.IsParameterizedChildContentProperty()) &&
                !builder.BoundAttributes.Any(a => string.Equals(a.Name, ComponentMetadata.ChildContent.ParameterAttributeName, StringComparison.OrdinalIgnoreCase)))
            {
                // If we have any parameterized child content parameters, synthesize a 'Context' parameter to be
                // able to set the variable name (for all child content). If the developer defined a 'Context' parameter
                // already, then theirs wins.
                CreateContextParameter(builder, childContentName: null);
            }

            var descriptor = builder.Build();

            return(descriptor);
        }
Beispiel #3
0
        private TagHelperDescriptor CreateShortNameMatchingDescriptor(ComponentSymbols symbols, INamedTypeSymbol type)
        {
            var builder = CreateDescriptorBuilder(symbols, type);

            builder.TagMatchingRule(r => r.TagName = type.Name);

            return(builder.Build());
        }
    public void Execute(TagHelperDescriptorProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var compilation = context.GetCompilation();

        if (compilation == null)
        {
            // No compilation, nothing to do.
            return;
        }

        var symbols = ComponentSymbols.Create(compilation);

        var types   = new List <INamedTypeSymbol>();
        var visitor = new ComponentTypeVisitor(symbols, types);

        var targetAssembly = context.Items.GetTargetAssembly();

        if (targetAssembly is not null)
        {
            visitor.Visit(targetAssembly.GlobalNamespace);
        }
        else
        {
            visitor.Visit(compilation.Assembly.GlobalNamespace);
            foreach (var reference in compilation.References)
            {
                if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                {
                    visitor.Visit(assembly.GlobalNamespace);
                }
            }
        }

        for (var i = 0; i < types.Count; i++)
        {
            var type = types[i];

            // Components have very simple matching rules.
            // 1. The type name (short) matches the tag name.
            // 2. The fully qualified name matches the tag name.
            var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(symbols, type);
            context.Results.Add(shortNameMatchingDescriptor);
            var fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(symbols, type);
            context.Results.Add(fullyQualifiedNameMatchingDescriptor);

            foreach (var childContent in shortNameMatchingDescriptor.GetChildContentProperties())
            {
                // Synthesize a separate tag helper for each child content property that's declared.
                context.Results.Add(CreateChildContentDescriptor(symbols, shortNameMatchingDescriptor, childContent));
                context.Results.Add(CreateChildContentDescriptor(symbols, fullyQualifiedNameMatchingDescriptor, childContent));
            }
        }
    }
Beispiel #5
0
            public static ComponentSymbols Create(Compilation compilation)
            {
                // We find a bunch of important and fundamental types here that are needed to discover
                // components. If one of these isn't defined then we just bail, because the results will
                // be unpredictable.
                var symbols = new ComponentSymbols();

                symbols.ComponentBase = compilation.GetTypeByMetadataName(ComponentsApi.ComponentBase.MetadataName);
                if (symbols.ComponentBase == null)
                {
                    // No definition for ComponentBase, nothing to do.
                    return(null);
                }

                symbols.IComponent = compilation.GetTypeByMetadataName(ComponentsApi.IComponent.MetadataName);
                if (symbols.IComponent == null)
                {
                    // No definition for IComponent, nothing to do.
                    return(null);
                }

                symbols.ParameterAttribute = compilation.GetTypeByMetadataName(ComponentsApi.ParameterAttribute.MetadataName);
                if (symbols.ParameterAttribute == null)
                {
                    // No definition for [Parameter], nothing to do.
                    return(null);
                }

                symbols.RenderFragment = compilation.GetTypeByMetadataName(ComponentsApi.RenderFragment.MetadataName);
                if (symbols.RenderFragment == null)
                {
                    // No definition for RenderFragment, nothing to do.
                    return(null);
                }

                symbols.RenderFragmentOfT = compilation.GetTypeByMetadataName(ComponentsApi.RenderFragmentOfT.MetadataName);
                if (symbols.RenderFragmentOfT == null)
                {
                    // No definition for RenderFragment<T>, nothing to do.
                    return(null);
                }

                symbols.EventCallback = compilation.GetTypeByMetadataName(ComponentsApi.EventCallback.MetadataName);
                if (symbols.EventCallback == null)
                {
                    // No definition for EventCallback, nothing to do.
                    return(null);
                }

                symbols.EventCallbackOfT = compilation.GetTypeByMetadataName(ComponentsApi.EventCallbackOfT.MetadataName);
                if (symbols.EventCallbackOfT == null)
                {
                    // No definition for EventCallback<T>, nothing to do.
                    return(null);
                }

                return(symbols);
            }
Beispiel #6
0
        public void Execute(TagHelperDescriptorProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var compilation = context.GetCompilation();

            if (compilation == null)
            {
                // No compilation, nothing to do.
                return;
            }

            // We need to see private members too
            compilation = WithMetadataImportOptionsAll(compilation);

            var symbols = ComponentSymbols.Create(compilation);

            var types   = new List <INamedTypeSymbol>();
            var visitor = new ComponentTypeVisitor(symbols, types);

            // Visit the primary output of this compilation, as well as all references.
            visitor.Visit(compilation.Assembly);
            foreach (var reference in compilation.References)
            {
                // We ignore .netmodules here - there really isn't a case where they are used by user code
                // even though the Roslyn APIs all support them.
                if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                {
                    visitor.Visit(assembly);
                }
            }

            for (var i = 0; i < types.Count; i++)
            {
                var type = types[i];

                // Components have very simple matching rules.
                // 1. The type name (short) matches the tag name.
                // 2. The fully qualified name matches the tag name.
                var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(symbols, type);
                context.Results.Add(shortNameMatchingDescriptor);
                var fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(symbols, type);
                context.Results.Add(fullyQualifiedNameMatchingDescriptor);

                foreach (var childContent in shortNameMatchingDescriptor.GetChildContentProperties())
                {
                    // Synthesize a separate tag helper for each child content property that's declared.
                    context.Results.Add(CreateChildContentDescriptor(symbols, shortNameMatchingDescriptor, childContent));
                    context.Results.Add(CreateChildContentDescriptor(symbols, fullyQualifiedNameMatchingDescriptor, childContent));
                }
            }
        }
Beispiel #7
0
        private TagHelperDescriptor CreateFullyQualifiedNameMatchingDescriptor(ComponentSymbols symbols, INamedTypeSymbol type)
        {
            var builder             = CreateDescriptorBuilder(symbols, type);
            var containingNamespace = type.ContainingNamespace.ToDisplayString();
            var fullName            = $"{containingNamespace}.{type.Name}";

            builder.TagMatchingRule(r => r.TagName = fullName);
            builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch;

            return(builder.Build());
        }
Beispiel #8
0
    public static bool TryCreate(Compilation compilation, out ComponentSymbols symbols)
    {
        if (compilation == null)
        {
            throw new ArgumentNullException(nameof(compilation));
        }

        var parameterAttribute = compilation.GetTypeByMetadataName(ComponentsApi.ParameterAttribute.MetadataName);

        if (parameterAttribute == null)
        {
            symbols = null;
            return(false);
        }

        var cascadingParameterAttribute = compilation.GetTypeByMetadataName(ComponentsApi.CascadingParameterAttribute.MetadataName);

        if (cascadingParameterAttribute == null)
        {
            symbols = null;
            return(false);
        }

        var icomponentType = compilation.GetTypeByMetadataName(ComponentsApi.IComponent.MetadataName);

        if (icomponentType == null)
        {
            symbols = null;
            return(false);
        }

        var dictionary = compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2");
        var @string    = compilation.GetSpecialType(SpecialType.System_String);
        var @object    = compilation.GetSpecialType(SpecialType.System_Object);

        if (dictionary == null || @string == null || @object == null)
        {
            symbols = null;
            return(false);
        }

        var parameterCaptureUnmatchedValuesRuntimeType = dictionary.Construct(@string, @object);

        symbols = new ComponentSymbols(
            parameterAttribute,
            cascadingParameterAttribute,
            parameterCaptureUnmatchedValuesRuntimeType,
            icomponentType);
        return(true);
    }
Beispiel #9
0
    public static bool IsCascadingParameter(ComponentSymbols symbols, IPropertySymbol property)
    {
        if (symbols == null)
        {
            throw new ArgumentNullException(nameof(symbols));
        }

        if (property == null)
        {
            throw new ArgumentNullException(nameof(property));
        }

        return(property.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.CascadingParameterAttribute)));
    }
Beispiel #10
0
        public MainWindow()
        {
            //splash screen
            //StartUpSplash splash = new StartUpSplash();
            //splash.Show();

            //main code
            InitializeComponent();

            this.WindowState = WindowState.Maximized;
            this.Title       = "Node";
            ComponentSymbols.LoadSymbols();
            Log = SystemGrid.Informer;

            ConsoleManager.Show();
            try
            {
                canvas = VisualGrid.GenerateGrid();
                VisualGrid.SetStatusIndicator(this.Status);
                ParentCanvas.Children.Add(canvas);
            }
            catch (InvalidCanvasSizeExeption e)
            {
                Console.WriteLine(e.Message);
                MessageBox.Show(e.Message);
            }

            ComponentManager.schematicsCanvas = canvas;
            //databinding
            this.ComponentSelector.ItemsSource = ComponentManager.GetSymbolList();
            this.Output.ItemsSource            = Log.GetInfo();


            //adding functions
            VisualGrid.SelectedObjectChanged        += PropertyGridSelectionChange;
            this.ComponentSelector.SelectionChanged += ComponentSelector_SelectionChanged;
            this.canvas.MouseWheel          += Canvas_MouseWheel;
            this.canvas.MouseMove           += Canvas_MouseMove;
            this.canvas.MouseLeftButtonDown += Canvas_MouseLeftButtonDown;
            this.ParentCanvas.MouseWheel    += ParentCanvas_MouseWheel;
            this.KeyDown += KeyBoarInputPreview;
            //test
            this.Button_Simulate.Click += Test_Click;

            //end splash
            //Thread.Sleep(2000);
            //splash.Close();
        }
Beispiel #11
0
        private TagHelperDescriptor CreateChildContentDescriptor(ComponentSymbols symbols, TagHelperDescriptor component, BoundAttributeDescriptor attribute)
        {
            var typeName     = component.GetTypeName() + "." + attribute.Name;
            var assemblyName = component.AssemblyName;

            var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.ChildContent.TagHelperKind, typeName, assemblyName);

            builder.SetTypeName(typeName);
            builder.CaseSensitive = true;

            // This opts out this 'component' tag helper for any processing that's specific to the default
            // Razor ITagHelper runtime.
            builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.ChildContent.RuntimeName;

            // Opt out of processing as a component. We'll process this specially as part of the component's body.
            builder.Metadata[ComponentMetadata.SpecialKindKey] = ComponentMetadata.ChildContent.TagHelperKind;

            var xml = attribute.Documentation;

            if (!string.IsNullOrEmpty(xml))
            {
                builder.Documentation = xml;
            }

            // Child content matches the property name, but only as a direct child of the component.
            builder.TagMatchingRule(r =>
            {
                r.TagName   = attribute.Name;
                r.ParentTag = component.TagMatchingRules.First().TagName;
            });

            if (attribute.IsParameterizedChildContentProperty())
            {
                // For child content attributes with a parameter, synthesize an attribute that allows you to name
                // the parameter.
                CreateContextParameter(builder, attribute.Name);
            }

            if (component.IsComponentFullyQualifiedNameMatch())
            {
                builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch;
            }

            var descriptor = builder.Build();

            return(descriptor);
        }
Beispiel #12
0
    public static bool IsComponent(ComponentSymbols symbols, Compilation compilation, INamedTypeSymbol type)
    {
        if (symbols is null)
        {
            throw new ArgumentNullException(nameof(symbols));
        }

        if (type is null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        var conversion = compilation.ClassifyConversion(symbols.IComponentType, type);

        if (!conversion.Exists || !conversion.IsExplicit)
        {
            return(false);
        }

        return(true);
    }
    public override void Initialize(AnalysisContext context)
    {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
        context.RegisterCompilationStartAction(context =>
        {
            if (!ComponentSymbols.TryCreate(context.Compilation, out var symbols))
            {
                // Types we need are not defined.
                return;
            }

            context.RegisterOperationBlockStartAction(startBlockContext =>
            {
                startBlockContext.RegisterOperationAction(context =>
                {
                    IOperation leftHandSide;

                    if (context.Operation is IAssignmentOperation assignmentOperation)
                    {
                        leftHandSide = assignmentOperation.Target;
                    }
                    else
                    {
                        var incrementOrDecrementOperation = (IIncrementOrDecrementOperation)context.Operation;
                        leftHandSide = incrementOrDecrementOperation.Target;
                    }

                    if (leftHandSide == null)
                    {
                        // Malformed assignment, no left hand side.
                        return;
                    }

                    if (leftHandSide.Kind != OperationKind.PropertyReference)
                    {
                        // We don't want to capture situations where a user does
                        // MyOtherProperty = aComponent.SomeParameter
                        return;
                    }

                    var propertyReference = (IPropertyReferenceOperation)leftHandSide;
                    var componentProperty = (IPropertySymbol)propertyReference.Member;

                    if (!ComponentFacts.IsParameter(symbols, componentProperty))
                    {
                        // This is not a property reference that we care about, it is not decorated with [Parameter].
                        return;
                    }

                    var propertyContainingType = componentProperty.ContainingType;
                    if (!ComponentFacts.IsComponent(symbols, context.Compilation, propertyContainingType))
                    {
                        // Someone referenced a property as [Parameter] inside something that is not a component.
                        return;
                    }

                    var assignmentContainingType = startBlockContext.OwningSymbol?.ContainingType;
                    if (assignmentContainingType == null)
                    {
                        // Assignment location has no containing type. Most likely we're operating on malformed code, don't try and validate.
                        return;
                    }

                    var conversion = context.Compilation.ClassifyConversion(propertyContainingType, assignmentContainingType);
                    if (conversion.Exists && conversion.IsIdentity)
                    {
                        // The assignment is taking place inside of the declaring component.
                        return;
                    }

                    if (conversion.Exists && conversion.IsExplicit)
                    {
                        // The assignment is taking place within the components type hierarchy. This means the user is setting this in a supported
                        // scenario.
                        return;
                    }

                    // At this point the user is referencing a component parameter outside of its declaring class.

                    context.ReportDiagnostic(Diagnostic.Create(
                                                 DiagnosticDescriptors.ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent,
                                                 propertyReference.Syntax.GetLocation(),
                                                 propertyReference.Member.Name));
                }, OperationKind.SimpleAssignment, OperationKind.CompoundAssignment, OperationKind.CoalesceAssignment, OperationKind.Increment, OperationKind.Decrement);
            });
        });
    }
Beispiel #14
0
 public ComponentTypeVisitor(ComponentSymbols symbols, List <INamedTypeSymbol> results)
 {
     _symbols = symbols;
     _results = results;
 }
Beispiel #15
0
    public override void Initialize(AnalysisContext context)
    {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
        context.RegisterCompilationStartAction(context =>
        {
            if (!ComponentSymbols.TryCreate(context.Compilation, out var symbols))
            {
                // Types we need are not defined.
                return;
            }

            // This operates per-type because one of the validations we need has to look for duplicates
            // defined on the same type.
            context.RegisterSymbolStartAction(context =>
            {
                var properties = new List <IPropertySymbol>();

                var type = (INamedTypeSymbol)context.Symbol;
                foreach (var member in type.GetMembers())
                {
                    if (member is IPropertySymbol property && ComponentFacts.IsParameter(symbols, property))
                    {
                        // Annotated with [Parameter]. We ignore [CascadingParameter]'s because they don't interact with tooling and don't currently have any analyzer restrictions.
                        properties.Add(property);
                    }
                }

                if (properties.Count == 0)
                {
                    return;
                }

                context.RegisterSymbolEndAction(context =>
                {
                    var captureUnmatchedValuesParameters = new List <IPropertySymbol>();

                    // Per-property validations
                    foreach (var property in properties)
                    {
                        var propertyLocation = property.Locations.FirstOrDefault();
                        if (propertyLocation == null)
                        {
                            continue;
                        }

                        if (property.DeclaredAccessibility != Accessibility.Public)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(
                                                         DiagnosticDescriptors.ComponentParametersShouldBePublic,
                                                         propertyLocation,
                                                         property.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)));
                        }
                        else if (property.SetMethod?.DeclaredAccessibility != Accessibility.Public)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(
                                                         DiagnosticDescriptors.ComponentParameterSettersShouldBePublic,
                                                         propertyLocation,
                                                         property.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)));
                        }

                        if (ComponentFacts.IsParameterWithCaptureUnmatchedValues(symbols, property))
                        {
                            captureUnmatchedValuesParameters.Add(property);

                            // Check the type, we need to be able to assign a Dictionary<string, object>
                            var conversion = context.Compilation.ClassifyConversion(symbols.ParameterCaptureUnmatchedValuesRuntimeType, property.Type);
                            if (!conversion.Exists || conversion.IsExplicit)
                            {
                                context.ReportDiagnostic(Diagnostic.Create(
                                                             DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesHasWrongType,
                                                             propertyLocation,
                                                             property.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
                                                             property.Type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
                                                             symbols.ParameterCaptureUnmatchedValuesRuntimeType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)));
                            }
                        }
                        if (!IsAutoProperty(property) && !IsSameSemanticAsAutoProperty(property, context.CancellationToken))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(
                                                         DiagnosticDescriptors.ComponentParametersShouldBeAutoProperties,
                                                         propertyLocation,
                                                         property.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)));
                        }
                    }

                    // Check if the type defines multiple CaptureUnmatchedValues parameters. Doing this outside the loop means we place the
                    // errors on the type.
                    if (captureUnmatchedValuesParameters.Count > 1)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(
                                                     DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesMustBeUnique,
                                                     context.Symbol.Locations[0],
                                                     type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
                                                     Environment.NewLine,
                                                     string.Join(
                                                         Environment.NewLine,
                                                         captureUnmatchedValuesParameters.Select(p => p.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)).OrderBy(n => n))));
                    }
                });
            }, SymbolKind.NamedType);
        });
    }
Beispiel #16
0
        // Does a walk up the inheritance chain to determine the set of parameters by using
        // a dictionary keyed on property name.
        //
        // We consider parameters to be defined by properties satisfying all of the following:
        // - are visible (not shadowed)
        // - have the [Parameter] attribute
        // - have a setter, even if private
        // - are not indexers
        private IEnumerable <(IPropertySymbol property, PropertyKind kind)> GetProperties(ComponentSymbols symbols, INamedTypeSymbol type)
        {
            var properties = new Dictionary <string, (IPropertySymbol, PropertyKind)>(StringComparer.Ordinal);

            do
            {
                if (type == symbols.ComponentBase)
                {
                    // The ComponentBase base class doesn't have any [Parameter].
                    // Bail out now to avoid walking through its many members, plus the members
                    // of the System.Object base class.
                    break;
                }

                var members = type.GetMembers();
                for (var i = 0; i < members.Length; i++)
                {
                    var property = members[i] as IPropertySymbol;
                    if (property == null)
                    {
                        // Not a property
                        continue;
                    }

                    if (properties.ContainsKey(property.Name))
                    {
                        // Not visible
                        continue;
                    }

                    var kind = PropertyKind.Default;
                    if (property.Parameters.Length != 0)
                    {
                        // Indexer
                        kind = PropertyKind.Ignored;
                    }

                    if (property.SetMethod == null)
                    {
                        // No setter
                        kind = PropertyKind.Ignored;
                    }

                    if (property.IsStatic)
                    {
                        kind = PropertyKind.Ignored;
                    }

                    if (!property.GetAttributes().Any(a => a.AttributeClass == symbols.ParameterAttribute))
                    {
                        // Does not have [Parameter]
                        kind = PropertyKind.Ignored;
                    }

                    if (kind == PropertyKind.Default && property.Type.TypeKind == TypeKind.Enum)
                    {
                        kind = PropertyKind.Enum;
                    }

                    if (kind == PropertyKind.Default && property.Type == symbols.RenderFragment)
                    {
                        kind = PropertyKind.ChildContent;
                    }

                    if (kind == PropertyKind.Default &&
                        property.Type is INamedTypeSymbol namedType &&
                        namedType.IsGenericType &&
                        namedType.ConstructedFrom == symbols.RenderFragmentOfT)
                    {
                        kind = PropertyKind.ChildContent;
                    }

                    if (kind == PropertyKind.Default && property.Type == symbols.EventCallback)
                    {
                        kind = PropertyKind.EventCallback;
                    }

                    if (kind == PropertyKind.Default &&
                        property.Type is INamedTypeSymbol namedType2 &&
                        namedType2.IsGenericType &&
                        namedType2.ConstructedFrom == symbols.EventCallbackOfT)
                    {
                        kind = PropertyKind.EventCallback;
                    }

                    if (kind == PropertyKind.Default && property.Type.TypeKind == TypeKind.Delegate)
                    {
                        kind = PropertyKind.Delegate;
                    }

                    properties.Add(property.Name, (property, kind));
                }

                type = type.BaseType;
            }while (type != null);

            return(properties.Values);
        }
    private TagHelperDescriptorBuilder CreateDescriptorBuilder(ComponentSymbols symbols, INamedTypeSymbol type)
    {
        var typeName     = type.ToDisplayString(FullNameTypeDisplayFormat);
        var assemblyName = type.ContainingAssembly.Identity.Name;

        var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, typeName, assemblyName);

        builder.SetTypeName(typeName);
        builder.CaseSensitive = true;

        // This opts out this 'component' tag helper for any processing that's specific to the default
        // Razor ITagHelper runtime.
        builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Component.RuntimeName;

        if (type.IsGenericType)
        {
            builder.Metadata[ComponentMetadata.Component.GenericTypedKey] = bool.TrueString;

            var cascadeGenericTypeAttributes = type
                                               .GetAttributes()
                                               .Where(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.CascadingTypeParameterAttribute))
                                               .Select(attribute => attribute.ConstructorArguments.FirstOrDefault().Value as string)
                                               .ToList();

            for (var i = 0; i < type.TypeArguments.Length; i++)
            {
                var typeParameter = type.TypeArguments[i] as ITypeParameterSymbol;
                if (typeParameter != null)
                {
                    var cascade = cascadeGenericTypeAttributes.Contains(typeParameter.Name, StringComparer.Ordinal);
                    CreateTypeParameterProperty(builder, typeParameter, cascade);
                }
            }
        }

        var xml = type.GetDocumentationCommentXml();

        if (!string.IsNullOrEmpty(xml))
        {
            builder.Documentation = xml;
        }

        foreach (var property in GetProperties(symbols, type))
        {
            if (property.kind == PropertyKind.Ignored)
            {
                continue;
            }

            CreateProperty(builder, property.property, property.kind);
        }

        if (builder.BoundAttributes.Any(a => a.IsParameterizedChildContentProperty()) &&
            !builder.BoundAttributes.Any(a => string.Equals(a.Name, ComponentMetadata.ChildContent.ParameterAttributeName, StringComparison.OrdinalIgnoreCase)))
        {
            // If we have any parameterized child content parameters, synthesize a 'Context' parameter to be
            // able to set the variable name (for all child content). If the developer defined a 'Context' parameter
            // already, then theirs wins.
            CreateContextParameter(builder, childContentName: null);
        }

        return(builder);
    }
Beispiel #18
0
        // Does a walk up the inheritance chain to determine the set of parameters by using
        // a dictionary keyed on property name.
        //
        // We consider parameters to be defined by properties satisfying all of the following:
        // - are public
        // - are visible (not shadowed)
        // - have the [Parameter] attribute
        // - have a setter, even if private
        // - are not indexers
        private IEnumerable <(IPropertySymbol property, PropertyKind kind)> GetProperties(ComponentSymbols symbols, INamedTypeSymbol type)
        {
            var properties = new Dictionary <string, (IPropertySymbol, PropertyKind)>(StringComparer.Ordinal);

            do
            {
                if (SymbolEqualityComparer.Default.Equals(type, symbols.ComponentBase))
                {
                    // The ComponentBase base class doesn't have any [Parameter].
                    // Bail out now to avoid walking through its many members, plus the members
                    // of the System.Object base class.
                    break;
                }

                var members = type.GetMembers();
                for (var i = 0; i < members.Length; i++)
                {
                    var property = members[i] as IPropertySymbol;
                    if (property == null)
                    {
                        // Not a property
                        continue;
                    }

                    if (properties.ContainsKey(property.Name))
                    {
                        // Not visible
                        continue;
                    }

                    var kind = PropertyKind.Default;
                    if (property.DeclaredAccessibility != Accessibility.Public)
                    {
                        // Not public
                        kind = PropertyKind.Ignored;
                    }

                    if (property.Parameters.Length != 0)
                    {
                        // Indexer
                        kind = PropertyKind.Ignored;
                    }

                    if (property.SetMethod == null)
                    {
                        // No setter
                        kind = PropertyKind.Ignored;
                    }
                    else if (property.SetMethod.DeclaredAccessibility != Accessibility.Public)
                    {
                        // No public setter
                        kind = PropertyKind.Ignored;
                    }

                    if (property.IsStatic)
                    {
                        kind = PropertyKind.Ignored;
                    }

                    if (!property.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.ParameterAttribute)))
                    {
                        if (property.IsOverride)
                        {
                            // This property does not contain [Parameter] attribute but it was overridden. Don't ignore it for now.
                            // We can ignore it if the base class does not contains a [Parameter] as well.
                            continue;
                        }

                        // Does not have [Parameter]
                        kind = PropertyKind.Ignored;
                    }

                    if (kind == PropertyKind.Default && property.Type.TypeKind == TypeKind.Enum)
                    {
                        kind = PropertyKind.Enum;
                    }

                    if (kind == PropertyKind.Default && SymbolEqualityComparer.Default.Equals(property.Type, symbols.RenderFragment))
                    {
                        kind = PropertyKind.ChildContent;
                    }

                    if (kind == PropertyKind.Default &&
                        property.Type is INamedTypeSymbol namedType &&
                        namedType.IsGenericType &&
                        SymbolEqualityComparer.Default.Equals(namedType.ConstructedFrom, symbols.RenderFragmentOfT))
                    {
                        kind = PropertyKind.ChildContent;
                    }

                    if (kind == PropertyKind.Default && SymbolEqualityComparer.Default.Equals(property.Type, symbols.EventCallback))
                    {
                        kind = PropertyKind.EventCallback;
                    }

                    if (kind == PropertyKind.Default &&
                        property.Type is INamedTypeSymbol namedType2 &&
                        namedType2.IsGenericType &&
                        SymbolEqualityComparer.Default.Equals(namedType2.ConstructedFrom, symbols.EventCallbackOfT))
                    {
                        kind = PropertyKind.EventCallback;
                    }

                    if (kind == PropertyKind.Default && property.Type.TypeKind == TypeKind.Delegate)
                    {
                        kind = PropertyKind.Delegate;
                    }

                    properties.Add(property.Name, (property, kind));
                }

                type = type.BaseType;
            }while (type != null);

            return(properties.Values);
        }