d44851a5b36d779cc72bb74a5019b9be764a9aa4
[idea/community.git] / python / src / com / jetbrains / python / inspections / PyShadowingBuiltinsInspection.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.inspections;
17
18 import com.google.common.collect.ImmutableSet;
19 import com.intellij.codeInsight.intention.LowPriorityAction;
20 import com.intellij.codeInspection.*;
21 import com.intellij.codeInspection.ex.InspectionProfileModifiableModelKt;
22 import com.intellij.codeInspection.ui.ListEditForm;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.psi.PsiElement;
25 import com.intellij.psi.PsiElementVisitor;
26 import com.intellij.psi.PsiNameIdentifierOwner;
27 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
28 import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
29 import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix;
30 import com.jetbrains.python.psi.*;
31 import com.jetbrains.python.psi.impl.PyBuiltinCache;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34
35 import javax.swing.*;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.List;
39 import java.util.Set;
40
41 /**
42  * Warns about shadowing built-in names.
43  *
44  * @author vlan
45  */
46 public class PyShadowingBuiltinsInspection extends PyInspection {
47   // Persistent settings
48   public List<String> ignoredNames = new ArrayList<>();
49
50   @NotNull
51   @Override
52   public String getDisplayName() {
53     return "Shadowing built-ins";
54   }
55
56   @Override
57   public JComponent createOptionsPanel() {
58     final ListEditForm form = new ListEditForm("Ignore built-ins", ignoredNames);
59     return form.getContentPanel();
60   }
61
62   @NotNull
63   @Override
64   public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
65                                         boolean isOnTheFly,
66                                         @NotNull LocalInspectionToolSession session) {
67     return new Visitor(holder, session, ignoredNames);
68   }
69
70   private static class Visitor extends PyInspectionVisitor {
71     private final Set<String> myIgnoredNames;
72
73     public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session, @NotNull Collection<String> ignoredNames) {
74       super(holder, session);
75       myIgnoredNames = ImmutableSet.copyOf(ignoredNames);
76     }
77
78     @Override
79     public void visitPyClass(@NotNull PyClass node) {
80       processElement(node);
81     }
82
83     @Override
84     public void visitPyFunction(@NotNull PyFunction node) {
85       processElement(node);
86     }
87
88     @Override
89     public void visitPyNamedParameter(@NotNull PyNamedParameter node) {
90       processElement(node);
91     }
92
93     @Override
94     public void visitPyTargetExpression(@NotNull PyTargetExpression node) {
95       if (!node.isQualified()) {
96         processElement(node);
97       }
98     }
99
100     private void processElement(@NotNull PsiNameIdentifierOwner element) {
101       final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
102       if (owner instanceof PyClass) {
103         return;
104       }
105       final String name = element.getName();
106       if (name != null && !myIgnoredNames.contains(name)) {
107         final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(element);
108         final PsiElement builtin = builtinCache.getByName(name);
109         if (builtin != null && !PyUtil.inSameFile(builtin, element)) {
110           final PsiElement identifier = element.getNameIdentifier();
111           final PsiElement problemElement = identifier != null ? identifier : element;
112           registerProblem(problemElement, String.format("Shadows built-in name '%s'", name),
113                           ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix(), new PyIgnoreBuiltinQuickFix(name));
114         }
115       }
116     }
117
118     private static class PyIgnoreBuiltinQuickFix implements LocalQuickFix, LowPriorityAction {
119       @NotNull private final String myName;
120
121       private PyIgnoreBuiltinQuickFix(@NotNull String name) {
122         myName = name;
123       }
124
125       @NotNull
126       @Override
127       public String getName() {
128         return getFamilyName() + " \"" + myName + "\"";
129       }
130
131       @Override
132       public boolean startInWriteAction() {
133         return false;
134       }
135
136       @NotNull
137       @Override
138       public String getFamilyName() {
139         return "Ignore shadowed built-in name";
140       }
141
142       @Override
143       public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
144         final PsiElement element = descriptor.getPsiElement();
145         if (element != null) {
146           InspectionProfileModifiableModelKt.modifyAndCommitProjectProfile(project, it -> {
147             final String toolName = PyShadowingBuiltinsInspection.class.getSimpleName();
148             final PyShadowingBuiltinsInspection inspection = (PyShadowingBuiltinsInspection)it.getUnwrappedTool(toolName, element);
149             if (inspection != null) {
150               if (!inspection.ignoredNames.contains(myName)) {
151                 inspection.ignoredNames.add(myName);
152               }
153             }
154           });
155         }
156       }
157     }
158   }
159 }
160