diff --git a/src/AddIns/Analysis/CodeQuality/Src/Field.cs b/src/AddIns/Analysis/CodeQuality/Src/Field.cs index d07e93b5a4..e0a8b40035 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/Field.cs +++ b/src/AddIns/Analysis/CodeQuality/Src/Field.cs @@ -58,6 +58,16 @@ namespace ICSharpCode.CodeQualityAnalysis /// public bool IsReadOnly { get; set; } + /// + /// Whether the type of field is generic + /// + public bool IsGenericInstance { get; set; } + + /// + /// If the field has generic instance so all types used in generic are presented in this set. + /// + public ISet GenericTypes { get; set; } + public Field() { FieldType = null; diff --git a/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml b/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml index 7c4766722d..93f176f8c6 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml +++ b/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml @@ -35,7 +35,7 @@ - + + /// Parameters which are used by method + /// + public ISet Parameters { get; set; } + /// /// Types which are used in body of method /// @@ -88,14 +93,29 @@ namespace ICSharpCode.CodeQualityAnalysis /// public bool IsVirtual { get; set; } + /// + /// If the return type is generic instance so all types used in generic are presented in this set. + /// + public ISet GenericReturnTypes { get; set; } + + /// + /// Whether the return type is generic instance + /// + public bool IsReturnTypeGenericInstance { get; set; } + public Method() { + Parameters = new HashSet(); + TypeUses = new HashSet(); MethodUses = new HashSet(); FieldUses = new HashSet(); + GenericReturnTypes = new HashSet(); ReturnType = null; Owner = null; + + IsReturnTypeGenericInstance = false; } public BidirectionalGraph> BuildDependencyGraph() @@ -108,4 +128,43 @@ namespace ICSharpCode.CodeQualityAnalysis return Name; } } + + public class MethodParameter + { + /// + /// The type of the parameter + /// + public Type ParameterType { get; set; } + + /// + /// Whether the parameter is generic instance + /// + public bool IsGenericInstance { get; set; } + + /// + /// Whether the parameter is in + /// + public bool IsIn { get; set; } + + /// + /// Whether the parameter is out + /// + public bool IsOut { get; set; } + + /// + /// Whether the parameter is optional + /// + public bool IsOptional { get; set; } + + /// + /// If the parameter is generic instance so all types used in generic are presented in this set. + /// + public ISet GenericTypes { get; set; } + + public MethodParameter() + { + GenericTypes = new HashSet(); + IsGenericInstance = false; + } + } } diff --git a/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs b/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs index 8fec682623..1077e23b7a 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs +++ b/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; using Mono.Cecil; @@ -175,6 +176,13 @@ namespace ICSharpCode.CodeQualityAnalysis select t).SingleOrDefault(); type.BaseType = baseType; // if baseType is null so propably inherits from another assembly + + if (typeDefinition.BaseType.IsGenericInstance) + { + type.IsBaseTypeGenericInstance = true; + type.GenericBaseTypes = ReadGenericArguments(type.Namespace.Module, + (GenericInstanceType)typeDefinition.BaseType); + } } // looks for implemented interfaces @@ -189,6 +197,12 @@ namespace ICSharpCode.CodeQualityAnalysis if (implementedIc != null) type.ImplementedInterfaces.Add(implementedIc); + + if (ic.IsGenericInstance) + { + type.GenericBaseTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, + (GenericInstanceType)typeDefinition.BaseType)); + } } } @@ -227,11 +241,13 @@ namespace ICSharpCode.CodeQualityAnalysis var declaringType = (from n in type.Namespace.Module.Namespaces from t in n.Types - where t.Name == e.Name + where t.Name == FormatTypeName(eventDefinition.EventType) select t).SingleOrDefault(); e.EventType = declaringType; + // TODO:check eventDefinition.OtherMethods + // Mono.Cecil threats Events as regular fields // so I have to find a field and set IsEvent to true @@ -270,13 +286,20 @@ namespace ICSharpCode.CodeQualityAnalysis type.Fields.Add(field); - var declaringType = + var fieldType = (from n in type.Namespace.Module.Namespaces from t in n.Types - where t.Name == FormatTypeName(fieldDefinition.DeclaringType) + where t.Name == FormatTypeName(fieldDefinition.FieldType) select t).SingleOrDefault(); - field.FieldType = declaringType; + if (fieldDefinition.FieldType.IsGenericInstance) + { + field.IsGenericInstance = true; + field.GenericTypes = ReadGenericArguments(type.Namespace.Module, + (GenericInstanceType)fieldDefinition.FieldType); + } + + field.FieldType = fieldType; } } @@ -313,6 +336,44 @@ namespace ICSharpCode.CodeQualityAnalysis method.ReturnType = returnType; // if null so return type is outside of assembly + if (methodDefinition.ReturnType.IsGenericInstance) + { + method.IsReturnTypeGenericInstance = true; + method.GenericReturnTypes = ReadGenericArguments(type.Namespace.Module, + (GenericInstanceType) methodDefinition.ReturnType); + } + + // reading types from parameters + foreach (var parameter in methodDefinition.Parameters) + { + var parameterType = + (from n in type.Namespace.Module.Namespaces + from t in n.Types + where t.Name == FormatTypeName(parameter.ParameterType) + select t).SingleOrDefault(); + + if (parameterType != null) + { + var param = new MethodParameter + { + ParameterType = parameterType, + IsIn = parameter.IsIn, + IsOut = parameter.IsOut, + IsOptional = parameter.IsOptional, + }; + + // generic parameters + if (parameter.ParameterType.IsGenericInstance) + { + param.IsGenericInstance = true; + param.GenericTypes = ReadGenericArguments(type.Namespace.Module, + (GenericInstanceType) parameter.ParameterType); + } + + method.Parameters.Add(param); + } + } + type.Methods.Add(method); } @@ -357,7 +418,7 @@ namespace ICSharpCode.CodeQualityAnalysis where m.Name == FormatMethodName(md) select m).SingleOrDefault(); - if (findTargetMethod != null && type == method.ReturnType) + if (findTargetMethod != null && type == method.Owner) method.MethodUses.Add(findTargetMethod); } @@ -374,6 +435,34 @@ namespace ICSharpCode.CodeQualityAnalysis } } + /// + /// Reads generic arguments from type and returns them as a set of types + /// + /// The module where are types located + /// The instance type + /// A set of types used by generic instance + public ISet ReadGenericArguments(Module module, GenericInstanceType genericInstance) + { + var types = new HashSet(); + + foreach (var parameter in genericInstance.GenericArguments) + { + var type = + (from n in module.Namespaces + from t in n.Types + where t.Name == FormatTypeName(parameter) + select t).SingleOrDefault(); + + if (type != null) // + types.Add(type); + + if (parameter.IsGenericInstance) + types.UnionWith(ReadGenericArguments(module, (GenericInstanceType) parameter)); + } + + return types; + } + /// /// Reads instruction operand by recursive calling until non-instruction /// operand is found @@ -427,6 +516,8 @@ namespace ICSharpCode.CodeQualityAnalysis /// A type name public string FormatTypeName(TypeReference type) { + type = type.GetElementType(); + if (type.IsNested && type.DeclaringType != null) { return FormatTypeName(type.DeclaringType) + "+" + type.Name; @@ -439,7 +530,7 @@ namespace ICSharpCode.CodeQualityAnalysis bool hasNext = enumerator.MoveNext(); while (hasNext) { - builder.Append((enumerator.Current).Name); + builder.Append(enumerator.Current.Name); hasNext = enumerator.MoveNext(); if (hasNext) builder.Append(","); diff --git a/src/AddIns/Analysis/CodeQuality/Src/Type.cs b/src/AddIns/Analysis/CodeQuality/Src/Type.cs index 55c11abbad..1eb12fd1ae 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/Type.cs +++ b/src/AddIns/Analysis/CodeQuality/Src/Type.cs @@ -124,6 +124,20 @@ namespace ICSharpCode.CodeQualityAnalysis /// public bool IsNestedProtected { get; set; } + /// + /// Whether the base type is generic instance + /// + public bool IsBaseTypeGenericInstance { get; set; } + + /// + /// If the base type is generic instance so all types used in generic are presented in this set. + /// + public ISet GenericBaseTypes { get; set; } + + /// + /// If one of implemented interfaces is generic instance so all types used in generic are presented in this set. + /// + public ISet GenericImplementedInterfacesTypes { get; set; } public Type() { @@ -132,9 +146,13 @@ namespace ICSharpCode.CodeQualityAnalysis Events = new HashSet(); NestedTypes = new HashSet(); ImplementedInterfaces = new HashSet(); + GenericBaseTypes = new HashSet(); + GenericImplementedInterfacesTypes = new HashSet(); Owner = null; BaseType = null; + + IsBaseTypeGenericInstance = false; } /// @@ -148,14 +166,41 @@ namespace ICSharpCode.CodeQualityAnalysis foreach (var method in Methods) { set.UnionWith(method.TypeUses); + + foreach (var parameter in method.Parameters) + { + if (parameter.IsGenericInstance) + set.UnionWith(parameter.GenericTypes); + + if (parameter.ParameterType != null) + set.Add(parameter.ParameterType); + } + + if (method.IsReturnTypeGenericInstance) + set.UnionWith(method.GenericReturnTypes); + + if (method.ReturnType != null) + set.Add(method.ReturnType); } foreach (var field in Fields) { + if (field.IsGenericInstance) + set.UnionWith(field.GenericTypes); + if (field.FieldType != null) // if it is null so it is type from outside of this assembly set.Add(field.FieldType); // TODO: find solution to handle them } + if (BaseType != null) + set.Add(BaseType); + + if (IsBaseTypeGenericInstance) + set.UnionWith(GenericBaseTypes); + + set.UnionWith(ImplementedInterfaces); + set.UnionWith(GenericImplementedInterfacesTypes); + return set; }