View Javadoc
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 }