summaryrefslogtreecommitdiff
path: root/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java
blob: acf33c542e24b0667255265763ce2db16d7d4a8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * Copyright 2000-2013 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 com.jetbrains.python.inspections;

import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.inspections.quickfix.PyMakeFunctionFromMethodQuickFix;
import com.jetbrains.python.inspections.quickfix.PyMakeMethodStaticQuickFix;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
import com.jetbrains.python.testing.PythonUnitTestUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * User: ktisha
 *
 */
public class PyMethodMayBeStaticInspection extends PyInspection {
  @Nls
  @NotNull
  @Override
  public String getDisplayName() {
    return PyBundle.message("INSP.NAME.method.may.be.static");
  }

  @NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
                                        boolean isOnTheFly,
                                        @NotNull LocalInspectionToolSession session) {
    return new Visitor(holder, session);
  }


  private static class Visitor extends PyInspectionVisitor {
    public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
      super(holder, session);
    }

    @Override
    public void visitPyFunction(PyFunction node) {
      if (PyNames.getBuiltinMethods(LanguageLevel.forElement(node)).containsKey(node.getName())) return;
      final PyClass containingClass = node.getContainingClass();
      if (containingClass == null) return;
      if (PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) return;
      final PsiElement firstSuper = PySuperMethodsSearch.search(node).findFirst();
      if (firstSuper != null) return;
      final PyFunction firstOverride = PyOverridingMethodsSearch.search(node, true).findFirst();
      if (firstOverride != null) return;
      final PyDecoratorList decoratorList = node.getDecoratorList();
      if (decoratorList != null) return;
      if (node.getModifier() != null) return;
      final Property property = containingClass.findPropertyByCallable(node);
      if (property != null) return;

      final PyStatementList statementList = node.getStatementList();
      if (statementList == null) return;

      final PyStatement[] statements = statementList.getStatements();

      if (statements.length == 1 && statements[0] instanceof PyPassStatement) return;

      final PyParameter[] parameters = node.getParameterList().getParameters();

      final String selfName;
      if (parameters.length > 0) {
        final String name = parameters[0].getName();
        selfName = name != null ? name : parameters[0].getText();
      }
      else {
        selfName = PyNames.CANONICAL_SELF;
      }

      final boolean[] mayBeStatic = {true};
      PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() {
        @Override
        public void visitPyRaiseStatement(PyRaiseStatement node) {
          super.visitPyRaiseStatement(node);
          final PyExpression[] expressions = node.getExpressions();
          if (expressions.length == 1) {
            final PyExpression expression = expressions[0];
            if (expression instanceof PyCallExpression) {
              final PyExpression callee = ((PyCallExpression)expression).getCallee();
              if (callee != null && PyNames.NOT_IMPLEMENTED_ERROR.equals(callee.getText()))
                mayBeStatic[0] = false;
            }
            else if (PyNames.NOT_IMPLEMENTED_ERROR.equals(expression.getText())) {
              mayBeStatic[0] = false;
            }
          }
        }

        @Override
        public void visitPyReferenceExpression(PyReferenceExpression node) {
          super.visitPyReferenceExpression(node);
          if (selfName.equals(node.getName())) {
            mayBeStatic[0] = false;
          }

        }

      };
      node.accept(visitor);
      final PsiElement identifier = node.getNameIdentifier();
      if (mayBeStatic[0] && identifier != null) {
        registerProblem(identifier, PyBundle.message("INSP.method.may.be.static"), ProblemHighlightType.WEAK_WARNING,
                        null, new PyMakeMethodStaticQuickFix(), new PyMakeFunctionFromMethodQuickFix());
      }
    }
  }
}