Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.TypeSpec.Generator.Expressions;
using Microsoft.TypeSpec.Generator.Input.Extensions;
using Microsoft.TypeSpec.Generator.Primitives;
Expand Down Expand Up @@ -147,8 +148,9 @@ public void Update(
[MemberNotNull(nameof(_parameter))]
private void InitializeParameter()
{
var paramAttributes = Attributes.Where(a => a.Type.Namespace != CodeModelGenerator.CustomizationAttributeNamespace).ToArray();
_parameter = new(() => new ParameterProvider(
Name.ToVariableName(), Description ?? FormattableStringHelpers.Empty, Type, field: this, wireInfo: WireInfo, attributes: Attributes));
Name.ToVariableName(), Description ?? FormattableStringHelpers.Empty, Type, field: this, wireInfo: WireInfo, attributes: paramAttributes));
}

private MemberExpression? _asMember;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ protected internal override FieldProvider[] BuildFields()
fieldSymbol.Name,
this,
GetSymbolXmlDoc(fieldSymbol, "summary"),
initializationValue: GetFieldInitializer(fieldSymbol))
initializationValue: GetFieldInitializer(fieldSymbol),
attributes: fieldSymbol.GetAttributes().Select(a => new AttributeStatement(a)).ToArray())
{
OriginalName = GetOriginalName(fieldSymbol)
};
Expand All @@ -146,7 +147,8 @@ protected internal override PropertyProvider[] BuildProperties()
new AutoPropertyBody(
propertySymbol.SetMethod is not null,
InitializationExpression: GetPropertyInitializer(propertySymbol)),
this)
this,
attributes: propertySymbol.GetAttributes().Select(a => new AttributeStatement(a)).ToArray())
{
OriginalName = GetOriginalName(propertySymbol),
CustomProvider = new(() => propertySymbol.Type is INamedTypeSymbol propertyNamedTypeSymbol
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -1698,6 +1699,50 @@ public async Task CanCustomizeBaseModelToSystemType()
"System.Exception is from a referenced assembly and should use SystemObjectTypeProvider");
}

[Test]
public async Task CanReadPropertyAttributes()
{
await MockHelpers.LoadMockGeneratorAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());

var props = new[]
{
InputFactory.Property("Prop1", InputPrimitiveType.String)
};

var inputModel = InputFactory.Model("mockInputModel", properties: props);
var modelTypeProvider = new ModelProvider(inputModel);
var customCodeView = modelTypeProvider.CustomCodeView;

Assert.IsNotNull(customCodeView);
Assert.AreEqual(1, customCodeView!.Properties.Count);

var customProperty = customCodeView.Properties[0];
Assert.AreEqual("Prop1", customProperty.Name);

// Verify that attributes from custom code are populated, including a custom non-system attribute
Assert.AreEqual(3, customProperty.Attributes.Count);

// Validate [Obsolete("This property is now deprecated.", DiagnosticId = "OBS001")] - type, arguments, and positional arguments
var obsoleteAttr = customProperty.Attributes.Single(a => new CSharpType(typeof(ObsoleteAttribute)).Equals(a.Type));
Assert.AreEqual(1, obsoleteAttr.Arguments.Count);
Assert.AreEqual(1, obsoleteAttr.PositionalArguments.Count);
Assert.AreEqual("DiagnosticId", obsoleteAttr.PositionalArguments[0].Key);

// Validate [EditorBrowsable(EditorBrowsableState.Never)] - type and arguments
var editorBrowsableAttr = customProperty.Attributes.Single(a => new CSharpType(typeof(System.ComponentModel.EditorBrowsableAttribute)).Equals(a.Type));
Assert.AreEqual(1, editorBrowsableAttr.Arguments.Count);

// Validate [Custom("custom message")] - custom non-system attribute does not throw
var customAttr = customProperty.Attributes.Single(a => a.Type.Name == "CustomAttribute");
Assert.AreEqual(1, customAttr.Arguments.Count);

// Verify that field attributes from custom code are populated
var customField = customCodeView.Fields.Single(f => f.Name == "_customField");
Assert.AreEqual(1, customField.Attributes.Count);
var fieldObsoleteAttr = customField.Attributes.Single(a => new CSharpType(typeof(ObsoleteAttribute)).Equals(a.Type));
Assert.AreEqual(1, fieldObsoleteAttr.Arguments.Count);
}

private class TestNameVisitor : NameVisitor
{
public TypeProvider? InvokeVisit(TypeProvider type)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#nullable disable

using System;
using System.ComponentModel;

namespace Sample.Models
{
public class CustomAttribute : Attribute
{
public CustomAttribute(string message) { }
}

public partial class MockInputModel
{
[Obsolete("This property is now deprecated.", DiagnosticId = "OBS001")]
[EditorBrowsable(EditorBrowsableState.Never)]
[Custom("custom message")]
public string Prop1 { get; set; }

[Obsolete("This field is now deprecated.")]
private int _customField;
}
}
Loading