parent
02d782f556
commit
8d3f922056
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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,4 @@ |
|||||||
|
#ifndef TEST2 |
||||||
|
#define TEST2 |
||||||
|
void test2(); |
||||||
|
#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 @@ |
|||||||
|
MACRO |
@ -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 @@ |
|||||||
|
class TestClass1{}; |
@ -0,0 +1,2 @@ |
|||||||
|
class TestClass5_A{}; |
||||||
|
class TestClass5_B{}; |
@ -0,0 +1,4 @@ |
|||||||
|
class TestClass6{ |
||||||
|
void test_foo(); |
||||||
|
void test_bar(); |
||||||
|
}; |
@ -0,0 +1,2 @@ |
|||||||
|
#include "Test6.hh" |
||||||
|
void TestClass6::test_foo(){}; |
@ -0,0 +1,2 @@ |
|||||||
|
#include "Test6.hh" |
||||||
|
void TestClass6::test_bar(){}; |
@ -0,0 +1,4 @@ |
|||||||
|
namespace my_test_suite { |
||||||
|
struct my_test { void test_method(); }; |
||||||
|
void my_test::test_method() {} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
class TestClass2{}; |
@ -0,0 +1,3 @@ |
|||||||
|
namespace ns{ |
||||||
|
class TestClass3{}; |
||||||
|
}; |
@ -0,0 +1,2 @@ |
|||||||
|
#include "Test4.hh" |
||||||
|
void ns::TestClass4::test1(){} |
@ -0,0 +1,5 @@ |
|||||||
|
namespace ns{ |
||||||
|
class TestClass4{ |
||||||
|
void test1(); |
||||||
|
}; |
||||||
|
}; |
@ -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 @@ |
|||||||
|
#define MACRO1(a) if(a); |
@ -0,0 +1 @@ |
|||||||
|
#define MACRO2(a) void func() { MACRO1(a) } |
@ -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 @@ |
|||||||
|
MACRO2(2) |
@ -0,0 +1 @@ |
|||||||
|
MACRO2(1) |
@ -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,7 @@ |
|||||||
|
/*
|
||||||
|
Header |
||||||
|
*/ |
||||||
|
|
||||||
|
int h1 = 0; |
||||||
|
|
||||||
|
/* EOF */ |
@ -0,0 +1 @@ |
|||||||
|
#define HEADER1 void header1(){} |
@ -0,0 +1 @@ |
|||||||
|
#define HEADER2 void header2(){} |
@ -0,0 +1 @@ |
|||||||
|
#define BAR void bar(){} |
@ -0,0 +1 @@ |
|||||||
|
#define INCLUDE1 void include1(){} |
@ -0,0 +1 @@ |
|||||||
|
#define INCLUDE2 void include2(){} |
@ -0,0 +1 @@ |
|||||||
|
#define INCLUDE3 void include3(){} |
@ -0,0 +1 @@ |
|||||||
|
#define INCLUDE4 void include4(){} |
@ -0,0 +1 @@ |
|||||||
|
#define WIDGET void widget(){} |
@ -0,0 +1 @@ |
|||||||
|
#include "subfolder/include_snd_subfolder_1.hh" |
@ -0,0 +1 @@ |
|||||||
|
#define INCLUDE_SND_SUBFOLDER_1 void include_snd_subfolder_1(){} |
@ -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 */ |
@ -0,0 +1,7 @@ |
|||||||
|
/*
|
||||||
|
Header |
||||||
|
*/ |
||||||
|
|
||||||
|
int h1 = 0; |
||||||
|
|
||||||
|
/* EOF */ |
@ -0,0 +1 @@ |
|||||||
|
<c++ source> |
Loading…
Reference in new issue