blob: 1c15cccee402ded1d45432ff818329ecbb31bfe4 [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.unassignedVariable;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.GrInspectionUtil;
import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle;
import org.jetbrains.plugins.groovy.codeInspection.GroovyLocalInspectionBase;
import org.jetbrains.plugins.groovy.gpp.GppTypeConverter;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrWhileStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrTraditionalForClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ControlFlowBuilderUtil;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import javax.swing.*;
/**
* @author ven
*/
public class UnassignedVariableAccessInspection extends GroovyLocalInspectionBase {
public boolean myIgnoreBooleanExpressions = true;
@Nullable
@Override
public JComponent createOptionsPanel() {
final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this);
optionsPanel.addCheckbox(GroovyInspectionBundle.message("ignore.boolean.expressions"), "myIgnoreBooleanExpressions");
return optionsPanel;
}
@Override
@Nls
@NotNull
public String getGroupDisplayName() {
return GroovyInspectionBundle.message("groovy.dfa.issues");
}
@Override
@Nls
@NotNull
public String getDisplayName() {
return GroovyInspectionBundle.message("unassigned.access");
}
@Override
@NonNls
@NotNull
public String getShortName() {
return "GroovyVariableNotAssigned";
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
protected void check(GrControlFlowOwner owner, ProblemsHolder problemsHolder) {
Instruction[] flow = owner.getControlFlow();
ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(flow, true);
for (ReadWriteVariableInstruction read : reads) {
PsiElement element = read.getElement();
if (element instanceof GroovyPsiElement && !(element instanceof GrClosableBlock)) {
String name = read.getVariableName();
GroovyPsiElement property = ResolveUtil.resolveProperty((GroovyPsiElement)element, name);
if (property != null &&
!(property instanceof PsiParameter) &&
!(property instanceof PsiField) &&
PsiTreeUtil.isAncestor(owner, property, false) &&
!GppTypeConverter.hasTypedContext(element) &&
!(myIgnoreBooleanExpressions && isBooleanCheck(element))
) {
problemsHolder.registerProblem(element, GroovyInspectionBundle.message("unassigned.access.tooltip", name));
}
}
}
}
private static boolean isBooleanCheck(PsiElement element) {
final PsiElement parent = element.getParent();
return parent instanceof GrIfStatement && ((GrIfStatement)parent).getCondition() == element ||
parent instanceof GrWhileStatement && ((GrWhileStatement)parent).getCondition() == element ||
parent instanceof GrTraditionalForClause && ((GrTraditionalForClause)parent).getCondition() == element ||
isLogicalExpression(parent) ||
parent instanceof GrUnaryExpression && ((GrUnaryExpression)parent).getOperationTokenType() == GroovyTokenTypes.mBNOT ||
isCheckForNull(parent, element);
}
private static boolean isLogicalExpression(PsiElement parent) {
return parent instanceof GrBinaryExpression &&
(((GrBinaryExpression)parent).getOperationTokenType() == GroovyTokenTypes.mLAND ||
((GrBinaryExpression)parent).getOperationTokenType() == GroovyTokenTypes.mLOR);
}
private static boolean isCheckForNull(PsiElement parent, PsiElement element) {
if (!(parent instanceof GrBinaryExpression)) return false;
final IElementType tokenType = ((GrBinaryExpression)parent).getOperationTokenType();
if (!(tokenType == GroovyTokenTypes.mEQUAL || tokenType == GroovyTokenTypes.mNOT_EQUAL)) return false;
if (element == ((GrBinaryExpression)parent).getLeftOperand()) {
final GrExpression rightOperand = ((GrBinaryExpression)parent).getRightOperand();
return rightOperand != null && GrInspectionUtil.isNull(rightOperand);
}
else {
return GrInspectionUtil.isNull(((GrBinaryExpression)parent).getLeftOperand());
}
}
}