Package org.intellij.erlang.inspection

Source Code of org.intellij.erlang.inspection.ErlangUndefinedCallbackFunctionInspection

/*
* 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.LocalQuickFixBase;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import org.intellij.erlang.psi.*;
import org.intellij.erlang.psi.impl.ErlangPsiImplUtil;
import org.intellij.erlang.quickfixes.ErlangExportFunctionFix;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class ErlangUndefinedCallbackFunctionInspection extends ErlangInspectionBase {
  @Override
  protected void checkFile(@NotNull ErlangFile file, @NotNull ProblemsHolder problemsHolder) {
    //noinspection unchecked
    ErlangCompositeElement warningHolder = PsiTreeUtil.getChildOfAnyType(file, ErlangAttribute.class, ErlangModule.class);
    if (warningHolder == null) return;

    List<ErlangCallbackSpec> toOverwrite = new LinkedList<ErlangCallbackSpec>();
    List<Pair<String, String >> namesAndNames = new LinkedList<Pair<String, String>>();

    for (ErlangBehaviour behaviour : file.getBehaviours()) {
      ErlangModuleRef moduleRef = behaviour.getModuleRef();
      PsiReference reference = moduleRef != null ? moduleRef.getReference() : null;
      PsiElement resolve = reference != null ? reference.resolve() : null;

      if (resolve instanceof ErlangModule) {
        PsiFile containingFile = resolve.getContainingFile();
        if (containingFile instanceof ErlangFile) {
          Map<String, ErlangCallbackSpec> callbackMap = ((ErlangFile) containingFile).getCallbackMap();
          for (Map.Entry<String, ErlangCallbackSpec> entry : callbackMap.entrySet()) {
            String fullName = entry.getKey();
            List<String> split = StringUtil.split(fullName, "/");
            if (split.size() != 2) continue;
            ErlangFunction function = file.getFunction(split.get(0), StringUtil.parseInt(split.get(1), -1));
            if (function == null) {
              ErlangCallbackSpec spec = entry.getValue();
              toOverwrite.add(spec);
              String fileName = spec.getContainingFile().getName();
              namesAndNames.add(Pair.create(fullName, fileName));
            }
          }
        }
      }
    }

    if (!toOverwrite.isEmpty()) {
      boolean multiple = toOverwrite.size() != 1;
      String message = "Undefined callback function" + (multiple ? "s" : "") + ": ";
      boolean first = true;
      for (Pair<String, String> pair : namesAndNames) {
        if (first) first = false;
        else message += ", ";
        message += "'" + pair.first + "'";
        message += " (behaviour " + pair.second + ")";
      }

      problemsHolder.registerProblem(warningHolder, message, new MyLocalQuickFixBase(toOverwrite));
    }
  }

  private static class MyLocalQuickFixBase extends LocalQuickFixBase {
    @NotNull
    private final Collection<ErlangCallbackSpec> myCallbackSpecs;

    protected MyLocalQuickFixBase(@NotNull Collection<ErlangCallbackSpec> callbackSpecs) {
      super("Implement all callbacks");
      myCallbackSpecs = callbackSpecs;
    }

    @Override
    public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) {
      PsiFile file = problemDescriptor.getPsiElement().getContainingFile();
      ErlangFile erlangFile = file instanceof ErlangFile ? (ErlangFile) file : null;
      if (erlangFile != null) {
        addCallbackImplementations(project, erlangFile);
        exportAddedCallbackImplementations(project, erlangFile);
      }
    }

    private void addCallbackImplementations(@NotNull Project project, @NotNull ErlangFile file) {
      for (ErlangCallbackSpec spec : myCallbackSpecs) {
        String name = ErlangPsiImplUtil.getCallbackSpecName(spec);
        List<ErlangTopType> topTypeList = ErlangPsiImplUtil.getCallBackSpecArguments(spec);

        List<String> vars = new LinkedList<String>();
        for (ErlangTopType type : topTypeList) {
          ErlangQVar qVar = type.getQVar();
          vars.add(qVar != null ? qVar.getName() : "_");
        }

        PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
        Document document = manager.getDocument(file);
        PsiElement lastChild = file.getLastChild();
        int textOffset = lastChild == null ? 0 : lastChild.getTextRange().getEndOffset();

        if (document == null) return;
        String join = StringUtil.join(vars, ", ");
        String newFunction = "\n" + name + "(" + join + ") ->\n erlang:error(not_implemented).\n";
        document.insertString(textOffset, newFunction);
        manager.commitDocument(document);
        CodeStyleManager.getInstance(project).reformatText(file, textOffset, textOffset + newFunction.length());
      }
    }

    private void exportAddedCallbackImplementations(@NotNull Project project, @NotNull ErlangFile file) {
      for (ErlangCallbackSpec spec : myCallbackSpecs) {
        String name = ErlangPsiImplUtil.getCallbackSpecName(spec);
        int arity = ErlangPsiImplUtil.getCallBackSpecArguments(spec).size();
        ErlangFunction function = name != null && arity >= 0 ? file.getFunction(name, arity) : null;
        if (function != null && ErlangUnusedFunctionInspection.isUnusedFunction(file, function)) {
          ErlangExportFunctionFix.processFunction(project, function);
        }
      }
    }
  }
}
TOP

Related Classes of org.intellij.erlang.inspection.ErlangUndefinedCallbackFunctionInspection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.