Skip to content

Commit f49ed7a

Browse files
committed
handle generic attributes (C#11) in the HaveCustomAttribute rule
1 parent a6a49ec commit f49ed7a

File tree

18 files changed

+347
-104
lines changed

18 files changed

+347
-104
lines changed

sources/NetArchTest/Condition.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private void AddFunctionCall(Func<FunctionSequenceExecutionContext, IEnumerable<
4949
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
5050
public ConditionList HaveCustomAttribute(Type attribute)
5151
{
52-
AddFunctionCall(x => FunctionDelegates.HaveCustomAttribute(x, attribute, true));
52+
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveCustomAttribute(context, inputTypes, attribute, true));
5353
return CreateConditionList();
5454
}
5555
/// <summary>
@@ -68,7 +68,7 @@ public ConditionList HaveCustomAttribute<T>()
6868
/// <returns>An updated set of conditions that can be applied to a list of types.</returns>
6969
public ConditionList NotHaveCustomAttribute(Type attribute)
7070
{
71-
AddFunctionCall(x => FunctionDelegates.HaveCustomAttribute(x, attribute, false));
71+
AddFunctionCall((context, inputTypes) => FunctionDelegates.HaveCustomAttribute(context, inputTypes, attribute, false));
7272
return CreateConditionList();
7373
}
7474
/// <summary>

sources/NetArchTest/Dependencies/TypeParser.cs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static IEnumerable<string> Parse(string fullName, bool parseNames)
2626

2727
var monoTypeParser = Activator.CreateInstance(mono_TypeParserType, BindingFlags.Instance | BindingFlags.NonPublic, null, args: new object[] { fullName }, null);
2828
var monoType = mono_ParseTypeMethod.Invoke(monoTypeParser, new object[] { false });
29-
foreach(var token in WalkThroughMonoType(monoType))
29+
foreach (var token in WalkThroughMonoType(monoType))
3030
{
3131
yield return token;
3232
}
@@ -83,5 +83,75 @@ private static IEnumerable<string> WalkThroughMonoType(object monoType)
8383
}
8484
}
8585
}
86+
87+
88+
89+
90+
public static string ParseReflectionNameToRuntimeName(string fullName)
91+
{
92+
var monoTypeParser = Activator.CreateInstance(mono_TypeParserType, BindingFlags.Instance | BindingFlags.NonPublic, null, args: new object[] { fullName }, null);
93+
var monoType = mono_ParseTypeMethod.Invoke(monoTypeParser, new object[] { false });
94+
return string.Concat(WalkThroughMonoType2(monoType));
95+
}
96+
97+
private static IEnumerable<string> WalkThroughMonoType2(object monoType)
98+
{
99+
yield return mono_type_fullnameField.GetValue(monoType) as string;
100+
101+
var nested = mono_nested_namesField.GetValue(monoType) as string[];
102+
if (nested != null)
103+
{
104+
foreach (var nestedName in nested)
105+
{
106+
yield return "/";
107+
yield return nestedName;
108+
}
109+
}
110+
111+
var generics = mono_generic_argumentsField.GetValue(monoType) as object[];
112+
if (generics != null)
113+
{
114+
yield return "<";
115+
for (int i = 0; i < generics.Length; i++)
116+
{
117+
object generic = generics[i];
118+
foreach (var token in WalkThroughMonoType(generic))
119+
{
120+
yield return token;
121+
}
122+
if (i < generics.Length - 1)
123+
{
124+
yield return ",";
125+
}
126+
}
127+
yield return ">";
128+
}
129+
130+
var specs = mono_specsField.GetValue(monoType) as int[];
131+
if (specs != null)
132+
{
133+
for (int i = 0; i < specs.Length; ++i)
134+
{
135+
if (specs[i] == -1)
136+
{
137+
yield return "*";
138+
}
139+
if (specs[i] == -2)
140+
{
141+
yield return "&";
142+
}
143+
if (specs[i] == -3)
144+
{
145+
yield return "[]";
146+
}
147+
if (specs[i] >= 2)
148+
{
149+
yield return "[,]";
150+
}
151+
}
152+
}
153+
}
154+
155+
86156
}
87157
}

sources/NetArchTest/Extensions/Mono.Cecil/TypeDefinitionExtensions.cs

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ namespace Mono.Cecil
77
{
88
static internal class TypeDefinitionExtensions
99
{
10-
/// <summary>
11-
/// Tests whether one class inherits from another.
12-
/// </summary>
13-
/// <param name="child">The class that is inheriting from the parent.</param>
14-
/// <param name="parent">The parent that is inherited.</param>
15-
/// <returns>An indication of whether the child inherits from the parent.</returns>
16-
public static bool IsSubclassOf(this TypeDefinition child, TypeDefinition parent)
10+
public static bool IsSubclassOf(this TypeReference child, TypeReference parent)
11+
{
12+
var typeDef = child.Resolve();
13+
return typeDef.IsSubclassOf(parent);
14+
}
15+
16+
public static bool IsSubclassOf(this TypeDefinition child, TypeReference parent)
1717
{
1818
if (parent != null)
1919
{
@@ -22,36 +22,8 @@ public static bool IsSubclassOf(this TypeDefinition child, TypeDefinition parent
2222
}
2323

2424
return false;
25-
}
26-
27-
/// <summary>
28-
/// Tests whether two type definitions are from the same assembly.
29-
/// The comparison is based on the full assembly names.
30-
/// </summary>
31-
/// <param name="a"></param>
32-
/// <param name="b"></param>
33-
/// <returns>An indication of whether the both types are from the same assembly.</returns>
34-
public static bool IsFromSameAssemblyAs(this TypeDefinition a, TypeDefinition b)
35-
{
36-
return a.Module.Assembly.ToString() == b.Module.Assembly.ToString();
37-
}
25+
}
3826

39-
/// <summary>
40-
/// Tests whether the provided types are the same type.
41-
/// </summary>
42-
/// <param name="a"></param>
43-
/// <param name="b"></param>
44-
/// <returns>An indication of whether the types are the same.</returns>
45-
public static bool IsSameTypeAs(this TypeDefinition a, TypeDefinition b)
46-
{
47-
return a.IsFromSameAssemblyAs(b) && a.MetadataToken == b.MetadataToken;
48-
}
49-
50-
/// <summary>
51-
/// Enumerate the base classes throughout the chain of inheritence.
52-
/// </summary>
53-
/// <param name="classType">The class to enumerate.</param>
54-
/// <returns>The enumeration of base classes.</returns>
5527
private static IEnumerable<TypeDefinition> EnumerateBaseClasses(this TypeDefinition classType)
5628
{
5729
for (var typeDefinition = classType; typeDefinition != null; typeDefinition = typeDefinition.BaseType?.Resolve())
@@ -60,27 +32,37 @@ private static IEnumerable<TypeDefinition> EnumerateBaseClasses(this TypeDefinit
6032
}
6133
}
6234

35+
public static bool IsAlmostEqualTo(this TypeReference child, TypeDefinition parent)
36+
{
37+
if (child is GenericInstanceType genericInstanceTypeB)
38+
{
39+
if (parent.IsSameTypeAs(genericInstanceTypeB.ElementType))
40+
{
41+
return true;
42+
}
43+
}
44+
45+
if (parent.IsSameTypeAs(child))
46+
{
47+
return true;
48+
}
49+
50+
return false;
51+
}
52+
53+
54+
6355
/// <summary>
6456
/// Convert the definition to a <see cref="Type"/> object instance.
6557
/// </summary>
6658
/// <param name="typeDefinition">The type definition to convert.</param>
6759
/// <returns>The equivalent <see cref="Type"/> object instance.</returns>
6860
public static Type ToType(this TypeDefinition typeDefinition)
6961
{
70-
var fullName = RuntimeNameToReflectionName(typeDefinition.FullName);
62+
var fullName = typeDefinition.FullName.RuntimeNameToReflectionName();
7163
return Type.GetType(string.Concat(fullName, ", ", typeDefinition.Module.Assembly.FullName), true);
7264
}
73-
public static string RuntimeNameToReflectionName(this string cliName)
74-
{
75-
// Nested types have a forward slash that should be replaced with "+"
76-
// C++ template instantiations contain comma separator for template arguments,
77-
// getting address operators and pointer type designations which should be prefixed by backslash
78-
var fullName = cliName.Replace("/", "+")
79-
.Replace(",", "\\,")
80-
.Replace("&", "\\&")
81-
.Replace("*", "\\*");
82-
return fullName;
83-
}
65+
8466

8567

8668

@@ -150,9 +132,9 @@ public static string GetNameWithoutGenericPart(this TypeDefinition typeDefinitio
150132
}
151133
return typeDefinition.Name.RemoveGenericPart();
152134
}
153-
154135

155136

137+
156138

157139

158140
public static bool IsDelegate(this TypeDefinition typeDefinition)

sources/NetArchTest/Extensions/Mono.Cecil/TypeReferenceExtensions.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,31 @@ public static string GetFullNameWithoutGenericParameters(this TypeReference type
4141

4242
return typeReference.FullName;
4343
}
44+
45+
public static bool IsSameTypeAs(this TypeReference a, TypeReference b)
46+
{
47+
bool sameAssembly = a.IsFromSameAssemblyAs(b);
48+
bool sameName = string.Equals(a.FullName, b.FullName, StringComparison.Ordinal);
49+
return sameAssembly && sameName;
50+
}
51+
private static bool IsFromSameAssemblyAs(this TypeReference a, TypeReference b)
52+
{
53+
var aName = GetAssemblyName(a.Scope);
54+
var bName = GetAssemblyName(b.Scope);
55+
56+
return aName == bName;
57+
}
58+
private static string GetAssemblyName(IMetadataScope scope)
59+
{
60+
if (scope is ModuleDefinition moduleDefinition)
61+
{
62+
return moduleDefinition.Assembly.FullName;
63+
}
64+
if (scope is AssemblyNameReference assemblyNameReference)
65+
{
66+
return assemblyNameReference.FullName;
67+
}
68+
return scope.Name;
69+
}
4470
}
4571
}

sources/NetArchTest/Extensions/System/StringExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,24 @@ public static string RemoveGenericPart(this string name)
1818
}
1919
return name;
2020
}
21+
22+
23+
public static string RuntimeNameToReflectionName(this string cliName)
24+
{
25+
// Nested types have a forward slash that should be replaced with "+"
26+
// C++ template instantiations contain comma separator for template arguments,
27+
// getting address operators and pointer type designations which should be prefixed by backslash
28+
var fullName = cliName.Replace("/", "+")
29+
.Replace(",", "\\,")
30+
.Replace("&", "\\&")
31+
.Replace("*", "\\*");
32+
return fullName;
33+
}
34+
35+
public static string ReflectionNameToRuntimeName(this string typeName)
36+
{
37+
var fullName = typeName.Replace("+", "/");
38+
return fullName;
39+
}
2140
}
2241
}
Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,41 @@
1-
using System.Linq;
2-
using System.Reflection;
1+
using System.Reflection;
32
using Mono.Cecil;
3+
using NetArchTest.Dependencies;
44

55
namespace System
6-
{
6+
{
77
static internal class TypeExtensions
88
{
9-
/// <summary>
10-
/// Converts the value to a <see cref="TypeDefinition"/> instance.
11-
/// </summary>
12-
/// <param name="type">The type to convert.</param>
13-
/// <returns>The converted value.</returns>
149
public static TypeDefinition ToTypeDefinition(this Type type)
1510
{
16-
// Get the assembly using reflection
17-
var assembly = Assembly.GetAssembly(type);
11+
var reflectionAssembly = Assembly.GetAssembly(type);
12+
var assemblyDef = AssemblyDefinition.ReadAssembly(reflectionAssembly.Location);
1813

19-
// Load the assembly into the Mono.Cecil library
20-
var assemblyDef = AssemblyDefinition.ReadAssembly(assembly.Location);
14+
foreach (var module in assemblyDef.Modules)
15+
{
16+
var typeRef = module.GetType(type.FullName, true);
17+
var typeDef = typeRef?.Resolve();
18+
if (typeDef is not null) return typeDef;
19+
}
2120

22-
// Find the matching type
23-
var dependencies = (assemblyDef.Modules
24-
.SelectMany(t => t.Types)
25-
.Where(t => t.IsClass && t.Namespace != null && t.FullName.Equals(type.FullName, StringComparison.InvariantCultureIgnoreCase)));
21+
return null;
22+
}
23+
24+
public static string GetNormalizedFullName(this Type type)
25+
{
26+
var name = type.Name;
27+
var fullName = type.FullName;
28+
var toString = type.ToString();
29+
var assemblyQualifiedName = type.AssemblyQualifiedName;
30+
31+
var monoName = TypeParser.ParseReflectionNameToRuntimeName(type.FullName);
32+
33+
if (type.IsGenericType && type.ContainsGenericParameters == false)
34+
{
35+
//return toString.ReflectionNameToRuntimeName();
36+
}
2637

27-
return dependencies.FirstOrDefault();
38+
return monoName;
2839
}
2940
}
30-
}
41+
}

0 commit comments

Comments
 (0)