mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -9,7 +9,9 @@ import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -19,6 +21,7 @@ import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.python.edu.course.*;
|
||||
import com.jetbrains.python.edu.editor.StudyEditor;
|
||||
import com.jetbrains.python.edu.ui.StudyToolWindowFactory;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -185,4 +188,9 @@ public class StudyUtils {
|
||||
FileUtil.copy(new File(pathToResource, sourceName), resourceFile);
|
||||
return resourceFile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Sdk findPythonSdk(@NotNull final Project project) {
|
||||
return PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.BalloonBuilder;
|
||||
@@ -33,6 +35,8 @@ import com.jetbrains.python.edu.editor.StudyEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@@ -41,7 +45,7 @@ public class StudyCheckAction extends DumbAwareAction {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(StudyCheckAction.class.getName());
|
||||
private static final String ANSWERS_POSTFIX = "_answers.py";
|
||||
public static final String ACTION_ID = "CheckAction";
|
||||
public static final String ACTION_ID = "CheckAction";
|
||||
public static final String SHORTCUT = "ctrl alt pressed ENTER";
|
||||
|
||||
|
||||
@@ -108,9 +112,15 @@ public class StudyCheckAction extends DumbAwareAction {
|
||||
VirtualFile taskDir = studyState.getTaskDir();
|
||||
flushWindows(task, taskDir);
|
||||
StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
|
||||
if (runAction != null && taskFiles.size() == 1) {
|
||||
runAction.run(project);
|
||||
if (runAction == null) {
|
||||
return;
|
||||
}
|
||||
Sdk sdk = StudyUtils.findPythonSdk(project);
|
||||
if (sdk == null) {
|
||||
createNoPythonInterpreterPopUp(project);
|
||||
return;
|
||||
}
|
||||
runAction.run(project, sdk);
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -157,6 +167,27 @@ public class StudyCheckAction extends DumbAwareAction {
|
||||
});
|
||||
}
|
||||
|
||||
private static void createNoPythonInterpreterPopUp(@NotNull final Project project) {
|
||||
String text = "<html>No Python interpreter configured for the project<br><a href=\"\">Configure interpreter</a></html>";
|
||||
BalloonBuilder balloonBuilder =
|
||||
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, MessageType.WARNING.getPopupBackground(), new HyperlinkListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(HyperlinkEvent event) {
|
||||
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(project, "Project Interpreter");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
balloonBuilder.setHideOnLinkClick(true);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
showCheckPopUp(project, balloon);
|
||||
}
|
||||
|
||||
private static void navigateToFailedTaskWindow(@NotNull final StudyState studyState,
|
||||
@NotNull final Task task,
|
||||
@NotNull final VirtualFile taskDir,
|
||||
@@ -261,6 +292,13 @@ public class StudyCheckAction extends DumbAwareAction {
|
||||
BalloonBuilder balloonBuilder =
|
||||
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(text, null, color, null);
|
||||
final Balloon balloon = balloonBuilder.createBalloon();
|
||||
showCheckPopUp(project, balloon);
|
||||
}
|
||||
|
||||
/**
|
||||
* shows pop up in the center of "check task" button in study editor
|
||||
*/
|
||||
private static void showCheckPopUp(@NotNull final Project project, @NotNull final Balloon balloon) {
|
||||
StudyEditor studyEditor = StudyEditor.getSelectedStudyEditor(project);
|
||||
assert studyEditor != null;
|
||||
JButton checkButton = studyEditor.getCheckButton();
|
||||
|
||||
@@ -9,18 +9,18 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.python.edu.StudyResourceManger;
|
||||
import com.jetbrains.python.edu.StudyTaskManager;
|
||||
import com.jetbrains.python.edu.StudyUtils;
|
||||
import com.jetbrains.python.edu.course.Task;
|
||||
import com.jetbrains.python.edu.course.TaskFile;
|
||||
import com.jetbrains.python.edu.editor.StudyEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class StudyRunAction extends DumbAwareAction {
|
||||
private static final Logger LOG = Logger.getInstance(StudyRunAction.class.getName());
|
||||
public static final String ACTION_ID = "StudyRunAction";
|
||||
|
||||
public void run(Project project) {
|
||||
public void run(@NotNull final Project project, @NotNull final Sdk sdk) {
|
||||
Editor selectedEditor = StudyEditor.getSelectedEditor(project);
|
||||
FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
|
||||
assert selectedEditor != null;
|
||||
@@ -37,9 +37,7 @@ public class StudyRunAction extends DumbAwareAction {
|
||||
if (openedFile != null && openedFile.getCanonicalPath() != null) {
|
||||
String filePath = openedFile.getCanonicalPath();
|
||||
GeneralCommandLine cmd = new GeneralCommandLine();
|
||||
cmd.setWorkDirectory(openedFile.getParent().getCanonicalPath());
|
||||
Sdk sdk = PythonSdkType.findPythonSdk(ModuleManager.getInstance(project).getModules()[0]);
|
||||
if (sdk != null) {
|
||||
cmd.withWorkDirectory(openedFile.getParent().getCanonicalPath());
|
||||
String pythonPath = sdk.getHomePath();
|
||||
if (pythonPath != null) {
|
||||
cmd.setExePath(pythonPath);
|
||||
@@ -79,11 +77,18 @@ public class StudyRunAction extends DumbAwareAction {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
run(e.getProject());
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
Sdk sdk = StudyUtils.findPythonSdk(project);
|
||||
if (sdk == null) {
|
||||
return;
|
||||
}
|
||||
run(project, sdk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl;
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -35,6 +36,7 @@ import com.intellij.util.ui.EmptyClipboardOwner;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.python.edu.StudyDocumentListener;
|
||||
import com.jetbrains.python.edu.StudyTaskManager;
|
||||
import com.jetbrains.python.edu.StudyUtils;
|
||||
import com.jetbrains.python.edu.actions.*;
|
||||
import com.jetbrains.python.edu.course.Task;
|
||||
import com.jetbrains.python.edu.course.TaskFile;
|
||||
@@ -209,23 +211,26 @@ public class StudyEditor implements TextEditor {
|
||||
myRefreshButton = addButton(taskActionsPanel, StudyRefreshTaskFileAction.ACTION_ID, AllIcons.Actions.Refresh, StudyRefreshTaskFileAction.SHORTCUT);
|
||||
JButton myShowHintButton = addButton(taskActionsPanel, StudyShowHintAction.ACTION_ID, StudyIcons.ShowHint, StudyShowHintAction.SHORTCUT);
|
||||
if (!taskFile.getTask().getUserTests().isEmpty()) {
|
||||
JButton runButton = addButton(taskActionsPanel, StudyRunAction.ACTION_ID, AllIcons.General.Run, null);
|
||||
runButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StudyRunAction studyRunAction = (StudyRunAction)ActionManager.getInstance().getAction("StudyRunAction");
|
||||
studyRunAction.run(myProject);
|
||||
}
|
||||
});
|
||||
JButton watchInputButton = addButton(taskActionsPanel, "WatchInputAction", StudyIcons.WatchInput, null);
|
||||
watchInputButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StudyEditInputAction studyEditInputAction =
|
||||
(StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction");
|
||||
studyEditInputAction.showInput(myProject);
|
||||
}
|
||||
});
|
||||
final Sdk sdk = StudyUtils.findPythonSdk(myProject);
|
||||
if (sdk != null) {
|
||||
JButton runButton = addButton(taskActionsPanel, StudyRunAction.ACTION_ID, AllIcons.General.Run, null);
|
||||
runButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StudyRunAction studyRunAction = (StudyRunAction)ActionManager.getInstance().getAction("StudyRunAction");
|
||||
studyRunAction.run(myProject, sdk);
|
||||
}
|
||||
});
|
||||
JButton watchInputButton = addButton(taskActionsPanel, "WatchInputAction", StudyIcons.WatchInput, null);
|
||||
watchInputButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StudyEditInputAction studyEditInputAction =
|
||||
(StudyEditInputAction)ActionManager.getInstance().getAction("WatchInputAction");
|
||||
studyEditInputAction.showInput(myProject);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
myCheckButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
|
||||
@@ -46,7 +46,7 @@ public interface Scope {
|
||||
List<PyImportedNameDefiner> getImportedNameDefiners();
|
||||
|
||||
@Nullable
|
||||
PsiNamedElement getNamedElement(String name);
|
||||
PsiNamedElement getNamedElement(String name, boolean includeNestedGlobals);
|
||||
|
||||
@NotNull
|
||||
Collection<PsiNamedElement> getNamedElements();
|
||||
|
||||
@@ -118,7 +118,7 @@ public class ScopeImpl implements Scope {
|
||||
if (isNonlocal(name)) {
|
||||
return false;
|
||||
}
|
||||
if (getNamedElement(name) != null) {
|
||||
if (getNamedElement(name, true) != null) {
|
||||
return true;
|
||||
}
|
||||
if (isAugAssignment(name)) {
|
||||
@@ -143,7 +143,7 @@ public class ScopeImpl implements Scope {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiNamedElement getNamedElement(String name) {
|
||||
public PsiNamedElement getNamedElement(String name, boolean includeNestedGlobals) {
|
||||
if (myNamedElements == null) {
|
||||
collectDeclarations();
|
||||
}
|
||||
@@ -151,9 +151,9 @@ public class ScopeImpl implements Scope {
|
||||
if (element != null) {
|
||||
return element;
|
||||
}
|
||||
if (isGlobal(name)) {
|
||||
if (includeNestedGlobals && isGlobal(name)) {
|
||||
for (Scope scope : myNestedScopes) {
|
||||
final PsiNamedElement global = scope.getNamedElement(name);
|
||||
final PsiNamedElement global = scope.getNamedElement(name, true);
|
||||
if (global != null) {
|
||||
return global;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,9 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
|
||||
for (Instruction instruction : instructions) {
|
||||
final PsiElement element = instruction.getElement();
|
||||
if (element instanceof PyFunction && owner instanceof PyFunction) {
|
||||
if (PyKnownDecoratorUtil.hasUnknownDecorator((PyFunction)element, myTypeEvalContext)) {
|
||||
continue;
|
||||
}
|
||||
if (!myUsedElements.contains(element)) {
|
||||
myUnusedElements.add(element);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@ public class GenerateBinaryStubsFix implements LocalQuickFix {
|
||||
public static Collection<GenerateBinaryStubsFix> generateFixes(@NotNull final PyImportStatementBase importStatementBase) {
|
||||
final List<String> names = importStatementBase.getFullyQualifiedObjectNames();
|
||||
final List<GenerateBinaryStubsFix> result = new ArrayList<GenerateBinaryStubsFix>(names.size());
|
||||
if (importStatementBase instanceof PyFromImportStatement && names.isEmpty()) {
|
||||
final QualifiedName qName = ((PyFromImportStatement)importStatementBase).getImportSourceQName();
|
||||
if (qName != null)
|
||||
result.add(new GenerateBinaryStubsFix(importStatementBase, qName.toString()));
|
||||
}
|
||||
for (final String qualifiedName : names) {
|
||||
result.add(new GenerateBinaryStubsFix(importStatementBase, qualifiedName));
|
||||
}
|
||||
@@ -225,7 +230,8 @@ public class GenerateBinaryStubsFix implements LocalQuickFix {
|
||||
* @return true if this fix could work
|
||||
*/
|
||||
public static boolean isApplicable(@NotNull final PyImportStatementBase importStatementBase) {
|
||||
if (importStatementBase.getFullyQualifiedObjectNames().isEmpty()) {
|
||||
if (importStatementBase.getFullyQualifiedObjectNames().isEmpty() &&
|
||||
!(importStatementBase instanceof PyFromImportStatement && ((PyFromImportStatement)importStatementBase).isStarImport())) {
|
||||
return false;
|
||||
}
|
||||
final Sdk sdk = getPythonSdk(importStatementBase);
|
||||
|
||||
+36
-35
@@ -27,6 +27,7 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User: catherine
|
||||
@@ -63,28 +64,14 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
|
||||
final String expressionText = expression.getText();
|
||||
final StringBuilder stringBuilder = new StringBuilder(expressionText + " (");
|
||||
|
||||
final PsiElement whiteSpace = expression.getContainingFile().findElementAt(expression.getTextOffset() + expression.getTextLength());
|
||||
PsiElement next = null;
|
||||
if (whiteSpace instanceof PsiWhiteSpace) {
|
||||
final String whiteSpaceText = whiteSpace.getText();
|
||||
if (!whiteSpaceText.contains("\n")) {
|
||||
next = whiteSpace.getNextSibling();
|
||||
while (next instanceof PsiWhiteSpace && whiteSpaceText.contains("\\")) {
|
||||
next = next.getNextSibling();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
next = whiteSpace;
|
||||
|
||||
RemoveUnnecessaryBackslashQuickFix.removeBackSlash(next);
|
||||
if (whiteSpace != null) whiteSpace.delete();
|
||||
final PsiElement next = getNextElement(expression);
|
||||
if (next == null) {
|
||||
stringBuilder.append(")");
|
||||
expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class,
|
||||
stringBuilder.toString()));
|
||||
return;
|
||||
}
|
||||
final String commentText = getComment(next);
|
||||
if (next instanceof PyExpressionStatement) {
|
||||
final PyExpression expr = ((PyExpressionStatement)next).getExpression();
|
||||
if (expr instanceof PyBinaryExpression) {
|
||||
@@ -107,7 +94,7 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
|
||||
}
|
||||
}
|
||||
else {
|
||||
stringBuilder.append(next.getText());
|
||||
stringBuilder.append(((PyExpressionStatement)next).getExpression().getText());
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -115,10 +102,24 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
|
||||
}
|
||||
next.delete();
|
||||
stringBuilder.append(")");
|
||||
if (commentText != null) {
|
||||
stringBuilder.append(commentText);
|
||||
}
|
||||
expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class,
|
||||
stringBuilder.toString()));
|
||||
}
|
||||
|
||||
private static String getComment(@Nullable final PsiElement next) {
|
||||
String commentText = null;
|
||||
if (next != null) {
|
||||
final PsiElement lastChild = next.getLastChild();
|
||||
if (lastChild instanceof PsiComment) {
|
||||
commentText = lastChild.getText();
|
||||
}
|
||||
}
|
||||
return commentText;
|
||||
}
|
||||
|
||||
private static void addInArguments(@NotNull final StringBuilder stringBuilder, @NotNull final PyBinaryExpression binaryExpression) {
|
||||
stringBuilder.append(binaryExpression.getLeftExpression().getText());
|
||||
stringBuilder.append(", ");
|
||||
@@ -132,6 +133,23 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
|
||||
final String expressionText = expression.getText();
|
||||
final StringBuilder stringBuilder = new StringBuilder(expressionText + " (");
|
||||
|
||||
final PsiElement next = getNextElement(expression);
|
||||
String commentText = getComment(next);
|
||||
if (next != null) {
|
||||
final String text = next instanceof PyExpressionStatement ? ((PyExpressionStatement)next).getExpression().getText() : next.getText();
|
||||
stringBuilder.append(text);
|
||||
if (text.endsWith(",")) stringBuilder.append(" end=' '");
|
||||
next.delete();
|
||||
}
|
||||
stringBuilder.append(")");
|
||||
if (commentText != null) {
|
||||
stringBuilder.append(commentText);
|
||||
}
|
||||
expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class,
|
||||
stringBuilder.toString()));
|
||||
}
|
||||
|
||||
private static PsiElement getNextElement(@NotNull final PsiElement expression) {
|
||||
final PsiElement whiteSpace = expression.getContainingFile().findElementAt(expression.getTextOffset() + expression.getTextLength());
|
||||
PsiElement next = null;
|
||||
if (whiteSpace instanceof PsiWhiteSpace) {
|
||||
@@ -148,23 +166,6 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
|
||||
|
||||
RemoveUnnecessaryBackslashQuickFix.removeBackSlash(next);
|
||||
if (whiteSpace != null) whiteSpace.delete();
|
||||
String commentText = null;
|
||||
if (next != null) {
|
||||
final PsiElement lastChild = next.getLastChild();
|
||||
if (lastChild instanceof PsiComment) {
|
||||
commentText = lastChild.getText();
|
||||
}
|
||||
final String text = next instanceof PyExpressionStatement ? ((PyExpressionStatement)next).getExpression().getText() : next.getText();
|
||||
|
||||
stringBuilder.append(text);
|
||||
if (text.endsWith(",")) stringBuilder.append(" end=' '");
|
||||
next.delete();
|
||||
}
|
||||
stringBuilder.append(")");
|
||||
if (commentText != null) {
|
||||
stringBuilder.append(commentText);
|
||||
}
|
||||
expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class,
|
||||
stringBuilder.toString()));
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -736,7 +736,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
}
|
||||
if (type instanceof PyFunctionType) {
|
||||
final Callable callable = ((PyFunctionType)type).getCallable();
|
||||
if (callable instanceof PyFunction && ((PyFunction)callable).getDecoratorList() != null) {
|
||||
if (callable instanceof PyFunction && PyKnownDecoratorUtil.hasNonBuiltinDecorator((PyFunction)callable, myTypeEvalContext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.containers.ContainerUtil.newMapFromValues;
|
||||
import static com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.*;
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* Contains list of well-behaved decorators from Pythons standard library, that don't change
|
||||
* signature of underlying function/class or use it implicitly somewhere (e.g. register as a callback).
|
||||
*
|
||||
* @author Mikhail Golubev
|
||||
*/
|
||||
public class PyKnownDecoratorUtil {
|
||||
|
||||
private PyKnownDecoratorUtil() {
|
||||
}
|
||||
|
||||
// TODO provide more information about these decorators: attributes (e.g. lru_cache(f).cache_info), side-effects etc.
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public enum KnownDecorator {
|
||||
|
||||
STATICMETHOD(PyNames.STATICMETHOD),
|
||||
CLASSMETHOD(PyNames.CLASSMETHOD),
|
||||
PROPERTY(PyNames.PROPERTY),
|
||||
|
||||
CONTEXTLIB_CONTEXTMANAGER("contextlib.contextmanager"),
|
||||
|
||||
FUNCTOOLS_LRU_CACHE("functools.lru_cache"),
|
||||
FUNCTOOLS_WRAPS("functools.wraps"),
|
||||
FUNCTOOLS_TOTAL_ORDERING("functools.total_ordering"),
|
||||
FUNCTOOLS_SINGLEDISPATCH("functools.singledispatch"),
|
||||
|
||||
ABC_ABSTRACTMETHOD("abc.abstractmethod"),
|
||||
ABC_ABSTRACTCLASSMETHOD("abc.abstractclassmethod"),
|
||||
ABC_ABSTRACTSTATICMETHOD("abc.abstractstaticmethod"),
|
||||
ABC_ABSTRACTPROPERTY("abc.abstractproperty"),
|
||||
|
||||
//ATEXIT_REGISTER("atexit.register", true),
|
||||
//ATEXIT_UNREGISTER("atexit.unregister", false),
|
||||
|
||||
ASYNCIO_COROUTINE("asyncio.tasks.coroutine"),
|
||||
|
||||
UNITTEST_SKIP("unittest.case.skip"),
|
||||
UNITTEST_SKIP_IF("unittest.case.skipIf"),
|
||||
UNITTEST_SKIP_UNLESS("unittest.case.skipUnless"),
|
||||
UNITTEST_EXPECTED_FAILURE("unittest.case.expectedFailure"),
|
||||
UNITTEST_MOCK_PATCH("unittest.mock.patch"),
|
||||
|
||||
REPRLIB_RECURSIVE_REPR("reprlib.recursive_repr");
|
||||
|
||||
private final QualifiedName myQualifiedName;
|
||||
|
||||
KnownDecorator(@NotNull String qualifiedName) {
|
||||
myQualifiedName = QualifiedName.fromDottedString(qualifiedName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public QualifiedName getQualifiedName() {
|
||||
return myQualifiedName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getShortName() {
|
||||
//noinspection ConstantConditions
|
||||
return myQualifiedName.getLastComponent();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Set<KnownDecorator> BUILTIN_DECORATORS = EnumSet.of(PROPERTY, CLASSMETHOD, STATICMETHOD);
|
||||
private static final Set<KnownDecorator> ABSTRACT_DECORATORS = EnumSet.of(ABC_ABSTRACTMETHOD,
|
||||
ABC_ABSTRACTPROPERTY,
|
||||
ABC_ABSTRACTSTATICMETHOD,
|
||||
ABC_ABSTRACTCLASSMETHOD);
|
||||
|
||||
private static final Map<String, KnownDecorator> ourByShortName = newMapFromValues(Iterators.forArray(values()),
|
||||
new Convertor<KnownDecorator, String>() {
|
||||
@Override
|
||||
public String convert(KnownDecorator o) {
|
||||
return o.getShortName();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Map decorators of element to {@link com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator}.
|
||||
*
|
||||
* @param element decoratable element to check
|
||||
* @param context type evaluation context. If it's {@code null} or doesn't allow switch to AST only textual comparison will be performed.
|
||||
* @return list of known decorators in declaration order with duplicates (with any)
|
||||
*/
|
||||
@NotNull
|
||||
public static List<KnownDecorator> getKnownDecorators(@NotNull PyDecoratable element, @Nullable TypeEvalContext context) {
|
||||
final PyDecoratorList decoratorList = element.getDecoratorList();
|
||||
if (decoratorList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<KnownDecorator> result = new ArrayList<KnownDecorator>();
|
||||
final boolean allowResolve = context != null && context.maySwitchToAST((PsiElement)element);
|
||||
for (PyDecorator decorator : decoratorList.getDecorators()) {
|
||||
final QualifiedName qualifiedName = decorator.getQualifiedName();
|
||||
if (qualifiedName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final KnownDecorator knownDecorator = ourByShortName.get(qualifiedName.getLastComponent());
|
||||
if (knownDecorator != null) {
|
||||
if (allowResolve) {
|
||||
PyQualifiedNameOwner resolved = as(resolveDecorator(decorator), PyQualifiedNameOwner.class);
|
||||
if (resolved instanceof PyFunction && PyNames.INIT.equals(resolved.getName())) {
|
||||
resolved = ((PyFunction)resolved).getContainingClass();
|
||||
}
|
||||
if (resolved != null && resolved.getQualifiedName() != null) {
|
||||
final QualifiedName resolvedName = QualifiedName.fromDottedString(resolved.getQualifiedName());
|
||||
if (resolvedName.equals(knownDecorator.getQualifiedName())) {
|
||||
result.add(knownDecorator);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.add(knownDecorator);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveDecorator(@NotNull PyDecorator decorator) {
|
||||
final PyExpression callee = decorator.getCallee();
|
||||
if (callee == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiReference reference = callee.getReference();
|
||||
if (reference == null) {
|
||||
return null;
|
||||
}
|
||||
return reference.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that given element has any non-standard (read "unreliable") decorators.
|
||||
*
|
||||
* @param element decoratable element to check
|
||||
* @param context type evaluation context. If it's {@code null} or doesn't allow switch to AST only textual comparison will be performed.
|
||||
* @see PyKnownDecoratorUtil.KnownDecorator
|
||||
*/
|
||||
public static boolean hasUnknownDecorator(@NotNull PyDecoratable element, @Nullable TypeEvalContext context) {
|
||||
return !allDecoratorsAreKnown(element, getKnownDecorators(element, context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that given element has any non-builtin decorators.
|
||||
*
|
||||
* @param element decoratable element to check
|
||||
* @param context type evaluation context. If it's {@code null} or doesn't allow switch to AST only textual comparison will be performed.
|
||||
* @see PyKnownDecoratorUtil.KnownDecorator
|
||||
*/
|
||||
public static boolean hasNonBuiltinDecorator(@NotNull PyDecoratable element, @Nullable TypeEvalContext context) {
|
||||
final List<KnownDecorator> knownDecorators = getKnownDecorators(element, context);
|
||||
if (!allDecoratorsAreKnown(element, knownDecorators)) {
|
||||
return true;
|
||||
}
|
||||
knownDecorators.removeAll(BUILTIN_DECORATORS);
|
||||
return !knownDecorators.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that given function has any decorators from {@code abc} module.
|
||||
*
|
||||
* @param element Python function to check
|
||||
* @param context type evaluation context. If it's {@code null} or doesn't allow switch to AST only textual comparison will be performed.
|
||||
* @see PyKnownDecoratorUtil.KnownDecorator
|
||||
*/
|
||||
public static boolean hasAbstractDecorator(@NotNull PyDecoratable element, @Nullable TypeEvalContext context) {
|
||||
final List<KnownDecorator> knownDecorators = getKnownDecorators(element, context);
|
||||
if (knownDecorators.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
knownDecorators.retainAll(ABSTRACT_DECORATORS);
|
||||
return !knownDecorators.isEmpty();
|
||||
}
|
||||
|
||||
private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List<KnownDecorator> decorators) {
|
||||
final PyDecoratorList decoratorList = element.getDecoratorList();
|
||||
return decoratorList == null ? decorators.isEmpty() : decoratorList.getDecorators().length == decorators.size();
|
||||
}
|
||||
}
|
||||
@@ -695,34 +695,11 @@ public class PyUtil {
|
||||
}
|
||||
|
||||
public static boolean hasCustomDecorators(@NotNull PyDecoratable decoratable) {
|
||||
PyDecoratorList decoratorList = decoratable.getDecoratorList();
|
||||
if (decoratorList == null) {
|
||||
return false;
|
||||
}
|
||||
for (PyDecorator decorator : decoratorList.getDecorators()) {
|
||||
QualifiedName name = decorator.getQualifiedName();
|
||||
if (name == null || (!PyNames.CLASSMETHOD.equals(name.toString()) && !PyNames.STATICMETHOD.equals(name.toString()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return PyKnownDecoratorUtil.hasNonBuiltinDecorator(decoratable, null);
|
||||
}
|
||||
|
||||
public static boolean isDecoratedAsAbstract(@NotNull final PyDecoratable decoratable) {
|
||||
final PyDecoratorList decoratorList = decoratable.getDecoratorList();
|
||||
if (decoratorList == null) {
|
||||
return false;
|
||||
}
|
||||
for (PyDecorator decorator : decoratorList.getDecorators()) {
|
||||
final QualifiedName qualifiedName = decorator.getQualifiedName();
|
||||
if (qualifiedName != null) {
|
||||
final String name = qualifiedName.toString();
|
||||
if (name.endsWith(PyNames.ABSTRACTMETHOD) || name.endsWith(PyNames.ABSTRACTPROPERTY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return PyKnownDecoratorUtil.hasAbstractDecorator(decoratable, null);
|
||||
}
|
||||
|
||||
public static ASTNode createNewName(PyElement element, String name) {
|
||||
|
||||
@@ -133,7 +133,8 @@ public class PyResolveUtil {
|
||||
final Scope scope = ControlFlowCache.getScope(scopeOwner);
|
||||
boolean found = false;
|
||||
if (name != null) {
|
||||
final PsiElement resolved = scope.getNamedElement(name);
|
||||
final boolean includeNestedGlobals = scopeOwner instanceof PyFile;
|
||||
final PsiElement resolved = scope.getNamedElement(name, includeNestedGlobals);
|
||||
if (resolved != null) {
|
||||
if (!processor.execute(resolved, ResolveState.initial())) {
|
||||
found = true;
|
||||
|
||||
+4
-6
@@ -7,7 +7,7 @@ class MyClass(object):
|
||||
pass
|
||||
|
||||
|
||||
# Unbound method still treated as __method in Python 2
|
||||
# Unbound methods are still treated as __method in Python 2
|
||||
MyClass.method.__func__
|
||||
MyClass.method.<warning descr="Cannot find reference '__defaults__' in 'function'">__defaults__</warning>
|
||||
|
||||
@@ -16,10 +16,8 @@ inst = MyClass()
|
||||
inst.method.__func__
|
||||
inst.method.<warning descr="Cannot find reference '__defaults__' in 'function'">__defaults__</warning>
|
||||
|
||||
# Reassigned bound method without qualifier
|
||||
m = inst.method
|
||||
|
||||
# Static method
|
||||
# This reference should be marked as unresolved, but such warnings are suppressed for methods with decorators
|
||||
inst.static_method.__func__
|
||||
inst.static_method.<warning descr="Cannot find reference '__func__' in 'function'">__func__</warning>
|
||||
inst.static_method.__defaults__
|
||||
MyClass.static_method.<warning descr="Cannot find reference '__func__' in 'function'">__func__</warning>
|
||||
MyClass.static_method.__defaults__
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .tasks import *
|
||||
@@ -0,0 +1,2 @@
|
||||
def coroutine(func):
|
||||
return func
|
||||
@@ -0,0 +1,7 @@
|
||||
import asyncio
|
||||
|
||||
# PY-9778
|
||||
def process():
|
||||
@asyncio.coroutine
|
||||
def <weak_warning descr="Local function 'func' is not used">func</weak_warning>():
|
||||
pass
|
||||
@@ -3,11 +3,17 @@
|
||||
<problem>
|
||||
<file>test.py</file>
|
||||
<line>2</line>
|
||||
<description>Local function 'bar' is not used</description>
|
||||
<description>Local function 'bar' is not used</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>test.py</file>
|
||||
<line>13</line>
|
||||
<description>Local function 'bar1' is not used</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>test.py</file>
|
||||
<line>29</line>
|
||||
<module>light_idea_test_case</module>
|
||||
<description>Local function 'func' is not used</description>
|
||||
</problem>
|
||||
</problems>
|
||||
@@ -21,3 +21,25 @@ def foo():
|
||||
def baz(): #pass
|
||||
bar()
|
||||
baz()
|
||||
|
||||
|
||||
# PY-9778
|
||||
def unused_inner_function_with_known_decorator():
|
||||
@staticmethod
|
||||
def func(): # fail
|
||||
yield
|
||||
|
||||
|
||||
def unused_inner_function_with_unknown_decorator():
|
||||
def register(f):
|
||||
return f
|
||||
|
||||
@register
|
||||
@contextmanager
|
||||
def func(): # pass
|
||||
pass
|
||||
|
||||
def unused_inner_function_with_incomplete_decorator():
|
||||
@
|
||||
def func(): # pass
|
||||
pass
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<warning descr="Statement seems to have no effect and can be replaced with function call to have effect">exec</warning><error descr="End of statement expected"> </error><warning descr="Statement seems to have no effect">1</warning> # <- doesn't work either
|
||||
@@ -0,0 +1 @@
|
||||
exec(1) # <- doesn't work either
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
foo = 0
|
||||
|
||||
def outer():
|
||||
def inner():
|
||||
global fo<ref>o
|
||||
print(foo)
|
||||
inner()
|
||||
|
||||
outer()
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.inspections.PyMissingConstructorInspection;
|
||||
import com.jetbrains.python.inspections.PyStatementEffectInspection;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
@TestDataPath("$CONTENT_ROOT/../testData/inspections/")
|
||||
public class Py3QuickFixTest extends PyTestCase {
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return PyTestCase.ourPy3Descriptor;
|
||||
}
|
||||
|
||||
public void testReplacePrintEnd() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("ReplacePrintEnd.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true);
|
||||
}});
|
||||
}
|
||||
|
||||
public void testReplacePrintComment() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("ReplacePrintComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true,
|
||||
true);
|
||||
}});
|
||||
}
|
||||
|
||||
public void testReplaceExecComment() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("ReplaceExecComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true,
|
||||
true);
|
||||
}});
|
||||
}
|
||||
|
||||
public void testAddCallSuperAnnotations() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("AddCallSuperAnnotations.py",
|
||||
PyMissingConstructorInspection.class,
|
||||
PyBundle.message("QFIX.add.super"), true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNls
|
||||
protected String getTestDataPath() {
|
||||
return PythonTestUtil.getTestDataPath() + "/inspections/";
|
||||
}
|
||||
|
||||
protected void doInspectionTest(@TestDataFile @NonNls String testFileName,
|
||||
final Class inspectionClass,
|
||||
@NonNls String quickFixName,
|
||||
boolean applyFix,
|
||||
boolean available) {
|
||||
doInspectionTest(new String[]{testFileName}, inspectionClass, quickFixName, applyFix, available);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs daemon passes and looks for given fix within infos.
|
||||
*
|
||||
* @param testFiles names of files to participate; first is used for inspection and then for check by "_after".
|
||||
* @param inspectionClass what inspection to run
|
||||
* @param quickFixName how the resulting fix should be named (the human-readable name users see)
|
||||
* @param applyFix true if the fix needs to be applied
|
||||
* @param available true if the fix should be available, false if it should be explicitly not available.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void doInspectionTest(@NonNls String[] testFiles,
|
||||
final Class inspectionClass,
|
||||
@NonNls String quickFixName,
|
||||
boolean applyFix,
|
||||
boolean available) {
|
||||
myFixture.enableInspections(inspectionClass);
|
||||
myFixture.configureByFiles(testFiles);
|
||||
myFixture.checkHighlighting(true, false, false);
|
||||
final IntentionAction intentionAction = myFixture.findSingleIntention(quickFixName);
|
||||
if (available) {
|
||||
assertNotNull(intentionAction);
|
||||
if (applyFix) {
|
||||
myFixture.launchAction(intentionAction);
|
||||
|
||||
myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
assertNull(intentionAction);
|
||||
}
|
||||
}
|
||||
|
||||
// Turns "name.ext" to "name_insertion.ext"
|
||||
|
||||
@NonNls
|
||||
private static String graftBeforeExt(String name, String insertion) {
|
||||
int dotpos = name.indexOf('.');
|
||||
if (dotpos < 0) dotpos = name.length();
|
||||
return name.substring(0, dotpos) + insertion + name.substring(dotpos, name.length());
|
||||
}
|
||||
}
|
||||
@@ -253,40 +253,27 @@ public class PyQuickFixTest extends PyTestCase {
|
||||
|
||||
public void testStatementEffect() { // PY-1362, PY-2585
|
||||
doInspectionTest("StatementEffect.py", PyStatementEffectInspection.class,
|
||||
PyBundle.message("QFIX.statement.effect"), true, true);
|
||||
PyBundle.message("QFIX.statement.effect"), true, true);
|
||||
}
|
||||
|
||||
public void testStatementEffectIntroduceVariable() { // PY-1265
|
||||
doInspectionTest("StatementEffectIntroduceVariable.py", PyStatementEffectInspection.class,
|
||||
PyBundle.message("QFIX.statement.effect.introduce.variable"), true, true);
|
||||
}
|
||||
|
||||
public void testReplacePrintEnd() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("ReplacePrintEnd.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true, true);
|
||||
}});
|
||||
}
|
||||
|
||||
public void testReplacePrintComment() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("ReplacePrintComment.py", PyStatementEffectInspection.class, PyBundle.message("QFIX.statement.effect"), true,
|
||||
true);
|
||||
}});
|
||||
PyBundle.message("QFIX.statement.effect.introduce.variable"), true, true);
|
||||
}
|
||||
|
||||
public void testUnresolvedWith() { // PY-2083
|
||||
setLanguageLevel(LanguageLevel.PYTHON25);
|
||||
doInspectionTest("UnresolvedWith.py", PyUnresolvedReferencesInspection.class,
|
||||
PyBundle.message("QFIX.unresolved.reference.add.future"), true, true);
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON25, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("UnresolvedWith.py", PyUnresolvedReferencesInspection.class,
|
||||
PyBundle.message("QFIX.unresolved.reference.add.future"), true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testUnresolvedRefCreateFunction() { // PY-2092
|
||||
doInspectionTest("UnresolvedRefCreateFunction.py", PyUnresolvedReferencesInspection.class,
|
||||
PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref"), true, true);
|
||||
PyBundle.message("QFIX.unresolved.reference.create.function.$0", "ref"), true, true);
|
||||
}
|
||||
|
||||
public void testUnresolvedRefNoCreateFunction() {
|
||||
@@ -343,17 +330,6 @@ public class PyQuickFixTest extends PyTestCase {
|
||||
PyBundle.message("QFIX.add.super"), true, true);
|
||||
}
|
||||
|
||||
public void testAddCallSuperAnnotations() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("AddCallSuperAnnotations.py",
|
||||
PyMissingConstructorInspection.class,
|
||||
PyBundle.message("QFIX.add.super"), true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testAddCallSuperPass() { //PY-8654
|
||||
doInspectionTest("AddCallSuperPass.py", PyMissingConstructorInspection.class,
|
||||
PyBundle.message("QFIX.add.super"), true, true);
|
||||
@@ -387,9 +363,13 @@ public class PyQuickFixTest extends PyTestCase {
|
||||
}
|
||||
|
||||
public void testSetFunctionToLiteral() { //PY-3120
|
||||
setLanguageLevel(LanguageLevel.PYTHON27);
|
||||
doInspectionTest("SetFunctionToLiteral.py", PySetFunctionToLiteralInspection.class,
|
||||
PyBundle.message("QFIX.replace.function.set.with.literal"), true, true);
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doInspectionTest("SetFunctionToLiteral.py", PySetFunctionToLiteralInspection.class,
|
||||
PyBundle.message("QFIX.replace.function.set.with.literal"), true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testDictComprehensionToCall() {
|
||||
|
||||
@@ -160,13 +160,19 @@ public class PyResolveTest extends PyResolveTestCase {
|
||||
|
||||
public void testTryExceptElse() {
|
||||
PsiElement targetElement = resolve();
|
||||
Assert.assertTrue(targetElement instanceof PyTargetExpression);
|
||||
assertTrue(targetElement instanceof PyTargetExpression);
|
||||
}
|
||||
|
||||
public void testGlobal() {
|
||||
PsiElement targetElement = resolve();
|
||||
Assert.assertTrue(targetElement instanceof PyTargetExpression);
|
||||
Assert.assertTrue(targetElement.getParent() instanceof PyAssignmentStatement);
|
||||
assertTrue(targetElement instanceof PyTargetExpression);
|
||||
assertTrue(targetElement.getParent() instanceof PyAssignmentStatement);
|
||||
}
|
||||
|
||||
public void testGlobalInNestedFunction() {
|
||||
PsiElement targetElement = resolve();
|
||||
assertInstanceOf(targetElement, PyTargetExpression.class);
|
||||
assertInstanceOf(ScopeUtil.getScopeOwner(targetElement), PyFile.class);
|
||||
}
|
||||
|
||||
public void testGlobalDefinedLocally() {
|
||||
|
||||
@@ -101,6 +101,12 @@ public class PythonInspectionsTest extends PyTestCase {
|
||||
doTest(getTestName(false), inspection);
|
||||
}
|
||||
|
||||
// PY-9778
|
||||
public void testPyUnusedLocalCoroutine() {
|
||||
myFixture.copyDirectoryToProject("inspections/" + getTestName(false), "");
|
||||
doHighlightingTest(PyUnusedLocalInspection.class, LanguageLevel.PYTHON34);
|
||||
}
|
||||
|
||||
public void testPyDictCreationInspection() {
|
||||
doHighlightingTest(PyDictCreationInspection.class, LanguageLevel.PYTHON26);
|
||||
}
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
|
||||
doMultiFileTest();
|
||||
}
|
||||
|
||||
// PY-9342
|
||||
// PY-9342, PY-13791
|
||||
public void testMethodSpecialAttributes() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class HtmlPolicy extends XmlFormattingPolicy {
|
||||
|
||||
private int getLines(final XmlTag parentTag) {
|
||||
final TextRange textRange = parentTag.getTextRange();
|
||||
return myDocumentModel.getLineNumber(textRange.getEndOffset()) - myDocumentModel.getLineNumber(textRange.getStartOffset());
|
||||
return myDocumentModel.getLineNumber(textRange.getEndOffset()) - myDocumentModel.getLineNumber(textRange.getStartOffset()) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user