Class UnnecessaryFullyQualifiedTypeCheck

All Implemented Interfaces:
Configurable, Contextualizable

Checks for fully qualified type references that are unnecessary because the type could simply be imported and referenced by its simple name.

A fully qualified type reference, such as java.util.Map, is reported as unnecessary unless another type with the same simple name is also used in the file. For example, java.util.Map is allowed only when a different Map (e.g. com.example.Map, a locally declared Map, or a same-package or java.lang Map) is also referenced, since in that case qualification is required to tell the two types apart.

Using fully qualified names where they are not needed reduces readability and is inconsistent with common Java style conventions, which favor importing the type instead.

The following limitations apply:

  • To distinguish a package qualified type (such as java.util.Map) from a reference to a nested type (such as Map.Entry), the check relies on the common convention that package names start with a lowercase letter. A qualified reference is treated as fully qualified only when the identifier preceding the simple name starts with a lowercase letter.
  • References to nested types via an enclosing type (for example java.util.Map.Entry) and fully qualified annotations (for example @java.lang.Override) are not reported.
  • On-demand (wildcard) imports, such as import java.util.*;, are checked against classes reachable on this check's own class loader. This reliably detects collisions from JDK packages (for example a fully qualified reference to a third-party Observer type being left alone because import java.util.*; also brings a different java.util.Observer into scope), but on-demand imports of third-party or project-local packages cannot be resolved this way and may still be flagged incorrectly.
Since:
14.1.0
  • Field Details

  • Constructor Details

  • Method Details

    • getDefaultTokens

      public int[] getDefaultTokens()
      Description copied from class: AbstractCheck
      Returns the default token a check is interested in. Only used if the configuration for a check does not define the tokens.
      Specified by:
      getDefaultTokens in class AbstractCheck
      Returns:
      the default tokens
      See Also:
    • getAcceptableTokens

      public int[] getAcceptableTokens()
      Description copied from class: AbstractCheck
      The configurable token set. Used to protect Checks against malicious users who specify an unacceptable token set in the configuration file. The default implementation returns the check's default tokens.
      Specified by:
      getAcceptableTokens in class AbstractCheck
      Returns:
      the token set this check is designed for.
      See Also:
    • getRequiredTokens

      public int[] getRequiredTokens()
      Description copied from class: AbstractCheck
      The tokens that this check must be registered for.
      Specified by:
      getRequiredTokens in class AbstractCheck
      Returns:
      the token set this must be registered for.
      See Also:
    • beginTree

      public void beginTree(DetailAST rootAST)
      Description copied from class: AbstractCheck
      Called before the starting to process a tree. Ideal place to initialize information that is to be collected whilst processing a tree.
      Overrides:
      beginTree in class AbstractCheck
      Parameters:
      rootAST - the root of the tree
    • visitToken

      public void visitToken(DetailAST ast)
      Description copied from class: AbstractCheck
      Called to process a token.
      Overrides:
      visitToken in class AbstractCheck
      Parameters:
      ast - the token to process
    • finishTree

      public void finishTree(DetailAST rootAST)
      Description copied from class: AbstractCheck
      Called after finished processing a tree. Ideal place to report on information collected whilst processing a tree.
      Overrides:
      finishTree in class AbstractCheck
      Parameters:
      rootAST - the root of the tree
    • processPackage

      private void processPackage(DetailAST ast)
      Records the package the file belongs to.
      Parameters:
      ast - the TokenTypes.PACKAGE_DEF node
    • processImport

      private void processImport(DetailAST ast)
      Records an import by its simple name so that later references can be resolved. On-demand (wildcard) imports do not have a single simple name, so the imported package is recorded separately instead.
      Parameters:
      ast - the TokenTypes.IMPORT node
    • processDot

      private void processDot(DetailAST ast)
      Collects a fully qualified type reference if the given TokenTypes.DOT node represents one.
      Parameters:
      ast - the TokenTypes#DOT node
    • buildFullName

      private static String buildFullName(DetailAST ast)
      Reconstructs the fully qualified name for a TokenTypes#DOT node, handling the extra child produced by an embedded type-use annotation.
      Parameters:
      ast - the TokenTypes#DOT node
      Returns:
      the fully qualified name the node represents
    • processIdent

      private void processIdent(DetailAST ast)
      Records a reference made through a simple name. The name is either an unqualified type reference or the name of a type declared in the file.
      Parameters:
      ast - the TokenTypes.IDENT node
    • isUnnecessary

      private boolean isUnnecessary(String simpleName)
      Determines whether a type reference with the given simple name is unnecessary. It is unnecessary when this fully qualified reference is the only type with that simple name used in the file, so it could be imported and referenced by its simple name without introducing ambiguity.
      Parameters:
      simpleName - the simple name of the type
      Returns:
      true if the fully qualified reference is unnecessary
    • collectIdentities

      private Set<String> collectIdentities(String simpleName)
      Collects every distinct identity claiming the given simple name in the file: a direct import, each fully qualified reference sharing that simple name, a locally declared type, an unqualified reference resolving elsewhere, and a type reachable via an on-demand import. A simple name is unambiguous, and therefore its fully qualified reference unnecessary, exactly when this set has a single member.
      Parameters:
      simpleName - the simple name to collect identities for
      Returns:
      the set of distinct identities claiming this simple name
    • addQualifiedReferenceIdentities

      private void addQualifiedReferenceIdentities(String simpleName, Set<String> identities)
      Adds an identity for each fully qualified reference sharing the given simple name.
      Parameters:
      simpleName - the simple name to match references against
      identities - the identity set to add to
    • resolvesToQualifiedReference

      private boolean resolvesToQualifiedReference(String simpleName)
      Checks whether any fully qualified reference with the given simple name resolves to the same type as an unqualified reference would.
      Parameters:
      simpleName - the simple name to check
      Returns:
      true if an unqualified reference resolves to a matching qualified type
    • addImplicitIdentities

      private void addImplicitIdentities(String simpleName, boolean unqualifiedResolvedToReference, Set<String> identities)
      Adds identities that are not established by an explicit import: an unqualified reference resolving to a same-package or java.lang type, and a type reachable via an on-demand import. Only relevant when no direct import of this simple name exists, since a direct import always takes priority over an on-demand import.
      Parameters:
      simpleName - the simple name to check
      unqualifiedResolvedToReference - whether a same-simple-name qualified reference already resolves to what an unqualified use would mean
      identities - the identity set to add to
    • resolvesUnqualified

      private boolean resolvesUnqualified(String fullName)
      Determines whether a reference by simple name (without import or local declaration) would resolve to the type denoted by the given fully qualified name. This is the case when that name belongs to the java.lang package or to the package the file itself belongs to.
      Parameters:
      fullName - a fully qualified type name
      Returns:
      true if an unqualified reference would resolve to it
    • resolveViaOnDemandImport

      @Nullable private String resolveViaOnDemandImport(String simpleName)
      Resolves a simple name against the file's on-demand imports, returning the fully qualified name of the class it resolves to, if any. Resolution is attempted only against classes reachable on this check's own class loader, so it is reliable for JDK packages but cannot detect collisions caused by third-party or project-local on-demand imports.
      Parameters:
      simpleName - the simple name to look for
      Returns:
      the fully qualified name the simple name resolves to via an on-demand import, or null if none of the on-demand imported packages contain a class of that name
    • isInTypeContext

      private static boolean isInTypeContext(DetailAST ast)
      Checks whether the given node is a direct child of a type context token.
      Parameters:
      ast - the node to check
      Returns:
      true if the node is in type context
    • isFullyQualified

      private static boolean isFullyQualified(String name)
      Determines whether the given qualified name is a package qualified (fully qualified) type reference, as opposed to a reference to a nested type. The check relies on the convention that package names start with a lowercase letter, so the name is treated as fully qualified when the identifier preceding the simple name starts with a lowercase letter.
      Parameters:
      name - a qualified name containing at least one dot, as produced from a TokenTypes.DOT node
      Returns:
      true if the name is a fully qualified type reference
    • extractSimpleName

      private static String extractSimpleName(String name)
      Extracts the simple name from a qualified name.
      Parameters:
      name - a qualified name
      Returns:
      the part of the name after the last dot