blob: f88cd61dc2549d9911c371687ea94b73850f5053 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 org.jetbrains.plugins.groovy.codeInspection.assignment;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
/**
* @author Maxim.Medvedev
*/
public class GroovyUncheckedAssignmentOfMemberOfRawTypeInspection extends BaseInspection {
@Override
@Nls
@NotNull
public String getGroupDisplayName() {
return ASSIGNMENT_ISSUES;
}
@Override
protected String buildErrorString(Object... args) {
PsiType expectedType = (PsiType)args[0];
PsiType rType = (PsiType)args[1];
return GroovyBundle.message("cannot.assign", rType.getPresentableText(), expectedType.getPresentableText());
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
@NotNull
protected BaseInspectionVisitor buildVisitor() {
return new Visitor();
}
@Override
@Nls
@NotNull
public String getDisplayName() {
return "Unchecked assignment from members of raw type";
}
private static class Visitor extends BaseInspectionVisitor {
@Override
public void visitReturnStatement(GrReturnStatement returnStatement) {
final GrExpression value = returnStatement.getReturnValue();
if (value != null) {
final PsiType type = value.getType();
if (type != null) {
final GrParametersOwner owner = PsiTreeUtil.getParentOfType(returnStatement, GrMethod.class, GrClosableBlock.class);
if (owner instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)owner;
if (!method.isConstructor()) {
final PsiType methodType = method.getReturnType();
final PsiType returnType = value.getType();
if (methodType != null) {
if (!PsiType.VOID.equals(methodType)) {
if (returnType != null) {
checkAssignability(methodType, value, value);
}
}
}
}
}
}
}
}
@Override
public void visitNamedArgument(GrNamedArgument argument) {
final GrArgumentLabel label = argument.getLabel();
if (label != null) {
PsiType expectedType = label.getExpectedArgumentType();
if (expectedType != null) {
expectedType = TypeConversionUtil.erasure(expectedType);
final GrExpression expr = argument.getExpression();
if (expr != null) {
final PsiType argType = expr.getType();
if (argType != null) {
final PsiClassType listType = JavaPsiFacade.getInstance(argument.getProject()).getElementFactory()
.createTypeByFQClassName(CommonClassNames.JAVA_UTIL_LIST, argument.getResolveScope());
if (listType.isAssignableFrom(argType)) return; //this is constructor arguments list
checkAssignability(expectedType, expr, argument);
}
}
}
}
}
@Override
public void visitVariable(GrVariable variable) {
PsiType varType = variable.getType();
GrExpression initializer = variable.getInitializerGroovy();
if (initializer != null) {
PsiType rType = initializer.getType();
if (rType != null) {
checkAssignability(varType, initializer, initializer);
}
}
}
@Override
public void visitAssignmentExpression(@NotNull GrAssignmentExpression assignment) {
super.visitAssignmentExpression(assignment);
GrExpression lValue = assignment.getLValue();
if (!PsiUtil.mightBeLValue(lValue)) return;
IElementType opToken = assignment.getOperationTokenType();
if (opToken != GroovyTokenTypes.mASSIGN) return;
GrExpression rValue = assignment.getRValue();
if (rValue == null) return;
PsiType lType = lValue.getNominalType();
PsiType rType = rValue.getType();
// For assignments with spread dot
if (PsiImplUtil.isSpreadAssignment(lValue) && lType != null && lType instanceof PsiClassType) {
final PsiClassType pct = (PsiClassType)lType;
final PsiClass clazz = pct.resolve();
if (clazz != null && CommonClassNames.JAVA_UTIL_LIST.equals(clazz.getQualifiedName())) {
final PsiType[] types = pct.getParameters();
if (types.length == 1 && types[0] != null && rType != null) {
checkAssignability(types[0], rValue, assignment);
}
}
return;
}
if (lValue instanceof GrReferenceExpression &&
((GrReferenceExpression)lValue).resolve() instanceof GrReferenceExpression) { //lvalue is nondeclared variable
return;
}
if (lType != null && rType != null) {
checkAssignability(lType, rValue, rValue);
}
}
private void checkAssignability(PsiType lType, GrExpression rExpr, GroovyPsiElement element) {
if (PsiUtil.isRawClassMemberAccess(rExpr)) {
final PsiType rType = rExpr.getType();
if (!TypesUtil.isAssignable(lType, rType, element)) {
registerError(element, lType, rType);
}
}
}
}
}