001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.modifier; 021 022import java.util.Set; 023 024import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 029 030/** 031 * <div>Checks for redundant modifiers on fields and methods declared directly 032 * in a compact source file. Ordinary and nested declarations are checked by 033 * {@code RedundantModifier}.</div> 034 * 035 * @since 14.4.0 036 */ 037@FileStatefulCheck 038public class RedundantModifierCompactSourceCheck extends AbstractCheck { 039 040 /** A key is pointing to the warning message text in "messages.properties" file. */ 041 public static final String MSG_KEY = "redundantModifierCompactSource"; 042 043 /** Modifiers that are redundant on a method declared directly in a compact source file. */ 044 private static final Set<Integer> METHOD_MODIFIERS = Set.of( 045 TokenTypes.LITERAL_PUBLIC, TokenTypes.LITERAL_PROTECTED, 046 TokenTypes.LITERAL_PRIVATE, TokenTypes.LITERAL_STATIC, 047 TokenTypes.FINAL, TokenTypes.STRICTFP); 048 049 /** Modifiers that are redundant on a field declared directly in a compact source file. */ 050 private static final Set<Integer> FIELD_MODIFIERS = Set.of( 051 TokenTypes.LITERAL_PUBLIC, TokenTypes.LITERAL_PROTECTED, 052 TokenTypes.LITERAL_PRIVATE, TokenTypes.LITERAL_STATIC); 053 054 /** One of these is mandatory on a valid @SafeVarargs method. */ 055 private static final Set<Integer> SAFE_VARARGS_REQUIRED = Set.of( 056 TokenTypes.LITERAL_STATIC, TokenTypes.FINAL, TokenTypes.LITERAL_PRIVATE); 057 058 /** Simple and qualified names of the {@code SafeVarargs} annotation. */ 059 private static final Set<String> SAFE_VARARGS_NAMES = 060 Set.of("SafeVarargs", "java.lang.SafeVarargs"); 061 062 /** 063 * Root of the file being processed. Only a compact source file can have a 064 * field or method as a direct child of its root. 065 */ 066 private DetailAST root; 067 068 /** 069 * Creates a new {@code RedundantModifierCompactSourceCheck} instance. 070 */ 071 public RedundantModifierCompactSourceCheck() { 072 // no code by default 073 } 074 075 @Override 076 public void beginTree(DetailAST rootAST) { 077 root = rootAST; 078 } 079 080 @Override 081 public int[] getDefaultTokens() { 082 return getRequiredTokens(); 083 } 084 085 @Override 086 public int[] getAcceptableTokens() { 087 return getRequiredTokens(); 088 } 089 090 @Override 091 public int[] getRequiredTokens() { 092 return new int[] {TokenTypes.METHOD_DEF, TokenTypes.VARIABLE_DEF}; 093 } 094 095 @Override 096 public void visitToken(DetailAST ast) { 097 if (ast.getParent() == root) { 098 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 099 if (ast.getType() == TokenTypes.METHOD_DEF) { 100 checkModifiers(modifiers, METHOD_MODIFIERS, 101 isRequiredModifierKept(ast, modifiers)); 102 } 103 else { 104 checkModifiers(modifiers, FIELD_MODIFIERS, false); 105 } 106 } 107 } 108 109 /** 110 * Logs every modifier that is redundant for the declaration. 111 * 112 * @param modifiers the MODIFIERS node of the declaration 113 * @param redundant token types of modifiers that are redundant 114 * @param keepRequired whether the modifier making {@code @SafeVarargs} valid must be kept 115 */ 116 private void checkModifiers(DetailAST modifiers, Set<Integer> redundant, 117 boolean keepRequired) { 118 for (DetailAST modifier = modifiers.getFirstChild(); 119 modifier != null; modifier = modifier.getNextSibling()) { 120 final int type = modifier.getType(); 121 if (redundant.contains(type) 122 && !(keepRequired && SAFE_VARARGS_REQUIRED.contains(type))) { 123 log(modifier, MSG_KEY, modifier.getText()); 124 } 125 } 126 } 127 128 /** 129 * Checks whether a modifier is the single one making {@code @SafeVarargs} valid. 130 * 131 * @param methodDef the METHOD_DEF node 132 * @param modifiers the MODIFIERS node of the method 133 * @return true if the required modifier must not be reported 134 */ 135 private static boolean isRequiredModifierKept(DetailAST methodDef, DetailAST modifiers) { 136 return AnnotationUtil.containsAnnotation(methodDef, SAFE_VARARGS_NAMES) 137 && countRequiredModifiers(modifiers) == 1; 138 } 139 140 /** 141 * Counts the modifiers that can make a {@code @SafeVarargs} method valid. 142 * 143 * @param modifiers the MODIFIERS node 144 * @return number of such modifiers 145 */ 146 private static int countRequiredModifiers(DetailAST modifiers) { 147 int count = 0; 148 for (DetailAST modifier = modifiers.getFirstChild(); 149 modifier != null; modifier = modifier.getNextSibling()) { 150 if (SAFE_VARARGS_REQUIRED.contains(modifier.getType())) { 151 count++; 152 } 153 } 154 return count; 155 } 156 157}