From b8d407c798e4576af5d31bea53d7780443593c43 Mon Sep 17 00:00:00 2001 From: Guo XIn <371864209@qq.com> Date: Sun, 14 Jan 2024 18:52:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9Ac++=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E9=83=A8=E7=BD=B2=E5=90=8E=E5=9C=A8=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E6=9C=89=E9=97=AE=E9=A2=98=E7=9A=84=E8=A2=AB=E6=B5=8B=E4=BB=B6?= =?UTF-8?q?=E4=BD=86=E6=89=AB=E6=8F=8F=E7=BB=93=E6=9E=9C=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=97=A0=E9=97=AE=E9=A2=98=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/keyware/sonar/cxx/CxxLanguage.java | 2 +- .../java/com/keyware/sonar/cxx/CxxPlugin.java | 2 +- .../keyware/sonar/cxx/CxxSonarWayProfile.java | 48 +- .../com/keyware/sonar/cxx/CxxSquidSensor.java | 976 +++++++++--------- .../sonar/cxx/SubscriptionAstVisitor.java | 9 +- .../rules/SecurityDesignRuleRepository.java | 2 +- .../keyware/sonar/cxx/CxxLanguageTest.java | 4 +- .../com/keyware/sonar/cxx/CxxPluginTest.java | 24 +- 8 files changed, 539 insertions(+), 528 deletions(-) diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxLanguage.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxLanguage.java index 869d791..ea5a82f 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxLanguage.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxLanguage.java @@ -40,7 +40,7 @@ public class CxxLanguage extends AbstractLanguage { /** * Default cxx files knows suffixes */ - public static final String DEFAULT_FILE_SUFFIXES = "-"; + public static final String DEFAULT_FILE_SUFFIXES = ".cxx,.cpp,.cc,.c,.hxx,.hpp,.hh,.h"; /** * Settings of the plugin. diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java index dd237b8..563275d 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java @@ -64,9 +64,9 @@ public final class CxxPlugin implements Plugin { // plugin elements l.add(CxxLanguage.class); l.add(CxxSonarWayProfile.class); + l.add(SecurityDesignWayProfile.class); l.add(CxxRuleRepository.class); l.add(SecurityDesignRuleRepository.class); - l.add(SecurityDesignWayProfile.class); // reusable elements l.addAll(getSensorsImpl()); diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSonarWayProfile.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSonarWayProfile.java index ce04ca4..e85b9df 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSonarWayProfile.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSonarWayProfile.java @@ -8,6 +8,7 @@ package com.keyware.sonar.cxx; import com.google.common.io.Resources; import com.google.gson.Gson; +import com.keyware.sonar.cxx.rules.SecurityDesignRuleRepository; import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; import org.sonar.cxx.checks.CheckList; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -23,34 +24,33 @@ import java.util.List; @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); + 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); - }); + 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); + } - sonarWay.done(); - } + @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 { + static class Profile { - public String name; - public List ruleKeys; - } + public String name; + public List ruleKeys; + } } diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java index 1660807..68b0a11 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java @@ -6,6 +6,7 @@ */ package com.keyware.sonar.cxx; +import com.keyware.sonar.cxx.rules.SecurityDesignRuleRepository; import com.sonar.cxx.sslr.api.Grammar; import org.sonar.api.PropertyType; import org.sonar.api.batch.fs.InputFile; @@ -57,508 +58,515 @@ import java.util.stream.StreamSupport; */ 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 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>(); - for (var check : checks.all()) { - RuleKey key = checks.ruleKey(check); - if (key != null) { - if (context.activeRules().find(key) != null) { - visitors.add(check); - } - } + 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()) + .addChecks(SecurityDesignRuleRepository.REPOSITORY_KEY, SecurityDesignRuleRepository.RULE_CHECKERS) + .addCustomChecks(customRulesDefinition); + this.fileLinesContextFactory = fileLinesContextFactory; + this.noSonarFilter = noSonarFilter; } - var squidConfig = createConfiguration(); - var scanner = CxxAstScanner.create(squidConfig, visitors.toArray(new SquidAstVisitor[visitors.size()])); + public static List 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() + )); + } - Iterable inputFiles = getInputFiles(context, squidConfig); - scanner.scanInputFiles(inputFiles); + @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)); + } - Collection squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceFile.class)); - save(squidSourceFiles); - } + /** + * {@inheritDoc} + */ + @Override + public void execute(SensorContext context) { + this.context = context; + + // add visitor only if corresponding rule is active + var visitors = new ArrayList>(); + for (var check : checks.all()) { + RuleKey key = checks.ruleKey(check); + if (key != null) { + if (context.activeRules().find(key) != null) { + visitors.add(check); + } + } + } - @Override - public String toString() { - return getClass().getSimpleName(); - } + var squidConfig = createConfiguration(); + var scanner = CxxAstScanner.create(squidConfig, visitors.toArray(new SquidAstVisitor[visitors.size()])); - private String[] stripValue(String key, String regex) { - Optional value = context.config().get(key); - if (value.isPresent()) { - var PATTERN = Pattern.compile(regex); - return PATTERN.split(value.get(), -1); + Iterable inputFiles = getInputFiles(context, squidConfig); + scanner.scanInputFiles(inputFiles); + + Collection squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceFile.class)); + save(squidSourceFiles); } - 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 logFiles = CxxUtils.getFiles(context, MsBuild.REPORT_PATH_KEY); - squidConfig.readMsBuildFiles(logFiles, context.config().get(MsBuild.REPORT_ENCODING_DEF) - .orElse(MsBuild.DEFAULT_ENCODING_DEF)); + + @Override + public String toString() { + return getClass().getSimpleName(); } - return squidConfig; - } - - private Iterable getInputFiles(SensorContext context, CxxSquidConfiguration squidConfig) { - Iterable 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'." - ); - } + private String[] stripValue(String key, String regex) { + Optional value = context.config().get(key); + if (value.isPresent()) { + var PATTERN = Pattern.compile(regex); + return PATTERN.split(value.get(), -1); + } + return new String[0]; } - return inputFiles; - } + 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 logFiles = CxxUtils.getFiles(context, MsBuild.REPORT_PATH_KEY); + squidConfig.readMsBuildFiles(logFiles, context.config().get(MsBuild.REPORT_ENCODING_DEF) + .orElse(MsBuild.DEFAULT_ENCODING_DEF)); + } - private void save(Collection 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()); - } + return squidConfig; } - } - - 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(); + + private Iterable getInputFiles(SensorContext context, CxxSquidConfiguration squidConfig) { + Iterable 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'." + ); + } } - RuleKey ruleKey = checks.ruleKey((SquidAstVisitor) 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); + return inputFiles; + } + + private void save(Collection 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()); + } } - } } - 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; + 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(); + } + // 增加对规则库的判断 + var checker = (SquidAstVisitor) message.getCheck(); + var repositoryKey = CheckList.REPOSITORY_KEY; + if (checker.getClass().getPackageName().startsWith("com.keyware.sonar.cxx.rules.checkers")) { + repositoryKey = SecurityDesignRuleRepository.REPOSITORY_KEY; + } + + RuleKey ruleKey = checks.ruleKey(checker); + if (ruleKey != null) { + var newIssue = context.newIssue().forRule(RuleKey.of(repositoryKey, ruleKey.rule())); + var location = newIssue.newLocation() + .on(inputFile) + .at(inputFile.selectLine(line)) + .message(message.getText(Locale.CHINA)); + + 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); } - newIssue.save(); - } - MultiLocatitionSquidCheck.eraseMultilineCheckMessages(sourceFile); } - } - - private void saveFileLinesContext(InputFile inputFile, SourceFile sourceFile) { - // measures for the lines of file - var fileLinesContext = fileLinesContextFactory.createFor(inputFile); - List linesOfCode = (List) 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 executableLines = (List) 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 data = (List) 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 data = (List) 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 void saveMetric(InputFile file, Metric metric, T value) { - context.newMeasure() - .withValue(value) - .forMetric(metric) - .on(file) - .save(); - } + + private void saveFileLinesContext(InputFile inputFile, SourceFile sourceFile) { + // measures for the lines of file + var fileLinesContext = fileLinesContextFactory.createFor(inputFile); + List linesOfCode = (List) 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 executableLines = (List) 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 data = (List) 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 data = (List) 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 void saveMetric(InputFile file, Metric metric, T value) { + context.newMeasure() + .withValue(value) + .forMetric(metric) + .on(file) + .save(); + } } diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java index 5a69849..6e894f6 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java @@ -9,6 +9,7 @@ package com.keyware.sonar.cxx; import com.sonar.cxx.sslr.api.AstNode; import com.sonar.cxx.sslr.api.AstNodeType; import com.sonar.cxx.sslr.api.Grammar; +import org.sonar.cxx.squidbridge.SquidAstVisitor; import org.sonar.cxx.squidbridge.checks.SquidCheck; import java.util.List; @@ -19,13 +20,15 @@ import java.util.List; * @author GuoXin * @date 2024/1/13 */ -public abstract class SubscriptionAstVisitor extends SquidCheck { +public abstract class SubscriptionAstVisitor extends SquidAstVisitor { + private SquidCheck checker; /** * 构造函数需要传入初代访问器 * * @param checker 初代规则检查器 */ public SubscriptionAstVisitor(SquidCheck checker) { + this.checker = checker; setContext(checker.getContext()); visitNodeTypes().forEach(this::subscribeTo); } @@ -95,7 +98,7 @@ public abstract class SubscriptionAstVisitor extends SquidCheck { * @param messageParameters 可选消息参数(请参阅 java.text.MessageFormat) */ protected void reportIssue(String message, Object... messageParameters) { - getContext().createFileViolation(this, message, messageParameters); + getContext().createFileViolation(checker, message, messageParameters); } /** @@ -106,7 +109,7 @@ public abstract class SubscriptionAstVisitor extends SquidCheck { * @param messageParameters 可选消息参数(请参阅 java.text.MessageFormat) */ protected void reportIssue(AstNode node, String message, Object... messageParameters) { - getContext().createLineViolation(this, message, node, messageParameters); + getContext().createLineViolation(checker, message, node, messageParameters); } } diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/SecurityDesignRuleRepository.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/SecurityDesignRuleRepository.java index 1d42211..b2afaa9 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/SecurityDesignRuleRepository.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/SecurityDesignRuleRepository.java @@ -25,7 +25,7 @@ public class SecurityDesignRuleRepository implements RulesDefinition { public final static String REPOSITORY_NAME = "C++信息安全性设计准则"; // 规则检查器的集合,当有新的规则开发完毕后,需要添加到下面的集合中 - private final List RULE_CHECKERS = Arrays.asList(ABCVarNameChecker.class); + public final static List RULE_CHECKERS = Arrays.asList(ABCVarNameChecker.class); @Override public void define(Context context) { diff --git a/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxLanguageTest.java b/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxLanguageTest.java index 6079b48..4a92caa 100644 --- a/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxLanguageTest.java +++ b/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxLanguageTest.java @@ -32,7 +32,7 @@ class CxxLanguageTest { @Test void shouldReturnDefaultFileSuffixes1() { var cxx = new CxxLanguage(settings.asConfig()); - String[] expected = {"disabled"}; + String[] expected = {".cxx", ".cpp", ".cc", ".c", ".hxx", ".hpp", ".hh", ".h"}; assertThat(cxx.getFileSuffixes()).contains(expected); } @@ -40,7 +40,7 @@ class CxxLanguageTest { void shouldReturnDefaultFileSuffixes2() { settings.setProperty(CxxLanguage.FILE_SUFFIXES_KEY, ""); var cxx = new CxxLanguage(settings.asConfig()); - String[] expected = {"disabled"}; + String[] expected = {".cxx", ".cpp", ".cc", ".c", ".hxx", ".hpp", ".hh", ".h"}; assertThat(cxx.getFileSuffixes()).contains(expected); } diff --git a/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxPluginTest.java b/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxPluginTest.java index 1081029..d0ae58c 100644 --- a/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxPluginTest.java +++ b/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/CxxPluginTest.java @@ -18,17 +18,17 @@ 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); - } + @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(85); + } }