1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.indentation;
21
22 import com.puppycrawl.tools.checkstyle.api.DetailAST;
23 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24
25 /**
26 * Handler for method definitions.
27 *
28 */
29 public class MethodDefHandler extends BlockParentHandler {
30
31 /**
32 * Construct an instance of this handler with the given indentation check,
33 * abstract syntax tree, and parent handler.
34 *
35 * @param indentCheck the indentation check
36 * @param ast the abstract syntax tree
37 * @param parent the parent handler
38 */
39 public MethodDefHandler(IndentationCheck indentCheck,
40 DetailAST ast, AbstractExpressionHandler parent) {
41 super(indentCheck, getHandlerName(ast), ast, parent);
42 }
43
44 @Override
45 protected DetailAST getTopLevelAst() {
46 // we check this stuff ourselves below
47 return null;
48 }
49
50 @Override
51 protected void checkModifiers() {
52 final DetailAST modifier = getMainAst().findFirstToken(TokenTypes.MODIFIERS);
53 if (isOnStartOfLine(modifier)
54 && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) {
55 logError(modifier, "modifier", expandedTabsColumnNo(modifier));
56 }
57 }
58
59 /**
60 * Check the indentation level of the throws clause.
61 */
62 private void checkThrows() {
63 final DetailAST throwsAst = getMainAst().findFirstToken(TokenTypes.LITERAL_THROWS);
64
65 if (throwsAst != null) {
66 checkWrappingIndentation(throwsAst, throwsAst.getNextSibling(), getIndentCheck()
67 .getThrowsIndent(), getLineStart(getMethodDefLineStart(getMainAst())),
68 !isOnStartOfLine(throwsAst));
69 }
70 }
71
72 /**
73 * Gets the start line of the method, excluding any annotations. This is required because the
74 * current {@link TokenTypes#METHOD_DEF} may not always be the start as seen in
75 * <a href="https://github.com/checkstyle/checkstyle/issues/3145">#3145</a>.
76 *
77 * @param mainAst
78 * The method definition ast.
79 * @return The start column position of the method.
80 */
81 private static int getMethodDefLineStart(DetailAST mainAst) {
82 // get first type position
83 int lineStart = mainAst.findFirstToken(TokenTypes.IDENT).getLineNo();
84
85 // check if there is a type before the indent
86 final DetailAST typeNode = mainAst.findFirstToken(TokenTypes.TYPE);
87 if (typeNode != null) {
88 lineStart = getFirstLine(typeNode);
89 }
90
91 // check if there is a modifier before the type
92 for (DetailAST node = mainAst.findFirstToken(TokenTypes.MODIFIERS).getFirstChild();
93 node != null;
94 node = node.getNextSibling()) {
95 // skip annotations as we check them else where as outside the method
96 if (node.getType() == TokenTypes.ANNOTATION) {
97 continue;
98 }
99
100 if (node.getLineNo() < lineStart) {
101 lineStart = node.getLineNo();
102 }
103 }
104
105 return lineStart;
106 }
107
108 @Override
109 public void checkIndentation() {
110 checkModifiers();
111 checkThrows();
112
113 if (getMethodDefParamRightParen(getMainAst()) != null) {
114 checkWrappingIndentation(getMainAst(), getMethodDefParamRightParen(getMainAst()));
115 }
116 // abstract method def -- no body
117 if (getLeftCurly() != null) {
118 super.checkIndentation();
119 }
120 }
121
122 /**
123 * Returns right parenthesis of method definition parameter list.
124 *
125 * @param methodDefAst
126 * method definition ast node(TokenTypes.LITERAL_IF)
127 * @return right parenthesis of method definition parameter list.
128 */
129 private static DetailAST getMethodDefParamRightParen(DetailAST methodDefAst) {
130 return methodDefAst.findFirstToken(TokenTypes.RPAREN);
131 }
132
133 /**
134 * Creates a handler name for this class according to ast type.
135 *
136 * @param ast the abstract syntax tree.
137 * @return handler name for this class.
138 */
139 private static String getHandlerName(DetailAST ast) {
140
141 return switch (ast.getType()) {
142 case TokenTypes.CTOR_DEF -> "ctor def";
143 case TokenTypes.ANNOTATION_FIELD_DEF -> "annotation field def";
144 case TokenTypes.COMPACT_CTOR_DEF -> "compact ctor def";
145 default -> "method def";
146 };
147 }
148
149 }