Skip to content

Commit 276380b

Browse files
committed
Attached properties will now accept a Property DP.
Closes #426
1 parent 51519a0 commit 276380b

File tree

3 files changed

+91
-32
lines changed

3 files changed

+91
-32
lines changed

mdoc/Mono.Documentation/Util/AttachedEntitiesHelper.cs

+46-18
Original file line numberDiff line numberDiff line change
@@ -114,34 +114,62 @@ private static IEnumerable<AttachedPropertyReference> GetAttachedProperties(Type
114114
if (IsAttachedProperty(field, methods))
115115
yield return new AttachedPropertyReference(field);
116116
}
117+
foreach (var prop in type.Properties)
118+
{
119+
if (IsAttachedProperty(prop, methods))
120+
yield return new AttachedPropertyReference(prop);
121+
}
117122
}
118123

119124
private static bool IsAttachedProperty(FieldDefinition field, Dictionary<string, IEnumerable<MethodDefinition>> methods)
120125
{
121-
// https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview
122-
// https://github.com/mono/api-doc-tools/issues/63#issuecomment-328995418
123-
if (!field.Name.EndsWith(PropertyConst, StringComparison.Ordinal))
126+
string fieldName = field.Name;
127+
TypeReference fieldType = field.FieldType;
128+
TypeDefinition declaringType = field?.DeclaringType;
129+
bool isPublic = field.IsPublic;
130+
bool isStatic = field.IsStatic;
131+
bool isInitOnly = field.IsInitOnly;
132+
return IsAttachedPropertyCore(methods, fieldName, fieldType, declaringType, isPublic, isStatic, isInitOnly);
133+
134+
}
135+
136+
private static bool IsAttachedProperty(PropertyDefinition prop, Dictionary<string, IEnumerable<MethodDefinition>> methods)
137+
{
138+
string fieldName = prop.Name;
139+
TypeReference fieldType = prop.PropertyType;
140+
TypeDefinition declaringType = prop?.DeclaringType;
141+
bool isPublic = prop.GetMethod.IsPublic;
142+
bool isStatic = prop.GetMethod.IsStatic;
143+
bool isInitOnly = prop.SetMethod == null || !prop.SetMethod.IsPublic;
144+
return IsAttachedPropertyCore(methods, fieldName, fieldType, declaringType, isPublic, isStatic, isInitOnly);
145+
146+
}
147+
148+
private static bool IsAttachedPropertyCore(Dictionary<string, IEnumerable<MethodDefinition>> methods, string fieldName, TypeReference fieldType, TypeDefinition declaringType, bool isPublic, bool isStatic, bool isInitOnly)
149+
{
150+
// https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview
151+
// https://github.com/mono/api-doc-tools/issues/63#issuecomment-328995418
152+
if (!fieldName.EndsWith(PropertyConst, StringComparison.Ordinal))
124153
return false;
125-
var propertyName = GetPropertyName(field.Name);
154+
var propertyName = GetPropertyName(fieldName);
126155
var getMethodName = $"Get{propertyName}";
127156
var setMethodName = $"Set{propertyName}";
128157

129-
var hasExistingProperty = field?.DeclaringType?.Properties.Any (p => p.Name.Equals (propertyName, System.StringComparison.Ordinal));
130-
var hasExistingField = field?.DeclaringType?.Fields.Any (f => f.Name.Equals (propertyName, System.StringComparison.Ordinal));
158+
var hasExistingProperty = declaringType?.Properties.Any(p => p.Name.Equals(propertyName, System.StringComparison.Ordinal));
159+
var hasExistingField = declaringType?.Fields.Any(f => f.Name.Equals(propertyName, System.StringComparison.Ordinal));
131160

132-
return !hasExistingProperty.IsTrue () && !hasExistingField.IsTrue () &&
133-
// Class X has a static field of type DependencyProperty [Name]Property
134-
(field.FieldType.FullName == Consts.DependencyPropertyFullName || field.FieldType.FullName == Consts.DependencyPropertyFullNameXaml)
135-
&& field.IsPublic
136-
&& field.IsStatic
137-
&& field.IsInitOnly
138-
139-
// Class X also has static methods with the following names: Get[Name] and Set[Name]
161+
return !hasExistingProperty.IsTrue() && !hasExistingField.IsTrue() &&
162+
// Class X has a static field of type DependencyProperty [Name]Property
163+
(fieldType.FullName == Consts.DependencyPropertyFullName || fieldType.FullName == Consts.DependencyPropertyFullNameXaml)
164+
&& isPublic
165+
&& isStatic
166+
&& isInitOnly
167+
168+
// Class X also has static methods with the following names: Get[Name] and Set[Name]
140169
&& ((methods.ContainsKey(getMethodName) && methods[getMethodName].Any(IsAttachedPropertyGetMethod))
141-
|| (methods.ContainsKey(setMethodName) && methods[setMethodName].Any(IsAttachedPropertySetMethod)));
142-
143-
}
144-
170+
|| (methods.ContainsKey(setMethodName) && methods[setMethodName].Any(IsAttachedPropertySetMethod)));
171+
}
172+
145173
private static bool IsAttachedPropertyGetMethod(MethodDefinition method)
146174
{
147175
return method.Parameters.Count == 1

mdoc/Mono.Documentation/Util/AttachedPropertyDefinition.cs

+31-12
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,65 @@ namespace Mono.Documentation.Util
55
{
66
public class AttachedPropertyDefinition : AttachedPropertyReference, IMemberDefinition
77
{
8-
private readonly FieldDefinition fieldDefinition;
8+
string defName;
9+
Collection<CustomAttribute> defAttributes;
10+
bool defHasAttributes, defIsSpecialName, defIsRuntimeSpecialName;
11+
TypeDefinition defDeclaringType;
12+
913

1014
public AttachedPropertyDefinition(FieldDefinition fieldDefinition, MetadataToken metadataToken) : base(fieldDefinition)
1115
{
12-
this.fieldDefinition = fieldDefinition;
1316
MetadataToken = metadataToken;
17+
defName = fieldDefinition.Name;
18+
defAttributes = fieldDefinition.CustomAttributes;
19+
defHasAttributes = fieldDefinition.HasCustomAttributes;
20+
defIsSpecialName = fieldDefinition.IsSpecialName;
21+
defIsRuntimeSpecialName = fieldDefinition.IsRuntimeSpecialName;
22+
defDeclaringType = fieldDefinition.DeclaringType;
23+
}
24+
public AttachedPropertyDefinition(PropertyDefinition propDefinition, MetadataToken metadataToken) : base(propDefinition)
25+
{
26+
MetadataToken = metadataToken;
27+
defName = propDefinition.Name;
28+
defAttributes = propDefinition.CustomAttributes;
29+
defHasAttributes = propDefinition.HasCustomAttributes;
30+
defIsSpecialName = propDefinition.IsSpecialName;
31+
defIsRuntimeSpecialName = propDefinition.IsRuntimeSpecialName;
32+
defDeclaringType = propDefinition.DeclaringType;
1433
}
1534

1635
public MemberReference GetMethod
1736
{
1837
get => this.DeclaringType.GetMember(
19-
$"Get{AttachedEntitiesHelper.GetPropertyName(fieldDefinition.Name)}",
38+
$"Get{AttachedEntitiesHelper.GetPropertyName(defName)}",
2039
m => (m as MethodReference)?.Parameters.Count == 1);
2140
}
2241
public MemberReference SetMethod
2342
{
2443
get => this.DeclaringType.GetMember(
25-
$"Set{AttachedEntitiesHelper.GetPropertyName(fieldDefinition.Name)}",
44+
$"Set{AttachedEntitiesHelper.GetPropertyName(defName)}",
2645
m => (m as MethodReference)?.Parameters.Count == 2);
2746
}
2847

29-
public Collection<CustomAttribute> CustomAttributes => fieldDefinition.CustomAttributes;
30-
public bool HasCustomAttributes => fieldDefinition.HasCustomAttributes;
48+
public Collection<CustomAttribute> CustomAttributes => defAttributes;
49+
public bool HasCustomAttributes => defHasAttributes;
3150

3251
public bool IsSpecialName
3352
{
34-
get { return fieldDefinition.IsSpecialName; }
35-
set { fieldDefinition.IsSpecialName = value; }
53+
get { return defIsSpecialName; }
54+
set { defIsSpecialName = value; }
3655
}
3756

3857
public bool IsRuntimeSpecialName
3958
{
40-
get { return fieldDefinition.IsRuntimeSpecialName; }
41-
set { fieldDefinition.IsRuntimeSpecialName = value; }
59+
get { return defIsRuntimeSpecialName; }
60+
set { defIsRuntimeSpecialName = value; }
4261
}
4362

4463
public new TypeDefinition DeclaringType
4564
{
46-
get { return fieldDefinition.DeclaringType; }
47-
set { fieldDefinition.DeclaringType = value; }
65+
get { return defDeclaringType; }
66+
set { defDeclaringType = value; }
4867
}
4968
}
5069
}

mdoc/Mono.Documentation/Util/AttachedPropertyReference.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,29 @@ namespace Mono.Documentation.Util
55
public class AttachedPropertyReference : FieldReference
66
{
77
private readonly FieldDefinition fieldDefinition;
8+
private readonly PropertyDefinition propDefinition;
89
private AttachedPropertyDefinition definition;
910

1011
public AttachedPropertyReference(FieldDefinition fieldDefinition) : base(AttachedEntitiesHelper.GetPropertyName(fieldDefinition.Name), fieldDefinition.FieldType, fieldDefinition.DeclaringType)
1112
{
1213
this.fieldDefinition = fieldDefinition;
1314
}
15+
public AttachedPropertyReference(PropertyDefinition propDefinition) : base(AttachedEntitiesHelper.GetPropertyName(propDefinition.Name), propDefinition.PropertyType, propDefinition.DeclaringType)
16+
{
17+
this.propDefinition = propDefinition;
18+
}
1419

1520
protected override IMemberDefinition ResolveDefinition()
1621
{
17-
return definition ??
18-
(definition = new AttachedPropertyDefinition(fieldDefinition, MetadataToken));
22+
if (definition == null)
23+
{
24+
if (fieldDefinition != null)
25+
definition = new AttachedPropertyDefinition(fieldDefinition, MetadataToken);
26+
27+
if (propDefinition != null)
28+
definition = new AttachedPropertyDefinition(propDefinition, MetadataToken);
29+
}
30+
return definition;
1931
}
2032
}
2133
}

0 commit comments

Comments
 (0)