module codesmells_metrics
import frontend
-import metrics_base
-import mclasses_metrics
-import semantize
+import nitsmell_toolcontext
import method_analyze_metrics
import mclassdef_collect
for mclass in mainmodule.flatten_mclass_hierarchy do
mclass_codesmell.collect(mclass.mclassdefs,self)
end
- mclass_codesmell.print_top(10)
+ if toolcontext.opt_get_all.value then
+ mclass_codesmell.print_all
+ else
+ mclass_codesmell.print_top(10)
+ end
end
fun set_all_average_metrics do
# Code smell list
var bad_conception_elements = new Array[BadConceptionFinder]
- # Print all element conception
+ # Print all collected code smell sort in decroissant order
fun print_all do
- for bad_conception in bad_conception_elements do
- bad_conception.print_all
+ for bad_conception in self.sort do
+ bad_conception.print_collected_data
end
end
- # Print number of top element conception
+ # Print the n top element
fun print_top(number: Int) do
- var test = self.get_numbers_of_elements(number)
- for bad_conception in test do
- bad_conception.print_all
+ for bad_conception in self.get_numbers_of_elements(number) do
+ bad_conception.print_collected_data
end
end
- # Collection
+ # Collect method take Array of mclassdef to find the code smells for every class
fun collect(mclassdefs: Array[MClassDef],phase: CodeSmellsMetricsPhase) do
for mclassdef in mclassdefs do
var bad_conception_class = new BadConceptionFinder(mclassdef,phase)
end
end
+ # Sort the bad_conception_elements array
fun sort: Array[BadConceptionFinder]
do
var res = bad_conception_elements
return res
end
+ # Return an array with n elements
fun get_numbers_of_elements(number : Int) : Array[BadConceptionFinder]do
var return_values = new Array[BadConceptionFinder]
var list = self.sort
var mclassdef: MClassDef
var array_badconception = new Array[BadConception]
var phase: CodeSmellsMetricsPhase
+ var score = 0.0
+ # Collect code smell with selected toolcontext option
fun collect do
var bad_conception_elements = new Array[BadConception]
- bad_conception_elements.add(new LargeClass(phase))
- bad_conception_elements.add(new LongParameterList(phase))
- bad_conception_elements.add(new FeatureEnvy(phase))
- bad_conception_elements.add(new LongMethod(phase))
+ # Check toolcontext option
+ if phase.toolcontext.opt_feature_envy.value or phase.toolcontext.opt_all.value then bad_conception_elements.add(new FeatureEnvy(phase))
+ if phase.toolcontext.opt_long_method.value or phase.toolcontext.opt_all.value then bad_conception_elements.add(new LongMethod(phase))
+ if phase.toolcontext.opt_long_params.value or phase.toolcontext.opt_all.value then bad_conception_elements.add(new LongParameterList(phase))
+ if phase.toolcontext.opt_no_abstract_implementation.value or phase.toolcontext.opt_all.value then bad_conception_elements.add(new NoAbstractImplementation(phase))
+ if phase.toolcontext.opt_large_class.value or phase.toolcontext.opt_all.value then bad_conception_elements.add(new LargeClass(phase))
+ # Collected all code smell if their state is true
for bad_conception_element in bad_conception_elements do
if bad_conception_element.collect(self.mclassdef,phase.toolcontext.modelbuilder) then array_badconception.add(bad_conception_element)
end
+ # Compute global score
+ collect_global_score
end
- fun print_all do
+ fun print_collected_data do
if array_badconception.length != 0 then
- print "-----------"
- print "{mclassdef.full_name}"
+ print "--------------------"
+ print phase.toolcontext.format_h1("Full name: {mclassdef.full_name} Location: {mclassdef.location}")
for bad_conception in array_badconception do
bad_conception.print_result
end
end
end
+
+ fun collect_global_score do
+ if array_badconception.not_empty then
+ for bad_conception in array_badconception do
+ self.score += bad_conception.score
+ end
+ end
+ end
end
-class BadConception
+abstract class BadConception
var phase: CodeSmellsMetricsPhase
+ var score = 0.0
+
# Name
fun name: String is abstract
# Show results in console
fun print_result is abstract
+
+ # Compute code smell score to sort
+ fun score_rate do
+ score = 1.0
+ end
end
class LargeClass
redef fun desc do return "Large class"
redef fun collect(mclassdef, model_builder): Bool do
- number_attribut = mclassdef.collect_intro_and_redef_mattributes(model_builder.model.private_view).length
- # get the number of methods (subtract the get and set of attibutes with (numberAtribut*2))
- number_method = mclassdef.collect_intro_and_redef_methods(model_builder.model.private_view).length
- return number_method.to_f > phase.average_number_of_method and number_attribut.to_f > phase.average_number_of_attribute
+ self.number_attribut = mclassdef.collect_intro_and_redef_mattributes(model_builder.model.private_view).length
+ # Get the number of methods (Accessor include) (subtract the get and set of attibutes with (numberAtribut*2))
+ self.number_method = mclassdef.collect_intro_and_redef_methods(model_builder.model.private_view).length
+ self.score_rate
+ return self.number_method.to_f > phase.average_number_of_method and self.number_attribut.to_f > phase.average_number_of_attribute
end
redef fun print_result do
print phase.toolcontext.format_h2("{desc}: {number_attribut} attributes and {number_method} methods ({phase.average_number_of_attribute}A {phase.average_number_of_method}M Average)")
end
+
+ redef fun score_rate do
+ score = (number_method.to_f + number_attribut.to_f) / (phase.average_number_of_method + phase.average_number_of_attribute)
+ end
end
class LongParameterList
redef fun collect(mclassdef, model_builder): Bool do
for meth in mclassdef.collect_intro_and_redef_mpropdefs(model_builder.model.private_view) do
- # check if the property is a method definition
+ var threshold_value = 4
+ # Get the threshold value from the toolcontext command
+ if phase.toolcontext.opt_long_params_threshold.value != 0 then threshold_value = phase.toolcontext.opt_long_params_threshold.value
+ # Check if the property is a method definition
if not meth isa MMethodDef then continue
# Check if method has a signature
if meth.msignature == null then continue
- if meth.msignature.mparameters.length <= 4 then continue
- bad_methods.add(meth)
+ if meth.msignature.mparameters.length <= threshold_value then continue
+ self.bad_methods.add(meth)
end
- return bad_methods.not_empty
+ self.score_rate
+ return self.bad_methods.not_empty
end
-
redef fun print_result do
- print "{desc}:"
- if bad_methods.length >= 1 then
+ print phase.toolcontext.format_h2("{desc}:")
+ if self.bad_methods.not_empty then
print " Affected method(s):"
- for method in bad_methods do
+ for method in self.bad_methods do
print " -{method.name} has {method.msignature.mparameters.length} parameters"
end
end
end
+
+ redef fun score_rate do
+ if self.bad_methods.not_empty then
+ self.score = self.bad_methods.length.to_f/ phase.average_number_of_method
+ end
+ end
end
class FeatureEnvy
redef fun collect(mclassdef, model_builder): Bool do
var mmethoddefs = call_analyze_methods(mclassdef,model_builder)
for mmethoddef in mmethoddefs do
- if mmethoddef.total_extern_call <= mmethoddef.total_self_call then continue
- bad_methods.add(mmethoddef)
+ var max_class_call = mmethoddef.class_call.max
+ # Check if the class with the maximum call is >= auto-call and the maximum call class is != of this class
+ if mmethoddef.class_call[max_class_call] <= mmethoddef.total_self_call or max_class_call.mclass.full_name == mclassdef.mclass.full_name then continue
+ self.bad_methods.add(mmethoddef)
end
- return bad_methods.not_empty
+ self.score_rate
+ return self.bad_methods.not_empty
end
redef fun print_result do
- print "{desc}:"
- if bad_methods.length >= 1 then
+ print phase.toolcontext.format_h2("{desc}:")
+ if self.bad_methods.not_empty then
print " Affected method(s):"
- for method in bad_methods do
- print " -{method.name} {method.total_self_call}/{method.total_call}"
+ for method in self.bad_methods do
+ var max_class_call = method.class_call.max
+ if max_class_call != null then
+ # Check if the type of max call class is generique
+ if max_class_call.mclass.mclass_type isa MGenericType and not phase.toolcontext.opt_move_generics.value then
+ print " -{method.name}({method.msignature.mparameters.join(", ")}) {method.total_self_call}/{method.class_call[max_class_call]}"
+ else
+ print " -{method.name}({method.msignature.mparameters.join(", ")}) {method.total_self_call}/{method.class_call[max_class_call]} move to {max_class_call}"
+ end
+ end
end
end
end
+
+ redef fun score_rate do
+ if self.bad_methods.not_empty then
+ self.score = self.bad_methods.length.to_f / phase.average_number_of_method
+ end
+ end
end
class LongMethod
redef fun collect(mclassdef, model_builder): Bool do
var mmethoddefs = call_analyze_methods(mclassdef,model_builder)
+ var threshold_value = phase.average_number_of_lines.to_i
+ # Get the threshold value from the toolcontext command
+ if phase.toolcontext.opt_long_method_threshold.value != 0 then threshold_value = phase.toolcontext.opt_long_method_threshold.value
+
for mmethoddef in mmethoddefs do
- if mmethoddef.line_number <= phase.average_number_of_lines.to_i then continue
- bad_methods.add(mmethoddef)
+ if mmethoddef.line_number <= threshold_value then continue
+ self.bad_methods.add(mmethoddef)
end
- return bad_methods.not_empty
+ self.score_rate
+ return self.bad_methods.not_empty
end
redef fun print_result do
- print "{desc}: Average {phase.average_number_of_lines.to_i} lines"
- if bad_methods.length >= 1 then
+ print phase.toolcontext.format_h2("{desc}: Average {phase.average_number_of_lines.to_i} lines")
+ if self.bad_methods.not_empty then
print " Affected method(s):"
- for method in bad_methods do
+ for method in self.bad_methods do
print " -{method.name} has {method.line_number} lines"
end
end
end
+
+ redef fun score_rate do
+ if self.bad_methods.not_empty then
+ self.score = self.bad_methods.length.to_f / phase.average_number_of_method
+ end
+ end
+end
+
+class NoAbstractImplementation
+ super BadConception
+ var bad_methods = new Array[MMethodDef]
+
+ redef fun name do return "LONGMETH"
+
+ redef fun desc do return "No Implemented abstract property"
+
+ redef fun collect(mclassdef, model_builder): Bool do
+ if not mclassdef.mclass.is_abstract and not mclassdef.mclass.is_interface then
+ if mclassdef.collect_abstract_methods(model_builder.model.private_view).not_empty then
+ bad_methods.add_all(mclassdef.collect_not_define_properties(model_builder.model.private_view))
+ end
+ end
+ self.score_rate
+ return bad_methods.not_empty
+ end
+
+ redef fun print_result do
+ print phase.toolcontext.format_h2("{desc}:")
+ if self.bad_methods.not_empty then
+ print " Affected method(s):"
+ for method in self.bad_methods do
+ print " -{method.name}"
+ end
+ end
+ end
+
+ redef fun score_rate do
+ if self.bad_methods.not_empty then
+ self.score = self.bad_methods.length.to_f / phase.average_number_of_method
+ end
+ end
end
redef class ModelView
class BadConceptionComparator
super Comparator
redef type COMPARED: BadConceptionFinder
- redef fun compare(a,b) do return a.array_badconception.length <=> b.array_badconception.length
+ redef fun compare(a,b) do
+ var test = a.array_badconception.length <=> b.array_badconception.length
+ if test == 0 then
+ return a.score <=> b.score
+ end
+ return a.array_badconception.length <=> b.array_badconception.length
+ end
end
return res
end
+ # Collect all mmethod inehrited,intro and redef
fun collect_all_methods(view: ModelView): Set[MMethod] do
var set = new HashSet[MMethod]
set.add_all collect_intro_mmethods(view)
return set
end
+ # Collect all mattributs inehrited,intro and redef
fun collect_all_mattributes(view: ModelView): Set[MAttribute] do
var set = new HashSet[MAttribute]
set.add_all collect_redef_mattributes(view)
return set
end
+ # Collect intro and redef mmethods
fun collect_intro_and_redef_methods(view: ModelView): Set[MMethod] do
var set = new HashSet[MMethod]
set.add_all collect_intro_mmethods(view)
return set
end
+ # Collect intro and redef mattributs
fun collect_intro_and_redef_mattributes(view: ModelView): Set[MAttribute] do
var set = new HashSet[MAttribute]
set.add_all collect_redef_mattributes(view)
return set
end
- fun collect_intro_and_redef_mproperties(view: ModelView): Set[MProperty] do
- var set = new HashSet[MProperty]
- set.add_all collect_redef_mproperties(view)
- set.add_all collect_intro_mproperties(view)
- return set
- end
-
+ # Collect intro and redef mpropdefs
fun collect_intro_and_redef_mpropdefs(view: ModelView): Set[MPropDef] do
var set = new HashSet[MPropDef]
set.add_all collect_intro_mpropdefs(view)
set.add_all collect_redef_mpropdefs(view)
return set
end
+
+ # Collect intro abstract mmethodDef
+ fun collect_abstract_methods(view: ModelView): Set[MMethodDef] do
+ var set = new HashSet[MMethodDef]
+ var mpropdefs = collect_intro_mpropdefs(view)
+ for mpropdef in mpropdefs do
+ if mpropdef isa MMethodDef then
+ if mpropdef.is_abstract then set.add(mpropdef)
+ end
+ end
+ return set
+ end
+
+ # Collect not defined properties
+ fun collect_not_define_properties(view: ModelView):Set[MMethodDef] do
+ var set = new HashSet[MMethodDef]
+ for mpropdef in collect_abstract_methods(view) do
+ var redef_count = 0
+ for mprop in mpropdef.mproperty.mpropdefs do
+ if mprop.is_abstract then continue
+ redef_count += 1
+ end
+ if redef_count == 0 then set.add(mpropdef)
+ end
+ return set
+ end
end
# We usualy need specific phases
# NOTE: `frontend` is sufficent in most case (it is often too much)
-import metrics_base
-import mclasses_metrics
-import semantize
+import nitsmell_toolcontext
import mclassdef_collect
end
fun set_analyse_result_methoddef(mmethoddef: MMethodDef, visitor: MethodAnalyzeMetrics): MMethodDef do
- mmethoddef.total_call = visitor.total_call
mmethoddef.line_number = visitor.line_number.length
mmethoddef.total_self_call = visitor.total_self_call
- mmethoddef.total_extern_call = visitor.total_call - visitor.total_self_call
+ mmethoddef.class_call = visitor.class_call
return mmethoddef
end
public class MethodAnalyzeMetrics
super Visitor
var ameth_prop_def: AMethPropdef
- var total_call = 0
var line_number = new Counter[nullable Int]
var total_self_call = 0
+ var class_call = new Counter[MClassType]
redef fun visit(n) do
n.visit_all(self)
end
end
end
-
if n isa ASendExpr then
var callsite = n.callsite
if callsite != null then
- self.total_call += 1
+ var class_site_recv = callsite.recv
+ if class_site_recv isa MClassType then class_call.inc(class_site_recv)
if callsite.recv_is_self then self.total_self_call += 1
end
end
end
redef class MMethodDef
- var total_call = 0
var line_number = 0
var total_self_call = 0
- var total_extern_call = 0
+ var class_call = new Counter[MClassType]
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module nitsmell_toolcontext
+
+import modelbuilder
+import csv
+import counter
+import console
+
+redef class ToolContext
+
+ # --all
+ var opt_all = new OptionBool("Print all code smells for top 10 class", "--all")
+ # --get-all
+ var opt_get_all = new OptionBool("Print all code smells for all class", "--get-all")
+ # --feature-envy
+ var opt_feature_envy = new OptionBool("Print feature envy", "--feature-envy")
+ # --large-class
+ var opt_large_class = new OptionBool("Print large class", "--large-class")
+ # --long-methods
+ var opt_long_method = new OptionBool("Print long method", "--long-methods")
+ # --no-abstract-implementation
+ var opt_no_abstract_implementation = new OptionBool("Print the no implemented abstract method", "--no-abstract-implementation")
+ # --long-params
+ var opt_long_params = new OptionBool("Print long parameters", "--long-params")
+ # --move-generics
+ var opt_move_generics = new OptionBool("Print the move proposition for generic class", "--move-generics")
+ # --move-generics
+ var opt_long_method_threshold = new OptionInt("Select long method threshold", 0 ,"--long-method-threshold")
+ # --long-method-threshold
+ var opt_long_params_threshold = new OptionInt("Select long method parameters threshold", 0 ,"--long-params-threshold")
+ # --long-params-threshold
+ var opt_nocolors = new OptionBool("Disable colors in console outputs", "--no-colors")
+
+ redef init
+ do
+ super
+ self.option_context.add_option(opt_all)
+ self.option_context.add_option(opt_large_class)
+ self.option_context.add_option(opt_get_all)
+ self.option_context.add_option(opt_feature_envy)
+ self.option_context.add_option(opt_long_method)
+ self.option_context.add_option(opt_no_abstract_implementation)
+ self.option_context.add_option(opt_long_params)
+ self.option_context.add_option(opt_long_method_threshold)
+ self.option_context.add_option(opt_long_params_threshold)
+ self.option_context.add_option(opt_move_generics)
+ self.option_context.add_option(opt_nocolors)
+ end
+
+ # Format and colorize a string heading of level 1 for console output.
+ #
+ # Default style is yellow and bold.
+ fun format_h1(str: String): String do
+ if opt_nocolors.value then return str
+ return str.yellow.bold
+ end
+
+ # Format and colorize a string heading of level 2 for console output.
+ #
+ # Default style is white and bold.
+ fun format_h2(str: String): String do
+ if opt_nocolors.value then return str
+ return str.bold
+ end
+
+ # Format and colorize a string heading of level 3 for console output.
+ #
+ # Default style is white and nobold.
+ fun format_h3(str: String): String do
+ if opt_nocolors.value then return str
+ return str
+ end
+
+ # Format and colorize a string heading of level 4 for console output.
+ #
+ # Default style is green.
+ fun format_h4(str: String): String do
+ if opt_nocolors.value then return str
+ return str.green
+ end
+
+ # Format and colorize a string heading of level 5 for console output.
+ #
+ # Default style is light gray.
+ fun format_p(str: String): String do
+ if opt_nocolors.value then return str
+ return str.light_gray
+ end
+end
\ No newline at end of file
# See the License for the specific language governing permissions and
# limitations under the License.
-import metrics_base
-import mclasses_metrics
+import nitsmell_toolcontext
import semantize
import codesmells_metrics
+# For force nit to do the good linéarisation to remove the toolcontext redef warning
+redef class ToolContext
+ redef init do super
+end
# Create a tool context to handle options and paths
var toolcontext = new ToolContext
var testVariable = 0
fun test do end
-end
\ No newline at end of file
+end
var starter = new Starter
test_variable = 3
end
-end
\ No newline at end of file
+
+ fun extra_long_method do
+ var starter = new Starter
+ test_variable = 3
+ test_variable = 5
+ test_variable = 8
+ end
+end
class Starter
fun no_para do end
- fun no_para2() do end
+ fun no_para2 do end
end
class Test
fun long_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int, para4 : Starter) do
var starter = new Starter
end
+
+ fun extra_long_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int, para4 : Starter,para5 : Int, para6 : Int) do
+ var starter = new Starter
+ end
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A test program with a fake model to check model tools.
+module longparameterlist
+
+import platform
+
+class Starter
+ fun no_para do end
+
+ fun no_para2 do end
+
+ fun name: Int is abstract
+
+ fun name2: Int is abstract
+
+ var toto: Int is abstract
+end
+
+class NoImplemented
+ super Starter
+
+ fun short_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int) do
+ var starter = new Starter
+ end
+
+ fun long_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int, para4 : Starter) do
+ var starter = new Starter
+ end
+end
+
+class Implemented
+ super NoImplemented
+
+ redef fun name do return 1 end
+end
---no-colors test_prog/
---no-colors TestNitsmells/FeatureEnvy/
---no-colors TestNitsmells/LargeClass/
---no-colors TestNitsmells/LongMethod/
---no-colors TestNitsmells/LongParameterList/
\ No newline at end of file
+--no-colors --all test_prog/
+--no-colors --feature-envy TestNitsmells/FeatureEnvy/
+--no-colors --large-class TestNitsmells/LargeClass/
+--no-colors --long-methods TestNitsmells/LongMethod/
+--no-colors --long-params TestNitsmells/LongParameterList/
+--no-colors --no-abstract-implementation TestNitsmells/NoAbstractImplemented/
+--no-colors --long-methods --long-method-threshold 3 TestNitsmells/LongMethod/
+--no-colors --long-params --long-params-threshold 5 TestNitsmells/LongParameterList/
*** CODE SMELLS METRICS ***
--- Code Smells Metrics ---
------------
-test_prog$Character
-Large class: 6 attributes and 18 methods (5.414A 7.161M Average)
-Feature envy:
- Affected method(s):
- -total_strengh 4/9
- -total_endurance 4/9
- -total_intelligence 4/9
+--------------------
+Full name: test_prog$Character Location: test_prog/rpg/character.nit:21,1--68,3
Long method: Average 1 lines
Affected method(s):
-total_strengh has 2 lines
-total_endurance has 2 lines
-total_intelligence has 2 lines
------------
-test_prog::combat$Dwarf
-Feature envy:
- Affected method(s):
- -dps 1/3
+Large class: 6 attributes and 18 methods (5.414A 7.161M Average)
*** CODE SMELLS METRICS ***
--- Code Smells Metrics ---
------------
-TestNitsmells$FeatureEnvy
+--------------------
+Full name: TestNitsmells$FeatureEnvy Location: TestNitsmells/FeatureEnvy/featureenvy.nit:32,1--42,3
Feature envy:
Affected method(s):
- -feature_envy_method 2/6
+ -feature_envy_method() 2/3 move to Starter
*** CODE SMELLS METRICS ***
--- Code Smells Metrics ---
------------
-TestNitsmells$LargeClass
+--------------------
+Full name: TestNitsmells$LargeClass Location: TestNitsmells/LargeClass/largeclass.nit:20,1--55,3
Large class: 18 attributes and 48 methods (17.515A 30.464M Average)
*** CODE SMELLS METRICS ***
--- Code Smells Metrics ---
------------
-TestNitsmells$LongMethodClass
-Long method: Average 1 lines
+--------------------
+Full name: TestNitsmells$LongMethodClass Location: TestNitsmells/LongMethod/longmethod.nit:31,1--45,3
+Long method: Average 3 lines
Affected method(s):
- -long_method has 2 lines
+ -extra_long_method has 4 lines
--- /dev/null
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+--------------------
+Full name: TestNitsmells$Test Location: TestNitsmells/LongParameterList/longparameterlist.nit:26,1--38,3
+Long parameter list:
+ Affected method(s):
+ -long_list_parameter has 5 parameters
+ -extra_long_list_parameter has 7 parameters
--- /dev/null
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+--------------------
+Full name: TestNitsmells$Starter Location: TestNitsmells/NoAbstractImplemented/longparameterlist.nit:20,1--30,3
+No Implemented abstract property:
+ Affected method(s):
+ -name2
+ -toto
+ -toto=
--- /dev/null
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+--------------------
+Full name: TestNitsmells$LongMethodClass Location: TestNitsmells/LongMethod/longmethod.nit:31,1--45,3
+Long method: Average 3 lines
+ Affected method(s):
+ -extra_long_method has 4 lines
--- /dev/null
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+--------------------
+Full name: TestNitsmells$Test Location: TestNitsmells/LongParameterList/longparameterlist.nit:26,1--38,3
+Long parameter list:
+ Affected method(s):
+ -extra_long_list_parameter has 7 parameters