rule.setConfigKey("Checker/TreeWalker/ConstantName");
rule.setSeverity(RulePriority.BLOCKER);
rule.setStatus(Rule.STATUS_BETA);
- rule.setTags(new String[] {"style", "security"});
+ rule.setTags(new String[] {"style", "clumsy"});
rule.createParameter("format").setDescription("Regular expression").setDefaultValue("A-Z").setType("REGULAR_EXPRESSION");
return Arrays.asList(rule);
}
assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/ConstantName");
assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
- assertThat(rule.tags()).containsOnly("style", "security");
+ assertThat(rule.tags()).containsOnly("style", "clumsy");
assertThat(rule.params()).hasSize(1);
RulesDefinition.Param param = rule.param("format");
assertThat(param).isNotNull();
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.server.rule;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * @see org.sonar.api.server.rule.RulesDefinition.NewRule#setType(RulesDefinition.Type)
+ * @since 5.5
+ */
+class RuleTagsToTypeConverter {
+
+ public static final String TAG_BUG = "bug";
+ public static final String TAG_SECURITY = "security";
+ static final Set<String> RESERVED_TAGS = ImmutableSet.of(TAG_BUG, TAG_SECURITY);
+
+
+ private RuleTagsToTypeConverter() {
+ // only statics
+ }
+
+ static RulesDefinition.Type convert(Collection<String> tags) {
+ if (tags.contains(TAG_BUG)) {
+ return RulesDefinition.Type.BUG;
+ }
+ if (tags.contains(TAG_SECURITY)) {
+ return RulesDefinition.Type.VULNERABILITY;
+ }
+ return RulesDefinition.Type.CODE_SMELL;
+ }
+}
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.utils.log.Loggers;
+import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
* // default severity when the rule is activated on a Quality profile. Default value is MAJOR.
* .setSeverity(Severity.MINOR);
*
+ * // optional type for SonarQube Quality Model. Default is RulesDefinition.Type.CODE_SMELL.
+ * .setType(RulesDefinition.Type.BUG)
+ *
* x1Rule
* .setDebtRemediationFunction(x1Rule.debtRemediationFunctions().linearWithOffset("1h", "30min"));
*
@ExtensionPoint
public interface RulesDefinition {
+ /**
+ * Rule type according to SonarQube Quality Model
+ * @since 5.5
+ */
+ enum Type {
+ CODE_SMELL, BUG, VULNERABILITY
+ }
/**
* Default sub-characteristics of technical debt model. See http://www.sqale.org
* @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model.
class NewRule {
private final String repoKey;
private final String key;
+ private Type type;
private String name;
private String htmlDescription;
private String markdownDescription;
return this;
}
+ /**
+ * The type as defined by the SonarQube Quality Model.
+ * <p/>
+ * When a plugin does not define rule type, then it is deduced from
+ * tags:
+ * <ul>
+ * <li>if the rule has the "bug" tag then type is {@link Type#BUG}</li>
+ * <li>if the rule has the "security" tag then type is {@link Type#VULNERABILITY}</li>
+ * <li>if the rule has both tags "bug" and "security", then type is {@link Type#BUG}</li>
+ * <li>default type is {@link Type#CODE_SMELL}</li>
+ * </ul>
+ * Finally the "bug" and "security" tags are considered as reserved. They
+ * are silently removed from the final state of definition.
+ * @since 5.5
+ */
+ public NewRule setType(Type t) {
+ this.type = t;
+ return this;
+ }
+
/**
* The optional description, in HTML format, has no max length. It's exclusive with markdown description
* (see {@link #setMarkdownDescription(String)})
* @see org.sonar.api.server.rule.RulesDefinition.SubCharacteristics for constant values
* @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. This method does nothing.
* See https://jira.sonarsource.com/browse/MMF-184
+ * @see #setType(Type)
*/
public NewRule setDebtSubCharacteristic(@Nullable String s) {
return this;
private final String repoKey;
private final String key;
private final String name;
+ private final Type type;
private final String htmlDescription;
private final String markdownDescription;
private final String internalKey;
this.status = newRule.status;
this.debtRemediationFunction = newRule.debtRemediationFunction;
this.effortToFixDescription = newRule.effortToFixDescription;
- this.tags = ImmutableSortedSet.copyOf(newRule.tags);
+ this.type = (newRule.type == null ? RuleTagsToTypeConverter.convert(newRule.tags) : newRule.type);
+ this.tags = ImmutableSortedSet.copyOf(Sets.difference(newRule.tags, RuleTagsToTypeConverter.RESERVED_TAGS));
ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder();
for (NewParam newParam : newRule.paramsByKey.values()) {
paramsBuilder.put(newParam.key, new Param(newParam));
return name;
}
+ /**
+ * @since 5.5
+ * @see NewRule#setType(Type)
+ */
+ public Type type() {
+ return type;
+ }
+
public String severity() {
return severity;
}
/**
* @deprecated in 5.5. SQALE Quality Model is replaced by SonarQube Quality Model. {@code null} is
* always returned. See https://jira.sonarsource.com/browse/MMF-184
+ * @see #type()
*/
@CheckForNull
@Deprecated
* <!-- Status displayed in rules console. Possible values are BETA, READY (default), DEPRECATED. -->
* <status>BETA</status>
*
+ * <!-- Type as defined by the SonarQube Quality Model. Possible values are CODE_SMELL (default), BUG and VULNERABILITY.-->
+ * <type>BUG</type>
+ *
* <!-- Optional tags. See org.sonar.api.server.rule.RuleTagFormat. The maximal length of all tags is 4000 characters. -->
- * <tag>style</tag>
- * <tag>security</tag>
+ * <tag>misra</tag>
+ * <tag>multi-threading</tag>
*
* <!-- Optional parameters -->
* <param>
* <param>
* <key>another-param</key>
* </param>
- *
- * <!-- SQALE debt - key of sub-characteristic -->
- * <!-- See {@link org.sonar.api.server.rule.RulesDefinition.SubCharacteristics} for core supported values.
- * Any other values can be used. If sub-characteristic does not exist at runtime in the SQALE model,
- * then the rule is created without any sub-characteristic. -->
- * <!-- Since 5.3 -->
- * <debtSubCharacteristic>MODULARITY</debtSubCharacteristic>
- *
+ **
* <!-- SQALE debt - type of debt remediation function -->
* <!-- See enum {@link org.sonar.api.server.debt.DebtRemediationFunction.Type} for supported values -->
* <!-- Since 5.3 -->
* <tag>cwe</tag>
* <tag>security</tag>
* <tag>user-experience</tag>
- * <debtSubCharacteristic>SECURITY_FEATURES</debtSubCharacteristic>
* <debtRemediationFunction>CONSTANT_ISSUE</debtRemediationFunction>
* <debtRemediationFunctionOffset>10min</debtRemediationFunctionOffset>
* </rule>
String descriptionFormat = DescriptionFormat.HTML.name();
String internalKey = null;
String severity = Severity.defaultSeverity();
+ String type = null;
RuleStatus status = RuleStatus.defaultStatus();
boolean template = false;
String effortToFixDescription = null;
if (equalsIgnoreCase("name", nodeName)) {
name = nodeValue(cursor);
+ } else if (equalsIgnoreCase("type", nodeName)) {
+ type = nodeValue(cursor);
+
} else if (equalsIgnoreCase("description", nodeName)) {
description = nodeValue(cursor);
.setTemplate(template)
.setStatus(status)
.setEffortToFixDescription(effortToFixDescription);
+ if (type != null) {
+ rule.setType(RulesDefinition.Type.valueOf(type));
+ }
fillDescription(rule, descriptionFormat, description);
fillRemediationFunction(rule, debtRemediationFunction, debtRemediationFunctionOffset, debtRemediationFunctionCoeff);
fillParams(rule, params);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.server.rule;
+
+import java.util.Collections;
+import org.junit.Test;
+import org.sonar.test.TestUtils;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.server.rule.RuleTagsToTypeConverter.convert;
+
+public class RuleTagsToTypeConverterTest {
+
+ @Test
+ public void type_is_bug_if_has_tag_bug() {
+ assertThat(convert(asList("misra", "bug"))).isEqualTo(RulesDefinition.Type.BUG);
+ // "bug" has priority on "security"
+ assertThat(convert(asList("security", "bug"))).isEqualTo(RulesDefinition.Type.BUG);
+ }
+
+ @Test
+ public void type_is_vulnerability_if_has_tag_security() {
+ assertThat(convert(asList("misra", "security"))).isEqualTo(RulesDefinition.Type.VULNERABILITY);
+ }
+
+ @Test
+ public void default_is_code_smell() {
+ assertThat(convert(asList("clumsy", "spring"))).isEqualTo(RulesDefinition.Type.CODE_SMELL);
+ assertThat(convert(Collections.<String>emptyList())).isEqualTo(RulesDefinition.Type.CODE_SMELL);
+ }
+
+ @Test
+ public void only_statics() {
+ assertThat(TestUtils.hasOnlyPrivateConstructors(RuleTagsToTypeConverter.class)).isTrue();
+
+ }
+}
RulesDefinition.Repository repository = load(RuleWithTags.class);
assertThat(repository.rules()).hasSize(1);
RulesDefinition.Rule rule = repository.rules().get(0);
- assertThat(rule.tags()).containsOnly("style", "security");
+ assertThat(rule.tags()).containsOnly("misra", "clumsy");
}
@Test
public String property;
}
- @org.sonar.check.Rule(key = "foo", name = "bar", description = "Bar", tags = {"style", "security"})
+ @org.sonar.check.Rule(key = "foo", name = "bar", description = "Bar", tags = {"misra", "clumsy"})
static class RuleWithTags {
}
}
RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
assertThat(rule.debtSubCharacteristic()).isNull();
}
+
+ @Test
+ public void type_is_defined() {
+ RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java");
+ newRepository.createRule("NPE").setName("NPE").setHtmlDescription("desc")
+ .setType(RulesDefinition.Type.VULNERABILITY).setTags("bug", "misra");
+ newRepository.done();
+
+ RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
+ // type VULNERABILITY is kept even if the tag "bug" is present
+ assertThat(rule.type()).isEqualTo(RulesDefinition.Type.VULNERABILITY);
+ // tag "bug" is reserved and removed.
+ assertThat(rule.tags()).containsOnly("misra");
+ }
+
+ @Test
+ public void guess_type_from_tags_if_type_is_missing() {
+ RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java");
+ newRepository.createRule("NPE").setName("NPE").setHtmlDescription("desc").setTags("bug", "misra");
+ newRepository.done();
+
+ RulesDefinition.Rule rule = context.repository("findbugs").rule("NPE");
+ assertThat(rule.type()).isEqualTo(RulesDefinition.Type.BUG);
+ // tag "bug" is reserved and removed
+ assertThat(rule.tags()).containsOnly("misra");
+ }
}
assertThat(rule.template()).isTrue();
assertThat(rule.status()).isEqualTo(RuleStatus.BETA);
assertThat(rule.internalKey()).isEqualTo("Checker/TreeWalker/LocalVariableName");
- assertThat(rule.tags()).containsOnly("style", "security");
+ assertThat(rule.type()).isEqualTo(RulesDefinition.Type.BUG);
+ assertThat(rule.tags()).containsOnly("misra", "spring");
assertThat(rule.params()).hasSize(2);
RulesDefinition.Param ignore = rule.param("ignore");
assertThat(rule.params()).isEmpty();
assertThat(rule.status()).isEqualTo(RuleStatus.READY);
assertThat(rule.severity()).isEqualTo(Severity.MAJOR);
+ assertThat(rule.type()).isEqualTo(RulesDefinition.Type.CODE_SMELL);
}
@Test
<severity>BLOCKER</severity>
<cardinality>MULTIPLE</cardinality>
<status>BETA</status>
- <tag>style</tag>
- <tag>security</tag>
+ <type>BUG</type>
+ <tag>misra</tag>
+ <tag>spring</tag>
<param>
<key>tokens</key>
<description>