Browse Source

Added support for Cyclomatic Complexity metric and for the number of variables inside of a method.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@6435 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Tomáš Linhart 15 years ago
parent
commit
b7dd8b62f3
  1. 23
      src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml.cs
  2. 15
      src/AddIns/Analysis/CodeQuality/Src/Method.cs
  3. 65
      src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs

23
src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml.cs

@ -204,6 +204,8 @@ namespace ICSharpCode.CodeQualityAnalysis
} else if (content == "Method") { } else if (content == "Method") {
cbxMetrics.Items.Add(new ComboBoxItem { Content = "IL instructions" }); cbxMetrics.Items.Add(new ComboBoxItem { Content = "IL instructions" });
cbxMetrics.Items.Add(new ComboBoxItem { Content = "Cyclomatic Complexity" });
cbxMetrics.Items.Add(new ComboBoxItem { Content = "Variables" });
} }
} }
@ -219,6 +221,8 @@ namespace ICSharpCode.CodeQualityAnalysis
var metric = metricItem.Content.ToString(); var metric = metricItem.Content.ToString();
// TODO: Redone this with enums or some smarter way
if (level == "Assembly") { if (level == "Assembly") {
//cbxMetrics.Items.Add(new ComboBoxItem { Content = }); //cbxMetrics.Items.Add(new ComboBoxItem { Content = });
} else if (level == "Namespace") { } else if (level == "Namespace") {
@ -228,12 +232,19 @@ namespace ICSharpCode.CodeQualityAnalysis
} else if (level == "Field") { } else if (level == "Field") {
} else if (level == "Method") { } else if (level == "Method") {
if (metric == "IL instructions") { treemap.ItemsSource = from ns in MetricsReader.MainModule.Namespaces
treemap.ItemsSource = from ns in MetricsReader.MainModule.Namespaces from type in ns.Types
from type in ns.Types from method in type.Methods
from method in type.Methods select method;
select method;
} if (metric == "IL instructions")
treemap.ItemDefinition.ValuePath = "Instructions.Count";
if (metric == "Cyclomatic Complexity")
treemap.ItemDefinition.ValuePath = "CyclomaticComplexity";
if (metric == "Variables")
treemap.ItemDefinition.ValuePath = "Variables";
} }
} }
} }

15
src/AddIns/Analysis/CodeQuality/Src/Method.cs

@ -111,6 +111,16 @@ namespace ICSharpCode.CodeQualityAnalysis
/// </summary> /// </summary>
public bool IsReturnTypeGenericInstance { get; set; } public bool IsReturnTypeGenericInstance { get; set; }
/// <summary>
/// Cyclomatic Complexity of the method
/// </summary>
public int CyclomaticComplexity { get; set; }
/// <summary>
/// The number of variables declared in the body of the method
/// </summary>
public int Variables { get; set; }
public Method() public Method()
{ {
Parameters = new HashSet<MethodParameter>(); Parameters = new HashSet<MethodParameter>();
@ -127,6 +137,9 @@ namespace ICSharpCode.CodeQualityAnalysis
IsReturnTypeGenericInstance = false; IsReturnTypeGenericInstance = false;
Dependency = null; Dependency = null;
CyclomaticComplexity = 0;
Variables = 0;
} }
public Relationship GetRelationship(INode node) public Relationship GetRelationship(INode node)
@ -149,6 +162,8 @@ namespace ICSharpCode.CodeQualityAnalysis
builder.Append(Environment.NewLine); builder.Append(Environment.NewLine);
builder.AppendLine(String.Format("Name: {0}", Name)); builder.AppendLine(String.Format("Name: {0}", Name));
builder.AppendLine(String.Format("Parameters: {0}", Parameters.Count)); builder.AppendLine(String.Format("Parameters: {0}", Parameters.Count));
builder.AppendLine(String.Format("Cyclomatic Complexity: {0}", CyclomaticComplexity));
builder.AppendLine(String.Format("Variables: {0}", Variables));
// more to come // more to come
builder.Append(Environment.NewLine); builder.Append(Environment.NewLine);

65
src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs

@ -131,19 +131,19 @@ namespace ICSharpCode.CodeQualityAnalysis
IsStruct = typeDefinition.IsValueType && !typeDefinition.IsEnum && typeDefinition.IsSealed, IsStruct = typeDefinition.IsValueType && !typeDefinition.IsEnum && typeDefinition.IsSealed,
IsInternal = typeDefinition.IsNotPublic, IsInternal = typeDefinition.IsNotPublic,
IsDelegate = (typeDefinition.BaseType != null ? IsDelegate = (typeDefinition.BaseType != null ?
typeDefinition.BaseType.FullName == "System.MulticastDelegate" : false), typeDefinition.BaseType.FullName == "System.MulticastDelegate" : false),
IsNestedPrivate = typeDefinition.IsNestedPrivate, IsNestedPrivate = typeDefinition.IsNestedPrivate,
IsNestedPublic = typeDefinition.IsNestedPublic, IsNestedPublic = typeDefinition.IsNestedPublic,
IsNestedProtected = (!typeDefinition.IsNestedPrivate && !typeDefinition.IsNestedPublic && IsNestedProtected = (!typeDefinition.IsNestedPrivate && !typeDefinition.IsNestedPublic &&
typeDefinition.IsNestedFamily) typeDefinition.IsNestedFamily)
}; };
// try find namespace // try find namespace
var nsName = GetNamespaceName(typeDefinition); var nsName = GetNamespaceName(typeDefinition);
var ns = (from n in module.Namespaces var ns = (from n in module.Namespaces
where n.Name == nsName where n.Name == nsName
select n).SingleOrDefault(); select n).SingleOrDefault();
if (ns == null) if (ns == null)
{ {
@ -182,9 +182,9 @@ namespace ICSharpCode.CodeQualityAnalysis
if (typeDefinition.BaseType != null) if (typeDefinition.BaseType != null)
{ {
var baseType = (from n in module.Namespaces var baseType = (from n in module.Namespaces
from t in n.Types from t in n.Types
where (t.FullName == FormatTypeName(typeDefinition.BaseType, true)) where (t.FullName == FormatTypeName(typeDefinition.BaseType, true))
select t).SingleOrDefault(); select t).SingleOrDefault();
type.BaseType = baseType; // if baseType is null so propably inherits from another assembly type.BaseType = baseType; // if baseType is null so propably inherits from another assembly
@ -192,7 +192,7 @@ namespace ICSharpCode.CodeQualityAnalysis
{ {
type.IsBaseTypeGenericInstance = true; type.IsBaseTypeGenericInstance = true;
type.GenericBaseTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, type.GenericBaseTypes.UnionWith(ReadGenericArguments(type.Namespace.Module,
(GenericInstanceType)typeDefinition.BaseType)); (GenericInstanceType)typeDefinition.BaseType));
} }
} }
@ -202,9 +202,9 @@ namespace ICSharpCode.CodeQualityAnalysis
foreach (var ic in typeDefinition.Interfaces) foreach (var ic in typeDefinition.Interfaces)
{ {
var implementedIc = (from n in module.Namespaces var implementedIc = (from n in module.Namespaces
from t in n.Types from t in n.Types
where (t.FullName == FormatTypeName(ic, true)) where (t.FullName == FormatTypeName(ic, true))
select t).SingleOrDefault(); select t).SingleOrDefault();
if (implementedIc != null) if (implementedIc != null)
type.ImplementedInterfaces.Add(implementedIc); type.ImplementedInterfaces.Add(implementedIc);
@ -212,7 +212,7 @@ namespace ICSharpCode.CodeQualityAnalysis
if (ic.IsGenericInstance) if (ic.IsGenericInstance)
{ {
type.GenericBaseTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, type.GenericBaseTypes.UnionWith(ReadGenericArguments(type.Namespace.Module,
(GenericInstanceType)ic)); (GenericInstanceType)ic));
} }
} }
} }
@ -307,7 +307,7 @@ namespace ICSharpCode.CodeQualityAnalysis
{ {
field.IsGenericInstance = true; field.IsGenericInstance = true;
field.GenericTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, field.GenericTypes.UnionWith(ReadGenericArguments(type.Namespace.Module,
(GenericInstanceType)fieldDefinition.FieldType)); (GenericInstanceType)fieldDefinition.FieldType));
} }
field.FieldType = fieldType; field.FieldType = fieldType;
@ -336,7 +336,8 @@ namespace ICSharpCode.CodeQualityAnalysis
IsAbstract = methodDefinition.IsAbstract, IsAbstract = methodDefinition.IsAbstract,
IsSetter = methodDefinition.IsSetter, IsSetter = methodDefinition.IsSetter,
IsGetter = methodDefinition.IsGetter, IsGetter = methodDefinition.IsGetter,
IsVirtual = methodDefinition.IsVirtual IsVirtual = methodDefinition.IsVirtual,
Variables = methodDefinition.Body != null ? methodDefinition.Body.Variables.Count : 0
}; };
var returnType = var returnType =
@ -351,7 +352,7 @@ namespace ICSharpCode.CodeQualityAnalysis
{ {
method.IsReturnTypeGenericInstance = true; method.IsReturnTypeGenericInstance = true;
method.GenericReturnTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, method.GenericReturnTypes.UnionWith(ReadGenericArguments(type.Namespace.Module,
(GenericInstanceType) methodDefinition.ReturnType)); (GenericInstanceType) methodDefinition.ReturnType));
} }
// reading types from parameters // reading types from parameters
@ -378,7 +379,7 @@ namespace ICSharpCode.CodeQualityAnalysis
{ {
param.IsGenericInstance = true; param.IsGenericInstance = true;
param.GenericTypes = ReadGenericArguments(type.Namespace.Module, param.GenericTypes = ReadGenericArguments(type.Namespace.Module,
(GenericInstanceType) parameter.ParameterType); (GenericInstanceType) parameter.ParameterType);
} }
method.Parameters.Add(param); method.Parameters.Add(param);
@ -391,8 +392,8 @@ namespace ICSharpCode.CodeQualityAnalysis
foreach (MethodDefinition methodDefinition in methods) foreach (MethodDefinition methodDefinition in methods)
{ {
var method = (from m in type.Methods var method = (from m in type.Methods
where m.Name == FormatMethodName(methodDefinition) where m.Name == FormatMethodName(methodDefinition)
select m).SingleOrDefault(); select m).SingleOrDefault();
if (methodDefinition.Body != null) if (methodDefinition.Body != null)
{ {
@ -408,15 +409,19 @@ namespace ICSharpCode.CodeQualityAnalysis
/// <param name="methodDefinition">A method definition with instructions</param> /// <param name="methodDefinition">A method definition with instructions</param>
/// <param name="instructions">A collection of instructions</param> /// <param name="instructions">A collection of instructions</param>
public void ReadInstructions(Method method, MethodDefinition methodDefinition, public void ReadInstructions(Method method, MethodDefinition methodDefinition,
Collection<Mono.Cecil.Cil.Instruction> instructions) Collection<Mono.Cecil.Cil.Instruction> instructions)
{ {
foreach (var instruction in instructions) foreach (var instruction in instructions)
{ {
method.Instructions.Add(new Instruction method.Instructions.Add(new Instruction
{ {
DeclaringMethod = method, DeclaringMethod = method,
// Operand = instruction.Operand.ToString() // for now operand as string should be enough // Operand = instruction.Operand.ToString() // for now operand as string should be enough
}); });
// IL cyclomatic complexity
if (instruction.OpCode.FlowControl == FlowControl.Cond_Branch)
method.CyclomaticComplexity++;
var operand = ReadOperand(instruction); var operand = ReadOperand(instruction);
@ -424,15 +429,15 @@ namespace ICSharpCode.CodeQualityAnalysis
{ {
var md = operand as MethodDefinition; var md = operand as MethodDefinition;
var type = (from n in method.DeclaringType.Namespace.Module.Namespaces var type = (from n in method.DeclaringType.Namespace.Module.Namespaces
from t in n.Types from t in n.Types
where t.FullName == FormatTypeName(md.DeclaringType, true) where t.FullName == FormatTypeName(md.DeclaringType, true)
select t).SingleOrDefault(); select t).SingleOrDefault();
method.TypeUses.Add(type); method.TypeUses.Add(type);
var findTargetMethod = (from m in type.Methods var findTargetMethod = (from m in type.Methods
where m.Name == FormatMethodName(md) where m.Name == FormatMethodName(md)
select m).SingleOrDefault(); select m).SingleOrDefault();
if (findTargetMethod != null && type == method.DeclaringType) if (findTargetMethod != null && type == method.DeclaringType)
method.MethodUses.Add(findTargetMethod); method.MethodUses.Add(findTargetMethod);
@ -442,8 +447,8 @@ namespace ICSharpCode.CodeQualityAnalysis
{ {
var fd = operand as FieldDefinition; var fd = operand as FieldDefinition;
var field = (from f in method.DeclaringType.Fields var field = (from f in method.DeclaringType.Fields
where f.Name == fd.Name where f.Name == fd.Name
select f).SingleOrDefault(); select f).SingleOrDefault();
if (field != null) if (field != null)
method.FieldUses.Add(field); method.FieldUses.Add(field);

Loading…
Cancel
Save