1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3 * SPDX-License-Identifier: MIT
4 */
5 package com.qulice.checkstyle;
6
7 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8 import com.puppycrawl.tools.checkstyle.api.DetailAST;
9 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10
11 /**
12 * Checks that constructors do not contain a redundant {@code super()}
13 * call when the enclosing class does not extend any class explicitly.
14 *
15 * <p>A class without an {@code extends} clause implicitly extends
16 * {@link Object}. Calling {@code super()} in its constructor invokes
17 * the {@link Object} constructor, which the compiler inserts on its own.
18 * The explicit call is therefore redundant and is most often a sign
19 * of confusion between class extension and interface implementation,
20 * for example:
21 *
22 * <pre>
23 * public class NewAgent implements Agent {
24 * public NewAgent() {
25 * super();
26 * }
27 * }
28 * </pre>
29 *
30 * <p>The rule only applies to constructors of {@code class} declarations.
31 * It does not apply to records or enums, where {@code super(...)}
32 * either targets a fixed superclass or is illegal.
33 *
34 * @since 0.24
35 */
36 public final class RedundantSuperConstructorCheck extends AbstractCheck {
37
38 @Override
39 public int[] getDefaultTokens() {
40 return new int[] {TokenTypes.CTOR_DEF};
41 }
42
43 @Override
44 public int[] getAcceptableTokens() {
45 return this.getDefaultTokens();
46 }
47
48 @Override
49 public int[] getRequiredTokens() {
50 return this.getDefaultTokens();
51 }
52
53 @Override
54 public void visitToken(final DetailAST ast) {
55 final DetailAST clazz = RedundantSuperConstructorCheck.enclosingClass(ast);
56 if (clazz != null
57 && clazz.findFirstToken(TokenTypes.EXTENDS_CLAUSE) == null) {
58 final DetailAST body = ast.findFirstToken(TokenTypes.SLIST);
59 if (body != null) {
60 this.reportSuperCalls(body);
61 }
62 }
63 }
64
65 /**
66 * Find the enclosing {@code CLASS_DEF} of the given constructor node.
67 * @param ctor Constructor node
68 * @return The CLASS_DEF, or null if the enclosing type is not a class
69 */
70 private static DetailAST enclosingClass(final DetailAST ctor) {
71 DetailAST result = null;
72 final DetailAST block = ctor.getParent();
73 if (block != null && block.getType() == TokenTypes.OBJBLOCK) {
74 final DetailAST owner = block.getParent();
75 if (owner != null && owner.getType() == TokenTypes.CLASS_DEF) {
76 result = owner;
77 }
78 }
79 return result;
80 }
81
82 /**
83 * Report any direct {@code super(...)} call inside the constructor body.
84 * Nested anonymous class bodies and lambdas are skipped because their
85 * {@code super(...)} calls belong to a different enclosing class.
86 * @param node Root of the constructor body subtree
87 */
88 private void reportSuperCalls(final DetailAST node) {
89 for (DetailAST child = node.getFirstChild();
90 child != null; child = child.getNextSibling()) {
91 final int type = child.getType();
92 if (type == TokenTypes.LAMBDA
93 || type == TokenTypes.OBJBLOCK
94 || type == TokenTypes.CLASS_DEF) {
95 continue;
96 }
97 if (type == TokenTypes.SUPER_CTOR_CALL) {
98 this.log(
99 child.getLineNo(),
100 "Redundant super() call when class does not extend any other class"
101 );
102 }
103 this.reportSuperCalls(child);
104 }
105 }
106 }