|
16 | 16 | */ |
17 | 17 | package org.sonar.java.checks; |
18 | 18 |
|
| 19 | +import java.util.ArrayList; |
19 | 20 | import java.util.Collections; |
20 | 21 | import java.util.List; |
21 | 22 | import java.util.Set; |
22 | 23 | import java.util.stream.Collectors; |
23 | 24 | import org.sonar.check.Rule; |
24 | 25 | import org.sonar.java.checks.helpers.AnnotationsHelper; |
25 | 26 | import org.sonar.java.checks.helpers.ClassPatternsUtils; |
| 27 | +import org.sonar.java.checks.helpers.QuickFixHelper; |
26 | 28 | import org.sonar.java.model.ModifiersUtils; |
| 29 | +import org.sonar.java.reporting.JavaQuickFix; |
| 30 | +import org.sonar.java.reporting.JavaTextEdit; |
27 | 31 | import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; |
28 | 32 | import org.sonar.plugins.java.api.semantic.Type; |
29 | 33 | import org.sonar.plugins.java.api.tree.AnnotationTree; |
@@ -71,7 +75,12 @@ public void visitNode(Tree tree) { |
71 | 75 | } |
72 | 76 | } |
73 | 77 | if (hasImplicitPublicConstructor && !hasCompliantGeneratedConstructors(classTree)) { |
74 | | - reportIssue(classTree.simpleName(), "Add a private constructor to hide the implicit public one."); |
| 78 | + QuickFixHelper.newIssue(context) |
| 79 | + .forRule(this) |
| 80 | + .onTree(classTree.simpleName()) |
| 81 | + .withMessage("Add a private constructor to hide the implicit public one.") |
| 82 | + .withQuickFixes(() -> computeQuickFixes(classTree)) |
| 83 | + .report(); |
75 | 84 | } |
76 | 85 | } |
77 | 86 |
|
@@ -126,4 +135,32 @@ private static boolean isAccessLevelNotPublic(ExpressionTree tree) { |
126 | 135 | return !"PUBLIC".equals(valueName); |
127 | 136 | } |
128 | 137 |
|
| 138 | + private static List<JavaQuickFix> computeQuickFixes(ClassTree classTree) { |
| 139 | + int firstMemberColumnOffset = classTree.members().get(0).firstToken().range().start().columnOffset(); |
| 140 | + int columnOffsetDiff = firstMemberColumnOffset - classTree.firstToken().range().start().columnOffset(); |
| 141 | + |
| 142 | + String leftPadding = " ".repeat(firstMemberColumnOffset); |
| 143 | + String constructor = leftPadding + "private " + classTree.simpleName() + "() {\n" + leftPadding + " ".repeat(columnOffsetDiff) |
| 144 | + + "/* This utility class should not be instantiated */\n" + leftPadding + "}"; |
| 145 | + |
| 146 | + List<JavaQuickFix> quickFixes = new ArrayList<>(); |
| 147 | + quickFixes.add(JavaQuickFix.newQuickFix("Add an empty private constructor as the first member of the class.") |
| 148 | + .addTextEdit(JavaTextEdit.insertAfterTree(classTree.openBraceToken(), "\n" + constructor + "\n")) |
| 149 | + .build()); |
| 150 | + quickFixes.add(JavaQuickFix.newQuickFix("Add an empty private constructor as the last member of the class.") |
| 151 | + .addTextEdit(JavaTextEdit.insertAfterTree(classTree.members().get(classTree.members().size() - 1), "\n\n" + constructor)) |
| 152 | + .build()); |
| 153 | + |
| 154 | + if (classTree.members().stream().anyMatch(tree -> tree.is(Tree.Kind.METHOD))) { |
| 155 | + List<Tree> membersBeforeFirstMethod = classTree.members().stream().takeWhile(tree -> !tree.is(Tree.Kind.METHOD)).toList(); |
| 156 | + if (!membersBeforeFirstMethod.isEmpty()) { |
| 157 | + quickFixes.add(JavaQuickFix.newQuickFix("Add an empty private constructor before the first method in the class.") |
| 158 | + .addTextEdit(JavaTextEdit.insertAfterTree(membersBeforeFirstMethod.get(membersBeforeFirstMethod.size() - 1), "\n\n" + constructor)) |
| 159 | + .build()); |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + return quickFixes; |
| 164 | + } |
| 165 | + |
129 | 166 | } |
0 commit comments