All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.errorprone.bugpatterns.collectionincompatibletype.AbstractCollectionIncompatibleTypeMatcher Maven / Gradle / Ivy

There is a newer version: 2.29.2
Show newest version
/*
 * Copyright 2015 The Error Prone Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.errorprone.bugpatterns.collectionincompatibletype;

import com.google.auto.value.AutoValue;
import com.google.errorprone.VisitorState;
import com.google.errorprone.fixes.Fix;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;

/**
 * Extracts the necessary information from a {@link MethodInvocationTree} to check whether calls to
 * a method are using incompatible types and to emit a helpful error message.
 */
public abstract class AbstractCollectionIncompatibleTypeMatcher {

  /**
   * Returns a matcher for the appropriate method invocation for this matcher. For example, this
   * might match {@link Collection#remove(Object)} or {@link Map#containsKey(Object)}.
   */
  abstract Matcher methodMatcher();

  /**
   * Extracts the source type that must be castable to the target type. For example, in this code
   * sample:
   *
   * 
{@code
   * Collection collection;
   * collection.contains("foo");
   * }
* * The source type is String. * * @return the source type or null if not available */ @Nullable abstract Type extractSourceType(MethodInvocationTree tree, VisitorState state); @Nullable abstract Type extractSourceType(MemberReferenceTree tree, VisitorState state); /** * Returns the AST node from which the source type was extracted. Needed to produce readable error * messages. For example, in this code sample: * *
{@code
   * Collection collection;
   * collection.contains("foo");
   * }
* * The source tree is "foo". * * @return the source AST node or null if not available */ @Nullable abstract ExpressionTree extractSourceTree(MethodInvocationTree tree, VisitorState state); @Nullable abstract ExpressionTree extractSourceTree(MemberReferenceTree tree, VisitorState state); /** * Extracts the target type to which the source type must be castable. For example, in this code * sample: * *
{@code
   * Collection collection;
   * collection.contains("foo");
   * }
* * The target type is Integer. * * @return the target type or null if not available */ @Nullable abstract Type extractTargetType(MethodInvocationTree tree, VisitorState state); @Nullable abstract Type extractTargetType(MemberReferenceTree tree, VisitorState state); /** * Encapsulates the result of matching a {@link Collection#contains}-like call, including the * source and target types. */ @AutoValue public abstract static class MatchResult { public abstract ExpressionTree sourceTree(); public abstract Type sourceType(); public abstract Type targetType(); public abstract AbstractCollectionIncompatibleTypeMatcher matcher(); public static MatchResult create( ExpressionTree sourceTree, Type sourceType, Type targetType, AbstractCollectionIncompatibleTypeMatcher matcher) { return new AutoValue_AbstractCollectionIncompatibleTypeMatcher_MatchResult( sourceTree, sourceType, targetType, matcher); } public String message(String sourceType, String targetType) { return matcher().message(this, sourceType, targetType); } public Optional buildFix() { return matcher().buildFix(this); } } @Nullable public final MatchResult matches(ExpressionTree tree, VisitorState state) { if (!methodMatcher().matches(tree, state)) { return null; } return new SimpleTreeVisitor() { @Override public MatchResult visitMethodInvocation( MethodInvocationTree methodInvocationTree, Void unused) { return getMatchResult( extractSourceTree(methodInvocationTree, state), extractSourceType(methodInvocationTree, state), extractTargetType(methodInvocationTree, state)); } @Override public MatchResult visitMemberReference( MemberReferenceTree memberReferenceTree, Void unused) { return getMatchResult( extractSourceTree(memberReferenceTree, state), extractSourceType(memberReferenceTree, state), extractTargetType(memberReferenceTree, state)); } }.visit(tree, null); } private MatchResult getMatchResult( @Nullable ExpressionTree sourceTree, @Nullable Type sourceType, @Nullable Type targetType) { if (sourceTree == null || sourceType == null || targetType == null) { return null; } return MatchResult.create(sourceTree, sourceType, targetType, this); } /** * Extracts the appropriate type argument from a specific supertype of the given {@code type}. * This handles the case when a subtype has different type arguments than the expected type. For * example, {@code ClassToInstanceMap} implements {@code Map, T>}. * * @param type the (sub)type from which to extract the type argument * @param superTypeSym the symbol of the supertype on which the type parameter is defined * @param typeArgIndex the index of the type argument to extract from the supertype * @param types the {@link Types} utility class from the {@link VisitorState} * @return the type argument, if defined, or null otherwise */ @Nullable protected static Type extractTypeArgAsMemberOfSupertype( Type type, Symbol superTypeSym, int typeArgIndex, Types types) { Type collectionType = types.asSuper(type, superTypeSym); if (collectionType == null) { return null; } com.sun.tools.javac.util.List tyargs = collectionType.getTypeArguments(); if (tyargs.size() <= typeArgIndex) { // Collection is raw, nothing we can do. return null; } return tyargs.get(typeArgIndex); } Optional buildFix(MatchResult result) { return Optional.empty(); } protected String message(MatchResult result, String sourceType, String targetType) { return String.format( "Argument '%s' should not be passed to this method; its type %s is not compatible with %s", result.sourceTree(), sourceType, targetType); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy