diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S8491.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S8491.json new file mode 100644 index 00000000000..87020fc2c82 --- /dev/null +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S8491.json @@ -0,0 +1,6 @@ +{ + "ruleKey": "S8491", + "hasTruePositives": true, + "falseNegatives": 0, + "falsePositives": 0 +} diff --git a/its/ruling/src/test/resources/guava/java-S8491.json b/its/ruling/src/test/resources/guava/java-S8491.json new file mode 100644 index 00000000000..0ef7eb2ddda --- /dev/null +++ b/its/ruling/src/test/resources/guava/java-S8491.json @@ -0,0 +1,5 @@ +{ +"com.google.guava:guava:src/com/google/common/util/concurrent/Monitor.java": [ +1017 +] +} diff --git a/java-checks-common/src/main/java/org/sonar/java/checks/helpers/TreeHelper.java b/java-checks-common/src/main/java/org/sonar/java/checks/helpers/TreeHelper.java index 1a8f13bab44..63455b2b60c 100644 --- a/java-checks-common/src/main/java/org/sonar/java/checks/helpers/TreeHelper.java +++ b/java-checks-common/src/main/java/org/sonar/java/checks/helpers/TreeHelper.java @@ -19,10 +19,15 @@ import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; + +import org.sonar.java.ast.visitors.PublicApiChecker; import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.VariableTree; + public class TreeHelper { private TreeHelper() { @@ -48,6 +53,18 @@ public static Tree findClosestParentOfKind(Tree tree, Set nodeKinds) return null; } + public static Tree reportTree(Tree tree) { + Tree reportTree = tree; + if (reportTree.is(PublicApiChecker.classKinds())) { + reportTree = ExpressionsHelper.reportOnClassTree((ClassTree) reportTree); + } else if (reportTree.is(PublicApiChecker.methodKinds())) { + reportTree = ((MethodTree) reportTree).simpleName(); + } else if (reportTree.is(Tree.Kind.VARIABLE)) { + reportTree = ((VariableTree) reportTree).simpleName(); + } + return reportTree; + } + private static class ReachableMethodsFinder extends BaseTreeVisitor { private final Map reachableMethods = new IdentityHashMap<>(); diff --git a/java-checks-common/src/test/java/org/sonar/java/checks/helpers/TreeHelperTest.java b/java-checks-common/src/test/java/org/sonar/java/checks/helpers/TreeHelperTest.java index 7429e7f4b0e..05727180d33 100644 --- a/java-checks-common/src/test/java/org/sonar/java/checks/helpers/TreeHelperTest.java +++ b/java-checks-common/src/test/java/org/sonar/java/checks/helpers/TreeHelperTest.java @@ -17,12 +17,17 @@ package org.sonar.java.checks.helpers; import java.util.Set; +import java.util.stream.Stream; import javax.annotation.Nullable; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.sonar.java.model.statement.BlockTreeImpl; import org.sonar.plugins.java.api.tree.BlockTree; import org.sonar.plugins.java.api.tree.ExpressionStatementTree; import org.sonar.plugins.java.api.tree.ForEachStatement; +import org.sonar.plugins.java.api.tree.IdentifierTree; import org.sonar.plugins.java.api.tree.IfStatementTree; import org.sonar.plugins.java.api.tree.MethodInvocationTree; import org.sonar.plugins.java.api.tree.MethodTree; @@ -95,4 +100,88 @@ private static Tree assertFoundNode(Tree tree, Set kinds, @Nullable T } return actual; } + + @ParameterizedTest(name = "[{index}] {1}") + @MethodSource("reportTreeTestCases") + void reportTree(String code, String description, TreeExtractor treeExtractor, @Nullable String expectedName) { + Tree tree = treeExtractor.extract(code); + Tree reportTree = TreeHelper.reportTree(tree); + + if (expectedName != null) { + assertThat(reportTree).isInstanceOf(IdentifierTree.class); + assertThat(((IdentifierTree) reportTree).name()).isEqualTo(expectedName); + } else { + assertThat(reportTree).isSameAs(tree); + } + } + + private static Stream reportTreeTestCases() { + return Stream.of( + Arguments.of( + "class MyClass { }", + "Class", + (TreeExtractor) TreeHelperTest::classTree, + "MyClass" + ), + Arguments.of( + "interface MyInterface { }", + "Interface", + (TreeExtractor) TreeHelperTest::classTree, + "MyInterface" + ), + Arguments.of( + "enum MyEnum { }", + "Enum", + (TreeExtractor) TreeHelperTest::classTree, + "MyEnum" + ), + Arguments.of( + "record MyRecord() { }", + "Record", + (TreeExtractor) TreeHelperTest::classTree, + "MyRecord" + ), + Arguments.of( + "@interface MyAnnotation { }", + "Annotation", + (TreeExtractor) TreeHelperTest::classTree, + "MyAnnotation" + ), + Arguments.of( + newCode("void myMethod() { }"), + "Method", + (TreeExtractor) TreeHelperTest::methodTree, + "myMethod" + ), + Arguments.of( + "class A { A() { } }", + "Constructor", + (TreeExtractor) code -> classTree(code).members().get(0), + "A" + ), + Arguments.of( + "class A { String myField; }", + "Field", + (TreeExtractor) code -> classTree(code).members().get(0), + "myField" + ), + Arguments.of( + newCode("void method() { int localVar = 42; }"), + "Local variable", + (TreeExtractor) code -> methodTree(code).block().body().get(0), + "localVar" + ), + Arguments.of( + newCode("void method() { if (true) { } }"), + "Other tree (if statement)", + (TreeExtractor) code -> methodTree(code).block().body().get(0), + null + ) + ); + } + + @FunctionalInterface + private interface TreeExtractor { + Tree extract(String code); + } } diff --git a/java-checks-test-sources/default/src/main/java/checks/DanglingJavadocCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/DanglingJavadocCheckSample.java new file mode 100644 index 00000000000..53ed65eceb1 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/DanglingJavadocCheckSample.java @@ -0,0 +1,83 @@ +package checks; + +class DanglingJavadocCheckSample { + private String name; + + /** + * This is a dangling javadoc comment that will be ignored. + */ + /** + * This is the actual javadoc that will be used. + * @param name the name parameter + */ + public void setName(String name) { // Noncompliant {{Remove or merge the dangling Javadoc comment(s).}} +// ^^^^^^^ + this.name = name; + } + + /** + * Sets the name parameter. + * @param name the name parameter + */ + public void setNameCompliant(String name) { // Compliant + this.name = name; + } + + /** + * First dangling comment + */ + /** + * Second dangling comment + */ + /** + * The actual comment for this field + */ + public String multipleJavadocs; // Noncompliant +// ^^^^^^^^^^^^^^^^ + /** + * Dangling method comment + */ + /** + * Actual method comment + * @return the value + */ + public int getValue() { // Noncompliant + return 42; + } +} + +/** + * Old documentation that is no longer relevant. + */ +/** + * Updated documentation for this class. + */ +class OldUser { // Noncompliant + private String name; +} + +/** + * This traditional javadoc will be ignored. + */ +/// This markdown javadoc will be used. +/// Represents a customer entity. +class Customer { // Noncompliant +// ^^^^^^^^ + private String customerId; +} + +/** + * Updated documentation for this class. + */ +class UserCompliant { // Compliant + private String name; +} + +/// Represents a customer entity. +class CustomerCompliant { // Compliant + private String customerId; +} + +class NoJavadoc { // Compliant + private String field; +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/DanglingJavadocCheck.java b/java-checks/src/main/java/org/sonar/java/checks/DanglingJavadocCheck.java new file mode 100644 index 00000000000..ac47eed13a2 --- /dev/null +++ b/java-checks/src/main/java/org/sonar/java/checks/DanglingJavadocCheck.java @@ -0,0 +1,44 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2025 SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA. + * + * 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks; + +import java.util.Arrays; +import java.util.List; +import org.sonar.check.Rule; +import org.sonar.java.ast.visitors.PublicApiChecker; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.Tree; + +import static org.sonar.java.checks.helpers.TreeHelper.reportTree; + +@Rule(key = "S8491") +public class DanglingJavadocCheck extends IssuableSubscriptionVisitor { + + @Override + public List nodesToVisit() { + return Arrays.asList(PublicApiChecker.apiKinds()); + } + + @Override + public void visitNode(Tree tree) { + int javadocCount = PublicApiChecker.getApiJavadocsCount(tree); + if (javadocCount > 1) { + reportIssue(reportTree(tree), "Remove or merge the dangling Javadoc comment(s)."); + } + } + +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/DeprecatedTagPresenceCheck.java b/java-checks/src/main/java/org/sonar/java/checks/DeprecatedTagPresenceCheck.java index 41d0ac65cec..d6a1a754511 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/DeprecatedTagPresenceCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/DeprecatedTagPresenceCheck.java @@ -28,7 +28,7 @@ import static org.sonar.java.checks.helpers.DeprecatedCheckerHelper.deprecatedAnnotation; import static org.sonar.java.checks.helpers.DeprecatedCheckerHelper.getAnnotationAttributeValue; import static org.sonar.java.checks.helpers.DeprecatedCheckerHelper.hasJavadocDeprecatedTagWithoutLegitimateDocumentation; -import static org.sonar.java.checks.helpers.DeprecatedCheckerHelper.reportTreeForDeprecatedTree; +import static org.sonar.java.checks.helpers.TreeHelper.reportTree; @Rule(key = "S1133") public class DeprecatedTagPresenceCheck extends IssuableSubscriptionVisitor { @@ -41,7 +41,7 @@ public List nodesToVisit() { @Override public void visitNode(Tree tree) { if (hasDeprecatedAnnotation(tree) || hasJavadocDeprecatedTagWithoutLegitimateDocumentation(tree)) { - reportIssue(reportTreeForDeprecatedTree(tree), "Do not forget to remove this deprecated code someday."); + reportIssue(reportTree(tree), "Do not forget to remove this deprecated code someday."); } } @@ -51,12 +51,8 @@ private static boolean hasDeprecatedAnnotation(Tree tree) { return false; } Optional forRemovalValue = getAnnotationAttributeValue(annotation, "forRemoval", Boolean.class); - // If forRemoval was not specified, we consider the deprecated code should be removed in the future. - if (forRemovalValue.isEmpty()) { - return true; - } - // Otherwise, we check the value of forRemoval and return that, so that only @Deprecated(forRemoval = true) is considered. - return Boolean.TRUE.equals(forRemovalValue.get()); + // If forRemoval was not specified or equals true, we consider the deprecated code should be removed in the future. + return forRemovalValue.map(Boolean.TRUE::equals).orElse(true); } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/MissingDeprecatedCheck.java b/java-checks/src/main/java/org/sonar/java/checks/MissingDeprecatedCheck.java index a70eb05c72a..0c5a39b4599 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/MissingDeprecatedCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/MissingDeprecatedCheck.java @@ -24,7 +24,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey; -import static org.sonar.java.checks.helpers.DeprecatedCheckerHelper.reportTreeForDeprecatedTree; +import static org.sonar.java.checks.helpers.TreeHelper.reportTree; @DeprecatedRuleKey(ruleKey = "MissingDeprecatedCheck", repositoryKey = "squid") @Rule(key = "S1123") @@ -39,10 +39,10 @@ void handleDeprecatedElement(Tree tree, @CheckForNull AnnotationTree deprecatedA boolean hasDeprecatedAnnotation = deprecatedAnnotation != null; if (hasDeprecatedAnnotation) { if (!hasJavadocDeprecatedTag) { - reportIssue(reportTreeForDeprecatedTree(tree), "Add the missing @deprecated Javadoc tag."); + reportIssue(reportTree(tree), "Add the missing @deprecated Javadoc tag."); } } else if (hasJavadocDeprecatedTag) { - reportIssue(reportTreeForDeprecatedTree(tree), "Add the missing @Deprecated annotation."); + reportIssue(reportTree(tree), "Add the missing @Deprecated annotation."); } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/DeprecatedCheckerHelper.java b/java-checks/src/main/java/org/sonar/java/checks/helpers/DeprecatedCheckerHelper.java index a05b6d54eb8..3a990e30cb6 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/DeprecatedCheckerHelper.java +++ b/java-checks/src/main/java/org/sonar/java/checks/helpers/DeprecatedCheckerHelper.java @@ -156,18 +156,6 @@ private static AnnotationTree deprecatedAnnotation(Iterable anno return null; } - public static Tree reportTreeForDeprecatedTree(Tree tree) { - Tree reportTree = tree; - if (reportTree.is(PublicApiChecker.classKinds())) { - reportTree = ExpressionsHelper.reportOnClassTree((ClassTree) reportTree); - } else if (reportTree.is(PublicApiChecker.methodKinds())) { - reportTree = ((MethodTree) reportTree).simpleName(); - } else if (reportTree.is(Tree.Kind.VARIABLE)) { - reportTree = ((VariableTree) reportTree).simpleName(); - } - return reportTree; - } - private static boolean isDeprecated(AnnotationTree tree) { return tree.annotationType().is(Kind.IDENTIFIER) && "Deprecated".equals(((IdentifierTree) tree.annotationType()).name()); diff --git a/java-checks/src/test/java/org/sonar/java/checks/DanglingJavadocCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/DanglingJavadocCheckTest.java new file mode 100644 index 00000000000..d1d1a54e10e --- /dev/null +++ b/java-checks/src/test/java/org/sonar/java/checks/DanglingJavadocCheckTest.java @@ -0,0 +1,34 @@ +/* + * SonarQube Java + * Copyright (C) 2012-2025 SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA. + * + * 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath; + +class DanglingJavadocCheckTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/DanglingJavadocCheckSample.java")) + .withCheck(new DanglingJavadocCheck()) + .verifyIssues(); + } + +} diff --git a/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java b/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java index ab6a420c7b6..83df73a209b 100644 --- a/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java +++ b/java-frontend/src/main/java/org/sonar/java/ast/visitors/PublicApiChecker.java @@ -143,4 +143,15 @@ public static Optional getApiJavadoc(Tree tree) { // Get last element of stream, as the last javadoc comment is the one we are looking for. .reduce((first, second) -> second); } + + public static int getApiJavadocsCount(Tree tree) { + if (!tree.is(API_KINDS)) { + return 0; + } + return (int) tree.firstToken() + .trivias() + .stream() + .filter(trivia -> trivia.isComment(CommentKind.JAVADOC, CommentKind.MARKDOWN)) + .count(); + } } diff --git a/java-frontend/src/test/files/ast/PublicApi.java b/java-frontend/src/test/files/ast/PublicApi.java index a7f1ec4f9d8..03133a1dcc1 100644 --- a/java-frontend/src/test/files/ast/PublicApi.java +++ b/java-frontend/src/test/files/ast/PublicApi.java @@ -156,4 +156,4 @@ public int getMyVarGetSet() { public void setMyVarGetSet(int myVarGetSet) { this.myVarGetSet = myVarGetSet; } -} \ No newline at end of file +} diff --git a/java-frontend/src/test/java/org/sonar/java/ast/visitors/PublicApiCheckerTest.java b/java-frontend/src/test/java/org/sonar/java/ast/visitors/PublicApiCheckerTest.java index 6f28d681d1b..8541df3f02f 100644 --- a/java-frontend/src/test/java/org/sonar/java/ast/visitors/PublicApiCheckerTest.java +++ b/java-frontend/src/test/java/org/sonar/java/ast/visitors/PublicApiCheckerTest.java @@ -24,8 +24,12 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.sonar.java.model.JParserTestUtils; import org.sonar.plugins.java.api.tree.ClassTree; import org.sonar.plugins.java.api.tree.CompilationUnitTree; @@ -144,6 +148,137 @@ class A { } .isPresent() .contains("/**\n* documented\n*/"); } + + @ParameterizedTest(name = "[{index}] {1}") + @MethodSource("getApiJavadocsCountTestCases") + void getApiJavadocsCount(String code, String description, TreeExtractor treeExtractor, int expectedCount) { + CompilationUnitTree cut = JParserTestUtils.parse(code); + Tree tree = treeExtractor.extract(cut); + assertThat(PublicApiChecker.getApiJavadocsCount(tree)) + .as(description) + .isEqualTo(expectedCount); + } + + private static Stream getApiJavadocsCountTestCases() { + return Stream.of( + Arguments.of( + "class A { }", + "No javadoc", + (TreeExtractor) cut -> cut.types().get(0), + 0 + ), + Arguments.of( + """ + /** + * documented + */ + class A { } + """, + "Single javadoc", + (TreeExtractor) cut -> cut.types().get(0), + 1 + ), + Arguments.of( + """ + /** + * first javadoc + */ + /** + * second javadoc + */ + class A { } + """, + "Multiple javadocs (2)", + (TreeExtractor) cut -> cut.types().get(0), + 2 + ), + Arguments.of( + """ + /** + * first javadoc + */ + /** + * second javadoc + */ + /** + * third javadoc + */ + class A { } + """, + "Three javadocs", + (TreeExtractor) cut -> cut.types().get(0), + 3 + ), + Arguments.of( + """ + class A { + /** + * old documentation + */ + /** + * actual documentation + */ + public void foo() { } + } + """, + "Method with multiple javadocs", + (TreeExtractor) cut -> ((ClassTree) cut.types().get(0)).members().get(0), + 2 + ), + Arguments.of( + """ + class A { + /** + * first comment + */ + /** + * second comment + */ + public String field; + } + """, + "Field with multiple javadocs", + (TreeExtractor) cut -> ((ClassTree) cut.types().get(0)).members().get(0), + 2 + ), + Arguments.of( + """ + /** + * traditional javadoc + */ + /// markdown javadoc + class A { } + """, + "Traditional and markdown javadoc", + (TreeExtractor) cut -> cut.types().get(0), + 2 + ), + Arguments.of( + """ + /// markdown javadoc + class A { } + """, + "Markdown javadoc", + (TreeExtractor) cut -> cut.types().get(0), + 1 + ), + Arguments.of( + "class A { void foo() { int x = 1 + 2; } }", + "Non-API kind returns zero", + (TreeExtractor) cut -> { + ClassTree classTree = (ClassTree) cut.types().get(0); + MethodTree method = (MethodTree) classTree.members().get(0); + return method.block().body().get(0); + }, + 0 + ) + ); + } + + @FunctionalInterface + private interface TreeExtractor { + Tree extract(CompilationUnitTree cut); + } } @Test diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8491.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8491.html new file mode 100644 index 00000000000..6c46e501177 --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8491.html @@ -0,0 +1,84 @@ +

Why is this an issue?

+

When multiple Javadoc comments (/** */ and /// ///) are placed consecutively above a class, method, or field, only the +last one (the one closest to the documented element) will be processed by the Javadoc tool. Any preceding Javadoc comments become "dangling" comments +that are ignored by the documentation generator.

+

This pattern is counter-intuitive and can lead to several problems:

+
    +
  • Important documentation may be accidentally omitted from the generated Javadoc
  • +
  • Developers may mistakenly believe that all Javadoc comments are being included in the documentation
  • +
  • The presence of multiple Javadoc blocks creates confusion about which documentation is actually used
  • +
  • Dangling Javadoc comments may contain outdated or incorrect information that appears to be part of the documentation but is actually + ignored
  • +
+

How to fix it

+

Remove or merge the dangling Javadoc comments, keeping only the Javadoc comment that is immediately adjacent to the documented element.

+

If the dangling comment contains useful information, merge it into the active Javadoc comment. If the dangling comment is outdated or unnecessary, +remove it entirely.

+

Code examples

+

Noncompliant code example

+
+/**
+ * This is a dangling javadoc comment that will be ignored.
+ */
+/**
+ * This is the actual javadoc that will be used.
+ * @param name the name parameter
+ */
+public void setName(String name) { // Noncompliant - dangling Javadoc comment above
+  this.name = name;
+}
+
+
+/**
+ * Old documentation that is no longer relevant.
+ */
+/**
+ * Updated documentation for this class.
+ */
+public class User { // Noncompliant - dangling Javadoc comment above
+  private String name;
+}
+
+
+/**
+ * This traditional javadoc will be ignored.
+ */
+/// This markdown javadoc will be used.
+/// Represents a customer entity.
+public class Customer { // Noncompliant - dangling traditional Javadoc before markdown Javadoc
+  private String customerId;
+}
+
+

Compliant solution

+
+/**
+ * Sets the name parameter.
+ * @param name the name parameter
+ */
+public void setName(String name) { // Compliant
+  this.name = name;
+}
+
+
+/**
+ * Updated documentation for this class.
+ */
+public class User { // Compliant
+  private String name;
+}
+
+
+/// This markdown javadoc will be used.
+/// Represents a customer entity.
+public class Customer { // Compliant
+  private String customerId;
+}
+
+

Resources

+

Documentation

+ + diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8491.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8491.json new file mode 100644 index 00000000000..e38b2e8bbb2 --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8491.json @@ -0,0 +1,24 @@ +{ + "title": "Dangling Javadoc comments should be removed", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "javadoc", + "confusing" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-8491", + "sqKey": "S8491", + "scope": "All", + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "CLEAR" + } +} diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json index 65bd04a9a73..85a39068bed 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json @@ -527,6 +527,7 @@ "S8447", "S8450", "S8465", - "S8469" + "S8469", + "S8491" ] }