blob: 6ce007026ea0f0518617f20059f5b6ec53b09900 [file] [log] [blame]
/*
* Copyright 2007-2008 Dave Griffith
*
* 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.threading;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiModifier;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspection;
import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrBlockStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrWhileStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
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.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import javax.swing.*;
public class GroovyWhileLoopSpinsOnFieldInspection extends BaseInspection {
@SuppressWarnings({"PublicField", "WeakerAccess"})
public boolean ignoreNonEmtpyLoops = false;
@Override
@Nls
@NotNull
public String getGroupDisplayName() {
return THREADING_ISSUES;
}
@Override
@NotNull
public String getDisplayName() {
return "While loop spins on field";
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return "<code>#ref</code> loop spins on field #loc";
}
@Override
@Nullable
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel("Only warn if loop is empty",
this, "ignoreNonEmtpyLoops");
}
@NotNull
@Override
public BaseInspectionVisitor buildVisitor() {
return new WhileLoopSpinsOnFieldVisitor();
}
private class WhileLoopSpinsOnFieldVisitor
extends BaseInspectionVisitor {
@Override
public void visitWhileStatement(@NotNull GrWhileStatement statement) {
super.visitWhileStatement(statement);
final GrStatement body = statement.getBody();
if (ignoreNonEmtpyLoops && !statementIsEmpty(body)) {
return;
}
final GrCondition condition = statement.getCondition();
if (!(condition instanceof GrExpression)) {
return;
}
if (!isSimpleFieldComparison((GrExpression) condition)) {
return;
}
registerStatementError(statement);
}
private boolean isSimpleFieldComparison(GrExpression condition) {
condition = (GrExpression)PsiUtil.skipParentheses(condition, false);
if (condition == null) {
return false;
}
if (isSimpleFieldAccess(condition)) {
return true;
}
if (condition instanceof GrUnaryExpression && ((GrUnaryExpression)condition).isPostfix()) {
final GrUnaryExpression postfixExpression = (GrUnaryExpression) condition;
final GrExpression operand =
postfixExpression.getOperand();
return isSimpleFieldComparison(operand);
}
if (condition instanceof GrUnaryExpression) {
final GrUnaryExpression unaryExpression =
(GrUnaryExpression) condition;
final GrExpression operand =
unaryExpression.getOperand();
return isSimpleFieldComparison(operand);
}
if (condition instanceof GrBinaryExpression) {
final GrBinaryExpression binaryExpression =
(GrBinaryExpression) condition;
final GrExpression lOperand = binaryExpression.getLeftOperand();
final GrExpression rOperand = binaryExpression.getRightOperand();
if (isLiteral(rOperand)) {
return isSimpleFieldComparison(lOperand);
} else if (isLiteral(lOperand)) {
return isSimpleFieldComparison(rOperand);
} else {
return false;
}
}
return false;
}
private boolean isLiteral(GrExpression expression) {
expression = (GrExpression)PsiUtil.skipParentheses(expression, false);
if (expression == null) {
return false;
}
return expression instanceof PsiLiteralExpression;
}
private boolean isSimpleFieldAccess(GrExpression expression) {
expression = (GrExpression)PsiUtil.skipParentheses(expression, false);
if (expression == null) {
return false;
}
if (!(expression instanceof GrReferenceExpression)) {
return false;
}
final GrReferenceExpression reference =
(GrReferenceExpression) expression;
final GrExpression qualifierExpression =
reference.getQualifierExpression();
if (qualifierExpression != null) {
return false;
}
final PsiElement referent = reference.resolve();
if (!(referent instanceof PsiField)) {
return false;
}
final PsiField field = (PsiField) referent;
return !field.hasModifierProperty(PsiModifier.VOLATILE);
}
private boolean statementIsEmpty(GrStatement statement) {
if (statement == null) {
return false;
}
if (statement instanceof GrBlockStatement) {
final GrBlockStatement blockStatement =
(GrBlockStatement) statement;
final GrOpenBlock codeBlock = blockStatement.getBlock();
final GrStatement[] codeBlockStatements = codeBlock.getStatements();
for (GrStatement codeBlockStatement : codeBlockStatements) {
if (!statementIsEmpty(codeBlockStatement)) {
return false;
}
}
return true;
}
return false;
}
}
}