Skip to content
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
merged 19 commits into from
Jun 12, 2018

Conversation

ericlemes
Copy link
Contributor

@ericlemes ericlemes commented Jan 19, 2018

Contains "Complex Functions" metric, which counts number of function that has cyclomatic complexity greater than sonar.cxx.funccomplexity.threshold (default 10). "% of Complex Functions" is the same metric expressed as a percent ((complex functions * 100)/total functions).

"Big functions", counts number of functions with more than sonar.cxx.funcsize.threshold (default 20). "% of Big Functions" is the same expressed as a percent.


This change is Reviewable

@Bertk
Copy link
Contributor

Bertk commented Jan 20, 2018

@ericlemes Hi, please just create another commit and sync it or close obsolete PRs.

SonarQube server fails to start:

2018.01.19 14:46:03 INFO  web[][o.s.s.s.GeneratePluginIndex] Generate scanner plugin index
2018.01.19 14:46:04 INFO  web[][o.s.s.s.RegisterPlugins] Register plugins
2018.01.19 14:46:04 ERROR web[][o.s.s.p.Platform] Background initialization failed. Stopping SonarQube
java.lang.IllegalStateException: Metric [complex_functions] is already defined by the repository [org.sonar.cxx.sensors.functioncomplexity.FunctionComplexityMetrics@1713eca4]
	at org.sonar.server.startup.RegisterMetrics.checkMetrics(RegisterMetrics.java:127)
	at org.sonar.server.startup.RegisterMetrics.getPluginMetrics(RegisterMetrics.java:112)
	at org.sonar.server.startup.RegisterMetrics.start(RegisterMetrics.java:63)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.picocontainer.lifecycle.ReflectionLifecycleStrategy.invokeMethod(ReflectionLifecycleStrategy.java:110)
	at org.picocontainer.lifecycle.ReflectionLifecycleStrategy.start(ReflectionLifecycleStrategy.java:89)
	at org.picocontainer.injectors.AbstractInjectionFactory$LifecycleAdapter.start(AbstractInjectionFactory.java:84)
	at org.picocontainer.behaviors.AbstractBehavior.start(AbstractBehavior.java:169)
	at org.picocontainer.behaviors.Stored$RealComponentLifecycle.start(Stored.java:132)
	at org.picocontainer.behaviors.Stored.start(Stored.java:110)
	at org.picocontainer.DefaultPicoContainer.potentiallyStartAdapter(DefaultPicoContainer.java:1016)
	at org.picocontainer.DefaultPicoContainer.startAdapters(DefaultPicoContainer.java:1009)
	at org.picocontainer.DefaultPicoContainer.start(DefaultPicoContainer.java:767)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:134)
	at org.sonar.server.platform.platformlevel.PlatformLevel.start(PlatformLevel.java:90)
	at org.sonar.server.platform.platformlevel.PlatformLevelStartup.access$001(PlatformLevelStartup.java:45)
	at org.sonar.server.platform.platformlevel.PlatformLevelStartup$1.doPrivileged(PlatformLevelStartup.java:83)
	at org.sonar.server.user.DoPrivileged.execute(DoPrivileged.java:45)
	at org.sonar.server.platform.platformlevel.PlatformLevelStartup.start(PlatformLevelStartup.java:80)
	at org.sonar.server.platform.Platform.executeStartupTasks(Platform.java:196)
	at org.sonar.server.platform.Platform.access$400(Platform.java:46)
	at org.sonar.server.platform.Platform$1.lambda$doRun$1(Platform.java:121)
	at org.sonar.server.platform.Platform$AutoStarterRunnable.runIfNotAborted(Platform.java:371)
	at org.sonar.server.platform.Platform$1.doRun(Platform.java:121)
	at org.sonar.server.platform.Platform$AutoStarterRunnable.run(Platform.java:355)
	at java.lang.Thread.run(Thread.java:748)
2018.01.19 14:46:04 INFO  web[][o.s.p.StopWatcher] Stopping process

@ericlemes
Copy link
Contributor Author

Hi @Bertk, they are 3 independent pull requests. None are obsolete. I can merge them in one, if this facilitates the review process.

@Bertk
Copy link
Contributor

Bertk commented Jan 20, 2018

@ericlemes Hi,
the last PR also contains the other commits. Merging this PR is sufficient.
Cheers Bert
dumping list of ranked functions

@ericlemes
Copy link
Contributor Author

@Bertk, you are right. It contains the other commits. I'll close the other PR's then. I still didn't understand why it is not starting sonar server. My local version works. I'll investigate further.

@ericlemes ericlemes changed the title Dump cc and loc Function complexity and size metrics Jan 20, 2018
@ericlemes
Copy link
Contributor Author

The reason why it is failing is because I'm registering the metrics on both plugins (C and C++) and they have the same ID. I'm not sure which is the right way of doing this and will investigate further. If you load only one once, works fine.

I'm not sure if the right way here is having one different metric ID per language (does not make much sense for me), or have one separated plugin just to register the metrics.

@guwirth
Copy link
Collaborator

guwirth commented Jan 21, 2018

@ericlemes before going into details:

  • Could you explain a little bit what the intention of the metrics is and how they are to use?
  • What can someone derive for his code base using these metrics?

@ericlemes
Copy link
Contributor Author

Hi @guwirth

The goals here are:

  • Have a percentage of code that is hard to maintain, according to lines of code per function criteria (as suggested by Clean Code)
  • Have a percentage of code that is hard to maintain, according to cyclomatic complexity criteria (as suggested by McCabe)
  • Have a metric that can be easily compared different code bases.

What someone can derive is:

  • How much of my code base needs refactoring?
  • How well crafted is my source code compared to other code bases?

Regards,

Eric

@guwirth
Copy link
Collaborator

guwirth commented Jan 22, 2018

@ericlemes

Have a percentage of code that is hard to maintain, according to cyclomatic complexity criteria (as suggested by McCabe)

Here the question is if cyclomatic complexity or cognitive complexity is the right choice to us.

  • cyclomatic complexity: is testability
  • cognitive complexity: is readability

Somewhere I saw / read that you like to create files on server side. Think that's not a good idea because it's not ensured to have write access. Right way would be to create a REST API to ask for it?

@ericlemes
Copy link
Contributor Author

Hi @guwirth,

I'd keep both complexities. The way the metric is expressed as %, can be done for cognitive complexity as well.

In terms of the file dump. What I'm trying to do is just the "drill down" per function. Since sonar doesn't have a mechanism which it can be sent to the server (measure per function instead of per file), my implementation just dumps to a file (scanner side) if the file name is provided as a property. I'm investigating options of how to upload this.

I've tested these metrics in my sonar instance, and I'm quite happy with the results. I'm thinking about implementing another one which is the same idea of expressing as a %, but expressing % of Lines of Code in Functions with more than 20 lines of code and % of lines of code in functions with more than cyclomatic complexity 10. The idea is just to observe what is the distortion in the %. Probably one of both would be enough, but I personally don't see harm in keeping both views.

I really enjoyed how the % of functions with cyclomatic complexity is more representative than the avg cc/function. In some repos I've analysed, the avg cc/function has a variation of .5 CC/function between 2 repos. Using the % of complex functions, the same 2 repos has difference of 1.5% of code in complex functions.

@ericlemes
Copy link
Contributor Author

So guys, after a while trying to figure out what is going on here, I finally got to the point. A lot of learning about sonar plugin building going on.

The simple explanation is: I've defined new metrics in both plugins. You can't do that. Metrics should have a unique key and be shared across plugins.

There are 2 fixes for this, one that is hack, the other one which is sounds right, but requires more work. I'm happy to do the work, but I'd like to have an idea about how likely is for this PR being merged before I do a lot of work then throw everything on the bin. To cut to the chase, I feel that you are not comfortable with the new metrics at all.

Fix 1 (the hack): Define 2 different metrics, with different keys. One for C the other one for C++. The result won't be consolidated. I don't like this. Because I believe these metrics can be consolidated between different languages.
Fix 2: Define the metrics on another plugin, that defines the metrics and compute the consolidated numbers (using a MeasureComputer). This plugin with these new metrics can be shared with other languages that want to implement the same metrics (for example, C#, Java or TypeScript), and ideally should be in a new project at the same level of sonar-cxx (I'm really accepting name suggestions). Then make the C and C++ plugin implementing these extra metrics.

I'd like to hear your thoughts before moving forward, so I can manage my expectations.

Regards

Eric

@guwirth
Copy link
Collaborator

guwirth commented Jan 25, 2018

@ericlemes like the ideas of the new metrics. I'm not sure if the c plugin has a future. So maybe we combine it anyway to one plugin again. Think what should not be done is to write local files during scanner run. So most likely fix 1 is ok.

@ericlemes
Copy link
Contributor Author

@guwirth nice to hear that.

I think the discussion about the new metrics in a separated, non cxx project would make sense anyway. I'm not sure if the future of the c plugin would be blocking this discussion.

Regarding the files, I just got some hope about that. I was chatting in the sonar list, and apparently it is possible to push this data and display in a custom JS page. I'll put some effort on that, but I'm happy to take this off the plugin to simplify the merge.

So, can you help me giving some sort of "steps" we can follow together to help this code and this PR be accepted? In my point of view they are:

  1. Create a separated project only defined in the metrics. The reason is that other languages could push the same metrics.
  2. Remove the "file dump" code from existing PR
  3. Adapt existing PR to cope with separated metrics project and make sure C and C++ plugin works.
  4. Merge the PR.

Does that make sense?

Cheers

Eric

@ericlemes
Copy link
Contributor Author

Hey @guwirth,

I did some changes, removing the file dumps and code from C Plugin. Now apparently some integration tests are breaking and I have no idea why, since I'm not dumping any new warnings.

Can you help me?

@Bertk
Copy link
Contributor

Bertk commented Jan 31, 2018

Hi,

2 scenarios of the BDD tests are failing

[00:09:32] Failing scenarios:
[00:09:32]   integration-tests\features\multimodule_analysis.feature:5  cpp-multimodule-project
[00:09:32]   integration-tests\features\x_importing_cppcheck_reports_c_cpp.feature:19  Importing cppcheck issues when c language issues are in report. -- @1.1 

@SqApi67
Feature: cpp-multimodule-project
  Test multimodule project with reports at root of the project
  Scenario: cpp-multimodule-project 
    Given the project "cpp-multimodule-project"
    When I run "sonar-scanner -X"
    Then the analysis finishes successfully
    And the analysis in server has completed
    Read Log : _cpp-multimodule-project_.log
     Get Analysis In Background : http://localhost:9000/api/ce/task?id=AWFMoDBhMEyVgfEFWJcT
     CURRENT STATUS : PENDING
     CURRENT STATUS : FAILED
      Assertion Failed: Analysis in Background Task Failed
@SqApi67
Feature: Importing Cppcheck ANSI-C reports
  Scenario Outline: Importing cppcheck issues when c language issues are in report. -- @1.1  
    Given the project "cppcheck_project_c_cpp"
    And declared header extensions of language c++ are ".hpp,.hh"
    And declared source extensions of language c++ are ".cpp,.cc"
    When I run "sonar-scanner -X -Dsonar.cxx.cppcheck.reportPath=cppcheck-v2.xml"
    Then the analysis finishes successfully
      Assertion Failed: Exit code is 1, but should be zero
    And the analysis in server has completed
    And the server log (if locatable) contains no error/warning messages

The log file shows the exception and the log files are located here:
https://ci.appveyor.com/project/SonarOpenCommunity/sonar-cxx/build/1180/artifacts

Please see cppcheck_project_c_cpp.log

14:32:43.944 ERROR: Error during SonarQube Scanner execution
java.lang.UnsupportedOperationException: Can not add the same measure twice on [key=cppcheck_project_c_cpp]: DefaultMeasure[component=[key=cppcheck_project_c_cpp],metric=Metric[id=<null>,key=complex_functions,description=Number of functions with high cyclomatic complexity,type=INT,direction=-1,domain=Complexity,name=Complex Functions,qualitative=false,userManaged=false,enabled=true,worstValue=<null>,bestValue=<null>,optimizedBestValue=false,hidden=false,deleteHistoricalData=false,decimalScale=<null>],value=0,fromCore=false]
	at org.sonar.scanner.sensor.DefaultSensorStorage.saveMeasure(DefaultSensorStorage.java:268)
	at org.sonar.scanner.sensor.DefaultSensorStorage.store(DefaultSensorStorage.java:212)
	at org.sonar.api.batch.sensor.measure.internal.DefaultMeasure.doSave(DefaultMeasure.java:95)
	at org.sonar.api.batch.sensor.internal.DefaultStorable.save(DefaultStorable.java:45)
	at org.sonar.cxx.sensors.functioncomplexity.CxxFunctionComplexitySquidSensor.publishComplexFunctionMetrics(CxxFunctionComplexitySquidSensor.java:180)
	at org.sonar.cxx.sensors.functioncomplexity.CxxFunctionComplexitySquidSensor.publishMeasureForProject(CxxFunctionComplexitySquidSensor.java:171)
	at org.sonar.cxx.sensors.squid.CxxSquidSensor.save(CxxSquidSensor.java:250)
	at org.sonar.cxx.sensors.squid.CxxSquidSensor.execute(CxxSquidSensor.java:185)
	at org.sonar.scanner.sensor.SensorWrapper.analyse(SensorWrapper.java:53)
	at org.sonar.scanner.phases.SensorsExecutor.executeSensor(SensorsExecutor.java:88)
	at org.sonar.scanner.phases.SensorsExecutor.execute(SensorsExecutor.java:82)
	at org.sonar.scanner.phases.SensorsExecutor.execute(SensorsExecutor.java:68)
	at org.sonar.scanner.phases.AbstractPhaseExecutor.execute(AbstractPhaseExecutor.java:88)
	at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:180)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:121)
	at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:288)
	at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:283)
	at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:261)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:121)
	at org.sonar.scanner.task.ScanTask.execute(ScanTask.java:48)
	at org.sonar.scanner.task.TaskContainer.doAfterStart(TaskContainer.java:84)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:121)
	at org.sonar.scanner.bootstrap.GlobalContainer.executeTask(GlobalContainer.java:121)
	at org.sonar.batch.bootstrapper.Batch.doExecuteTask(Batch.java:116)
	at org.sonar.batch.bootstrapper.Batch.executeTask(Batch.java:111)
	at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:63)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
	at com.sun.proxy.$Proxy0.execute(Unknown Source)
	at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:233)
	at org.sonarsource.scanner.api.EmbeddedScanner.runAnalysis(EmbeddedScanner.java:151)
	at org.sonarsource.scanner.cli.Main.runAnalysis(Main.java:123)
	at org.sonarsource.scanner.cli.Main.execute(Main.java:77)
	at org.sonarsource.scanner.cli.Main.main(Main.java:61)

Best regards
Bert

@ericlemes
Copy link
Contributor Author

Thanks @Bertk . After I learnt how to read the logs, I finally managed to fix all the problems.

@guwirth
Copy link
Collaborator

guwirth commented Feb 2, 2018

@ericlemes is it ready to review?

@ericlemes
Copy link
Contributor Author

@guwirth, yes it is.

Copy link
Contributor

@Bertk Bertk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice work 👍

* 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;
Copy link
Contributor

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'

public class FunctionScoreComparator implements Comparator<FunctionScore>{

@Override
public int compare(FunctionScore o1, FunctionScore o2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a unit test.
common-java:InsufficientLineCoverage - Lines should have sufficient coverage by tests

*/
package org.sonar.cxx.sensors.functioncomplexity;

import java.io.ByteArrayOutputStream;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove unused imports.

*/
package org.sonar.cxx.sensors.functioncomplexity;

import com.sonar.sslr.api.AstNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove unused imports.


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add log guard
rule pmd:GuardLogStatement

public static final Metric<Integer> BIG_FUNCTIONS = new Metric.Builder("big_functions", "Big Functions", Metric.ValueType.INT)
.setDescription("Number of functions with too many lines")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use Boolean.FALSE
rule fb-contrib:NAB_NEEDLESS_BOOLEAN_CONSTANT_CONVERSION

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(false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use Boolean.FALSE
rule fb-contrib:NAB_NEEDLESS_BOOLEAN_CONSTANT_CONVERSION


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add if (LOG.isDebugEnabled())


public CxxFunctionSizeSquidSensor(CxxLanguage language){
this.sizeThreshold = language.getIntegerOption(FUNCTION_SIZE_THRESHOLD_KEY).orElse(20);
LOG.debug("Function size threshold: " + this.sizeThreshold);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add if (LOG.isDebugEnabled())

}

protected void registerSquidSensors(){
if (this.language.getKey() == "c++"){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use "c++" use constant for plugin key instead

@@ -40,7 +40,7 @@
public CxxLanguage(String key, Configuration settings) {
super(key);
this.settings = settings;
this.MetricsCache = new HashMap<>();
this.MetricsCache = new HashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove blanks at eol

@@ -89,7 +93,7 @@
public static final String JSON_COMPILATION_DATABASE_KEY = LANG_PROP_PREFIX + "jsonCompilationDatabase";
public static final String SCAN_ONLY_SPECIFIED_SOURCES_KEY = LANG_PROP_PREFIX + "scanOnlySpecifiedSources";
public static final String CPD_IGNORE_LITERALS_KEY = LANG_PROP_PREFIX + "cpd.ignoreLiterals";
public static final String CPD_IGNORE_IDENTIFIERS_KEY = LANG_PROP_PREFIX + "cpd.ignoreIdentifiers";
public static final String CPD_IGNORE_IDENTIFIERS_KEY = LANG_PROP_PREFIX + "cpd.ignoreIdentifiers";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove blanks at eol

@guwirth
Copy link
Collaborator

guwirth commented Feb 3, 2018

keys in settings file:

sonar.cxx.funccomplexity.threshold
sonar.cxx.funcsize.threshold

Wouldn't it be better something like?

sonar.cxx.measure.func.complexity.threshold
sonar.cxx.measure.func.size.threshold

@guwirth
Copy link
Collaborator

guwirth commented Feb 3, 2018

@ericlemes how does the results look like in the UI? Can you add a screenshot please?

@guwirth
Copy link
Collaborator

guwirth commented Feb 3, 2018

@ericlemes question: think the metrics are very generic and could be used for all languages. Wouldn't it be better to add it to an own plugin? Like to avoid that we have it in one version and one version later it's removed again.

@guwirth
Copy link
Collaborator

guwirth commented Feb 3, 2018

@ericlemes solution I expected to solve this was:

  • add new rule/check to create messages if over threshold
  • define threshold in rule/check
  • count rule violations and add result to a measure

Why did you select this approach?

@guwirth
Copy link
Collaborator

guwirth commented Feb 3, 2018

@ericlemes code looks good 👍

@ericlemes
Copy link
Contributor Author

ericlemes commented Feb 5, 2018

Thanks @Bertk and @guwirth for the review. There's a lot going on here, but I'm glad that apart from all the requests you like the code ;-)

First question to @Bertk: Where did you get all this feedback about the rules? Is there any instance of Sonar that the CI is pushing information? I was struggling to get this, run the integration tests locally and the code coverage locally, and that's one of the reasons that I got loads of violations. If you can help me setting up a good environment, I can definitely be more productive. And Java is probably my 3rd language, so I know very little about the environment.

Second question to @guwirth. I totally agree with you. I think the metrics are very generic and should be implemented in a different plugin. But, I'd really like your thoughts and guidance on that. First, if they are generic and could be implemented to other languages (which I'm intending to do), I guess they should be outside sonar-cxx project. I was really thinking about something like sonar-opencommunity-metrics in a new github project. I'm definitely help to implement like that but I'd like to agree on general design before spending a lot of time refactoring everything. Also, I was really struggling with maven/sonar stuff to create a new project. If you could create a skeleton on Git Hub which I could fork, that would definitely not only save me time, but help us to be more assertive.

Generally speaking, I'm really happy that you guys are keen to accept this code. I personally doing a lot of experimentation with the metrics to understand how the numbers can be generic and meaningful at the same time.

I'm planning to do all the reviews, but I'd really would like a definition about the new plugin structure before moving forward. Can you help me with this?

@ericlemes
Copy link
Contributor Author

Here is the screenshot. The numbers are from WebKit project.

screenshot

- Adding reference to sonar-opencommunity-metrics
- Changing all code to reference metrics defined in sonar-opencommunity-metrics, which now is responsible for the aggregated numbers.
@guwirth guwirth added this to the 1.1 milestone Jun 9, 2018
@guwirth
Copy link
Collaborator

guwirth commented Jun 9, 2018

@ericlemes please tell us when we can do final review.

@guwirth guwirth requested review from ivangalkin and removed request for jmecosta June 9, 2018 12:10
Copy link
Contributor

@ivangalkin ivangalkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with that API, so my comments are related to the style only:

  1. Please remove the trailing whitespaces
  2. Please consider using if (condition) { statement; } instead of if (condition) statement; even for a single-line statements

Also I am not sure about the visualization of this metric. My experience with the similar complexity issues reminds me about the breakdown of a the total complexity score like

<primary location> Line X: Total complexity is 10
<secondary location 0> Line X': Nested if +2
<secondary location 1> Line X'': Nested for loop +2
<secondary location 2> Line X''': Nested if +2 etc

Will the warning have this level of details?

@ericlemes
Copy link
Contributor Author

@ivangalkin, I've fixed your style comments.

Related to the visualization of the metric, I think your comment does not apply. This metrics does not raise any issues in the code. Thy only present high level metrics, based on all the code base.

@ericlemes
Copy link
Contributor Author

@guwirth Ready for final review.

Copy link
Contributor

@ivangalkin ivangalkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ericlemes thx for applying my review comments. I believe that writing for() { statement; } instead of for () statement; would be better (less error-prone) too. Same for while. It's up to you if you want to fix it.

@guwirth guwirth changed the title Function complexity and size metrics Add function complexity and size metrics Jun 12, 2018
@guwirth guwirth merged commit d0abf2c into SonarOpenCommunity:master Jun 12, 2018
@guwirth
Copy link
Collaborator

guwirth commented Jun 12, 2018

Hi @ericlemes,
Thanks a lot for your contribution!
Regards,

@ericlemes
Copy link
Contributor Author

Hey @guwirth. You are welcome.
Do you have any idea when this will be released?

ivangalkin added a commit to ivangalkin/sonar-cxx that referenced this pull request Jul 19, 2018
Redesign of language-specific metrics
The following problems were identified and solved:

PROBLEM 1
* CxxLanguage::MetricsCache was not filled while
  construction of CxxLanguage object but only in
  CxxSquidSensor::CxxSquidSensor()

SOLUTION
* Introduce CxxMetricsFactory.
* It has no dependencies to sensors.
* It creates language-dependent metrics in the constructor
  of CxxSquidSensor

PROBLEM 2
* During the test MetricsCache was almost always empty.
  In order to prevent warnings (?) missing metrics' lookups
  were just ignored

SOLUTON
* Introduce `protected abstract Optional<CxxMetricsFactory.Key> CxxReportSensor::getMetricKey();`
* By means of Optional<> semantic sensors show if metric is required or not
* CxxLanguage::getMetric() checks thows an exception if metric hasn't been found

PROBLEM 3
* CxxLanguage::getMetric(<required metric key>) expected the key to be typed as String
* In order to fill the MetricsCache the format {METRIC_KEY} was used
* Cache lookups were done however in the format {LANG_PROP_KEY}-{METRIC_KEY}

SOLUTION
* In order to make the lookup key type-safe and less error-prone I introduced the type
  `CxxMetricsFactory.Key`

PROBLEM 4a
* The plugin-specific metrics for public API were missing on module level
  All CoreMetrics (incls. former Public API) have automatic aggregation:
  1. ComputeEngine implements some server-side task
  2. This task iterates over the project hierarchy
     `FILE -> DIRECTORY/MODULE -> ... -> ... -> PROJECT`
     and performs a recursive sum for all metrics raised
     on children component
  3. This sum is stored as aggregated metric for the parent component
  4. The number of hierarchy levels can be arbitrary

  5. Starting with version 6.2 the public API metrics were
     deprecated and Sonar-CXX introduces its own plugin-specific
     public API metrics
  6. These custom metrics are not aggregated automatically
     anymore

PROBLEM 4b
* PR SonarOpenCommunity#1398 introduced 9 new complexity metrics
  They supported the aggregation of the file-level metrics.
  The aggregation was implemeted inside of the `AstVisitor`,
  however the aggregation was done for the parent module only.

  Ths solution worked for the simple SonarQube projects only.
  Due to hierarchy depth == 2, aggregated results were stored
  on the project level. Multi-module projects however might
  have an arbitrary depth. So there was no proper aggregation
  for such projects.

  After public API metrics were deprecated as CoreMetrics
  the automatic aggregation
  It was not enough to set them on the file level. One had to aggregate these
  measurements and set the result on the module level. Otherwise
  they were not displayed in the SonarQube's Activity UI

SOLUTION
* Unify implementation of custom metrics for Public-API and complexity
* Implement 2 Compute Engine tasks (see `MeasureComputer`), which perform
  the aggregation and computation of these metrics
* Computaton/aggregation works for both types of SonarQube project -
  the simple and the multi-module ones

PROBLEM 5
* The metric `CoreMetrics.COGNITIVE_COMPLEXITY` was measured but not persisted

MISC
* Test has been added
* Minor refactoring and comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

4 participants