From b7dd8b62f3c58a36bc5332da6037aa7b1a4da69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Linhart?= Date: Sat, 21 Aug 2010 23:47:19 +0000 Subject: [PATCH] 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 --- .../CodeQuality/Src/MainWindow.xaml.cs | 23 +++++-- src/AddIns/Analysis/CodeQuality/Src/Method.cs | 15 +++++ .../Analysis/CodeQuality/Src/MetricsReader.cs | 65 ++++++++++--------- 3 files changed, 67 insertions(+), 36 deletions(-) diff --git a/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml.cs b/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml.cs index b6e2f8570b..9ffca969be 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml.cs +++ b/src/AddIns/Analysis/CodeQuality/Src/MainWindow.xaml.cs @@ -204,6 +204,8 @@ namespace ICSharpCode.CodeQualityAnalysis } else if (content == "Method") { 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(); + // TODO: Redone this with enums or some smarter way + if (level == "Assembly") { //cbxMetrics.Items.Add(new ComboBoxItem { Content = }); } else if (level == "Namespace") { @@ -228,12 +232,19 @@ namespace ICSharpCode.CodeQualityAnalysis } else if (level == "Field") { } else if (level == "Method") { - if (metric == "IL instructions") { - treemap.ItemsSource = from ns in MetricsReader.MainModule.Namespaces - from type in ns.Types - from method in type.Methods - select method; - } + treemap.ItemsSource = from ns in MetricsReader.MainModule.Namespaces + from type in ns.Types + from method in type.Methods + 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"; } } } diff --git a/src/AddIns/Analysis/CodeQuality/Src/Method.cs b/src/AddIns/Analysis/CodeQuality/Src/Method.cs index f4720a7b77..cafbd2a289 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/Method.cs +++ b/src/AddIns/Analysis/CodeQuality/Src/Method.cs @@ -111,6 +111,16 @@ namespace ICSharpCode.CodeQualityAnalysis /// public bool IsReturnTypeGenericInstance { get; set; } + /// + /// Cyclomatic Complexity of the method + /// + public int CyclomaticComplexity { get; set; } + + /// + /// The number of variables declared in the body of the method + /// + public int Variables { get; set; } + public Method() { Parameters = new HashSet(); @@ -127,6 +137,9 @@ namespace ICSharpCode.CodeQualityAnalysis IsReturnTypeGenericInstance = false; Dependency = null; + + CyclomaticComplexity = 0; + Variables = 0; } public Relationship GetRelationship(INode node) @@ -149,6 +162,8 @@ namespace ICSharpCode.CodeQualityAnalysis builder.Append(Environment.NewLine); builder.AppendLine(String.Format("Name: {0}", Name)); 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 builder.Append(Environment.NewLine); diff --git a/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs b/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs index 95ab741e0c..ca712384bc 100644 --- a/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs +++ b/src/AddIns/Analysis/CodeQuality/Src/MetricsReader.cs @@ -131,19 +131,19 @@ namespace ICSharpCode.CodeQualityAnalysis IsStruct = typeDefinition.IsValueType && !typeDefinition.IsEnum && typeDefinition.IsSealed, IsInternal = typeDefinition.IsNotPublic, IsDelegate = (typeDefinition.BaseType != null ? - typeDefinition.BaseType.FullName == "System.MulticastDelegate" : false), + typeDefinition.BaseType.FullName == "System.MulticastDelegate" : false), IsNestedPrivate = typeDefinition.IsNestedPrivate, IsNestedPublic = typeDefinition.IsNestedPublic, IsNestedProtected = (!typeDefinition.IsNestedPrivate && !typeDefinition.IsNestedPublic && - typeDefinition.IsNestedFamily) + typeDefinition.IsNestedFamily) }; // try find namespace var nsName = GetNamespaceName(typeDefinition); var ns = (from n in module.Namespaces - where n.Name == nsName - select n).SingleOrDefault(); + where n.Name == nsName + select n).SingleOrDefault(); if (ns == null) { @@ -182,9 +182,9 @@ namespace ICSharpCode.CodeQualityAnalysis if (typeDefinition.BaseType != null) { var baseType = (from n in module.Namespaces - from t in n.Types - where (t.FullName == FormatTypeName(typeDefinition.BaseType, true)) - select t).SingleOrDefault(); + from t in n.Types + where (t.FullName == FormatTypeName(typeDefinition.BaseType, true)) + select t).SingleOrDefault(); type.BaseType = baseType; // if baseType is null so propably inherits from another assembly @@ -192,7 +192,7 @@ namespace ICSharpCode.CodeQualityAnalysis { type.IsBaseTypeGenericInstance = true; 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) { var implementedIc = (from n in module.Namespaces - from t in n.Types - where (t.FullName == FormatTypeName(ic, true)) - select t).SingleOrDefault(); + from t in n.Types + where (t.FullName == FormatTypeName(ic, true)) + select t).SingleOrDefault(); if (implementedIc != null) type.ImplementedInterfaces.Add(implementedIc); @@ -212,7 +212,7 @@ namespace ICSharpCode.CodeQualityAnalysis if (ic.IsGenericInstance) { type.GenericBaseTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, - (GenericInstanceType)ic)); + (GenericInstanceType)ic)); } } } @@ -307,7 +307,7 @@ namespace ICSharpCode.CodeQualityAnalysis { field.IsGenericInstance = true; field.GenericTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, - (GenericInstanceType)fieldDefinition.FieldType)); + (GenericInstanceType)fieldDefinition.FieldType)); } field.FieldType = fieldType; @@ -336,7 +336,8 @@ namespace ICSharpCode.CodeQualityAnalysis IsAbstract = methodDefinition.IsAbstract, IsSetter = methodDefinition.IsSetter, IsGetter = methodDefinition.IsGetter, - IsVirtual = methodDefinition.IsVirtual + IsVirtual = methodDefinition.IsVirtual, + Variables = methodDefinition.Body != null ? methodDefinition.Body.Variables.Count : 0 }; var returnType = @@ -351,7 +352,7 @@ namespace ICSharpCode.CodeQualityAnalysis { method.IsReturnTypeGenericInstance = true; method.GenericReturnTypes.UnionWith(ReadGenericArguments(type.Namespace.Module, - (GenericInstanceType) methodDefinition.ReturnType)); + (GenericInstanceType) methodDefinition.ReturnType)); } // reading types from parameters @@ -378,7 +379,7 @@ namespace ICSharpCode.CodeQualityAnalysis { param.IsGenericInstance = true; param.GenericTypes = ReadGenericArguments(type.Namespace.Module, - (GenericInstanceType) parameter.ParameterType); + (GenericInstanceType) parameter.ParameterType); } method.Parameters.Add(param); @@ -391,8 +392,8 @@ namespace ICSharpCode.CodeQualityAnalysis foreach (MethodDefinition methodDefinition in methods) { var method = (from m in type.Methods - where m.Name == FormatMethodName(methodDefinition) - select m).SingleOrDefault(); + where m.Name == FormatMethodName(methodDefinition) + select m).SingleOrDefault(); if (methodDefinition.Body != null) { @@ -408,15 +409,19 @@ namespace ICSharpCode.CodeQualityAnalysis /// A method definition with instructions /// A collection of instructions public void ReadInstructions(Method method, MethodDefinition methodDefinition, - Collection instructions) + Collection instructions) { foreach (var instruction in instructions) { method.Instructions.Add(new Instruction - { - DeclaringMethod = method, - // Operand = instruction.Operand.ToString() // for now operand as string should be enough - }); + { + DeclaringMethod = method, + // 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); @@ -424,15 +429,15 @@ namespace ICSharpCode.CodeQualityAnalysis { var md = operand as MethodDefinition; var type = (from n in method.DeclaringType.Namespace.Module.Namespaces - from t in n.Types - where t.FullName == FormatTypeName(md.DeclaringType, true) - select t).SingleOrDefault(); + from t in n.Types + where t.FullName == FormatTypeName(md.DeclaringType, true) + select t).SingleOrDefault(); method.TypeUses.Add(type); var findTargetMethod = (from m in type.Methods - where m.Name == FormatMethodName(md) - select m).SingleOrDefault(); + where m.Name == FormatMethodName(md) + select m).SingleOrDefault(); if (findTargetMethod != null && type == method.DeclaringType) method.MethodUses.Add(findTargetMethod); @@ -442,8 +447,8 @@ namespace ICSharpCode.CodeQualityAnalysis { var fd = operand as FieldDefinition; var field = (from f in method.DeclaringType.Fields - where f.Name == fd.Name - select f).SingleOrDefault(); + where f.Name == fd.Name + select f).SingleOrDefault(); if (field != null) method.FieldUses.Add(field);