-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathUnusedPrivateMethodScalaCheck.java
More file actions
88 lines (79 loc) · 3.75 KB
/
UnusedPrivateMethodScalaCheck.java
File metadata and controls
88 lines (79 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/*
* SonarSource Scala
* Copyright (C) 2018-2025 SonarSource SA
* 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.sonarsource.scala.checks;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import org.sonar.check.Rule;
import org.sonarsource.slang.api.ClassDeclarationTree;
import org.sonarsource.slang.api.IdentifierTree;
import org.sonarsource.slang.api.PackageDeclarationTree;
import org.sonarsource.slang.api.Tree;
import org.sonarsource.slang.checks.UnusedPrivateMethodCheck;
import org.sonarsource.slang.checks.api.CheckContext;
import org.sonarsource.slang.impl.NativeTreeImpl;
@Rule(key = "S1144")
public class UnusedPrivateMethodScalaCheck extends UnusedPrivateMethodCheck {
// Serializable method should not raise any issue in Scala.
private static final Set<String> IGNORED_METHODS = new HashSet<>(Arrays.asList(
"writeObject",
"readObject",
"writeReplace",
"readResolve",
"readObjectNoData"));
// companion objects may use methods in the main class
private Set<String> usagesInCompanionObjects = new HashSet<>();
@Override
protected void processClassDeclaration(CheckContext context, ClassDeclarationTree classDeclarationTree) {
// only verify the outermost class in the file, to avoid raising the same issue multiple times
IdentifierTree identifier = classDeclarationTree.identifier();
if (context.ancestors().stream().noneMatch(ClassDeclarationTree.class::isInstance) &&
identifier != null) {
collectUsagesInCompanionObject(identifier.name(), context.ancestors());
reportUnusedPrivateMethods(context, classDeclarationTree);
}
}
@Override
protected boolean isUnusedMethod(IdentifierTree identifier, Set<String> usedIdentifierNames) {
return super.isUnusedMethod(identifier, usedIdentifierNames) &&
!IGNORED_METHODS.contains(identifier.name()) &&
!usagesInCompanionObjects.contains(identifier.name());
}
private void collectUsagesInCompanionObject(String className, Deque<Tree> ancestors) {
// a top-level class either has 1 ancestor (TopLevelTree) or could be inside a package
if (ancestors.size() == 1 || isInsidePackage(ancestors)) {
// search for the companion object and collect what's inside
ancestors.getFirst().descendants()
.filter(NativeTreeImpl.class::isInstance)
.map(NativeTreeImpl.class::cast)
.filter(n -> isObjectCompanionForClass(className, n))
.forEach(n -> {
MethodAndIdentifierCollector collector = new MethodAndIdentifierCollector(n.descendants());
usagesInCompanionObjects = collector.getUsedUniqueIdentifiers();
});
}
}
private static boolean isObjectCompanionForClass(String className, NativeTreeImpl nativeTree) {
return nativeTree.nativeKind().toString().contains("scala.meta.Defn$Object$DefnObjectImpl") &&
nativeTree.children().stream().anyMatch(i -> i instanceof IdentifierTree && ((IdentifierTree)i).identifier().equals(className));
}
// Two ancestors: PackageDeclarationTree and TopLevelTree
private static boolean isInsidePackage(Deque<Tree> ancestors) {
return ancestors.size() == 2 && ancestors.getFirst() instanceof PackageDeclarationTree;
}
}