C++插件调试

wuhaoyang
Guo XIn 10 months ago
parent 02d782f556
commit 8d3f922056
  1. 19
      sonar-keyware-plugins-cxx/pom.xml
  2. 62
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CustomCxxRulesDefinition.java
  3. 76
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxChecks.java
  4. 91
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxLanguage.java
  5. 29
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxMetricDefinition.java
  6. 164
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java
  7. 25
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxRuleRepository.java
  8. 56
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSonarWayProfile.java
  9. 564
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java
  10. 117
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/DroppedPropertiesSensor.java
  11. 4
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/CxxSecurityDesignRulesRepository.java
  12. 91
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CustomCxxRulesDefinitionTest.java
  13. 21
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxCheckListTest.java
  14. 147
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxChecksTest.java
  15. 104
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxFileLinesContextTest.java
  16. 228
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxHighlighterTest.java
  17. 55
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxLanguageTest.java
  18. 20
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxMetricDefinitionTest.java
  19. 34
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxPluginTest.java
  20. 26
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxRuleRepositoryTest.java
  21. 30
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxSonarWayProfileTest.java
  22. 237
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxSquidSensorTest.java
  23. 73
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/DroppedPropertiesSensorTest.java
  24. 73
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/TestUtils.java
  25. 12
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-includes-project/sonar-project.properties
  26. 5
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-includes-project/test1.hh
  27. 5
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-includes-project/test2.hh
  28. 5
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-packages-project/Package1/test1.hh
  29. 4
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-packages-project/Package2/test2.hh
  30. 6
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-packages-project/Package2/test3.hh
  31. 5
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-packages-project/Package2/test4.hh
  32. 12
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/circular-packages-project/sonar-project.properties
  33. 91
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/codechunks-project/code_chunks.cc
  34. 10
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/codechunks-project/sonar-project.properties
  35. 128
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/complexity-project/complexity.cc
  36. 11
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/complexity-project/sonar-project.properties
  37. 104
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/cpd.cc
  38. 36
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/documentation-project/documentation0.hh
  39. 11
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/documentation-project/sonar-project.properties
  40. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/external-macro-project/test.cc
  41. 15
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/sonar-project.properties
  42. 0
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/src/.dummy
  43. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/Test1.cc
  44. 2
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/Test5.cc
  45. 4
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/Test6.hh
  46. 2
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/Test6_A.cc
  47. 2
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/Test6_B.cc
  48. 4
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/Test7.cc
  49. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests1/subdir/Test2.cc
  50. 3
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests2/Test3.cc
  51. 2
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests2/Test4.cc
  52. 5
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/tests2/Test4.hh
  53. 12
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/finding-sources-project/xunit-report.xml
  54. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/force-include-project/include/force1.hh
  55. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/force-include-project/include/subfolder/force2.hh
  56. 14
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/force-include-project/sonar-project.properties
  57. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/force-include-project/src/scr2.cc
  58. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/force-include-project/src/src1.cc
  59. 125
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/highlighter.cc
  60. 7
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/highlighter.h
  61. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/HEADER1.hh
  62. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/HEADER2.hh
  63. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/bar.hh
  64. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/include1.hh
  65. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/include2.hh
  66. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/include3.hh
  67. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/subfolder/include4.hh
  68. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include/widget.hh
  69. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include_snd/include_snd_1.hh
  70. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/include_snd/subfolder/include_snd_subfolder_1.hh
  71. 14
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/sonar-project.properties
  72. 29
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/include-directories-project/src/main.cc
  73. 129
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/ncloc.cc
  74. 7
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/ncloc.h
  75. 1
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/source.cc

@ -18,7 +18,7 @@
<properties>
<sonar.artifact.path>target/${project.artifactId}-${project.version}.jar</sonar.artifact.path>
<sonar.pluginClass>com.keyware.sonar.cxx.CxxSecurityDesignPlugin</sonar.pluginClass>
<sonar.pluginClass>com.keyware.sonar.cxx.CxxPlugin</sonar.pluginClass>
<sonar.pluginName>C++ 信息安全性设计准则</sonar.pluginName>
<!-- in addition, a dependency must be set in 'integration-tests/pom.xml' to aggregate the results -->
<sonar.coverage.jacoco.xmlReportPaths>${basedir}/../${aggregate.report.dir}</sonar.coverage.jacoco.xmlReportPaths>
@ -30,7 +30,7 @@
<!-- we depend on API ${sonar.version} but we keep backward compatibility with LTS -->
<sonar.version>9.9.0.65466</sonar.version>
<sonar.plugin.api.version>9.14.0.375</sonar.plugin.api.version>
<sonarQubeMinVersion>8.9</sonarQubeMinVersion>
<pluginApiMinVersion>8.9</pluginApiMinVersion>
<gson.version>2.10.1</gson.version>
<guava.version>33.0.0-jre</guava.version>
<jsr305.version>3.0.2</jsr305.version>
@ -51,12 +51,6 @@
<artifactId>sonar-plugin-api-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.sonarsource.sonarqube-plugins.cxx</groupId>
<artifactId>sonar-cxx-plugin</artifactId>
<version>${sonar-cxx.versin}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.sonarsource.sonarqube-plugins.cxx</groupId>
<artifactId>cxx-squid</artifactId>
@ -123,15 +117,8 @@
<plugin>
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
<artifactId>sonar-packaging-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<pluginKey>keywareCxxPlugin</pluginKey>
<pluginName>C++ 安全性设计准则</pluginName>
<pluginClass>com.keyware.sonar.cxx.CxxSecurityDesignRulesPlugin</pluginClass>
<sonarLintSupported>true</sonarLintSupported>
<skipDependenciesPackaging>true</skipDependenciesPackaging>
<pluginApiMinVersion>9.14.0.375</pluginApiMinVersion>
<requirePlugins>java:${project.version}</requirePlugins>
<pluginApiMinVersion>${pluginApiMinVersion}</pluginApiMinVersion>
</configuration>
</plugin>

@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.sonar.api.resources.Language;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.cxx.squidbridge.annotations.AnnotationBasedRulesDefinition;
import java.util.Arrays;
/**
*
* @author jocs
*/
@ScannerSide
public abstract class CustomCxxRulesDefinition implements RulesDefinition {
@Override
public void define(Context context) {
var repo = context.createRepository(repositoryKey(), getLanguage().getKey())
.setName(repositoryName());
// Load metadata from check classes' annotations
new AnnotationBasedRulesDefinition(repo, getLanguage().getKey()).addRuleClasses(false,
Arrays.asList(checkClasses()));
repo.done();
}
/**
* Name of the custom rule repository.
*
* @return
*/
public abstract Language getLanguage();
/**
* Name of the custom rule repository.
*
* @return
*/
public abstract String repositoryName();
/**
* Key of the custom rule repository.
*
* @return
*/
public abstract String repositoryKey();
/**
* Array of the custom rules classes.
*/
@SuppressWarnings("rawtypes")
public abstract Class[] checkClasses();
}

@ -0,0 +1,76 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.sonar.cxx.sslr.api.Grammar;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.rule.Checks;
import org.sonar.api.rule.RuleKey;
import org.sonar.cxx.squidbridge.SquidAstVisitor;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.util.*;
public final class CxxChecks {
private final CheckFactory checkFactory;
private final Set<Checks<SquidAstVisitor<Grammar>>> checksByRepository = new HashSet<>();
private CxxChecks(CheckFactory checkFactory) {
this.checkFactory = checkFactory;
}
public static CxxChecks createCxxCheck(CheckFactory checkFactory) {
return new CxxChecks(checkFactory);
}
@SuppressWarnings("rawtypes")
public CxxChecks addChecks(String repositoryKey, Iterable<Class> checkClass) {
checksByRepository.add(checkFactory
.<SquidAstVisitor<Grammar>>create(repositoryKey)
.addAnnotatedChecks(checkClass));
return this;
}
public CxxChecks addCustomChecks(@Nullable CustomCxxRulesDefinition[] customRulesDefinitions) {
if (customRulesDefinitions != null) {
for (var rulesDefinition : customRulesDefinitions) {
addChecks(rulesDefinition.repositoryKey(), new ArrayList<>(Arrays.asList(rulesDefinition.checkClasses())));
}
}
return this;
}
public List<SquidAstVisitor<Grammar>> all() {
var allVisitors = new ArrayList<SquidAstVisitor<Grammar>>();
for (var checks : checksByRepository) {
allVisitors.addAll(checks.all());
}
return allVisitors;
}
@CheckForNull
public RuleKey ruleKey(SquidAstVisitor<Grammar> check) {
for (var checks : checksByRepository) {
RuleKey ruleKey = checks.ruleKey(check);
if (ruleKey != null) {
return ruleKey;
}
}
return null;
}
public Set<Checks<SquidAstVisitor<Grammar>>> getChecks() {
return new HashSet<>(checksByRepository);
}
}

@ -0,0 +1,91 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import org.sonar.api.config.Configuration;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.AbstractLanguage;
import org.sonar.api.resources.Qualifiers;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* {@inheritDoc}
*/
public class CxxLanguage extends AbstractLanguage {
/**
* cxx language key
*/
public static final String KEY = "cxx";
/**
* cxx language name
*/
public static final String NAME = "CXX";
/**
* Key of the file suffix parameter
*/
public static final String FILE_SUFFIXES_KEY = "sonar.cxx.file.suffixes";
/**
* Default cxx files knows suffixes
*/
public static final String DEFAULT_FILE_SUFFIXES = "-";
/**
* Settings of the plugin.
*/
private final Configuration config;
public CxxLanguage(Configuration config) {
super(KEY, NAME);
this.config = config;
}
public static List<PropertyDefinition> properties() {
return Collections.unmodifiableList(Arrays.asList(
PropertyDefinition.builder(FILE_SUFFIXES_KEY)
.defaultValue(DEFAULT_FILE_SUFFIXES)
.name("File suffixes")
.multiValues(true)
.description(
"List of suffixes for files to analyze (e.g. `.cxx,.cpp,.cc,.c,.hxx,.hpp,.hh,.h`)."
+ " In the SonarQube UI, enter one file suffixe per field."
+ " To turn off the CXX language, set the first entry to `-`."
)
.category("CXX")
.subCategory("(1) General")
.onQualifiers(Qualifiers.PROJECT)
.build()
));
}
/**
* {@inheritDoc}
*
* @see AbstractLanguage#getFileSuffixes()
*/
@Override
public String[] getFileSuffixes() {
String[] suffixes = Arrays.stream(config.getStringArray(FILE_SUFFIXES_KEY))
.filter(s -> s != null && !s.trim().isEmpty()).toArray(String[]::new);
if (suffixes.length == 0) {
suffixes = Iterables.toArray(Splitter.on(',').split(DEFAULT_FILE_SUFFIXES), String.class);
}
if ("-".equals(suffixes[0])) {
suffixes = new String[]{"disabled"};
}
return suffixes;
}
}

@ -6,20 +6,19 @@
*/
package com.keyware.sonar.cxx;
import com.keyware.sonar.cxx.rules.CxxSecurityDesignRulesRepository;
import org.sonar.api.Plugin;
import org.sonar.plugins.cxx.CxxLanguage;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.Metrics;
import org.sonar.cxx.CxxMetrics;
/**
* TODO CxxSecurityDesignRulesPlugin
*
* @author GuoXin
* @date 2024/1/6
*/
public class CxxSecurityDesignRulesPlugin implements Plugin {
@Override
public void define(Context context) {
context.addExtension(CxxLanguage.class);
context.addExtension(CxxSecurityDesignRulesRepository.class);
}
import java.util.Collections;
import java.util.List;
public class CxxMetricDefinition implements Metrics {
@Override
public List<Metric> getMetrics() {
return Collections.unmodifiableList(
CxxMetrics.getMetrics()
);
}
}

@ -0,0 +1,164 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.keyware.sonar.cxx.rules.CxxSecurityDesignRulesRepository;
import org.sonar.api.Plugin;
import org.sonar.cxx.AggregateMeasureComputer;
import org.sonar.cxx.DensityMeasureComputer;
import org.sonar.cxx.postjobs.FinalReport;
import org.sonar.cxx.prejobs.XlstSensor;
import org.sonar.cxx.sensors.clangsa.CxxClangSARuleRepository;
import org.sonar.cxx.sensors.clangsa.CxxClangSASensor;
import org.sonar.cxx.sensors.clangtidy.CxxClangTidyRuleRepository;
import org.sonar.cxx.sensors.clangtidy.CxxClangTidySensor;
import org.sonar.cxx.sensors.compiler.gcc.CxxCompilerGccRuleRepository;
import org.sonar.cxx.sensors.compiler.gcc.CxxCompilerGccSensor;
import org.sonar.cxx.sensors.compiler.vc.CxxCompilerVcRuleRepository;
import org.sonar.cxx.sensors.compiler.vc.CxxCompilerVcSensor;
import org.sonar.cxx.sensors.coverage.bullseye.CxxCoverageBullseyeSensor;
import org.sonar.cxx.sensors.coverage.cobertura.CxxCoverageCoberturaSensor;
import org.sonar.cxx.sensors.coverage.ctc.CxxCoverageTestwellCtcTxtSensor;
import org.sonar.cxx.sensors.coverage.vs.CxxCoverageVisualStudioSensor;
import org.sonar.cxx.sensors.cppcheck.CxxCppCheckRuleRepository;
import org.sonar.cxx.sensors.cppcheck.CxxCppCheckSensor;
import org.sonar.cxx.sensors.drmemory.CxxDrMemoryRuleRepository;
import org.sonar.cxx.sensors.drmemory.CxxDrMemorySensor;
import org.sonar.cxx.sensors.infer.CxxInferRuleRepository;
import org.sonar.cxx.sensors.infer.CxxInferSensor;
import org.sonar.cxx.sensors.other.CxxOtherRepository;
import org.sonar.cxx.sensors.other.CxxOtherSensor;
import org.sonar.cxx.sensors.pclint.CxxPCLintRuleRepository;
import org.sonar.cxx.sensors.pclint.CxxPCLintSensor;
import org.sonar.cxx.sensors.rats.CxxRatsRuleRepository;
import org.sonar.cxx.sensors.rats.CxxRatsSensor;
import org.sonar.cxx.sensors.tests.dotnet.CxxUnitTestResultsAggregator;
import org.sonar.cxx.sensors.tests.dotnet.CxxUnitTestResultsImportSensor;
import org.sonar.cxx.sensors.tests.xunit.CxxXunitSensor;
import org.sonar.cxx.sensors.utils.RulesDefinitionXmlLoader;
import org.sonar.cxx.sensors.valgrind.CxxValgrindRuleRepository;
import org.sonar.cxx.sensors.valgrind.CxxValgrindSensor;
import org.sonar.cxx.sensors.veraxx.CxxVeraxxRuleRepository;
import org.sonar.cxx.sensors.veraxx.CxxVeraxxSensor;
import java.util.ArrayList;
import java.util.List;
/**
* {@inheritDoc}
*/
public final class CxxPlugin implements Plugin {
/**
* {@inheritDoc}
*/
@Override
public void define(Context context) {
var l = new ArrayList<Object>();
// plugin elements
l.add(CxxLanguage.class);
l.add(CxxSonarWayProfile.class);
l.add(CxxRuleRepository.class);
l.add(CxxSecurityDesignRulesRepository.class);
// reusable elements
l.addAll(getSensorsImpl());
// properties elements
l.addAll(CxxLanguage.properties());
l.addAll(CxxSquidSensor.properties());
l.addAll(CxxCppCheckSensor.properties());
l.addAll(CxxValgrindSensor.properties());
l.addAll(CxxDrMemorySensor.properties());
l.addAll(CxxPCLintSensor.properties());
l.addAll(CxxRatsSensor.properties());
l.addAll(CxxVeraxxSensor.properties());
l.addAll(CxxOtherSensor.properties());
l.addAll(CxxClangTidySensor.properties());
l.addAll(CxxClangSASensor.properties());
l.addAll(CxxCoverageBullseyeSensor.properties());
l.addAll(CxxCoverageCoberturaSensor.properties());
l.addAll(CxxCoverageTestwellCtcTxtSensor.properties());
l.addAll(CxxCoverageVisualStudioSensor.properties());
l.addAll(CxxXunitSensor.properties());
l.addAll(CxxUnitTestResultsImportSensor.properties());
l.addAll(CxxCompilerVcSensor.properties());
l.addAll(CxxCompilerGccSensor.properties());
context.addExtensions(l);
}
static private List<Object> getSensorsImpl() {
var l = new ArrayList<Object>();
// utility classes
l.add(CxxUnitTestResultsAggregator.class);
l.add(RulesDefinitionXmlLoader.class);
// metrics
l.add(CxxMetricDefinition.class);
// ComputeEngine: propagate metrics through all levels (FILE -> MODULE -> PROJECT)
l.add(AggregateMeasureComputer.class);
// ComputeEngine: calculate new metrics from existing ones
l.add(DensityMeasureComputer.class);
// pre jobs
l.add(DroppedPropertiesSensor.class);
l.add(XlstSensor.class);
// issue sensors
l.add(CxxSquidSensor.class);
l.add(CxxRatsSensor.class);
l.add(CxxCppCheckSensor.class);
l.add(CxxInferSensor.class);
l.add(CxxPCLintSensor.class);
l.add(CxxDrMemorySensor.class);
l.add(CxxCompilerGccSensor.class);
l.add(CxxCompilerVcSensor.class);
l.add(CxxVeraxxSensor.class);
l.add(CxxValgrindSensor.class);
l.add(CxxClangTidySensor.class);
l.add(CxxClangSASensor.class);
l.add(CxxOtherSensor.class);
// test sensors
l.add(CxxXunitSensor.class);
l.add(CxxUnitTestResultsImportSensor.class);
// coverage sensors
l.add(CxxCoverageBullseyeSensor.class);
l.add(CxxCoverageCoberturaSensor.class);
l.add(CxxCoverageTestwellCtcTxtSensor.class);
l.add(CxxCoverageVisualStudioSensor.class);
// rule provides
l.add(CxxRatsRuleRepository.class);
l.add(CxxCppCheckRuleRepository.class);
l.add(CxxInferRuleRepository.class);
l.add(CxxPCLintRuleRepository.class);
l.add(CxxDrMemoryRuleRepository.class);
l.add(CxxCompilerVcRuleRepository.class);
l.add(CxxCompilerGccRuleRepository.class);
l.add(CxxVeraxxRuleRepository.class);
l.add(CxxValgrindRuleRepository.class);
l.add(CxxOtherRepository.class);
l.add(CxxClangTidyRuleRepository.class);
l.add(CxxClangSARuleRepository.class);
// post jobs
l.add(FinalReport.class);
return l;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}

@ -0,0 +1,25 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.cxx.checks.CheckList;
import org.sonar.cxx.squidbridge.annotations.AnnotationBasedRulesDefinition;
public class CxxRuleRepository implements RulesDefinition {
private static final String REPOSITORY_NAME = "SonarQube";
@Override
public void define(Context context) {
var repository = context.createRepository("cxx", CxxLanguage.KEY).
setName(REPOSITORY_NAME);
new AnnotationBasedRulesDefinition(repository, CxxLanguage.KEY).addRuleClasses(false, CheckList.getChecks());
repository.done();
}
}

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.google.common.io.Resources;
import com.google.gson.Gson;
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
import org.sonar.cxx.checks.CheckList;
import org.sonarsource.api.sonarlint.SonarLintSide;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* define built-in profile
*/
@SonarLintSide
public class CxxSonarWayProfile implements BuiltInQualityProfilesDefinition {
private static String readResource(URL resource) {
try {
return Resources.toString(resource, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Failed to read: " + resource, e);
}
}
static Profile readProfile() {
URL resource = CxxSonarWayProfile.class.getResource("/org/sonar/l10n/cxx/rules/cxx/Sonar_way_profile.json");
return new Gson().fromJson(readResource(resource), Profile.class);
}
@Override
public void define(Context context) {
var sonarWay = context.createBuiltInQualityProfile("Sonar way", CxxLanguage.KEY);
Profile jsonProfile = readProfile();
jsonProfile.ruleKeys.forEach((key) -> {
sonarWay.activateRule(CheckList.REPOSITORY_KEY, key);
});
sonarWay.done();
}
static class Profile {
public String name;
public List<String> ruleKeys;
}
}

@ -0,0 +1,564 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.sonar.cxx.sslr.api.Grammar;
import org.sonar.api.PropertyType;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.api.batch.sensor.issue.NewIssueLocation;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.issue.NoSonarFilter;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContextFactory;
import org.sonar.api.measures.Metric;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.scanner.sensor.ProjectSensor;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.cxx.CxxAstScanner;
import org.sonar.cxx.CxxMetrics;
import org.sonar.cxx.api.CxxMetric;
import org.sonar.cxx.checks.CheckList;
import org.sonar.cxx.config.CxxSquidConfiguration;
import org.sonar.cxx.config.MsBuild;
import org.sonar.cxx.sensors.utils.CxxUtils;
import org.sonar.cxx.squidbridge.SquidAstVisitor;
import org.sonar.cxx.squidbridge.api.SourceCode;
import org.sonar.cxx.squidbridge.api.SourceFile;
import org.sonar.cxx.squidbridge.indexer.QueryByType;
import org.sonar.cxx.visitors.CxxCpdVisitor;
import org.sonar.cxx.visitors.CxxHighlighterVisitor;
import org.sonar.cxx.visitors.CxxPublicApiVisitor;
import org.sonar.cxx.visitors.MultiLocatitionSquidCheck;
import javax.annotation.Nullable;
import java.io.File;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* {@inheritDoc}
*/
public class CxxSquidSensor implements ProjectSensor {
public static final String SQUID_DISABLED_KEY = "sonar.cxx.squid.disabled";
public static final String DEFINES_KEY = "sonar.cxx.defines";
public static final String INCLUDE_DIRECTORIES_KEY = "sonar.cxx.includeDirectories";
public static final String ERROR_RECOVERY_KEY = "sonar.cxx.errorRecoveryEnabled";
public static final String FORCE_INCLUDES_KEY = "sonar.cxx.forceIncludes";
public static final String JSON_COMPILATION_DATABASE_KEY = "sonar.cxx.jsonCompilationDatabase";
public static final String JSON_COMPILATION_DATABASE_ONLY_CONTAINED_FILES_KEY
= "sonar.cxx.jsonCompilationDatabase.analyzeOnlyContainedFiles";
public static final String FUNCTION_COMPLEXITY_THRESHOLD_KEY = "sonar.cxx.metric.func.complexity.threshold";
public static final String FUNCTION_SIZE_THRESHOLD_KEY = "sonar.cxx.metric.func.size.threshold";
public static final String CPD_IGNORE_LITERALS_KEY = "sonar.cxx.metric.cpd.ignoreLiterals";
public static final String CPD_IGNORE_IDENTIFIERS_KEY = "sonar.cxx.metric.cpd.ignoreIdentifiers";
private static final Logger LOG = Loggers.get(CxxSquidSensor.class);
private final FileLinesContextFactory fileLinesContextFactory;
private final CxxChecks checks;
private final NoSonarFilter noSonarFilter;
private SensorContext context;
/**
* {@inheritDoc}
*/
public CxxSquidSensor(FileLinesContextFactory fileLinesContextFactory,
CheckFactory checkFactory,
NoSonarFilter noSonarFilter) {
this(fileLinesContextFactory, checkFactory, noSonarFilter, null);
}
/**
* {@inheritDoc}
*/
public CxxSquidSensor(FileLinesContextFactory fileLinesContextFactory,
CheckFactory checkFactory,
NoSonarFilter noSonarFilter,
@Nullable CustomCxxRulesDefinition[] customRulesDefinition) {
this.checks = CxxChecks.createCxxCheck(checkFactory)
.addChecks(CheckList.REPOSITORY_KEY, CheckList.getChecks())
.addCustomChecks(customRulesDefinition);
this.fileLinesContextFactory = fileLinesContextFactory;
this.noSonarFilter = noSonarFilter;
}
public static List<PropertyDefinition> properties() {
return Collections.unmodifiableList(Arrays.asList(
PropertyDefinition.builder(INCLUDE_DIRECTORIES_KEY)
.multiValues(true)
.name("(2.2) Include Directories")
.description(
"Comma-separated list of directories where the preprocessor looks for include files."
+ " The path may be either absolute or relative to the project base directory."
+ " In the SonarQube UI, enter one entry per field."
)
.category("CXX")
.subCategory("(2) Preprocessor")
.onQualifiers(Qualifiers.PROJECT)
.build(),
PropertyDefinition.builder(FORCE_INCLUDES_KEY)
.multiValues(true)
.category("CXX")
.subCategory("(2) Preprocessor")
.name("(2.3) Force Includes")
.description(
"Comma-separated list of include files implicitly inserted at the beginning of each source file."
+ " This has the same effect as specifying the file with double quotation marks in an `#include` directive"
+ " on the first line of every source file. If you add multiple files they are included in the order they"
+ " are listed from left to right. The path may be either absolute or relative to the"
+ " project base directory."
+ " In the SonarQube UI, enter one entry per field."
)
.onQualifiers(Qualifiers.PROJECT)
.build(),
PropertyDefinition.builder(SQUID_DISABLED_KEY)
.defaultValue(Boolean.FALSE.toString())
.name("Disable Squid sensor")
.description(
"Disable parsing of source code, syntax hightligthing and metric generation."
+ " The source files are still indexed, reports can be read and their results displayed."
+ " Turning off will speed up reading of source files."
)
.category("CXX")
.subCategory("(1) General")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.BOOLEAN)
.build(),
PropertyDefinition.builder(DEFINES_KEY)
.name("(2.1) Macros")
.description(
"List of macros to be used by the preprocessor during analysis. Enter one macro per line."
+ " The syntax is the same as `#define` directives, except for the `#define` keyword itself."
)
.category("CXX")
.subCategory("(2) Preprocessor")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.TEXT)
.build(),
PropertyDefinition.builder(ERROR_RECOVERY_KEY)
.defaultValue(Boolean.TRUE.toString())
.name("Parse Error Recovery")
.description(
"Defines the mode for error handling of report files and parsing errors."
+ " `False` (strict) terminates after an error or `True` (tolerant) continues."
)
.category("CXX")
.subCategory("(1) General")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.BOOLEAN)
.build(),
PropertyDefinition.builder(MsBuild.REPORT_PATH_KEY)
.name("(2.6) Path(s) to MSBuild Log(s)")
.description(
"Read one ore more MSBuild .LOG files to automatically extract the required macros `sonar.cxx.defines`"
+ " and include directories `sonar.cxx.includeDirectories`. The path may be either absolute or relative"
+ " to the project base directory."
+ " In the SonarQube UI, enter one entry per field."
)
.category("CXX")
.subCategory("(2) Preprocessor")
.onQualifiers(Qualifiers.PROJECT)
.multiValues(true)
.build(),
PropertyDefinition.builder(MsBuild.REPORT_ENCODING_DEF)
.defaultValue(MsBuild.DEFAULT_ENCODING_DEF)
.name("(2.7) MSBuild Log Encoding")
.description(
"Defines the encoding to be used to read the files from `sonar.cxx.msbuild.reportPaths` (default is `UTF-8`)."
)
.category("CXX")
.subCategory("(2) Preprocessor")
.onQualifiers(Qualifiers.PROJECT)
.build(),
PropertyDefinition.builder(JSON_COMPILATION_DATABASE_KEY)
.category("CXX")
.subCategory("(2) Preprocessor")
.name("(2.4) JSON Compilation Database")
.description(
"Read a JSON Compilation Database file to automatically extract the required macros `sonar.cxx.defines`"
+ " and include directories `sonar.cxx.includeDirectories` from a file. The path may be either absolute"
+ " or relative to the project base directory."
)
.onQualifiers(Qualifiers.PROJECT)
.build(),
PropertyDefinition.builder(JSON_COMPILATION_DATABASE_ONLY_CONTAINED_FILES_KEY)
.defaultValue(Boolean.FALSE.toString())
.category("CXX")
.subCategory("(2) Preprocessor")
.name("(2.5) JSON Compilation Database analyze only contained files")
.description(
"If 'analyzeOnlyContainedFiles=True' is used, the analyzed files will be limited to the files contained"
+ " in the 'JSON Compilation Database' file - the intersection of the files configured via"
+ " 'sonar.projectBaseDir' and the files contained in the 'JSON Compilation Database' file"
+ " (default is False)."
)
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.BOOLEAN)
.build(),
PropertyDefinition.builder(CxxPublicApiVisitor.API_FILE_SUFFIXES_KEY)
.defaultValue(CxxPublicApiVisitor.API_DEFAULT_FILE_SUFFIXES)
.name("Public API File suffixes")
.multiValues(true)
.description(
"Comma-separated list of suffixes for files to be searched for API comments and to create API metrics."
+ " In the SonarQube UI, enter one entry per field."
)
.category("CXX")
.subCategory("(3) Metrics")
.onQualifiers(Qualifiers.PROJECT)
.build(),
PropertyDefinition.builder(FUNCTION_COMPLEXITY_THRESHOLD_KEY)
.defaultValue("10")
.name("Complex Functions ...")
.description(
"The parameter defines the threshold for `Complex Functions ...`."
+ " Functions and methods with a higher cyclomatic complexity are classified as `complex`."
)
.category("CXX")
.subCategory("(3) Metrics")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.INTEGER)
.build(),
PropertyDefinition.builder(FUNCTION_SIZE_THRESHOLD_KEY)
.defaultValue("20")
.name("Big Functions ...")
.description(
"The parameter defines the threshold for `Big Functions ...`."
+ " Functions and methods with more lines of code are classified as `big`."
)
.category("CXX")
.subCategory("(3) Metrics")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.INTEGER)
.build(),
PropertyDefinition.builder(CPD_IGNORE_LITERALS_KEY)
.defaultValue(Boolean.FALSE.toString())
.name("Ignores Literal Value Differences")
.description(
"Configure the metrics `Duplications` (Copy Paste Detection). `True` ignores literal"
+ " (numbers, characters and strings) value differences when evaluating a duplicate block. This means"
+ " that e.g. `foo=42;` and `foo=43;` will be seen as equivalent."
)
.category("CXX")
.subCategory("(4) Duplications")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.BOOLEAN)
.build(),
PropertyDefinition.builder(CPD_IGNORE_IDENTIFIERS_KEY)
.defaultValue(Boolean.FALSE.toString())
.name("Ignores Identifier Value Differences")
.description(
"Configure the metrics `Duplications` (Copy Paste Detection). `True` ignores identifier value differences"
+ " when evaluating a duplicate block e.g. variable names, methods names, and so forth."
)
.category("CXX")
.subCategory("(4) Duplications")
.onQualifiers(Qualifiers.PROJECT)
.type(PropertyType.BOOLEAN)
.build()
));
}
@Override
public void describe(SensorDescriptor descriptor) {
descriptor
.name("CXX")
.onlyOnLanguage(CxxLanguage.KEY)
.onlyOnFileType(InputFile.Type.MAIN)
.onlyWhenConfiguration(conf -> !conf.getBoolean(SQUID_DISABLED_KEY).orElse(false));
}
/**
* {@inheritDoc}
*/
@Override
public void execute(SensorContext context) {
this.context = context;
// add visitor only if corresponding rule is active
var visitors = new ArrayList<SquidAstVisitor<Grammar>>();
for (var check : checks.all()) {
RuleKey key = checks.ruleKey(check);
if (key != null) {
if (context.activeRules().find(key) != null) {
visitors.add(check);
}
}
}
var squidConfig = createConfiguration();
var scanner = CxxAstScanner.create(squidConfig, visitors.toArray(new SquidAstVisitor[visitors.size()]));
Iterable<InputFile> inputFiles = getInputFiles(context, squidConfig);
scanner.scanInputFiles(inputFiles);
Collection<SourceCode> squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceFile.class));
save(squidSourceFiles);
}
@Override
public String toString() {
return getClass().getSimpleName();
}
private String[] stripValue(String key, String regex) {
Optional<String> value = context.config().get(key);
if (value.isPresent()) {
var PATTERN = Pattern.compile(regex);
return PATTERN.split(value.get(), -1);
}
return new String[0];
}
private CxxSquidConfiguration createConfiguration() {
var squidConfig = new CxxSquidConfiguration(context.fileSystem().baseDir().getAbsolutePath(),
context.fileSystem().encoding());
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.ERROR_RECOVERY_ENABLED,
context.config().get(ERROR_RECOVERY_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.CPD_IGNORE_LITERALS,
context.config().get(CPD_IGNORE_LITERALS_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.CPD_IGNORE_IDENTIFIERS,
context.config().get(CPD_IGNORE_IDENTIFIERS_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.FUNCTION_COMPLEXITY_THRESHOLD,
context.config().get(FUNCTION_COMPLEXITY_THRESHOLD_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.FUNCTION_SIZE_THRESHOLD,
context.config().get(FUNCTION_SIZE_THRESHOLD_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.API_FILE_SUFFIXES,
context.config().getStringArray(CxxPublicApiVisitor.API_FILE_SUFFIXES_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.JSON_COMPILATION_DATABASE,
context.config().get(JSON_COMPILATION_DATABASE_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.DEFINES,
stripValue(DEFINES_KEY, "\\R"));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.FORCE_INCLUDES,
context.config().getStringArray(FORCE_INCLUDES_KEY));
squidConfig.add(CxxSquidConfiguration.SONAR_PROJECT_PROPERTIES, CxxSquidConfiguration.INCLUDE_DIRECTORIES,
context.config().getStringArray(INCLUDE_DIRECTORIES_KEY));
squidConfig.readJsonCompilationDb();
if (context.config().hasKey(MsBuild.REPORT_PATH_KEY)) {
List<File> logFiles = CxxUtils.getFiles(context, MsBuild.REPORT_PATH_KEY);
squidConfig.readMsBuildFiles(logFiles, context.config().get(MsBuild.REPORT_ENCODING_DEF)
.orElse(MsBuild.DEFAULT_ENCODING_DEF));
}
return squidConfig;
}
private Iterable<InputFile> getInputFiles(SensorContext context, CxxSquidConfiguration squidConfig) {
Iterable<InputFile> inputFiles = context.fileSystem().inputFiles(
context.fileSystem().predicates().and(
context.fileSystem().predicates().hasLanguage(CxxLanguage.KEY),
context.fileSystem().predicates().hasType(InputFile.Type.MAIN)
)
);
if (context.config().hasKey(JSON_COMPILATION_DATABASE_KEY)
&& context.config().getBoolean(JSON_COMPILATION_DATABASE_ONLY_CONTAINED_FILES_KEY).orElse(Boolean.FALSE)) {
// if the source of the configuration is JSON Compilation Database and analyzeOnlyContainedFiles=True,
// then analyze only the files contained in the db.
var inputFilesInConfig = squidConfig.getFiles();
var result = StreamSupport.stream(inputFiles.spliterator(), false)
.filter(f -> inputFilesInConfig.contains(Path.of(f.uri())))
.collect(Collectors.toList());
inputFiles = result;
LOG.info("Analyze only files contained in 'JSON Compilation Database': {} files", result.size());
if (result.isEmpty()) {
LOG.error(
"No files are analyzed, check the settings of 'sonar.projectBaseDir' and 'sonar.cxx.jsonCompilationDatabase'."
);
}
}
return inputFiles;
}
private void save(Collection<SourceCode> sourceCodeFiles) {
for (var sourceCodeFile : sourceCodeFiles) {
try {
var sourceFile = (SourceFile) sourceCodeFile;
InputFile inputFile = context.fileSystem().inputFile(
context.fileSystem().predicates().hasPath(sourceFile.getKey())
);
saveMeasures(inputFile, sourceFile);
saveViolations(inputFile, sourceFile);
saveFileLinesContext(inputFile, sourceFile);
saveCpdTokens(inputFile, sourceFile);
saveHighlighting(inputFile, sourceFile);
} catch (IllegalStateException e) {
var msg = "Cannot save all measures for file '" + sourceCodeFile.getKey() + "'";
CxxUtils.validateRecovery(msg, e, context.config());
}
}
}
private void saveMeasures(InputFile inputFile, SourceFile sourceFile) {
// NOSONAR
noSonarFilter.noSonarInFile(inputFile, sourceFile.getNoSonarTagLines());
// CORE METRICS
saveMetric(inputFile, CoreMetrics.NCLOC, sourceFile.getInt(CxxMetric.LINES_OF_CODE));
saveMetric(inputFile, CoreMetrics.STATEMENTS, sourceFile.getInt(CxxMetric.STATEMENTS));
saveMetric(inputFile, CoreMetrics.FUNCTIONS, sourceFile.getInt(CxxMetric.FUNCTIONS));
saveMetric(inputFile, CoreMetrics.CLASSES, sourceFile.getInt(CxxMetric.CLASSES));
saveMetric(inputFile, CoreMetrics.COMPLEXITY, sourceFile.getInt(CxxMetric.COMPLEXITY));
saveMetric(inputFile, CoreMetrics.COGNITIVE_COMPLEXITY, sourceFile.getInt(CxxMetric.COGNITIVE_COMPLEXITY));
saveMetric(inputFile, CoreMetrics.COMMENT_LINES, sourceFile.getInt(CxxMetric.COMMENT_LINES));
// CUSTOM METRICS
//
// non-core metrics are not aggregated automatically, see AggregateMeasureComputer
// below metrics are calculated by means of DensityMeasureComputer
//
// 1. PUBLIC API
saveMetric(inputFile, CxxMetrics.PUBLIC_API, sourceFile.getInt(CxxMetric.PUBLIC_API));
saveMetric(inputFile, CxxMetrics.PUBLIC_UNDOCUMENTED_API, sourceFile.getInt(CxxMetric.PUBLIC_UNDOCUMENTED_API));
// 2. FUNCTION COMPLEXITY
saveMetric(inputFile, CxxMetrics.COMPLEX_FUNCTIONS, sourceFile.getInt(CxxMetric.COMPLEX_FUNCTIONS));
saveMetric(inputFile, CxxMetrics.COMPLEX_FUNCTIONS_LOC, sourceFile.getInt(CxxMetric.COMPLEX_FUNCTIONS_LOC));
// 3. FUNCTION SIZE
saveMetric(inputFile, CxxMetrics.LOC_IN_FUNCTIONS, sourceFile.getInt(CxxMetric.LOC_IN_FUNCTIONS));
saveMetric(inputFile, CxxMetrics.BIG_FUNCTIONS, sourceFile.getInt(CxxMetric.BIG_FUNCTIONS));
saveMetric(inputFile, CxxMetrics.BIG_FUNCTIONS_LOC, sourceFile.getInt(CxxMetric.BIG_FUNCTIONS_LOC));
}
private void saveViolations(InputFile inputFile, SourceFile sourceFile) {
if (sourceFile.hasCheckMessages()) {
for (var message : sourceFile.getCheckMessages()) {
var line = 1;
if (message.getLine() != null && message.getLine() > 0) {
line = message.getLine();
}
RuleKey ruleKey = checks.ruleKey((SquidAstVisitor<Grammar>) message.getCheck());
if (ruleKey != null) {
var newIssue = context.newIssue().forRule(RuleKey.of(CheckList.REPOSITORY_KEY, ruleKey.rule()));
var location = newIssue.newLocation()
.on(inputFile)
.at(inputFile.selectLine(line))
.message(message.getText(Locale.ENGLISH));
newIssue.at(location);
newIssue.save();
} else {
LOG.debug("Unknown rule key: %s", message);
}
}
}
if (MultiLocatitionSquidCheck.hasMultiLocationCheckMessages(sourceFile)) {
for (var issue : MultiLocatitionSquidCheck.getMultiLocationCheckMessages(sourceFile)) {
var newIssue = context.newIssue().forRule(RuleKey.of(CheckList.REPOSITORY_KEY, issue.getRuleId()));
var locationNr = 0;
for (var location : issue.getLocations()) {
final Integer line = Integer.valueOf(location.getLine());
final NewIssueLocation newIssueLocation = newIssue.newLocation().on(inputFile).at(inputFile.selectLine(line))
.message(location.getInfo());
if (locationNr == 0) {
newIssue.at(newIssueLocation);
} else {
newIssue.addLocation(newIssueLocation);
}
++locationNr;
}
newIssue.save();
}
MultiLocatitionSquidCheck.eraseMultilineCheckMessages(sourceFile);
}
}
private void saveFileLinesContext(InputFile inputFile, SourceFile sourceFile) {
// measures for the lines of file
var fileLinesContext = fileLinesContextFactory.createFor(inputFile);
List<Integer> linesOfCode = (List<Integer>) sourceFile.getData(CxxMetric.NCLOC_DATA);
linesOfCode.stream().sequential().distinct().forEach((line) -> {
try {
fileLinesContext.setIntValue(CoreMetrics.NCLOC_DATA_KEY, line, 1);
} catch (IllegalArgumentException | IllegalStateException e) {
// ignore errors: parsing errors could lead to wrong location data
LOG.debug("NCLOC error in file '{}' at line:{}", inputFile.filename(), line);
}
});
List<Integer> executableLines = (List<Integer>) sourceFile.getData(CxxMetric.EXECUTABLE_LINES_DATA);
executableLines.stream().sequential().distinct().forEach((line) -> {
try {
fileLinesContext.setIntValue(CoreMetrics.EXECUTABLE_LINES_DATA_KEY, line, 1);
} catch (IllegalArgumentException | IllegalStateException e) {
// ignore errors: parsing errors could lead to wrong location data
LOG.debug("EXECUTABLE LINES error in file '{}' at line:{}", inputFile.filename(), line);
}
});
fileLinesContext.save();
}
private void saveCpdTokens(InputFile inputFile, SourceFile sourceFile) {
NewCpdTokens cpdTokens = context.newCpdTokens().onFile(inputFile);
List<CxxCpdVisitor.CpdToken> data = (List<CxxCpdVisitor.CpdToken>) sourceFile.getData(CxxMetric.CPD_TOKENS_DATA);
data.forEach((item) -> {
try {
TextRange range = inputFile.newRange(item.startLine, item.startCol, item.endLine, item.endCol);
cpdTokens.addToken(range, item.token);
} catch (IllegalArgumentException | IllegalStateException e) {
// ignore range errors: parsing errors could lead to wrong location data
LOG.debug("CPD error in file '{}' at line:{}, column:{}", inputFile.filename(), item.startLine, item.startCol);
}
});
cpdTokens.save();
}
private void saveHighlighting(InputFile inputFile, SourceFile sourceFile) {
NewHighlighting newHighlighting = context.newHighlighting().onFile(inputFile);
List<CxxHighlighterVisitor.Highlight> data = (List<CxxHighlighterVisitor.Highlight>) sourceFile.getData(
CxxMetric.HIGHLIGTHING_DATA);
data.forEach((item) -> {
try {
newHighlighting.highlight(item.startLine, item.startLineOffset, item.endLine, item.endLineOffset,
TypeOfText.forCssClass(item.typeOfText));
} catch (IllegalArgumentException | IllegalStateException e) {
// ignore highlight errors: parsing errors could lead to wrong location data
LOG.debug("Highlighting error in file '{}' at start:{}:{} end:{}:{}", inputFile.filename(),
item.startLine, item.startLineOffset, item.endLine, item.endLineOffset);
}
});
newHighlighting.save();
}
private <T extends Serializable> void saveMetric(InputFile file, Metric<T> metric, T value) {
context.<T>newMeasure()
.withValue(value)
.forMetric(metric)
.on(file)
.save();
}
}

@ -0,0 +1,117 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.sonar.api.batch.Phase;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.scanner.sensor.ProjectSensor;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Phase(name = Phase.Name.PRE)
public class DroppedPropertiesSensor implements ProjectSensor {
private static final Logger LOG = Loggers.get(DroppedPropertiesSensor.class);
private static final String MSG_COMPILER = "Use 'sonar.cxx.vc' or 'sonar.cxx.gcc' instead."
+ " Use 'sonar.cxx.msbuild' to read includes and defines from MSBuild log file.";
private static final Map<String, String> ALL_REMOVED_PROPERTIES = initRemovedProperties();
private final AnalysisWarnings analysisWarnings;
public DroppedPropertiesSensor(AnalysisWarnings analysisWarnings) {
this.analysisWarnings = analysisWarnings;
}
private static Map<String, String> initRemovedProperties() {
var map = new HashMap<String, String>();
map.put("sonar.cxx.include_directories", "Use 'sonar.cxx.includeDirectories' instead."); // V0.9.1
map.put("sonar.cxx.externalrules.reportPaths", "Use 'sonar.cxx.other.reportPaths' instead."); // V0.9.1
map.put("sonar.cxx.cppncss.reportPaths", ""); // V0.9.1
map.put("sonar.cxx.other.sqales", ""); // V0.9.6
map.put("sonar.cxx.xunit.provideDetails", ""); // V0.9.7
map.put("sonar.cxx.coverage.itReportPaths", ""); // V0.9.8
map.put("sonar.cxx.coverage.overallReportPaths", ""); // V0.9.8
map.put("sonar.cxx.forceZeroCoverage", ""); // V0.9.8
map.put("sonar.cxx.scanOnlySpecifiedSources", ""); // V1.0.0
map.put("sonar.cxx.compiler.parser", MSG_COMPILER); // V1.2.0
map.put("sonar.cxx.compiler.reportPaths", MSG_COMPILER); // V1.2.0
map.put("sonar.cxx.compiler.regex", MSG_COMPILER); // V1.2.0
map.put("sonar.cxx.compiler.charset", MSG_COMPILER); // V1.2.0
map.put("sonar.cxx.missingIncludeWarnings", "Turn debug info on to get the information."); // V1.2.0
map.put("sonar.cxx.cFilesPatterns",
"Define C++ keywords in an own header file and include it with 'sonar.cxx.forceIncludes' instead."); // V2.0.0
map.put("sonar.cxx.suffixes.sources", "Use key 'sonar.cxx.file.suffixes' instead."); // V2.0.0
map.put("sonar.cxx.suffixes.headers",
"Use key 'sonar.cxx.file.suffixes' instead. For API detection use 'sonar.cxx.metric.api.file.suffixes'."); // V2.0.0
map.put("sonar.cxx.other.xslt.1.stylesheet", "Use 'sonar.cxx.xslt.1.stylesheet' instead."); // V2.0.0
map.put("sonar.cxx.other.xslt.1.inputs", "Use 'sonar.cxx.xslt.1.inputs' instead."); // V2.0.0
map.put("sonar.cxx.other.xslt.1.outputs", "Use 'sonar.cxx.xslt.1.outputs' instead."); // V2.0.0
map.put("sonar.cxx.xunit.xsltURL", "Use 'sonar.cxx.xslt.xxx' instead."); // V2.0.0
map.put("sonar.cxx.clangsa.reportPath", "Use 'sonar.cxx.clangsa.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.clangtidy.reportPath", "Use 'sonar.cxx.clangtidy.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.gcc.reportPath", "Use 'sonar.cxx.gcc.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.vc.reportPath", "Use 'sonar.cxx.vc.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.cppcheck.reportPath", "Use 'sonar.cxx.cppcheck.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.drmemory.reportPath", "Use 'sonar.cxx.drmemory.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.other.reportPath", "Use 'sonar.cxx.other.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.pclint.reportPath", "Use 'sonar.cxx.pclint.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.xunit.reportPath", "Use 'sonar.cxx.xunit.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.valgrind.reportPath", "Use 'sonar.cxx.valgrind.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.vera.reportPath", "Use 'sonar.cxx.vera.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.msbuild.reportPath", "Use 'sonar.cxx.msbuild.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.coverage.reportPath", "Use 'sonar.cxx.bullseye.reportPaths'"
+ ", 'sonar.cxx.cobertura.reportPaths', 'sonar.cxx.vscoveragexml.reportPaths' or 'sonar.cxx.ctctxt.reportPaths'"
+ " instead."); // V2.0.0
map.put("sonar.cxx.funccomplexity.threshold",
"Use 'sonar.cxx.metric.func.complexity.threshold' instead."); // V2.0.0
map.put("sonar.cxx.funcsize.threshold", "Use 'sonar.cxx.metric.func.size.threshold' instead."); // V2.0.0
map.put("sonar.cxx.vstest.reportsPaths", "Use 'sonar.cxx.vstest.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.xunit.reportsPaths", "Use 'sonar.cxx.xunit.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.nunit.reportsPaths", "Use 'sonar.cxx.nunit.reportPaths' instead."); // V2.0.0
map.put("sonar.cxx.clangtidy.charset", "Use 'sonar.cxx.clangtidy.encoding' instead."); // V2.0.0
map.put("sonar.cxx.gcc.charset", "Use 'sonar.cxx.gcc.encoding' instead."); // V2.0.0
map.put("sonar.cxx.vc.charset", "Use 'sonar.cxx.vc.encoding' instead."); // V2.0.0
map.put("sonar.cxx.ctctxt.charset", "Use 'sonar.cxx.ctctxt.encoding' instead."); // V2.0.0
map.put("sonar.cxx.msbuild.charset", "Use 'sonar.cxx.msbuild.encoding' instead."); // V2.0.0
map.put("sonar.cxx.cpd.ignoreLiterals", "Use 'sonar.cxx.metric.cpd.ignoreLiterals' instead."); // V2.0.0
map.put("sonar.cxx.cpd.ignoreIdentifiers", "Use 'sonar.cxx.metric.cpd.ignoreIdentifiers' instead."); // V2.0.0
map.put("sonar.cxx.nunit.reportPaths", "If possible use 'sonar.cs.nunit.reportsPaths' instead."); // V2.0.0
map.put("sonar.cxx.vstest.reportPaths", "If possible use 'sonar.cs.vstest.reportsPaths' instead."); // V2.0.0
return Collections.unmodifiableMap(map);
}
@Override
public void describe(SensorDescriptor descriptor) {
descriptor
.onlyOnLanguage("cxx")
.onlyWhenConfiguration(configuration -> ALL_REMOVED_PROPERTIES.keySet().stream().anyMatch(configuration::hasKey))
.name("CXX verify analysis parameters");
}
@Override
public void execute(SensorContext context) {
ALL_REMOVED_PROPERTIES.forEach((key, info) -> {
if (context.config().hasKey(key)) {
var msg = "CXX property '" + key + "' is no longer supported.";
if (!info.isEmpty()) {
msg += " " + info;
}
analysisWarnings.addUnique(msg);
LOG.warn(msg);
}
});
}
}

@ -6,6 +6,8 @@
*/
package com.keyware.sonar.cxx.rules;
import com.keyware.sonar.cxx.CustomCxxRulesDefinition;
import com.keyware.sonar.cxx.CxxLanguage;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarProduct;
import org.sonar.api.SonarQubeSide;
@ -13,8 +15,6 @@ import org.sonar.api.SonarRuntime;
import org.sonar.api.resources.Language;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.Version;
import org.sonar.plugins.cxx.CustomCxxRulesDefinition;
import org.sonar.plugins.cxx.CxxLanguage;
import java.util.Collections;
import java.util.Objects;

@ -0,0 +1,91 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.sonar.cxx.sslr.api.Grammar;
import org.junit.jupiter.api.Test;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.resources.Language;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.cxx.squidbridge.checks.SquidCheck;
import org.sonar.cxx.tag.Tag;
import static org.assertj.core.api.Assertions.assertThat;
class CustomCxxRulesDefinitionTest {
private static final Language LANGUAGE = new CxxLanguage(new MapSettings().asConfig());
private static final String REPOSITORY_NAME = "Custom Rule Repository";
private static final String REPOSITORY_KEY = "CustomRuleRepository";
private static final String RULE_NAME = "This is my custom rule";
private static final String RULE_KEY = "MyCustomRule";
@Test
void test() {
var rulesDefinition = new MyCustomPlSqlRulesDefinition();
var context = new RulesDefinition.Context();
rulesDefinition.define(context);
RulesDefinition.Repository repository = context.repository(REPOSITORY_KEY);
assertThat(repository.name()).isEqualTo(REPOSITORY_NAME);
assertThat(repository.language()).isEqualTo(LANGUAGE.getKey());
assertThat(repository.rules()).hasSize(1);
RulesDefinition.Rule alertUseRule = repository.rule(RULE_KEY);
assertThat(alertUseRule).isNotNull();
assertThat(alertUseRule.name()).isEqualTo(RULE_NAME);
for (var rule : repository.rules()) {
for (var param : rule.params()) {
assertThat(param.description()).as("description for " + param.key()).isNotEmpty();
}
}
}
public static class MyCustomPlSqlRulesDefinition extends CustomCxxRulesDefinition {
@Override
public String repositoryName() {
System.out.println(REPOSITORY_NAME);
return REPOSITORY_NAME;
}
@Override
public String repositoryKey() {
return REPOSITORY_KEY;
}
@SuppressWarnings("rawtypes")
@Override
public Class[] checkClasses() {
return new Class[]{MyCustomRule.class};
}
@Override
public Language getLanguage() {
return LANGUAGE;
}
}
@Rule(
key = RULE_KEY,
name = RULE_NAME,
description = "desc",
tags = {Tag.BUG})
public class MyCustomRule extends SquidCheck<Grammar> {
@RuleProperty(
key = "customParam",
description = "Custom parameter",
defaultValue = "value")
public String customParam = "value";
}
}

@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import org.sonar.cxx.checks.CheckList;
import static org.assertj.core.api.Assertions.assertThat;
class CxxCheckListTest {
@Test
void count() {
assertThat(CheckList.getChecks()).hasSize(27);
}
}

@ -0,0 +1,147 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import com.sonar.cxx.sslr.api.Grammar;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.resources.Language;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.check.Rule;
import org.sonar.cxx.squidbridge.SquidAstVisitor;
import org.sonar.cxx.squidbridge.checks.SquidCheck;
import javax.annotation.CheckForNull;
import java.util.ArrayList;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
class CxxChecksTest {
private static final String DEFAULT_REPOSITORY_KEY = "DefaultRuleRepository";
private static final String DEFAULT_RULE_KEY = "MyRule";
private static final String CUSTOM_REPOSITORY_KEY = "CustomRuleRepository";
private static final String CUSTOM_RULE_KEY = "MyCustomRule";
private MyCustomPlSqlRulesDefinition customRulesDefinition;
private CheckFactory checkFactory;
@BeforeEach
public void setUp() {
var activeRules = new ActiveRulesBuilder()
.addRule(new NewActiveRule.Builder()
.setRuleKey(RuleKey.of(DEFAULT_REPOSITORY_KEY, DEFAULT_RULE_KEY))
.build())
.addRule(new NewActiveRule.Builder()
.setRuleKey(RuleKey.of(CUSTOM_REPOSITORY_KEY, CUSTOM_RULE_KEY))
.build())
.build();
checkFactory = new CheckFactory(activeRules);
customRulesDefinition = new MyCustomPlSqlRulesDefinition();
var context = new RulesDefinition.Context();
customRulesDefinition.define(context);
}
@SuppressWarnings("rawtypes")
@Test
void shouldReturnDefaultChecks() {
var checks = CxxChecks.createCxxCheck(checkFactory);
checks.addChecks(DEFAULT_REPOSITORY_KEY, new ArrayList<>(Collections.singletonList(MyRule.class)));
SquidAstVisitor<Grammar> defaultCheck = check(checks, DEFAULT_REPOSITORY_KEY, DEFAULT_RULE_KEY);
assertThat(checks.all()).hasSize(1);
assertThat(checks.ruleKey(defaultCheck)).isNotNull();
assertThat(checks.ruleKey(defaultCheck).rule()).isEqualTo(DEFAULT_RULE_KEY);
assertThat(checks.ruleKey(defaultCheck).repository()).isEqualTo(DEFAULT_REPOSITORY_KEY);
}
@Test
void shouldReturnCustomChecks() {
var checks = CxxChecks.createCxxCheck(checkFactory);
checks.addCustomChecks(new CustomCxxRulesDefinition[]{customRulesDefinition});
SquidAstVisitor<Grammar> customCheck = check(checks, CUSTOM_REPOSITORY_KEY, CUSTOM_RULE_KEY);
assertThat(checks.all()).hasSize(1);
assertThat(checks.ruleKey(customCheck)).isNotNull();
assertThat(checks.ruleKey(customCheck).rule()).isEqualTo(CUSTOM_RULE_KEY);
assertThat(checks.ruleKey(customCheck).repository()).isEqualTo(CUSTOM_REPOSITORY_KEY);
}
@Test
void shouldWorkWithoutCustomChecks() {
var checks = CxxChecks.createCxxCheck(checkFactory);
checks.addCustomChecks(null);
assertThat(checks.all()).isEmpty();
}
@SuppressWarnings("rawtypes")
@Test
void shouldNotReturnRuleKeyIfCheckDoesNotExists() {
var checks = CxxChecks.createCxxCheck(checkFactory);
checks.addChecks(DEFAULT_REPOSITORY_KEY, new ArrayList<>(Collections.singletonList(MyRule.class)));
assertThat(checks.ruleKey(new MyCustomRule())).isNull();
}
@CheckForNull
public SquidAstVisitor<Grammar> check(CxxChecks cxxChecks, String repository, String rule) {
RuleKey key = RuleKey.of(repository, rule);
SquidAstVisitor<Grammar> check;
for (var checks : cxxChecks.getChecks()) {
check = checks.of(key);
if (check != null) {
return check;
}
}
return null;
}
@Rule(key = DEFAULT_RULE_KEY, name = "This is the default rule", description = "desc")
public static class MyRule extends SquidCheck<Grammar> {
}
@Rule(key = CUSTOM_RULE_KEY, name = "This is the custom rule", description = "desc")
public static class MyCustomRule extends SquidCheck<Grammar> {
}
public static class MyCustomPlSqlRulesDefinition extends CustomCxxRulesDefinition {
private static final Language LANGUAGE = new CxxLanguage(new MapSettings().asConfig());
@Override
public String repositoryName() {
return "Custom Rule Repository";
}
@Override
public String repositoryKey() {
return CUSTOM_REPOSITORY_KEY;
}
@SuppressWarnings("rawtypes")
@Override
public Class[] checkClasses() {
return new Class[]{MyCustomRule.class};
}
@Override
public Language getLanguage() {
return LANGUAGE;
}
}
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.issue.internal.DefaultNoSonarFilter;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class CxxFileLinesContextTest {
private FileLinesContextForTesting fileLinesContext;
@BeforeEach
public void setUp() throws IOException {
ActiveRules rules = mock(ActiveRules.class);
var checkFactory = new CheckFactory(rules);
FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class);
fileLinesContext = new FileLinesContextForTesting();
when(fileLinesContextFactory.createFor(Mockito.any(InputFile.class))).thenReturn(fileLinesContext);
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx");
var context = SensorContextTester.create(baseDir);
var inputFile = TestUtils.buildInputFile(baseDir, "ncloc.cc");
context.fileSystem().add(inputFile);
var sensor = new CxxSquidSensor(fileLinesContextFactory, checkFactory, new DefaultNoSonarFilter(), null);
sensor.execute(context);
}
@Test
void TestLinesOfCode() throws UnsupportedEncodingException, IOException {
Set<Integer> linesOfCode = Stream.of(
8, 10, 14, 16, 17, 21, 22, 23, 26, 31, 34, 35, 42, 44, 45, 49, 51, 53, 55, 56,
58, 59, 63, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 79, 82, 84, 86, 87, 89,
90, 95, 98, 99, 100, 102, 107, 108, 109, 110, 111, 113, 115, 118, 119, 124, 126)
.collect(Collectors.toCollection(HashSet::new));
assertThat(fileLinesContext.linesOfCode).containsExactlyInAnyOrderElementsOf(linesOfCode);
}
@Test
void TestExecutableLinesOfCode() throws UnsupportedEncodingException, IOException {
assertThat(fileLinesContext.executableLines).containsExactlyInAnyOrder(
10, 26, 34, 35, 56, 59, 69, 70, 72, 73,
75, 76, 79, 87, 90, 98, 102, 118, 119, 126);
}
private class FileLinesContextForTesting implements FileLinesContext {
public final Set<Integer> executableLines = new HashSet<>();
public final Set<Integer> linesOfCode = new HashSet<>();
@Override
public void setIntValue(String metricKey, int line, int value) {
assertThat(value).isEqualTo(1);
switch (metricKey) {
case CoreMetrics.NCLOC_DATA_KEY:
linesOfCode.add(line);
break;
case CoreMetrics.EXECUTABLE_LINES_DATA_KEY:
executableLines.add(line);
break;
default:
fail("Unsupported metric key " + metricKey);
}
}
@Override
public void setStringValue(String metricKey, int line, String value) {
fail("unexpected method called: setStringValue()");
}
@Override
public void save() {
}
}
}

@ -0,0 +1,228 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.issue.internal.DefaultNoSonarFilter;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import java.io.File;
import java.io.IOException;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class CxxHighlighterTest {
private CxxSquidSensor sensor;
private SensorContextTester context;
private DefaultInputFile inputFile;
@BeforeEach
public void scanFile() throws IOException {
ActiveRules rules = mock(ActiveRules.class);
var checkFactory = new CheckFactory(rules);
FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class);
FileLinesContext fileLinesContext = mock(FileLinesContext.class);
when(fileLinesContextFactory.createFor(Mockito.any(InputFile.class))).thenReturn(fileLinesContext);
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx");
inputFile = TestUtils.buildInputFile(baseDir, "highlighter.cc");
context = SensorContextTester.create(baseDir);
context.fileSystem().add(inputFile);
sensor = new CxxSquidSensor(fileLinesContextFactory, checkFactory, new DefaultNoSonarFilter(), null);
sensor.execute(context);
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void keyword() {
checkOnRange(55, 0, 4, TypeOfText.KEYWORD); // void
checkOnRange(57, 3, 4, TypeOfText.KEYWORD); // auto
checkOnRange(59, 3, 4, TypeOfText.KEYWORD); // auto
checkOnRange(62, 3, 3, TypeOfText.KEYWORD); // for
checkOnRange(62, 7, 5, TypeOfText.KEYWORD); // const
checkOnRange(62, 13, 4, TypeOfText.KEYWORD); // auto
checkOnRange(64, 6, 2, TypeOfText.KEYWORD); // if
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void stringLiteral() {
checkOnRange(49, 19, 7, TypeOfText.STRING); // "hello"
checkOnRange(50, 19, 18, TypeOfText.STRING); // "hello\tworld\r\n"
checkOnRange(73, 32, 24, TypeOfText.STRING); // R"([.^$|()\[\]{}*+?\\])"
checkOnRange(83, 24, 5, TypeOfText.STRING); // "..."
checkOnRange(84, 24, 7, TypeOfText.STRING); // u8"..."
checkOnRange(85, 24, 6, TypeOfText.STRING); // L"..."
checkOnRange(86, 24, 6, TypeOfText.STRING); // u"..."
checkOnRange(87, 24, 6, TypeOfText.STRING); // U"..."
// "hello" " world"
checkOnRange(89, 24, 7, TypeOfText.STRING);
checkOnRange(89, 32, 8, TypeOfText.STRING);
// u"" "hello world"
checkOnRange(90, 24, 3, TypeOfText.STRING);
checkOnRange(90, 28, 13, TypeOfText.STRING);
// /*comment1*/ u"" /*comment2*/ "hello world" /*comment3*/; // issue #996
checkOnRange(91, 24, 12, TypeOfText.COMMENT);
checkOnRange(91, 37, 3, TypeOfText.STRING);
checkOnRange(91, 41, 12, TypeOfText.COMMENT);
checkOnRange(91, 54, 13, TypeOfText.STRING);
checkOnRange(91, 68, 12, TypeOfText.COMMENT);
checkOnRange(91, 82, 13, TypeOfText.COMMENT);
// /*comment4*/ "hello"
// /*comment5*/ " world" /*comment6*/;
checkOnRange(93, 24, 12, TypeOfText.COMMENT);
checkOnRange(93, 37, 7, TypeOfText.STRING);
checkOnRange(94, 24, 12, TypeOfText.COMMENT);
checkOnRange(94, 37, 8, TypeOfText.STRING);
checkOnRange(94, 46, 12, TypeOfText.COMMENT);
// "hello"
// "Mary"
// "Lou";
checkOnRange(96, 25, 7, TypeOfText.STRING);
checkOnRange(97, 25, 6, TypeOfText.STRING);
checkOnRange(98, 25, 5, TypeOfText.STRING);
// R"(\r\n Hello World!\r\n )" issue #1768
check(103, 14, TypeOfText.STRING);
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void character() {
checkOnRange(46, 10, 3, TypeOfText.STRING); // 'x'
checkOnRange(47, 10, 4, TypeOfText.STRING); // '\t'
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void comment() {
check(1, 0, TypeOfText.COMMENT);
/*\r\n comment\r\n*/
check(3, 1, TypeOfText.COMMENT);
checkOnRange(5, 0, 2, TypeOfText.COMMENT); //
checkOnRange(6, 0, 10, TypeOfText.COMMENT); // comment
checkOnRange(7, 0, 2, TypeOfText.COMMENT); //
checkOnRange(57, 22, 10, TypeOfText.COMMENT); // comment
checkOnRange(58, 3, 10, TypeOfText.COMMENT); // comment
checkOnRange(61, 3, 13, TypeOfText.COMMENT);
/* comment */
checkOnRange(64, 20, 13, TypeOfText.COMMENT);
/* comment */
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void number() {
checkOnRange(27, 10, 1, TypeOfText.CONSTANT); // 0
checkOnRange(28, 10, 1, TypeOfText.CONSTANT); // -1 (without minus)
checkOnRange(29, 10, 1, TypeOfText.CONSTANT); // +1 (without plus)
checkOnRange(31, 14, 2, TypeOfText.CONSTANT); // 0u
checkOnRange(32, 19, 3, TypeOfText.CONSTANT); // 1ul
checkOnRange(34, 9, 3, TypeOfText.CONSTANT); // 0x0
checkOnRange(35, 9, 3, TypeOfText.CONSTANT); // 0b0
checkOnRange(36, 9, 16, TypeOfText.CONSTANT); // 0b0100'1100'0110
checkOnRange(38, 12, 3, TypeOfText.CONSTANT); // 0.0
checkOnRange(39, 12, 3, TypeOfText.CONSTANT); // -1.0 (without minus)
checkOnRange(40, 12, 3, TypeOfText.CONSTANT); // +1.0 (without plus)
checkOnRange(41, 12, 8, TypeOfText.CONSTANT); // 3.14E-10
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void preprocessDirective() {
checkOnRange(12, 0, 8, TypeOfText.PREPROCESS_DIRECTIVE); // #include
checkOnRange(14, 0, 6, TypeOfText.PREPROCESS_DIRECTIVE); // #ifdef
checkOnRange(15, 0, 10, TypeOfText.PREPROCESS_DIRECTIVE); // # define
checkOnRange(16, 0, 5, TypeOfText.PREPROCESS_DIRECTIVE); // #else
checkOnRange(17, 0, 10, TypeOfText.PREPROCESS_DIRECTIVE); // # define
checkOnRange(18, 0, 6, TypeOfText.PREPROCESS_DIRECTIVE); // #endif
checkOnRange(20, 0, 7, TypeOfText.PREPROCESS_DIRECTIVE); // #define
}
@Test
@SuppressWarnings("squid:S2699") // ... checkOnRange contains the assertion
void identifiersWithSpecialMeaning() {
// identifier with special meaning => no highlighting
checkOnRange(112, 11, 6, null); // import
checkOnRange(119, 10, 6, null); // module
checkOnRange(120, 12, 6, null); // module
checkOnRange(122, 13, 6, null); // module
}
/**
* Checks the highlighting of a range of columns. The first column of a line has index 0. The range is the columns of
* the token.
*/
private void checkOnRange(int line, int firstColumn, int length, TypeOfText expectedTypeOfText) {
// check that every column of the token is highlighted (and with the expected type)
for (var column = firstColumn; column < firstColumn + length; column++) {
checkInternal(line, column, "", expectedTypeOfText);
}
// check that the column before the token is not highlighted
if (firstColumn != 0) {
checkInternal(line, firstColumn - 1, " (= before the token)", null);
}
// check that the column after the token is not highlighted
checkInternal(line, firstColumn + length, " (= after the token)", null);
}
/**
* Checks the highlighting of one column. The first column of a line has index 0.
*/
private void check(int line, int column, TypeOfText expectedTypeOfText) {
checkInternal(line, column, "", expectedTypeOfText);
}
private void checkInternal(int line, int column, String messageComplement, TypeOfText expectedTypeOfText) {
List<TypeOfText> foundTypeOfTexts = context.highlightingTypeAt(
"ProjectKey:" + inputFile.file().getName(), line, column);
int expectedNumberOfTypeOfText = expectedTypeOfText == null ? 0 : 1;
var message = "number of TypeOfTexts at line " + line + " and column " + column + messageComplement;
assertThat(foundTypeOfTexts).as(message).hasSize(expectedNumberOfTypeOfText);
if (expectedNumberOfTypeOfText > 0) {
message = "found TypeOfTexts at line " + line + " and column " + column + messageComplement;
assertThat(foundTypeOfTexts.get(0)).as(message).isEqualTo(expectedTypeOfText);
}
}
}

@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import org.sonar.api.config.internal.MapSettings;
import static org.assertj.core.api.Assertions.assertThat;
class CxxLanguageTest {
private final MapSettings settings = new MapSettings();
@Test
void testCxxLanguageStringConfiguration() throws Exception {
var language = new CxxLanguage(settings.asConfig());
assertThat(language.getKey()).isEqualTo("cxx");
}
@Test
void shouldReturnConfiguredFileSuffixes() {
settings.setProperty(CxxLanguage.FILE_SUFFIXES_KEY, ".C,.c,.H,.h");
var cxx = new CxxLanguage(settings.asConfig());
String[] expected = {".C", ".c", ".H", ".h"};
assertThat(cxx.getFileSuffixes()).contains(expected);
}
@Test
void shouldReturnDefaultFileSuffixes1() {
var cxx = new CxxLanguage(settings.asConfig());
String[] expected = {"disabled"};
assertThat(cxx.getFileSuffixes()).contains(expected);
}
@Test
void shouldReturnDefaultFileSuffixes2() {
settings.setProperty(CxxLanguage.FILE_SUFFIXES_KEY, "");
var cxx = new CxxLanguage(settings.asConfig());
String[] expected = {"disabled"};
assertThat(cxx.getFileSuffixes()).contains(expected);
}
@Test
void shouldBeDisabled() {
settings.setProperty(CxxLanguage.FILE_SUFFIXES_KEY, "-");
var cxx = new CxxLanguage(settings.asConfig());
String[] expected = {"disabled"};
assertThat(cxx.getFileSuffixes()).contains(expected);
}
}

@ -0,0 +1,20 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class CxxMetricDefinitionTest {
@Test
void metrics_defined() {
assertThat(new CxxMetricDefinition().getMetrics()).hasSize(12);
}
}

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import org.sonar.api.Plugin;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarQubeSide;
import org.sonar.api.SonarRuntime;
import org.sonar.api.internal.SonarRuntimeImpl;
import org.sonar.api.utils.Version;
import static org.assertj.core.api.Assertions.assertThat;
class CxxPluginTest {
@Test
void testGetExtensions() throws Exception {
SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(
Version.create(8, 6),
SonarQubeSide.SCANNER,
SonarEdition.COMMUNITY
);
var context = new Plugin.Context(runtime);
var plugin = new CxxPlugin();
plugin.define(context);
assertThat(context.getExtensions()).hasSize(84);
}
}

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.rule.RulesDefinition;
import static org.assertj.core.api.Assertions.assertThat;
class CxxRuleRepositoryTest {
@Test
void rulesTest() {
var context = new RulesDefinition.Context();
assertThat(context.repositories()).isEmpty();
new CxxRuleRepository().define(context);
assertThat(context.repositories()).hasSize(1);
assertThat(context.repository("cxx").rules()).hasSize(27);
}
}

@ -0,0 +1,30 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class CxxSonarWayProfileTest {
@Test
void should_create_sonar_way_profile() {
var profileDef = new CxxSonarWayProfile();
var context = new BuiltInQualityProfilesDefinition.Context();
profileDef.define(context);
BuiltInQualityProfilesDefinition.BuiltInQualityProfile profile = context.profile("cxx", "Sonar way");
assertThat(profile.language()).isEqualTo(CxxLanguage.KEY);
assertThat(profile.name()).isEqualTo("Sonar way");
List<BuiltInQualityProfilesDefinition.BuiltInActiveRule> activeRules = profile.rules();
assertThat(activeRules.size()).as("Expected number of rules in profile").isNotNegative();
}
}

@ -0,0 +1,237 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.sensor.cpd.internal.TokensLine;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.issue.internal.DefaultNoSonarFilter;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import org.sonar.cxx.CxxMetrics;
import java.io.File;
import java.io.IOException;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class CxxSquidSensorTest {
private CxxSquidSensor sensor;
private final MapSettings settings = new MapSettings();
@BeforeEach
public void setUp() {
ActiveRules rules = mock(ActiveRules.class);
var checkFactory = new CheckFactory(rules);
FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class);
FileLinesContext fileLinesContext = mock(FileLinesContext.class);
when(fileLinesContextFactory.createFor(Mockito.any(InputFile.class))).thenReturn(fileLinesContext);
sensor = new CxxSquidSensor(fileLinesContextFactory, checkFactory, new DefaultNoSonarFilter(), null);
}
@Test
void testCollectingSquidMetrics() throws IOException {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/codechunks-project");
var inputFile0 = TestUtils.buildInputFile(baseDir, "code_chunks.cc");
var context = SensorContextTester.create(baseDir);
context.fileSystem().add(inputFile0);
sensor.execute(context);
var softly = new SoftAssertions();
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.NCLOC).value()).isEqualTo(54);
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.STATEMENTS).value()).isEqualTo(50);
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.FUNCTIONS).value()).isEqualTo(7);
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.CLASSES).value()).isZero();
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.COMPLEXITY).value()).isEqualTo(19);
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.COGNITIVE_COMPLEXITY).value()).isEqualTo(8);
softly.assertThat(context.measure(inputFile0.key(), CoreMetrics.COMMENT_LINES).value()).isEqualTo(15);
softly.assertAll();
}
@Test
void testCpdTokens() throws Exception {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx");
var context = SensorContextTester.create(baseDir);
settings.setProperty(CxxSquidSensor.CPD_IGNORE_IDENTIFIERS_KEY, true);
settings.setProperty(CxxSquidSensor.CPD_IGNORE_LITERALS_KEY, true);
context.setSettings(settings);
var inputFile = TestUtils.buildInputFile(baseDir, "cpd.cc");
context.fileSystem().add(inputFile);
sensor.execute(context);
List<TokensLine> cpdTokenLines = context.cpdTokens("ProjectKey:" + inputFile.file().getName());
assertThat(cpdTokenLines).hasSize(75);
// ld &= 0xFF;
var firstTokensLine = cpdTokenLines.get(2);
assertThat(firstTokensLine.getValue()).isEqualTo("_I&=_N;");
assertThat(firstTokensLine.getStartLine()).isEqualTo(4);
assertThat(firstTokensLine.getStartUnit()).isEqualTo(10);
assertThat(firstTokensLine.getEndLine()).isEqualTo(4);
assertThat(firstTokensLine.getEndUnit()).isEqualTo(13);
// if (xosfile_read_stamped_no_path(fn, &ob, 1, 1, 1, 1, 1)) return 1;
var secondTokensLine = cpdTokenLines.get(48);
assertThat(secondTokensLine.getValue()).isEqualTo("if(_I(_I,&_I,_N,_N,_N,_N,_N))return_N;");
assertThat(secondTokensLine.getStartLine()).isEqualTo(60);
assertThat(secondTokensLine.getStartUnit()).isEqualTo(283);
assertThat(secondTokensLine.getEndLine()).isEqualTo(60);
assertThat(secondTokensLine.getEndUnit()).isEqualTo(305);
// case 3: return "three";
var thirdTokensLine = cpdTokenLines.get(71);
assertThat(thirdTokensLine.getValue()).isEqualTo("case_N:return_S;");
assertThat(thirdTokensLine.getStartLine()).isEqualTo(86);
assertThat(thirdTokensLine.getStartUnit()).isEqualTo(381);
assertThat(thirdTokensLine.getEndLine()).isEqualTo(86);
assertThat(thirdTokensLine.getEndUnit()).isEqualTo(386);
}
@Test
void testComplexitySquidMetrics() throws IOException {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/complexity-project");
var context = SensorContextTester.create(baseDir);
settings.setProperty(CxxSquidSensor.FUNCTION_COMPLEXITY_THRESHOLD_KEY, 3);
settings.setProperty(CxxSquidSensor.FUNCTION_SIZE_THRESHOLD_KEY, 3);
context.setSettings(settings);
var inputFile = TestUtils.buildInputFile(baseDir, "complexity.cc");
context.fileSystem().add(inputFile);
sensor.execute(context);
var softly = new SoftAssertions();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.FUNCTIONS).value()).isEqualTo(22);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.CLASSES).value()).isEqualTo(2);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.COMPLEXITY).value()).isEqualTo(38);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.COGNITIVE_COMPLEXITY).value()).isEqualTo(16);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.COMPLEX_FUNCTIONS).value()).isEqualTo(1);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.COMPLEX_FUNCTIONS_PERC)).isNull(); // see DensityMeasureComputer
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.COMPLEX_FUNCTIONS_LOC).value()).isEqualTo(6);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.COMPLEX_FUNCTIONS_LOC_PERC)).isNull(); // see DensityMeasureComputer
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.LOC_IN_FUNCTIONS).value()).isEqualTo(59);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.BIG_FUNCTIONS).value()).isEqualTo(9);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.BIG_FUNCTIONS_PERC)).isNull(); // see DensityMeasureComputer
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.BIG_FUNCTIONS_LOC).value()).isEqualTo(44);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.BIG_FUNCTIONS_LOC_PERC)).isNull(); // see DensityMeasureComputer
softly.assertAll();
}
@Test
void testDocumentationSquidMetrics() throws IOException {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/documentation-project");
var inputFile = TestUtils.buildInputFile(baseDir, "documentation0.hh");
var context = SensorContextTester.create(baseDir);
context.fileSystem().add(inputFile);
sensor.execute(context);
var softly = new SoftAssertions();
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.PUBLIC_API_KEY).value()).isEqualTo(8);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.PUBLIC_UNDOCUMENTED_API_KEY).value()).isEqualTo(2);
softly.assertThat(context.measure(inputFile.key(), CxxMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY)).isNull(); // see DensityMeasureComputer
String moduleKey = context.project().key();
softly.assertThat(context.measure(moduleKey, CxxMetrics.PUBLIC_API_KEY)).isNull(); // see AggregateMeasureComputer
softly.assertThat(context.measure(moduleKey, CxxMetrics.PUBLIC_UNDOCUMENTED_API_KEY)).isNull(); // see AggregateMeasureComputer
softly.assertThat(context.measure(moduleKey, CxxMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY)).isNull(); // see AggregateMeasureComputer
softly.assertAll();
}
@Test
void testReplacingOfExtenalMacros() throws IOException {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/external-macro-project");
var context = SensorContextTester.create(baseDir);
settings.setProperty(CxxSquidSensor.DEFINES_KEY, "MACRO class A{};");
context.setSettings(settings);
var inputFile = TestUtils.buildInputFile(baseDir, "test.cc");
context.fileSystem().add(inputFile);
sensor.execute(context);
var softly = new SoftAssertions();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(1);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.STATEMENTS).value()).isZero();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.FUNCTIONS).value()).isZero();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.CLASSES).value()).isEqualTo(1);
softly.assertAll();
}
@Test
void testFindingIncludedFiles() throws IOException {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/include-directories-project");
var context = SensorContextTester.create(baseDir);
settings.setProperty(CxxSquidSensor.INCLUDE_DIRECTORIES_KEY, "include");
context.setSettings(settings);
var inputFile = TestUtils.buildInputFile(baseDir, "src/main.cc");
context.fileSystem().add(inputFile);
sensor.execute(context);
var softly = new SoftAssertions();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(9);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.STATEMENTS).value()).isZero();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.FUNCTIONS).value()).isEqualTo(9);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.CLASSES).value()).isZero();
softly.assertAll();
}
@Test
void testForceIncludedFiles() throws IOException {
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/force-include-project");
var context = SensorContextTester.create(baseDir);
settings.setProperty(CxxSquidSensor.INCLUDE_DIRECTORIES_KEY, "include");
settings.setProperty(CxxSquidSensor.FORCE_INCLUDES_KEY, "force1.hh,subfolder/force2.hh");
context.setSettings(settings);
var inputFile = TestUtils.buildInputFile(baseDir, "src/src1.cc");
context.fileSystem().add(inputFile);
sensor.execute(context);
// These checks actually check the force include feature, since only if it works the metric values will be like follows
var softly = new SoftAssertions();
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(1);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.STATEMENTS).value()).isEqualTo(2);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.FUNCTIONS).value()).isEqualTo(1);
softly.assertThat(context.measure(inputFile.key(), CoreMetrics.CLASSES).value()).isZero();
softly.assertAll();
}
@Test
void testBehaviourOnCircularIncludes() throws IOException {
// especially: when two files, both belonging to the set of
// files to analyse, include each other, the preprocessor guards have to be disabled
// and both have to be counted in terms of metrics
File baseDir = TestUtils.loadResource("/com/keyware/sonar/cxx/circular-includes-project");
var inputFile = TestUtils.buildInputFile(baseDir, "test1.hh");
var context = SensorContextTester.create(baseDir);
context.fileSystem().add(inputFile);
sensor.execute(context);
assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(1);
}
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.log.LogTesterJUnit5;
import org.sonar.api.utils.log.LoggerLevel;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class DroppedPropertiesSensorTest {
@TempDir
File tempDir;
@RegisterExtension
public LogTesterJUnit5 logTester = new LogTesterJUnit5();
@Test
void testNoMsg() throws Exception {
var contextTester = SensorContextTester.create(tempDir);
var mapSettings = new MapSettings().setProperty("sonar.cxx.xxx", "value");
contextTester.setSettings(mapSettings);
List<String> analysisWarnings = new ArrayList<>();
var sensor = new DroppedPropertiesSensor(analysisWarnings::add);
sensor.execute(contextTester);
assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty();
assertThat(analysisWarnings).isEmpty();
}
@Test
void testNoLongerSupported() throws Exception {
var contextTester = SensorContextTester.create(tempDir);
var mapSettings = new MapSettings().setProperty("sonar.cxx.cppncss.reportPaths", "value");
contextTester.setSettings(mapSettings);
List<String> analysisWarnings = new ArrayList<>();
var sensor = new DroppedPropertiesSensor(analysisWarnings::add);
sensor.execute(contextTester);
var msg = "CXX property 'sonar.cxx.cppncss.reportPaths' is no longer supported.";
assertThat(logTester.logs(LoggerLevel.WARN)).contains(msg);
assertThat(analysisWarnings).containsExactly(msg);
}
@Test
void testNoLongerSupportedWithInfo() throws Exception {
var contextTester = SensorContextTester.create(tempDir);
var mapSettings = new MapSettings().setProperty("sonar.cxx.suffixes.sources", "value");
contextTester.setSettings(mapSettings);
List<String> analysisWarnings = new ArrayList<>();
var sensor = new DroppedPropertiesSensor(analysisWarnings::add);
sensor.execute(contextTester);
var msg = "CXX property 'sonar.cxx.suffixes.sources' is no longer supported."
+ " Use key 'sonar.cxx.file.suffixes' instead.";
assertThat(logTester.logs(LoggerLevel.WARN)).contains(msg);
assertThat(analysisWarnings).containsExactly(msg);
}
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx;
import org.assertj.core.util.Files;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import javax.annotation.CheckForNull;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public final class TestUtils {
public static File loadResource(String resourceName) {
URL resource = TestUtils.class.getResource(resourceName);
File resourceAsFile = null;
try {
resourceAsFile = new File(resource.toURI());
} catch (URISyntaxException e) {
System.out.println("Cannot load resource: " + resourceName);
}
return resourceAsFile;
}
/**
* Search for a test resource in the classpath. For example getResource("org/sonar/MyClass/foo.txt");
*
* @param path the starting slash is optional
* @return the resource. Null if resource not found
*/
@CheckForNull
public static File getResource(String path) {
String resourcePath = path;
if (!resourcePath.startsWith("/")) {
resourcePath = "/" + resourcePath;
}
URL url = TestUtils.class.getResource(resourcePath);
if (url != null) {
try {
return new File(url.toURI());
} catch (URISyntaxException e) {
return null;
}
}
return null;
}
public static DefaultInputFile buildInputFile(File baseDir, String fileName) throws IOException {
var target = new File(baseDir, fileName);
String content = Files.contentOf(target, StandardCharsets.UTF_8);
var inputFile = TestInputFileBuilder.create("ProjectKey", baseDir, target)
.setContents(content)
.setCharset(StandardCharsets.UTF_8)
.setLanguage("cxx")
.setType(InputFile.Type.MAIN).build();
return inputFile;
}
private TestUtils() {
// utility class
}
}

@ -0,0 +1,12 @@
# required metadata
sonar.projectKey=TEST_circular_includes
sonar.projectName=TEST_circular_includes
sonar.projectVersion=0.0.1
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sonar.sources=.

@ -0,0 +1,5 @@
#ifndef TEST1
#define TEST1
#include "test2.hh"
void test1();
#endif

@ -0,0 +1,5 @@
#ifndef TEST2
#define TEST2
#include "test1.hh"
void test2();
#endif

@ -0,0 +1,5 @@
#ifndef TEST1
#define TEST1
#include "../Package2/test2.hh"
void test1();
#endif

@ -0,0 +1,6 @@
#ifndef TEST3
#define TEST3
#include "../Package1/test1.hh"
#include "test2.hh"
void test3();
#endif

@ -0,0 +1,5 @@
#ifndef TEST4
#define TEST4
#include "../Package1/test1.hh"
void test4();
#endif

@ -0,0 +1,12 @@
# required metadata
sonar.projectKey=TEST_circular_packages
sonar.projectName=TEST_circular_packages
sonar.projectVersion=0.0.1
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sonar.sources=.

@ -0,0 +1,91 @@
/* Fudge unix isatty and fileno for RISCOS */
#include "unixstuff.h"
#include <math.h>
#include <time.h>
#include "oslib/osfile.h"
int fileno(FILE *f)
{ return (int)f;
}
int isatty(int fn)
{ return (fn==fileno(stdin));
}
bits unixtime(bits ld,bits ex)
{ ld&=0xFF;
ld-=51;
if(ex<1855547904U) ld--;
ex-=1855548004U;
return ex/100+42949673U*ld-ld/25;
}
/* from RISC OS infozip, preserves filetype in ld */
int acorntime(bits *ex, bits *ld, time_t utime)
{
unsigned timlo; /* 3 lower bytes of acorn file-time plus carry byte */
unsigned timhi; /* 2 high bytes of acorn file-time */
timlo = ((unsigned)utime & 0x00ffffffU) * 100 + 0x00996a00U;
timhi = ((unsigned)utime >> 24);
timhi = timhi * 100 + 0x0000336eU + (timlo >> 24);
if (timhi & 0xffff0000U)
return 1; /* calculation overflow, do not change time */
/* insert the five time bytes into loadaddr and execaddr variables */
*ex = (timlo & 0x00ffffffU) | ((timhi & 0x000000ffU) << 24);
*ld = (*ld & 0xffffff00U) | ((timhi >> 8) & 0x000000ffU);
return 0; /* subject to future extension to signal overflow */
}
int isdir(char *fn)
{ int ob;
if(xosfile_read_stamped_no_path(fn,&ob,0,0,0,0,0)) return 0;
switch (ob)
{ case osfile_IS_DIR:return 1;
case osfile_IS_IMAGE:return 1;
}
return 0;
}
int isfile(char *fn)
{ int ob;
if(xosfile_read_stamped_no_path(fn,&ob,0,0,0,0,0)) return 0;
switch (ob)
{ case osfile_IS_FILE:return 1;
case osfile_IS_IMAGE:return 1;
}
return 0;
}
int object_exists(char *fn)
{ int ob;
if(xosfile_read_stamped_no_path(fn,&ob,0,0,0,0,0)) return 0;
switch (ob)
{ case osfile_IS_FILE:return 1;
case osfile_IS_DIR:return 1;
case osfile_IS_IMAGE:return 1;
}
return 0;
}
// int object_exists(char *fn)
// { int ob;
// if(xosfile_read_stamped_no_path(fn,&ob,0,0,0,0,0)) return 0;
// switch (ob)
// { case osfile_IS_FILE:return 1;
// case osfile_IS_DIR:return 1;
// case osfile_IS_IMAGE:return 1;
// }
// return 0;
// }
//
//
//
//
//

@ -0,0 +1,10 @@
# required metadata
sonar.projectKey=SampleProject
sonar.projectName=SampleProject
sonar.projectVersion=1.0
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sources=.

@ -0,0 +1,128 @@
void test_function_definition() { // 1
}
int test_return() { // 1
return 1;
}
int test_if(int p) { // 2
if( p ) {
return 1;
}
return 0;
}
int test_if_else(int p) { // 2
if( p ) {
return 1;
}
else {
return 0;
}
}
int test_or(int p1, int p2) { // 3
if( p1 || p2 ) {
return 1;
}
else {
return 0;
}
}
int test_and(int p1, int p2) { // 3
if( p1 && p2 ) {
return 1;
}
else {
return 0;
}
}
int test_quest(int p) { // 2
return p ? 1 : 0;
}
void test_while() { // 2
int i = 10;
while(i) {
--i;
}
}
void test_for() { // 2
int j = 0;
for(int i=0; i<10; ++i) {
++j;
}
}
int test_switch_case(int p) { // 5
switch(p) {
case 1: return 1;
case 2: return 2;
case 3: return 3;
default: return -1;
}
}
int test_catch1() { // 2
int i = 0;
try {
i = i / 0;
}
catch(...) {
i = 0;
}
}
int test_catch2() { // 3
int i = 0;
try {
i = i / 0;
}
catch(const exception& e) {
i = 0;
}
catch(...) {
i = 0;
}
}
class ClassA { // 5
ClassA() {}
~ClassA() {}
void test_method1() {}
void test_method2() {}
void test_method3() {}
};
class ClassB { // definition outside
ClassB();
~ClassB();
void test_method1();
void test_method2();
void test_method3();
};
ClassB::ClassB() { // 1
}
ClassB::~ClassB() { // 1
}
void ClassB::test_method1() { // 1
}
void ClassB::test_method2() { // 1
}
void ClassB::test_method3() { // 1
}
// summary:
// functions/methods : 22
// classes : 2
// complexity : 38
// complexity in functions/methods: 38
// complexity in classes : 10

@ -0,0 +1,11 @@
# required metadata
sonar.projectKey=SampleProject
sonar.projectName=SampleProject
sonar.projectVersion=1.0
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sources=.

@ -0,0 +1,104 @@
//------------------------------------
bits unixtime1(bits ld, bits ex)
{
ld &= 0xFF;
ld -= 51;
if (ex < 1855547904U) ld--;
ex -= 1855548004U;
return ex / 100 + 42949673U * ld - ld / 25;
}
bits unixtime2(bits ld, bits ex)
{
if (ld && ex)
{
ld &= 0xFF;
ld -= 51;
if (ex < 1855547904U) ld--;
ex -= 1855548004U;
return ex / 100 + 42949673U * ld - ld / 25;
}
return 0;
}
//------------------------------------
int acorntime(bits *ex, bits *ld, time_t utime)
{
unsigned timlo; /* 3 lower bytes of acorn file-time plus carry byte */
unsigned timhi; /* 2 high bytes of acorn file-time */
timlo = ((unsigned)utime & 0x00ffffffU) * 100 + 0x00996a00U;
timhi = ((unsigned)utime >> 24);
timhi = timhi * 100 + 0x0000336eU + (timlo >> 24);
if (timhi & 0xffff0000U)
return 1; /* calculation overflow, do not change time */
/* insert the five time bytes into loadaddr and execaddr variables */
*ex = (timlo & 0x00ffffffU) | ((timhi & 0x000000ffU) << 24);
*ld = (*ld & 0xffffff00U) | ((timhi >> 8) & 0x000000ffU);
return 0; /* subject to future extension to signal overflow */
}
//------------------------------------
int object_exists1(char *fn)
{
int ob;
if (xosfile_read_stamped_no_path(fn, &ob, 0, 0, 0, 0, 0)) return 0;
switch (ob)
{
case osfile_IS_FILE:return 1;
case osfile_IS_DIR:return 1;
case osfile_IS_IMAGE:return 1;
}
return 0;
}
int object_exists2(char *fn)
{
int ob;
if (xosfile_read_stamped_no_path(fn, &ob, 1, 1, 1, 1, 1)) return 1;
switch (ob)
{
case osfile_IS_FILE:return 2;
case osfile_IS_DIR:return 2;
case osfile_IS_IMAGE:return 2;
}
return 0;
}
//------------------------------------
char* tostring1(int value)
{
switch (value)
{
case 0: return "zero";
case 1: return "one";
}
return "undefined";
}
char* tostring2(int value)
{
switch (value)
{
case 2: return "two";
case 3: return "three";
}
return "undefined";
}
// CPD should ignore declarations
class A {
public:
virtual void a();
virtual void b();
virtual void c();
};
class B : public A {
public:
virtual void a();
virtual void b();
virtual void c();
};

@ -0,0 +1,36 @@
#ifndef DOCUMENTATION0_H
#define DOCUMENTATION0_H
/**
* Encoding mode
*/
enum Mode { //1
/**
* stereo encoding
*/
STEREO = 0, //2
/**
* joint frequency encoding
*/
JOINT_STEREO, //3
/**
* mono encoding
*/
MONO //4
};
using RC = int; //5 [undocumented]
RC init(); //6 [undocumented]
/**
* Decode buffer of given length
*/
RC decode( unsigned char* buf, int len ); //7
/**
* Encode buffer of given length using the mode
*/
RC encode( unsigned char* buf, int len, Mode mode ); //8
#endif

@ -0,0 +1,11 @@
# required metadata
sonar.projectKey=SampleProject
sonar.projectName=SampleProject
sonar.projectVersion=1.0
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sources=.

@ -0,0 +1,15 @@
# required metadata
sonar.projectKey=project_finding_sources
sonar.projectName=project_finding_sources
sonar.projectVersion=0.0.1
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sonar.sources=src
sonar.tests=tests1,tests2
# paths to the reports
sonar.cxx.xunit.reportPaths=xunit-report.xml

@ -0,0 +1,4 @@
namespace my_test_suite {
struct my_test { void test_method(); };
void my_test::test_method() {}
}

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="1" failures="0" disabled="0" errors="0" time="0.1" name="AllTests">
<testsuite name="lalal" tests="1" failures="0" disabled="0" errors="0" time="0.1">
<testcase name="testcase1" status="run" time="0.1" classname="TestClass1" />
<testcase name="testcase2" status="run" time="0.1" classname="TestClass2" />
<testcase name="testcase3" status="run" time="0.1" classname="TestClass3" />
<testcase name="testcase4" status="run" time="0.1" classname="TestClass4" />
<testcase name="testcase5_a" status="run" time="0.1" classname="TestClass5_A" />
<testcase name="testcase5_b" status="run" time="0.1" classname="TestClass5_B" />
<testcase name="testcase6" status="run" time="0.1" classname="TestClass6" />
</testsuite>
</testsuites>

@ -0,0 +1,14 @@
# required metadata
sonar.projectKey=TEST_force_includes
sonar.projectName=TEST_force_includes
sonar.projectVersion=0.0.1
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sonar.sources=src
sonar.cxx.includeDirectories=include
sonar.cxx.forceIncludes=force1.hh,subfolder/force2.hh

@ -0,0 +1,125 @@
/*
comment
*/
//
// comment
//
//
// PREPROCESS_DIRECTIVE
//
#include "highlighter.h"
#ifdef _TEST
# define VALUE 1
#else
# define VALUE 2
#endif
#define FUNC(a) \
auto value = a; \
return value ? 0 : 1;
//
// CONSTANT
//
int i1 = 0;
int i2 = -1;
int i3 = +1;
unsigned u1 = 0u;
unsigned long u2 = 1ul;
int h1 = 0x0;
int b1 = 0b0;
int b2 = 0b0100'1100'0110;
float f1 = 0.0;
float f2 = -1.0;
float f3 = +1.0;
float f4 = 3.14E-10;
//
// STRING
//
char c1 = 'x';
char c1 = '\t';
const char* str1 = "hello";
const char* str1 = "hello\tworld\r\n";
//
// KEYWORD
//
void func()
{
auto str = "test"; // comment
// comment
auto iii = 0;
/* comment */
for(const auto& c : str)
{
if (c == 't') /* comment */
{
iii++;
}
}
}
void test1()
{
const std::regex RegexEscape(R"([.^$|()\[\]{}*+?\\])"); // raw string literal
}
void test2(const char* sourceFilename)
{
Warning() << "Failed to open file " << sourceFilename << " for reading";
}
void test3()
{
const char *t1 = "..."; // UTF-8 encoded
const char *t2 = u8"..."; // UTF-8 encoded
const wchar_t *t3 = L"..."; // Wide string
const char16_t *t4 = u"..."; // UTF-16 encoded
const char32_t *t5 = U"..."; // UTF-32 encoded
const char *t6 = "hello" " world";
const wchar_t *t7 = u"" "hello world";
const wchar_t *t8 = /*comment1*/ u"" /*comment2*/ "hello world" /*comment3*/; // issue #996
const char *t9 = /*comment4*/ "hello"
/*comment5*/ " world" /*comment6*/;
const char *t10 = "hello"
"Mary"
"Lou";
}
void test4()
{
auto txt = R"(
Hello World!
)";
}
// issue #2197
namespace Test {
class Test {
private:
bool import = false;
}
}
// issue #2192
void test5()
{
assert(module != nullptr);
for (int module=0; module<10; module++) {}
char modules[]= {}
for (auto module : modules) {}
}
/* EOF */

@ -0,0 +1,14 @@
# required metadata
sonar.projectKey=TEST_include_directories
sonar.projectName=TEST_include_directories
sonar.projectVersion=0.0.1
# disable xml
sonar.xml.file.suffixes=.disable-xml
# path to source directories (required)
sonar.sources=src
sonar.cxx.includeDirectories=include

@ -0,0 +1,29 @@
// 1. include via a relpath with a backstep
#include "../include/include1.hh"
// 2. include via a filename from the include root
#include "include2.hh"
// 3. the same, but using <>
#include <include3.hh>
// 4. include via a path relative to the include root
#include "subfolder/include4.hh"
// 5. indirect include of ../include_trd/something
#include "../include_snd/include_snd_1.hh"
// 6. macro replacement doesnt take place in <> and "" form of the include statement
#define HEADER FOO
#include "HEADER1.hh"
#include <HEADER2.hh>
// 7. ... but it does in the 'free' form of the include statement
#define LOCATION "bar.hh"
#include LOCATION
#define MACRO(p) p
#include MACRO("widget.hh")
INCLUDE1
INCLUDE2
INCLUDE3
INCLUDE4
INCLUDE_SND_SUBFOLDER_1
HEADER1
HEADER2
BAR //void bar(){}
WIDGET //void widget(){}

@ -0,0 +1,129 @@
/*
Header
*/
#include "ncloc.h"
// comment
void func1()
{
h1 = 0;
}
/* comment */
void func2()
{
const char* txt =
"Hello "
" World!";
}
void func3(
int a,
int b
)
{
return a + b;
}
#define NUMBER 10
void func4()
{
// comment
for(int iii=0; iii<NUMBER; ++iii) {
h1 += iii; // comment
}
}
#define ADD(a, b) \
((a) + (b))
void func5()
{
int a=1, b=2;
int sum = ADD(a, b);
}
// declaration
void func6();
void func7(bool flag)
{
int iii = 0;
if (flag) {
iii = 1;
}
else {
iii = 0;
}
}
int func8(int p)
{
int r;
switch (p) {
case 0:
r = 100;
break;
case 1:
r = 200;
break;
default:
r = 300;
break;
}
return r;
}
void func9()
{
int *p;
try {
p = new int[10];
}
catch (...) {
p = nullptr;
}
}
void func10()
{
// comment
for (int iii = 0;
iii < NUMBER;
++iii)
{
h1 += iii; // comment
}
}
// inline
class MyClass {
public:
MyClass();
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = default;
int method1();
int method2()
{
// comment
for(int iii=0; iii<NUMBER; ++iii) {
h1 += iii; // comment
}
}
};
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
/* EOF */
Loading…
Cancel
Save