-
Notifications
You must be signed in to change notification settings - Fork 363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add function complexity and size metrics #1398
Merged
guwirth
merged 19 commits into
SonarOpenCommunity:master
from
ericlemes:dump-cc-and-loc
Jun 12, 2018
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
165f8e9
Adding extension point to CxxSquidSensor
ericlemes 0a77b27
Adding Complex Functions metrics, returning number of functions over …
ericlemes c7eac35
Implementing % of complex functions metric.
ericlemes aa912ae
Adding Complex Functions metrics, returning number of functions over …
ericlemes 0b1e6f3
Implements Big Functions and % of Big Functions metrics, controlled b…
ericlemes 5381792
Dumping list of ranked functions by cyclomatic complexity and lines o…
ericlemes f2940bd
More metrics.
ericlemes d09de2c
Implementing lines of code in functions metric.
ericlemes 15c1c63
Removing new metrics from C Plugin
ericlemes 7696b0d
Removing the Scanner side file dumps.
ericlemes a31b02e
Registering new SquidSensors only for C++ language.
ericlemes 6993e23
Adding checks and tests to avoid NaN double metrics.
ericlemes dc0cbed
- Removing metric definitions from this project
ericlemes c37398b
Revert "- Removing metric definitions from this project"
ericlemes 2f729f2
Fixing license header and tests after rebase.
ericlemes 5e13013
Fixing a lot of minor requests on the Code Review.
ericlemes 42d62d3
Fixing package-info description
ericlemes 3cfc07e
Renaming metrics as suggested in the PR review.
ericlemes 68d6fd7
Style changes.
ericlemes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
216 changes: 216 additions & 0 deletions
216
.../main/java/org/sonar/cxx/sensors/functioncomplexity/CxxFunctionComplexitySquidSensor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove unused imports. |
||
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; | ||
} | ||
} | ||
|
||
} |
63 changes: 63 additions & 0 deletions
63
...ors/src/main/java/org/sonar/cxx/sensors/functioncomplexity/FunctionComplexityMetrics.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
||
} |
26 changes: 26 additions & 0 deletions
26
cxx-sensors/src/main/java/org/sonar/cxx/sensors/functioncomplexity/FunctionCount.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
62 changes: 62 additions & 0 deletions
62
cxx-sensors/src/main/java/org/sonar/cxx/sensors/functioncomplexity/FunctionScore.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add package-info.java.
squid:S1228 - Packages should have a javadoc file 'package-info.java'