11/*******************************************************************************
2- * Copyright (c) 2024 Microsoft Corporation and others.
2+ * Copyright (c) 2024, 2025 Microsoft Corporation and others.
33* All rights reserved. This program and the accompanying materials
44* are made available under the terms of the Eclipse Public License 2.0
55* which accompanies this distribution, and is available at
4747import com .sun .tools .javac .parser .Tokens .Comment ;
4848import com .sun .tools .javac .parser .Tokens .Comment .CommentStyle ;
4949import com .sun .tools .javac .tree .JCTree ;
50+ import com .sun .tools .javac .tree .JCTree .JCAnnotation ;
51+ import com .sun .tools .javac .tree .JCTree .JCAssign ;
5052import com .sun .tools .javac .tree .JCTree .JCClassDecl ;
5153import com .sun .tools .javac .tree .JCTree .JCCompilationUnit ;
54+ import com .sun .tools .javac .tree .JCTree .JCExpression ;
5255import com .sun .tools .javac .tree .JCTree .JCFieldAccess ;
5356import com .sun .tools .javac .tree .JCTree .JCIdent ;
5457import com .sun .tools .javac .tree .JCTree .JCImport ;
58+ import com .sun .tools .javac .tree .JCTree .JCLiteral ;
5559import com .sun .tools .javac .tree .JCTree .JCMemberReference ;
5660import com .sun .tools .javac .tree .JCTree .JCMethodDecl ;
61+ import com .sun .tools .javac .tree .JCTree .JCNewArray ;
5762import com .sun .tools .javac .tree .JCTree .JCNewClass ;
5863import com .sun .tools .javac .tree .JCTree .JCVariableDecl ;
5964
@@ -62,6 +67,8 @@ public class UnusedTreeScanner<R, P> extends TreeScanner<R, P> {
6267 final Set <Symbol > usedElements = new HashSet <>();
6368 final Map <String , JCImport > unusedImports = new LinkedHashMap <>();
6469 private CompilationUnitTree unit = null ;
70+ private boolean classSuppressUnused = false ;
71+ private boolean methodSuppressUnused = false ;
6572
6673 private final UnusedDocTreeScanner unusedDocTreeScanner = new UnusedDocTreeScanner ();
6774
@@ -105,8 +112,16 @@ public R visitImport(ImportTree node, P p) {
105112
106113 @ Override
107114 public R visitClass (ClassTree node , P p ) {
108- if (node instanceof JCClassDecl classDecl && this .isPotentialUnusedDeclaration (classDecl )) {
109- this .privateDecls .add (classDecl );
115+ if (node instanceof JCClassDecl classDecl ) {
116+ for (JCAnnotation annot : classDecl .mods .annotations ) {
117+ classSuppressUnused = isUnusedSuppressed (annot );
118+ break ;
119+ }
120+ if ( this .isPotentialUnusedDeclaration (classDecl )) {
121+ if (!classSuppressUnused ) {
122+ this .privateDecls .add (classDecl );
123+ }
124+ }
110125 }
111126
112127 return super .visitClass (node , p );
@@ -191,6 +206,10 @@ private boolean isPotentialUnusedDeclaration(Tree tree) {
191206 if (tree instanceof JCClassDecl classTree ) {
192207 return (classTree .getModifiers ().flags & Flags .PRIVATE ) != 0 ;
193208 } else if (tree instanceof JCMethodDecl methodTree ) {
209+ for (JCAnnotation annot : methodTree .mods .annotations ) {
210+ methodSuppressUnused = isUnusedSuppressed (annot );
211+ break ;
212+ }
194213 if (isConstructor (methodTree )) {
195214 return (methodTree .getModifiers ().flags & Flags .PRIVATE ) != 0
196215 && hasPackageVisibleConstructor (methodTree .sym .owner );
@@ -270,18 +289,50 @@ public List<CategorizedProblem> getUnusedImports(UnusedProblemFactory problemFac
270289
271290 public List <CategorizedProblem > getUnusedPrivateMembers (UnusedProblemFactory problemFactory ) {
272291 List <Tree > unusedPrivateMembers = new ArrayList <>();
273- for (Tree decl : this .privateDecls ) {
274- if (decl instanceof JCClassDecl classDecl && !this .usedElements .contains (classDecl .sym )) {
275- unusedPrivateMembers .add (decl );
276- } else if (decl instanceof JCMethodDecl methodDecl && !this .usedElements .contains (methodDecl .sym )) {
277- unusedPrivateMembers .add (decl );
278- } else if (decl instanceof JCVariableDecl variableDecl && !this .usedElements .contains (variableDecl .sym )) {
279- unusedPrivateMembers .add (decl );
292+ if (!classSuppressUnused &&!methodSuppressUnused ) {
293+ for (Tree decl : this .privateDecls ) {
294+ if (decl instanceof JCClassDecl classDecl && !this .usedElements .contains (classDecl .sym )) {
295+ unusedPrivateMembers .add (decl );
296+ } else if (decl instanceof JCMethodDecl methodDecl && !this .usedElements .contains (methodDecl .sym )) {
297+ unusedPrivateMembers .add (decl );
298+ } else if (decl instanceof JCVariableDecl variableDecl
299+ && !this .usedElements .contains (variableDecl .sym )) {
300+ boolean suppressed = false ;
301+ for (JCAnnotation annot : variableDecl .mods .annotations ) {
302+ suppressed = isUnusedSuppressed (annot );
303+ break ;
304+ }
305+ if (!suppressed ) {
306+ unusedPrivateMembers .add (decl );
307+ }
308+ }
280309 }
281310 }
282-
283311 return problemFactory .addUnusedPrivateMembers (unit , unusedPrivateMembers );
284312 }
313+
314+ private boolean isUnusedSuppressed (JCAnnotation annot ) {
315+ boolean suppressed = false ;
316+ JCTree type = annot .getAnnotationType ();
317+ if (type instanceof JCIdent id && id .sym .name .contentEquals ("SuppressWarnings" )) {
318+ for (JCExpression exp : annot .getArguments ()) {
319+ if (exp instanceof JCAssign assign && assign .lhs instanceof JCIdent lhsId && lhsId .sym .name .contentEquals ("value" )) {
320+ if ( assign .rhs instanceof JCLiteral rhsId && rhsId .value .equals ("unused" )) {
321+ suppressed =true ;
322+ break ;
323+ } else if (assign .rhs instanceof JCNewArray array ) {
324+ for (var el : array .elems ) {
325+ if (el instanceof JCLiteral lit && lit .value .equals ("unused" )) {
326+ suppressed =true ;
327+ break ;
328+ }
329+ }
330+ }
331+ }
332+ }
333+ }
334+ return suppressed ;
335+ }
285336
286337 private class UnusedDocTreeScanner extends com .sun .source .util .DocTreeScanner <R , P > {
287338 @ Override
0 commit comments