From: Florian Deljarry Date: Fri, 26 May 2017 18:39:47 +0000 (+0200) Subject: Adding code smell detection : X-Git-Url: http://nitlanguage.org Adding code smell detection : Long class Long method parameter list Long size method Feature envy Adding a visitor to analyse the contents of the methods Signed-off-by: Florian Deljarry --- diff --git a/src/metrics/codesmells_metrics.nit b/src/metrics/codesmells_metrics.nit new file mode 100644 index 0000000..1bd502d --- /dev/null +++ b/src/metrics/codesmells_metrics.nit @@ -0,0 +1,317 @@ +# 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. +# Detect the code smells and antipatterns in the code. + +module codesmells_metrics + +import frontend +import metrics_base +import mclasses_metrics +import semantize +import method_analyze_metrics +import mclassdef_collect + +redef class ToolContext + var codesmells_metrics_phase = new CodeSmellsMetricsPhase(self, null) +end + +class CodeSmellsMetricsPhase + super Phase + var average_number_of_lines = 0.0 + var average_number_of_parameter = 0.0 + var average_number_of_method = 0.0 + var average_number_of_attribute = 0.0 + + redef fun process_mainmodule(mainmodule, given_mmodules) do + print toolcontext.format_h1("--- Code Smells Metrics ---") + self.set_all_average_metrics + var mclass_codesmell = new BadConceptonController + var collect = new Counter[MClassDef] + var mclassdefs = new Array[MClassDef] + + for mclass in mainmodule.flatten_mclass_hierarchy do + mclass_codesmell.collect(mclass.mclassdefs,self) + end + mclass_codesmell.print_top(10) + end + + fun set_all_average_metrics do + var model_builder = toolcontext.modelbuilder + var model_view = model_builder.model.private_view + self.average_number_of_lines = model_view.get_avg_linenumber(model_builder) + self.average_number_of_parameter = model_view.get_avg_parameter + self.average_number_of_method = model_view.get_avg_method + self.average_number_of_attribute = model_view.get_avg_attribut + end +end + +class BadConceptonController + # Code smell list + var bad_conception_elements = new Array[BadConceptionFinder] + + # Print all element conception + fun print_all do + for bad_conception in bad_conception_elements do + bad_conception.print_all + end + end + + # Print number of top element conception + fun print_top(number: Int) do + var test = self.get_numbers_of_elements(number) + for bad_conception in test do + bad_conception.print_all + end + end + + # Collection + fun collect(mclassdefs: Array[MClassDef],phase: CodeSmellsMetricsPhase) do + for mclassdef in mclassdefs do + var bad_conception_class = new BadConceptionFinder(mclassdef,phase) + bad_conception_class.collect + bad_conception_elements.add(bad_conception_class) + end + end + + fun sort: Array[BadConceptionFinder] + do + var res = bad_conception_elements + var sorter = new BadConceptionComparator + sorter.sort(res) + return res + end + + fun get_numbers_of_elements(number : Int) : Array[BadConceptionFinder]do + var return_values = new Array[BadConceptionFinder] + var list = self.sort + var min = number + if list.length <= number*2 then min = list.length + for i in [0..min[ do + var t = list[list.length-i-1] + return_values.add(t) + end + return return_values + end +end + +class BadConceptionFinder + var mclassdef: MClassDef + var array_badconception = new Array[BadConception] + var phase: CodeSmellsMetricsPhase + + 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)) + for bad_conception_element in bad_conception_elements do + if bad_conception_element.collect(self.mclassdef,phase.toolcontext.modelbuilder) == true then array_badconception.add(bad_conception_element) + end + end + + fun print_all do + if array_badconception.length != 0 then + print "-----------" + print "{mclassdef.full_name}" + for bad_conception in array_badconception do + bad_conception.print_result + end + end + end +end + +class BadConception + var phase: CodeSmellsMetricsPhase + + # Name + fun name: String is abstract + + # Description + fun desc: String is abstract + + # Collection method + fun collect(mclassdef: MClassDef, model_builder: ModelBuilder): Bool is abstract + + # Show results in console + fun print_result is abstract +end + +class LargeClass + super BadConception + var number_attribut = 0 + + var number_method = 0 + + redef fun name do return "LARGC" + + 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 + 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 +end + +class LongParameterList + super BadConception + var bad_methods = new Array[MMethodDef] + + redef fun name do return "LONGPL" + + redef fun desc do return "Long parameter list" + + 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 + 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) + end + return bad_methods.not_empty + end + + + redef fun print_result do + print "{desc}:" + if bad_methods.length >= 1 then + print " Affected method(s):" + for method in bad_methods do + print " -{method.name} has {method.msignature.mparameters.length} parameters" + end + end + end +end + +class FeatureEnvy + super BadConception + var bad_methods = new Array[MMethodDef] + + redef fun name do return "FEM" + + redef fun desc do return "Feature envy" + + 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) + end + return bad_methods.not_empty + end + + redef fun print_result do + print "{desc}:" + if bad_methods.length >= 1 then + print " Affected method(s):" + for method in bad_methods do + print " -{method.name} {method.total_self_call}/{method.total_call}" + end + end + end +end + +class LongMethod + super BadConception + var bad_methods = new Array[MMethodDef] + + redef fun name do return "LONGMETH" + + redef fun desc do return "Long method" + + redef fun collect(mclassdef, model_builder): Bool do + var mmethoddefs = call_analyze_methods(mclassdef,model_builder) + for mmethoddef in mmethoddefs do + if mmethoddef.line_number <= phase.average_number_of_lines.to_i then continue + bad_methods.add(mmethoddef) + end + return 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 " Affected method(s):" + for method in bad_methods do + print " -{method.name} has {method.line_number} lines" + end + end + end +end + +redef class ModelView + fun get_avg_parameter: Float do + var counter = new Counter[MMethodDef] + for mclassdef in mclassdefs do + for method in mclassdef.collect_intro_and_redef_mpropdefs(self) do + # check if the property is a method definition + if not method isa MMethodDef then continue + #Check if method has a signature + if method.msignature == null then continue + if method.msignature.mparameters.length == 0 then continue + counter[method] = method.msignature.mparameters.length + end + end + return counter.avg + counter.std_dev + end + + fun get_avg_attribut: Float do + var counter = new Counter[MClassDef] + for mclassdef in mclassdefs do + var number_attributs = mclassdef.collect_intro_and_redef_mattributes(self).length + if number_attributs != 0 then counter[mclassdef] = number_attributs + end + return counter.avg + counter.std_dev + end + + fun get_avg_method: Float do + var counter = new Counter[MClassDef] + for mclassdef in mclassdefs do + var number_methodes = mclassdef.collect_intro_and_redef_methods(self).length + if number_methodes != 0 then counter[mclassdef] = number_methodes + end + return counter.avg + counter.std_dev + end + + fun get_avg_linenumber(model_builder: ModelBuilder): Float do + var methods_analyse_metrics = new Counter[MClassDef] + for mclassdef in mclassdefs do + var result = 0 + var count = 0 + for mmethoddef in call_analyze_methods(mclassdef,model_builder) do + result += mmethoddef.line_number + if mmethoddef.line_number == 0 then continue + count += 1 + end + if not mclassdef.collect_local_mproperties(self).length != 0 then continue + if count == 0 then continue + methods_analyse_metrics[mclassdef] = (result/count).to_i + end + return methods_analyse_metrics.avg + methods_analyse_metrics.std_dev + end +end + +class BadConceptionComparator + super Comparator + redef type COMPARED: BadConceptionFinder + redef fun compare(a,b) do return a.array_badconception.length <=> b.array_badconception.length +end diff --git a/src/metrics/mclassdef_collect.nit b/src/metrics/mclassdef_collect.nit new file mode 100644 index 0000000..5b6c937 --- /dev/null +++ b/src/metrics/mclassdef_collect.nit @@ -0,0 +1,167 @@ +# 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. +# This module redef Mclassdef to add new collect methods. + +module mclassdef_collect + +# We usualy need specific phases +# NOTE: `frontend` is sufficent in most case (it is often too much) +import frontend +import model_views +import model_collect + +redef class MClassDef + # Collect all mproperties introduced in 'self' with `visibility >= min_visibility`. + fun collect_intro_mproperties(view: ModelView): Set[MProperty] do + var set = new HashSet[MProperty] + for mprop in self.intro_mproperties do + if not view.accept_mentity(mprop) then continue + set.add(mprop) + end + return set + end + + # Collect mmethods introduced in 'self' with `visibility >= min_visibility`. + fun collect_intro_mmethods(view: ModelView): Set[MMethod] do + var res = new HashSet[MMethod] + for mproperty in collect_intro_mproperties(view) do + if not view.accept_mentity(mproperty) then continue + if mproperty isa MMethod then res.add(mproperty) + end + return res + end + + # Collect mmethods redefined in 'self' with `visibility >= min_visibility`. + fun collect_redef_mmethods(view: ModelView): Set[MMethod] do + var res = new HashSet[MMethod] + for mproperty in collect_redef_mproperties(view) do + if not view.accept_mentity(mproperty) then continue + if mproperty isa MMethod then res.add(mproperty) + end + return res + end + + # Collect mattributes redefined in 'self' with `visibility >= min_visibility`. + fun collect_redef_mattributes(view: ModelView): Set[MAttribute] do + var res = new HashSet[MAttribute] + for mproperty in collect_redef_mproperties(view) do + if not view.accept_mentity(mproperty) then continue + if mproperty isa MAttribute then res.add(mproperty) + end + return res + end + + # Collect mattributes introduced in 'self' with `visibility >= min_visibility`. + fun collect_intro_mattributes(view: ModelView): Set[MAttribute] do + var res = new HashSet[MAttribute] + for mproperty in collect_intro_mproperties(view) do + if not view.accept_mentity(mproperty) then continue + if mproperty isa MAttribute then res.add(mproperty) + end + return res + end + + # Collect all mproperties redefined in 'self' with `visibility >= min_visibility`. + fun collect_redef_mproperties(view: ModelView): Set[MProperty] do + var set = new HashSet[MProperty] + for mpropdef in self.mpropdefs do + if not view.accept_mentity(mpropdef) then continue + if mpropdef.mproperty.intro_mclassdef.mclass == self then continue + set.add(mpropdef.mproperty) + end + return set + end + + # Collect mmethods inherited by 'self' if accepted by `view`. + fun collect_inherited_mmethods(view: ModelView): Set[MMethod] do + var res = new HashSet[MMethod] + for mproperty in collect_inherited_mproperties(view) do + if not view.accept_mentity(mproperty) then continue + if mproperty isa MMethod then res.add(mproperty) + end + return res + end + + # Collect mproperties introduced and redefined in 'self' with `visibility >= min_visibility`. + fun collect_local_mproperties(view: ModelView): Set[MProperty] do + var set = new HashSet[MProperty] + set.add_all collect_intro_mproperties(view) + set.add_all collect_redef_mproperties(view) + return set + end + + # Collect all mproperties inehrited by 'self' with `visibility >= min_visibility`. + fun collect_inherited_mproperties(view: ModelView): Set[MProperty] do + var set = new HashSet[MProperty] + for parent in collect_parents(view) do + set.add_all(parent.collect_intro_mproperties(view)) + set.add_all(parent.collect_inherited_mproperties(view)) + end + return set + end + + # Collect mattributes inherited by 'self' with `visibility >= min_visibility`. + fun collect_inherited_mattributes(view: ModelView): Set[MAttribute] do + var res = new HashSet[MAttribute] + for mproperty in collect_inherited_mproperties(view) do + if not view.accept_mentity(mproperty) then continue + if mproperty isa MAttribute then res.add(mproperty) + end + return res + end + + fun collect_all_methods(view: ModelView): Set[MMethod] do + var set = new HashSet[MMethod] + set.add_all collect_intro_mmethods(view) + set.add_all collect_redef_mmethods(view) + set.add_all collect_inherited_mmethods(view) + return set + end + + fun collect_all_mattributes(view: ModelView): Set[MAttribute] do + var set = new HashSet[MAttribute] + set.add_all collect_redef_mattributes(view) + set.add_all collect_intro_mattributes(view) + set.add_all collect_inherited_mattributes(view) + return set + end + + fun collect_intro_and_redef_methods(view: ModelView): Set[MMethod] do + var set = new HashSet[MMethod] + set.add_all collect_intro_mmethods(view) + set.add_all collect_redef_mmethods(view) + return set + end + + fun collect_intro_and_redef_mattributes(view: ModelView): Set[MAttribute] do + var set = new HashSet[MAttribute] + set.add_all collect_redef_mattributes(view) + set.add_all collect_intro_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 + + 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 +end diff --git a/src/metrics/method_analyze_metrics.nit b/src/metrics/method_analyze_metrics.nit new file mode 100644 index 0000000..6aee2a9 --- /dev/null +++ b/src/metrics/method_analyze_metrics.nit @@ -0,0 +1,83 @@ +# 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. +# This module call visitor for get number of line, total attributs call and total of self attributes call + +module method_analyze_metrics + +# 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 mclassdef_collect + + +fun call_analyze_methods(mclassdef: MClassDef, model_builder: ModelBuilder): Array[MMethodDef] do + var mmethoddefs = new Array[MMethodDef] + for m_prop in mclassdef.collect_intro_and_redef_mpropdefs(model_builder.model.private_view) do + var n_prop = model_builder.mpropdef2node(m_prop) + #Check if the property is a method definition + if n_prop isa AMethPropdef and m_prop isa MMethodDef then + if n_prop.n_methid isa AIdMethid then + #Call visitor to analyse the method + var visitor = new MethodAnalyzeMetrics(n_prop) + visitor.enter_visit(n_prop) + mmethoddefs.add(set_analyse_result_methoddef(m_prop,visitor)) + end + end + end + return mmethoddefs +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 + 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 + + redef fun visit(n) do + n.visit_all(self) + if n isa AExpr then + if not n isa ABlockExpr then + if n.first_location != null then + line_number.inc(n.first_location.line_start) + end + end + end + + if n isa ASendExpr then + var callsite = n.callsite + if callsite != null then + self.total_call += 1 + if callsite.recv_is_self == true then self.total_self_call += 1 + end + end + end +end + +redef class MMethodDef + var total_call = 0 + var line_number = 0 + var total_self_call = 0 + var total_extern_call = 0 +end diff --git a/src/nitsmells.nit b/src/nitsmells.nit new file mode 100644 index 0000000..da8454a --- /dev/null +++ b/src/nitsmells.nit @@ -0,0 +1,36 @@ +# 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. + +import metrics_base +import mclasses_metrics +import semantize + +import codesmells_metrics + +# Create a tool context to handle options and paths +var toolcontext = new ToolContext +toolcontext.tooldescription = "Usage: nitsmells [OPTION]... ...\n Computes code smells on Nit programs." +# We do not add other options, so process them now! +toolcontext.process_options(args) +# Get arguments +var arguments = toolcontext.option_context.rest +# We need a model to collect stufs +var model = new Model +# An a model builder to parse files +var modelbuilder = new ModelBuilder(model, toolcontext) +# Here we load an process all modules passed on the command line +var mmodules = modelbuilder.parse_full(arguments) +modelbuilder.run_phases +print "*** CODE SMELLS METRICS ***" +toolcontext.run_global_phases(mmodules) diff --git a/tests/TestNitsmells/FeatureEnvy/featureenvy.nit b/tests/TestNitsmells/FeatureEnvy/featureenvy.nit new file mode 100644 index 0000000..6a11274 --- /dev/null +++ b/tests/TestNitsmells/FeatureEnvy/featureenvy.nit @@ -0,0 +1,42 @@ +# 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 featureenvy + +import platform + +class Starter + var attribute = 0 + var attribute1 = 0 + var attribute2 = 0 + fun start do + self.attribute1 = 10 + self.attribute = 2 + end + + fun ended do end +end + +class FeatureEnvy + var testVariable = 0 + + fun feature_envy_method do + var starter = new Starter + testVariable = starter.attribute + if starter.attribute == self.testVariable then + starter.attribute = 10 + end + end +end diff --git a/tests/TestNitsmells/LargeClass/largeclass.nit b/tests/TestNitsmells/LargeClass/largeclass.nit new file mode 100644 index 0000000..1c18141 --- /dev/null +++ b/tests/TestNitsmells/LargeClass/largeclass.nit @@ -0,0 +1,61 @@ +# 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 largeclass + +import platform + +class LargeClass + var attribute = 0 + var attribute1 = 0 + var attribute2 = 0 + var attribute3 = 0 + var attribute4 = 0 + var attribute5 = 0 + var attribut6 = 0 + var attribute7 = 0 + var attribute8 = 0 + var attribute9 = 0 + var attribute10 = 0 + var attribute11 = 0 + var attribute12 = 0 + var attribute13 = 0 + var attribute14 = 0 + var attribute15 = 0 + var attribute16 = 0 + var attribute17 = 0 + + fun start do + self.attribute16 = 10 + end + + fun ended do end + fun replay do end + fun restart do end + fun start1 do end + fun ended1 do end + fun replay1 do end + fun restart1 do end + fun start2 do end + fun ended2 do end + fun replay2 do end + fun restart2 do end +end + +class NoLargeclass + var testVariable = 0 + + fun test do end +end \ No newline at end of file diff --git a/tests/TestNitsmells/LongMethod/longmethod.nit b/tests/TestNitsmells/LongMethod/longmethod.nit new file mode 100644 index 0000000..584ef44 --- /dev/null +++ b/tests/TestNitsmells/LongMethod/longmethod.nit @@ -0,0 +1,38 @@ +# 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 longmethod + +import platform + +class Starter + var attribute = 0 + var attribute1 = 0 + var attribute2 = 0 + fun start do + self.attribute1 = 10 + end + + fun ended do end +end + +class LongMethodClass + var test_variable = 0 + + fun long_method do + var starter = new Starter + test_variable = 3 + end +end \ No newline at end of file diff --git a/tests/TestNitsmells/LongParameterList/longparameterlist.nit b/tests/TestNitsmells/LongParameterList/longparameterlist.nit new file mode 100644 index 0000000..6988536 --- /dev/null +++ b/tests/TestNitsmells/LongParameterList/longparameterlist.nit @@ -0,0 +1,34 @@ +# 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 +end + +class Test + 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 diff --git a/tests/TestNitsmells/platform/platform.nit b/tests/TestNitsmells/platform/platform.nit new file mode 100644 index 0000000..175e6fc --- /dev/null +++ b/tests/TestNitsmells/platform/platform.nit @@ -0,0 +1,59 @@ +# 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. + +# Declares base types allowed on the platform. +module platform + +import end + +# Root of everything. +interface Object + # Used for comparisons. + type OTHER: nullable Object + + # Is `other` equqls to `self`? + fun ==(other: OTHER): Bool is intern + + # Is `other` different from `self`? + fun !=(other: OTHER): Bool do return not self == other +end + +# Some services about Integers. +class Int + fun -: Int is intern + fun +(i: Int): Int is intern + fun -(i: Int): Int is intern + fun *(i: Int): Int is intern + fun /(i: Int): Int is intern + fun >(i: Int): Bool is intern + fun to_f: Float is intern +end + +# Some services about Floats. +class Float + fun +(f: Float): Float is intern + fun -(f: Float): Float is intern + fun *(f: Float): Float is intern + fun /(f: Float): Float is intern + fun >(f: Float): Bool is intern +end + +# Booleans, `true` or `false`. +class Bool end + +# Strings (there is no chars...). +class String end + +# List of things. +class List[E] end diff --git a/tests/nitsmells.args b/tests/nitsmells.args new file mode 100644 index 0000000..0032cd9 --- /dev/null +++ b/tests/nitsmells.args @@ -0,0 +1,5 @@ +--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 diff --git a/tests/sav/nitsmells.res b/tests/sav/nitsmells.res new file mode 100644 index 0000000..9b27b1c --- /dev/null +++ b/tests/sav/nitsmells.res @@ -0,0 +1,3 @@ +Usage: nitsmells [OPTION]... ... + Computes code smells on Nit programs. +Use --help for help diff --git a/tests/sav/nitsmells_args1.res b/tests/sav/nitsmells_args1.res new file mode 100644 index 0000000..68b3680 --- /dev/null +++ b/tests/sav/nitsmells_args1.res @@ -0,0 +1,20 @@ +*** 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 +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 diff --git a/tests/sav/nitsmells_args2.res b/tests/sav/nitsmells_args2.res new file mode 100644 index 0000000..26092c7 --- /dev/null +++ b/tests/sav/nitsmells_args2.res @@ -0,0 +1,7 @@ +*** CODE SMELLS METRICS *** +--- Code Smells Metrics --- +----------- +TestNitsmells$FeatureEnvy +Feature envy: + Affected method(s): + -feature_envy_method 2/6 diff --git a/tests/sav/nitsmells_args3.res b/tests/sav/nitsmells_args3.res new file mode 100644 index 0000000..fd60962 --- /dev/null +++ b/tests/sav/nitsmells_args3.res @@ -0,0 +1,5 @@ +*** CODE SMELLS METRICS *** +--- Code Smells Metrics --- +----------- +TestNitsmells$LargeClass +Large class: 18 attributes and 48 methods (17.515A 30.464M Average) diff --git a/tests/sav/nitsmells_args4.res b/tests/sav/nitsmells_args4.res new file mode 100644 index 0000000..1fe9b09 --- /dev/null +++ b/tests/sav/nitsmells_args4.res @@ -0,0 +1,7 @@ +*** CODE SMELLS METRICS *** +--- Code Smells Metrics --- +----------- +TestNitsmells$LongMethodClass +Long method: Average 1 lines + Affected method(s): + -long_method has 2 lines