4

I'm trying to write a rule for DTOs, that only allows auto-implemented properties. How do you detect that a property is auto-implemented using the FxCop API?

Roman Dvoskin
  • 384
  • 4
  • 16
  • 1. Are you *sure* you want to write custom FxCop bits in 2015? :) 2. As far as I know by the time you get to FxCop there's no such thing as an automatically implemented property any longer. – Billy ONeal Jun 08 '15 at 21:42
  • We need this as a way to enforce proper DTO coding standards. – Roman Dvoskin Jun 08 '15 at 21:57
  • What I'm saying is that once the code is compiled, there are no automatic properties any longer; the compiler turns them into real properties. FxCop works well after the compiler has done its job. – Billy ONeal Jun 08 '15 at 22:31
  • Do you think it's possible to detect if the compiled version of the property is equivalent to an auto- implemented property? Mainly we want to prevent any custom logic in the setters and getters. – Roman Dvoskin Jun 08 '15 at 22:38
  • You can examine the MSIL but of course if someone implements a property manually in the same way an auto-property does it'll pass. – Billy ONeal Jun 08 '15 at 22:52

2 Answers2

3

Actually there is a difference between auto-properties and properties implemented by user in the IL compiled code. Auto-properties setters and getters are tagged with System.Runtime.CompilerServices.CompilerGeneratedAttribute.

Auto properties compiled IL code

Hence with the tool NDepend, where you can write custom code queries and custom code rules through C# LINQ queries, matching auto-properties getters and setters is just a matter of writing the LINQ query:

from m in Application.Methods
where (m.IsPropertyGetter|| m.IsPropertySetter)
    && m.IsGeneratedByCompiler
select m

NDepend auto-properties code query matching

Notice in the screenshot above that you might match getters and setters in Resources generated classes, to prevent this you can add a clause like && !m.ParentType.IsUsing("System.Resources.ResourceManager"):

NDepend auto-properties code query matching without resources matching

Of course, all this can be adapted to FxCop custom ruling system.

Disclamer: I work for NDepend

Patrick from NDepend team
  • 13,237
  • 6
  • 61
  • 92
0

Thank you @Patrick from NDepend team. Here's the code for the resulting FxCop rule.

using Microsoft.FxCop.Sdk;
using System.Linq;
using System.Runtime.CompilerServices;

namespace CustomFxRules
{
    internal sealed class IsAutoImplementedProperty : BaseIntrospectionRule
    {
        public IsAutoImplementedProperty()
            : base("IsAutoImplementedProperty", "CustomFxRules.CustomFxRules", typeof(IsAutoImplementedProperty).Assembly)
        {
        }

        public override TargetVisibilities TargetVisibility
        {
            get { return TargetVisibilities.ExternallyVisible; }
        }

        private TypeNode CompilerGeneratedAttributeType { get; set; }

        public override void BeforeAnalysis()
        {
            base.BeforeAnalysis();

            this.CompilerGeneratedAttributeType = FrameworkAssemblies.Mscorlib.GetType(
                Identifier.For("System.Reflection"),
                Identifier.For("CompilerGeneratedAttribute"));
        }

        public override ProblemCollection Check(Member member)
        {
            if (member.NodeType != NodeType.Property)
                return base.Check(member);

            var property = (PropertyNode)member;
            VisitProperty(property);
            return this.Problems;
        }

        private const string AddAutoImplementedPropertyResolutionName = "Add auto-implemented property";

        public override void VisitProperty(PropertyNode property)
        {
            if (property.Getter == null)
            {
                Problems.Add(new Problem(new Resolution(AddAutoImplementedPropertyResolutionName, "{0} property must have a getter", property.FullName)));
            }
            else if (property.Getter.Attributes.All(attributeNode => attributeNode.Type.FullName != typeof(CompilerGeneratedAttribute).FullName))
            {
                Problems.Add(new Problem(new Resolution(AddAutoImplementedPropertyResolutionName, "{0} property must have an auto-impelemented getter", property.FullName)));
            }

            if (property.Setter == null)
            {
                Problems.Add(new Problem(new Resolution(AddAutoImplementedPropertyResolutionName, "{0} property must have a setter", property.FullName)));
            }
            else if (property.Setter.Attributes.All(attributeNode => attributeNode.Type.FullName != typeof(CompilerGeneratedAttribute).FullName))
            {
                Problems.Add(new Problem(new Resolution(AddAutoImplementedPropertyResolutionName, "{0} property must have an auto-impelemented setter", property.FullName)));
            }
        }
    }
}
Roman Dvoskin
  • 384
  • 4
  • 16