blob: 0d29a058fea0c8e2a3f8102d4b331e22d918efbb [file] [log] [blame]
/*
* 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.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Reports assignment to 'self' or 'cls'.
* @author dcheryasov
*/
public class PyMethodFirstArgAssignmentInspection extends PyInspection {
@Nls
@NotNull
public String getDisplayName() {
return PyBundle.message("INSP.NAME.first.arg.assign");
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
boolean isOnTheFly,
@NotNull LocalInspectionToolSession session) {
return new Visitor(holder, session);
}
public static class Visitor extends PyInspectionVisitor {
public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
super(holder, session);
}
private void complain(PsiElement element, String name) {
registerProblem(element, PyBundle.message("INSP.first.arg.$0.assigned", name));
}
private void handleTarget(PyQualifiedExpression target, String name) {
if (!target.isQualified() && name.equals(target.getText())) {
complain(target, name);
}
}
@Nullable
private static String extractFirstParamName(PyElement node) {
// are we a method?
List<? extends PsiElement> place = PyUtil.searchForWrappingMethod(node, true);
if (place == null || place.size() < 2) return null;
PyFunction method = (PyFunction)place.get(place.size()-2);
//PyClass owner = (PyClass)place.get(place.size()-1);
// what is our first param?
PyParameter[] params = method.getParameterList().getParameters();
if (params.length < 1) return null; // no params
PyNamedParameter first_parm = params[0].getAsNamed();
if (first_parm == null) return null;
if (first_parm.isKeywordContainer() || first_parm.isPositionalContainer()) return null; // legal but crazy cases; back off
final String first_param_name = first_parm.getName();
if (first_param_name == null || first_param_name.length() < 1) return null; // ignore cases of incorrect code
// is it a static method?
PyFunction.Modifier modifier = method.getModifier();
if (modifier == PyFunction.Modifier.STATICMETHOD) return null; // these may do whatever they please
return first_param_name;
}
private void markNameDefiner(NameDefiner definer) {
final String first_param_name = extractFirstParamName((PyElement)definer);
if (first_param_name != null) {
// check the targets
for (PyElement elt : definer.iterateNames()) {
if (elt instanceof PyTargetExpression) handleTarget((PyTargetExpression)elt, first_param_name);
}
}
}
@Override
public void visitPyAssignmentStatement(PyAssignmentStatement node) {
markNameDefiner(node);
}
@Override
public void visitPyAugAssignmentStatement(PyAugAssignmentStatement node) {
final String first_param_name = extractFirstParamName(node);
if (first_param_name != null) {
PyExpression target = node.getTarget();
if (target instanceof PyQualifiedExpression) handleTarget((PyQualifiedExpression)target, first_param_name);
else if (target instanceof PyTupleExpression) {
for (PyExpression elt : PyUtil.flattenedParensAndTuples(((PyTupleExpression)target).getElements())) {
if (elt instanceof PyQualifiedExpression) handleTarget((PyQualifiedExpression)elt, first_param_name);
}
}
}
}
@Override
public void visitPyForStatement(PyForStatement node) {
markNameDefiner(node);
}
/* TODO: implement when WithStmt is part-ified
@Override
public void visitPyWithStatement(PyWithStatement node) {
super.visitPyWithStatement(node);
}
*/
private void markDefinition(PyElement definer) {
final String first_param_name = extractFirstParamName(definer);
if (first_param_name != null && first_param_name.equals(definer.getName())) {
complain(definer.getNode().findChildByType(PyTokenTypes.IDENTIFIER).getPsi(), first_param_name); // no NPE here, or we won't have the name
}
}
@Override
public void visitPyFunction(PyFunction definer) {
markDefinition(definer);
}
@Override
public void visitPyClass(PyClass definer) {
markDefinition(definer);
}
}
}