Skip to content

Commit

Permalink
Merge pull request #1398 from ericlemes/dump-cc-and-loc
Browse files Browse the repository at this point in the history
Add function complexity and size metrics
  • Loading branch information
guwirth authored Jun 12, 2018
2 parents 8edbb8c + 68d6fd7 commit d0abf2c
Show file tree
Hide file tree
Showing 19 changed files with 1,302 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2018 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.cxx.sensors.functioncomplexity;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Grammar;
import java.util.Hashtable;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.cxx.CxxLanguage;
import org.sonar.cxx.api.CxxMetric;
import static org.sonar.cxx.checks.TooManyLinesOfCodeInFunctionCheck.getNumberOfLine;
import org.sonar.cxx.parser.CxxGrammarImpl;
import org.sonar.cxx.sensors.squid.SquidSensor;
import org.sonar.squidbridge.SquidAstVisitor;
import org.sonar.squidbridge.api.SourceFile;
import org.sonar.squidbridge.api.SourceFunction;
import org.sonar.squidbridge.checks.ChecksHelper;

public class CxxFunctionComplexitySquidSensor extends SquidAstVisitor<Grammar> implements SquidSensor {

private static final Logger LOG = Loggers.get(CxxFunctionComplexitySquidSensor.class);

public static final String FUNCTION_COMPLEXITY_THRESHOLD_KEY = "funccomplexity.threshold";

private int cyclomaticComplexityThreshold;

private int functionsBelowThreshold;

private int functionsOverThreshold;

private int linesOfCodeBelowThreshold;

private int linesOfCodeOverThreshold;

private Hashtable<SourceFile, FunctionCount> complexFunctionsPerFile = new Hashtable<>();

private Hashtable<SourceFile, FunctionCount> locInComplexFunctionsPerFile = new Hashtable<>();

private String fileName;

public CxxFunctionComplexitySquidSensor(CxxLanguage language){
this.cyclomaticComplexityThreshold = language.getIntegerOption(FUNCTION_COMPLEXITY_THRESHOLD_KEY).orElse(10);
if (LOG.isDebugEnabled()) {
LOG.debug("Cyclomatic complexity threshold: " + this.cyclomaticComplexityThreshold);
}
}

@Override
public SquidAstVisitor<Grammar> getVisitor() {
return this;
}

@Override
public void init() {
subscribeTo(CxxGrammarImpl.functionDefinition);
}

@Override
public void leaveNode(AstNode node) {
SourceFunction sourceFunction = (SourceFunction) getContext().peekSourceCode();
SourceFile sourceFile = (SourceFile)sourceFunction.getAncestor(SourceFile.class);

int complexity = ChecksHelper.getRecursiveMeasureInt(sourceFunction, CxxMetric.COMPLEXITY);
int lineCount = getNumberOfLine(node);

incrementFunctionByThresholdForAllFiles(complexity, lineCount);
incrementFunctionByThresholdForFile(sourceFile, complexity, lineCount);
}

private void incrementFunctionByThresholdForAllFiles(int complexity, int lineCount){
if (complexity > this.cyclomaticComplexityThreshold){
this.functionsOverThreshold++;
this.linesOfCodeOverThreshold += lineCount;
}
else {
this.functionsBelowThreshold++;
this.linesOfCodeBelowThreshold += lineCount;
}
}

private void incrementFunctionByThresholdForFile(SourceFile sourceFile, int complexity, int loc){
if (!complexFunctionsPerFile.containsKey(sourceFile)) {
complexFunctionsPerFile.put(sourceFile, new FunctionCount());
}

if (!locInComplexFunctionsPerFile.containsKey(sourceFile)) {
locInComplexFunctionsPerFile.put(sourceFile, new FunctionCount());
}

FunctionCount functionCount = complexFunctionsPerFile.get(sourceFile);
FunctionCount locCount = locInComplexFunctionsPerFile.get(sourceFile);
if (complexity > this.cyclomaticComplexityThreshold){
functionCount.countOverThreshold++;
locCount.countOverThreshold += loc;
}
else {
functionCount.countBelowThreshold++;
locCount.countBelowThreshold += loc;
}
}

@Override
public void publishMeasureForFile(InputFile inputFile, SourceFile squidFile, SensorContext context) {
publishComplexFunctionMetricsForFile(inputFile, squidFile, context);
publishLocInComplexFunctionMetricsForFile(inputFile, squidFile, context);
}

private void publishComplexFunctionMetricsForFile(InputFile inputFile, SourceFile squidFile, SensorContext context){
FunctionCount c = complexFunctionsPerFile.get(squidFile);
if (c == null){
c = new FunctionCount();
c.countBelowThreshold = 0;
c.countOverThreshold = 0;
}

context.<Integer>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS)
.on(inputFile)
.withValue((int)c.countOverThreshold)
.save();

context.<Double>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS_PERC)
.on(inputFile)
.withValue(calculatePercentual((int)c.countOverThreshold, (int)c.countBelowThreshold))
.save();
}

private void publishLocInComplexFunctionMetricsForFile(InputFile inputFile, SourceFile squidFile, SensorContext context){
FunctionCount locCount = locInComplexFunctionsPerFile.get(squidFile);

if (locCount == null){
locCount = new FunctionCount();
locCount.countBelowThreshold = 0;
locCount.countOverThreshold = 0;
}

context.<Integer>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS_LOC)
.on(inputFile)
.withValue(locCount.countOverThreshold)
.save();

context.<Double>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS_LOC_PERC)
.on(inputFile)
.withValue(calculatePercentual((int)locCount.countOverThreshold, (int)locCount.countBelowThreshold))
.save();
}

@Override
public void publishMeasureForProject(InputModule module, SensorContext context) {
publishComplexFunctionMetrics(module, context);
publishLinesOfCodeInComplexFunctionMetrics(module, context);
}

private void publishComplexFunctionMetrics(InputModule module, SensorContext context){
context.<Integer>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS)
.on(module)
.withValue(functionsOverThreshold)
.save();

context.<Double>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS_PERC)
.on(module)
.withValue(calculatePercentual(functionsOverThreshold, functionsBelowThreshold))
.save();
}

private void publishLinesOfCodeInComplexFunctionMetrics(InputModule module, SensorContext context){
context.<Integer>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS_LOC)
.on(module)
.withValue(linesOfCodeOverThreshold)
.save();

context.<Double>newMeasure()
.forMetric(FunctionComplexityMetrics.COMPLEX_FUNCTIONS_LOC_PERC)
.on(module)
.withValue(calculatePercentual(linesOfCodeOverThreshold, linesOfCodeBelowThreshold))
.save();
}

private double calculatePercentual(int overThreshold, int belowThreshold){
double total = (double)overThreshold + (double)belowThreshold;
if (total > 0) {
return ((float)overThreshold * 100.0) / ((float)overThreshold + (float)belowThreshold);
}
else {
return 0;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2018 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.cxx.sensors.functioncomplexity;

import static java.util.Arrays.asList;
import java.util.List;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.Metrics;

public class FunctionComplexityMetrics implements Metrics {

public static final Metric<Integer> COMPLEX_FUNCTIONS = new Metric.Builder("complex_functions", "Complex Functions", Metric.ValueType.INT)
.setDescription("Number of functions with high cyclomatic complexity")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(Boolean.FALSE)
.setDomain(CoreMetrics.DOMAIN_COMPLEXITY)
.create();

public static final Metric<Double> COMPLEX_FUNCTIONS_PERC = new Metric.Builder("perc_complex_functions", "Complex Functions (%)", Metric.ValueType.PERCENT)
.setDescription("% of functions with high cyclomatic complexity")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(Boolean.FALSE)
.setDomain(CoreMetrics.DOMAIN_COMPLEXITY)
.create();

public static final Metric<Integer> COMPLEX_FUNCTIONS_LOC = new Metric.Builder("loc_in_complex_functions", "Complex Functions Lines of Code", Metric.ValueType.INT)
.setDescription("Number of lines of code in functions with high cyclomatic complexity")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(Boolean.FALSE)
.setDomain(CoreMetrics.DOMAIN_COMPLEXITY)
.create();

public static final Metric<Double> COMPLEX_FUNCTIONS_LOC_PERC = new Metric.Builder("perc_loc_in_complex_functions", "Complex Functions Lines of Code (%)", Metric.ValueType.PERCENT)
.setDescription("% of lines of code in functions with high cyclomatic complexity")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(Boolean.FALSE)
.setDomain(CoreMetrics.DOMAIN_COMPLEXITY)
.create();

@Override
public List<Metric> getMetrics() {
return asList(COMPLEX_FUNCTIONS, COMPLEX_FUNCTIONS_PERC, COMPLEX_FUNCTIONS_LOC, COMPLEX_FUNCTIONS_LOC_PERC);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2018 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.cxx.sensors.functioncomplexity;

public class FunctionCount {
public int countOverThreshold;

public int countBelowThreshold;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2018 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.cxx.sensors.functioncomplexity;

public class FunctionScore {
private int score;

public int getScore(){
return this.score;
}

public void setScore(int value){
this.score = value;
}

private String componentName;

public String getComponentName(){
return this.componentName;
}

public void setComponentName(String value){
this.componentName = value;
}

private String functionId;

public String getFunctionId(){
return this.functionId;
}

public void setFunctionId(String value){
this.functionId = value;
}

public FunctionScore(){

}

public FunctionScore(int score, String componentName, String functionId){
this.score = score;
this.componentName = componentName;
this.functionId = functionId;
}
}
Loading

0 comments on commit d0abf2c

Please sign in to comment.