/*
* Copyright 2012-2014 Sergey Ignatov
*
* 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.intellij.erlang.inspection;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.LocalQuickFixBase;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.intellij.erlang.psi.*;
import org.intellij.erlang.psi.impl.ErlangElementFactory;
import org.intellij.erlang.psi.impl.ErlangPsiImplUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
//TODO add arity mismatch checks
public class ErlangHeadMismatchInspection extends ErlangInspectionBase implements DumbAware {
@NotNull
@Override
protected ErlangVisitor buildErlangVisitor(@NotNull final ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
return new ErlangVisitor() {
@Override
public void visitFunction(@NotNull ErlangFunction function) {
checkFunction(function, holder);
}
@Override
public void visitFunExpression(@NotNull ErlangFunExpression funExpression) {
checkFunExpression(funExpression, holder);
}
};
}
private void checkFunction(ErlangFunction function, ProblemsHolder problemsHolder) {
String functionName = function.getName();
List<ErlangFunctionClause> clauses = function.getFunctionClauseList();
if (clauses.size() <= 1) return;
for (ErlangFunctionClause clause : clauses) {
ErlangQAtom clauseHead = clause.getQAtom();
if (clauseHead.getMacros() != null) return;
String clauseSignature = ErlangPsiImplUtil.createFunctionClausePresentation(clause);
String functionSignature = ErlangPsiImplUtil.createFunctionPresentation(function);
if (!functionSignature.equals(clauseSignature)) {
registerProblem(problemsHolder, clauseHead, "Head mismatch: should be '" + functionSignature + "'",
new RenameFunctionClauseHeadQuickFix(functionName));
}
}
}
private void checkFunExpression(ErlangFunExpression funExpression, ProblemsHolder problemsHolder) {
ErlangFunClauses funClauses = funExpression.getFunClauses();
List<ErlangFunClause> funClauseList = funClauses != null ? funClauses.getFunClauseList() : Collections.<ErlangFunClause>emptyList();
if (funClauseList.size() <= 1) return;
ErlangFunClause firstClause = funClauseList.get(0);
String firstClauseName = getFunExpressionClauseName(firstClause);
for (ErlangFunClause funClause : funClauseList) {
String funClauseName = getFunExpressionClauseName(funClause);
if (!Comparing.equal(firstClauseName, funClauseName)) {
String problemDescription = firstClauseName == null ?
"Head mismatch: named clause in an unnamed fun expression" :
funClauseName == null ?
"Head mismatch: unnamed clause in a named fun expression" :
"Head mismatch: should be '" + firstClauseName + "'";
PsiElement elementForRange = funClauseName != null ? funClause.getArgumentDefinition() : funClause.getArgumentDefinitionList();
if (elementForRange != null) {
TextRange range = TextRange.create(elementForRange.getStartOffsetInParent(),
elementForRange.getStartOffsetInParent() + elementForRange.getTextLength());
registerProblem(problemsHolder, funClause, problemDescription, range, null, new ChangeFunExpressionNameQuickFix(firstClauseName));
}
}
}
}
@Nullable
private static String getFunExpressionClauseName(ErlangFunClause clause) {
ErlangArgumentDefinition argumentDefinition = clause.getArgumentDefinition();
return argumentDefinition != null ? argumentDefinition.getText() : null;
}
private static class RenameFunctionClauseHeadQuickFix extends LocalQuickFixBase {
private final String myFunctionName;
protected RenameFunctionClauseHeadQuickFix(String functionName) {
super("Rename clause head");
myFunctionName = functionName;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement oldHead = descriptor.getPsiElement();
if (oldHead instanceof ErlangQAtom) {
ErlangPsiImplUtil.renameQAtom((ErlangQAtom) oldHead, myFunctionName);
}
}
}
private static class ChangeFunExpressionNameQuickFix extends LocalQuickFixBase {
private String myNewName;
public ChangeFunExpressionNameQuickFix(@Nullable String newName) {
super("Change clause name");
myNewName = newName;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
ErlangFunClause funClause = (ErlangFunClause) descriptor.getPsiElement();
ErlangArgumentDefinition currentName = funClause.getArgumentDefinition();
if (currentName != null) {
currentName.delete();
}
if (myNewName != null) {
ErlangArgumentDefinition newName = ErlangElementFactory.createFunExpressionNameFromText(project, myNewName);
funClause.addBefore(newName, funClause.getFirstChild());
}
}
}
}