Search in sources :

Example 11 with Issue

use of com.android.tools.lint.detector.api.Issue in project android by JetBrains.

the class AndroidLintExternalAnnotator method apply.

@Override
public void apply(@NotNull PsiFile file, State state, @NotNull AnnotationHolder holder) {
    if (state.isDirty()) {
        return;
    }
    final Project project = file.getProject();
    if (DumbService.isDumb(project))
        return;
    for (ProblemData problemData : state.getProblems()) {
        final Issue issue = problemData.getIssue();
        final String message = problemData.getMessage();
        final TextRange range = problemData.getTextRange();
        if (range.getStartOffset() == range.getEndOffset()) {
            continue;
        }
        final Pair<AndroidLintInspectionBase, HighlightDisplayLevel> pair = AndroidLintUtil.getHighlighLevelAndInspection(project, issue, file);
        if (pair == null) {
            continue;
        }
        final AndroidLintInspectionBase inspection = pair.getFirst();
        HighlightDisplayLevel displayLevel = pair.getSecond();
        if (inspection != null) {
            final HighlightDisplayKey key = HighlightDisplayKey.find(inspection.getShortName());
            if (key != null) {
                final PsiElement startElement = file.findElementAt(range.getStartOffset());
                final PsiElement endElement = file.findElementAt(range.getEndOffset() - 1);
                if (startElement != null && endElement != null && !inspection.isSuppressedFor(startElement)) {
                    if (problemData.getConfiguredSeverity() != null) {
                        HighlightDisplayLevel configuredLevel = AndroidLintInspectionBase.toHighlightDisplayLevel(problemData.getConfiguredSeverity());
                        if (configuredLevel != null) {
                            displayLevel = configuredLevel;
                        }
                    }
                    final Annotation annotation = createAnnotation(holder, message, range, displayLevel, issue);
                    for (AndroidLintQuickFix fix : inspection.getQuickFixes(startElement, endElement, message)) {
                        if (fix.isApplicable(startElement, endElement, AndroidQuickfixContexts.EditorContext.TYPE)) {
                            annotation.registerFix(new MyFixingIntention(fix, startElement, endElement));
                        }
                    }
                    for (IntentionAction intention : inspection.getIntentions(startElement, endElement)) {
                        annotation.registerFix(intention);
                    }
                    String id = key.getID();
                    if (LintIdeIssueRegistry.CUSTOM_ERROR == issue || LintIdeIssueRegistry.CUSTOM_WARNING == issue) {
                        Issue original = LintIdeClient.findCustomIssue(message);
                        if (original != null) {
                            id = original.getId();
                        }
                    }
                    annotation.registerFix(new SuppressLintIntentionAction(id, startElement));
                    annotation.registerFix(new MyDisableInspectionFix(key));
                    annotation.registerFix(new MyEditInspectionToolsSettingsAction(key, inspection));
                    if (issue == DeprecationDetector.ISSUE || issue == GradleDetector.DEPRECATED) {
                        annotation.setHighlightType(ProblemHighlightType.LIKE_DEPRECATED);
                    }
                    if (INCLUDE_IDEA_SUPPRESS_ACTIONS) {
                        final SuppressQuickFix[] suppressActions = inspection.getBatchSuppressActions(startElement);
                        for (SuppressQuickFix action : suppressActions) {
                            if (action.isAvailable(project, startElement)) {
                                ProblemHighlightType type = annotation.getHighlightType();
                                annotation.registerFix(action, null, key, InspectionManager.getInstance(project).createProblemDescriptor(startElement, endElement, message, type, true, LocalQuickFix.EMPTY_ARRAY));
                            }
                        }
                    }
                }
            }
        }
    }
}
Also used : Issue(com.android.tools.lint.detector.api.Issue) HighlightDisplayLevel(com.intellij.codeHighlighting.HighlightDisplayLevel) HighlightDisplayKey(com.intellij.codeInsight.daemon.HighlightDisplayKey) Annotation(com.intellij.lang.annotation.Annotation) Project(com.intellij.openapi.project.Project) IntentionAction(com.intellij.codeInsight.intention.IntentionAction) PsiElement(com.intellij.psi.PsiElement)

Example 12 with Issue

use of com.android.tools.lint.detector.api.Issue in project android by JetBrains.

the class AndroidLintExternalAnnotator method getIssuesFromInspections.

@NotNull
static List<Issue> getIssuesFromInspections(@NotNull Project project, @Nullable PsiElement context) {
    final List<Issue> result = new ArrayList<>();
    final IssueRegistry fullRegistry = new LintIdeIssueRegistry();
    for (Issue issue : fullRegistry.getIssues()) {
        final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(project, issue);
        if (inspectionShortName == null) {
            continue;
        }
        final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName);
        if (key == null) {
            continue;
        }
        final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
        final boolean enabled = context != null ? profile.isToolEnabled(key, context) : profile.isToolEnabled(key);
        if (!enabled) {
            continue;
        } else if (!issue.isEnabledByDefault()) {
            // If an issue is marked as not enabled by default, lint won't run it, even if it's in the set
            // of issues provided by an issue registry. Since in the IDE we're enforcing the enabled-state via
            // inspection profiles, mark the issue as enabled to allow users to turn on a lint check directly
            // via the inspections UI.
            issue.setEnabledByDefault(true);
        }
        result.add(issue);
    }
    return result;
}
Also used : Issue(com.android.tools.lint.detector.api.Issue) HighlightDisplayKey(com.intellij.codeInsight.daemon.HighlightDisplayKey) ArrayList(java.util.ArrayList) IssueRegistry(com.android.tools.lint.client.api.IssueRegistry) NotNull(org.jetbrains.annotations.NotNull)

Example 13 with Issue

use of com.android.tools.lint.detector.api.Issue in project android by JetBrains.

the class UnusedResourcesProcessor method computeUnusedDeclarationElements.

@NotNull
private List<PsiElement> computeUnusedDeclarationElements(Map<Issue, Map<File, List<ProblemData>>> map) {
    final List<PsiElement> elements = Lists.newArrayList();
    // Make sure lint didn't put extra issues into the map
    for (Issue issue : Lists.newArrayList(map.keySet())) {
        if (issue != UnusedResourceDetector.ISSUE && issue != UnusedResourceDetector.ISSUE_IDS) {
            map.remove(issue);
        }
    }
    ApplicationManager.getApplication().assertReadAccessAllowed();
    PsiManager manager = PsiManager.getInstance(myProject);
    for (Issue issue : new Issue[] { UnusedResourceDetector.ISSUE, UnusedResourceDetector.ISSUE_IDS }) {
        Map<File, List<ProblemData>> fileListMap = map.get(issue);
        if (fileListMap != null && !fileListMap.isEmpty()) {
            Map<File, PsiFile> files = Maps.newHashMap();
            for (File file : fileListMap.keySet()) {
                VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file);
                if (virtualFile != null) {
                    if (!virtualFile.isDirectory()) {
                        // Gradle model errors currently don't have source positions
                        PsiFile psiFile = manager.findFile(virtualFile);
                        if (psiFile != null) {
                            files.put(file, psiFile);
                        }
                    }
                }
            }
            if (!files.isEmpty()) {
                for (File file : files.keySet()) {
                    PsiFile psiFile = files.get(file);
                    if (psiFile == null) {
                        // where we only had the project directory as the location from the Gradle model
                        continue;
                    }
                    if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, psiFile)) {
                        continue;
                    }
                    List<ProblemData> problems = fileListMap.get(file);
                    if (psiFile.getFileType().isBinary()) {
                        // Delete the whole file
                        if (matchesFilter(fileListMap, file)) {
                            elements.add(psiFile);
                        }
                    } else {
                        ResourceFolderType folderType = ResourceHelper.getFolderType(psiFile);
                        if (folderType == null) {
                            // file; see for example http://b.android.com/220069.)
                            if (psiFile.getFileType() == GroovyFileType.GROOVY_FILE_TYPE && psiFile instanceof GroovyFile) {
                                ((GroovyFile) psiFile).accept(new GroovyRecursiveElementVisitor() {

                                    @Override
                                    public void visitApplicationStatement(GrApplicationStatement applicationStatement) {
                                        super.visitApplicationStatement(applicationStatement);
                                        PsiMethod method = applicationStatement.resolveMethod();
                                        if (method != null && method.getName().equals("resValue")) {
                                            GrExpression[] args = applicationStatement.getArgumentList().getExpressionArguments();
                                            if (args.length >= 3) {
                                                Object typeString = GroovyConstantExpressionEvaluator.evaluate(args[0]);
                                                Object nameString = GroovyConstantExpressionEvaluator.evaluate(args[1]);
                                                // See if this is one of the unused resources
                                                if (typeString != null && nameString != null) {
                                                    List<ProblemData> problems = fileListMap.get(VfsUtilCore.virtualToIoFile(psiFile.getVirtualFile()));
                                                    if (problems != null) {
                                                        for (ProblemData problem : problems) {
                                                            String unusedResource = UnusedResourceDetector.getUnusedResource(problem.getMessage(), TextFormat.RAW);
                                                            if (unusedResource != null && unusedResource.equals(SdkConstants.R_PREFIX + typeString + '.' + nameString)) {
                                                                elements.add(applicationStatement);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                });
                            }
                            continue;
                        }
                        if (folderType != ResourceFolderType.VALUES) {
                            // also being deleted as unused
                            if (issue == UnusedResourceDetector.ISSUE_IDS) {
                                Map<File, List<ProblemData>> m = map.get(UnusedResourceDetector.ISSUE);
                                if (m != null && m.containsKey(file)) {
                                    // Yes - skip
                                    continue;
                                }
                                // Delete ranges within the file
                                addElementsInFile(elements, psiFile, problems);
                            } else {
                                // Unused non-value resource file: Delete the whole file
                                if (matchesFilter(fileListMap, file)) {
                                    elements.add(psiFile);
                                }
                            }
                        } else {
                            addElementsInFile(elements, psiFile, problems);
                        }
                    }
                }
            }
        }
    }
    return elements;
}
Also used : VirtualFile(com.intellij.openapi.vfs.VirtualFile) Issue(com.android.tools.lint.detector.api.Issue) GroovyRecursiveElementVisitor(org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor) GrApplicationStatement(org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement) ProblemData(org.jetbrains.android.inspections.lint.ProblemData) ResourceFolderType(com.android.resources.ResourceFolderType) List(java.util.List) XmlFile(com.intellij.psi.xml.XmlFile) VirtualFile(com.intellij.openapi.vfs.VirtualFile) GroovyFile(org.jetbrains.plugins.groovy.lang.psi.GroovyFile) File(java.io.File) GroovyFile(org.jetbrains.plugins.groovy.lang.psi.GroovyFile) NotNull(org.jetbrains.annotations.NotNull)

Example 14 with Issue

use of com.android.tools.lint.detector.api.Issue in project android by JetBrains.

the class LintNotificationPanel method updateExplanation.

private void updateExplanation(@Nullable IssueData selected) {
    // We have the capability to show markup text here, e.g.
    // myExplanationPane.setContentType(UIUtil.HTML_MIME)
    // and then use an HtmlBuilder to populate it with for
    // example issue.getExplanation(HTML). However, the builtin
    // HTML formatter ends up using a bunch of weird fonts etc
    // so the dialog just ends up looking tacky.
    String headerFontColor = HtmlBuilderHelper.getHeaderFontColor();
    HtmlBuilder builder = new HtmlBuilder();
    builder.openHtmlBody();
    if (selected != null) {
        builder.addHeading("Message: ", headerFontColor);
        builder.add(selected.message).newline();
        // Look for quick fixes
        AndroidLintInspectionBase inspection = selected.inspection;
        AndroidLintQuickFix[] quickFixes = inspection.getQuickFixes(selected.startElement, selected.endElement, selected.message);
        IntentionAction[] intentions = inspection.getIntentions(selected.startElement, selected.endElement);
        builder.addHeading("Suggested Fixes:", headerFontColor).newline();
        builder.beginList();
        for (final AndroidLintQuickFix fix : quickFixes) {
            builder.listItem();
            builder.addLink(fix.getName(), myLinkManager.createRunnableLink(() -> {
                myPopup.cancel();
                // TODO: Pull in editor context?
                WriteCommandAction.runWriteCommandAction(selected.startElement.getProject(), () -> {
                    fix.apply(selected.startElement, selected.endElement, AndroidQuickfixContexts.BatchContext.getInstance());
                });
            }));
        }
        for (final IntentionAction fix : intentions) {
            builder.listItem();
            builder.addLink(fix.getText(), myLinkManager.createRunnableLink(() -> {
                NlModel model = myScreenView.getModel();
                Editor editor = PsiEditorUtil.Service.getInstance().findEditorByPsiElement(selected.startElement);
                if (editor != null) {
                    editor.getCaretModel().getCurrentCaret().moveToOffset(selected.startElement.getTextOffset());
                    myPopup.cancel();
                    WriteCommandAction.runWriteCommandAction(model.getProject(), () -> {
                        fix.invoke(model.getProject(), editor, model.getFile());
                    });
                }
            }));
        }
        final SuppressLintIntentionAction suppress = new SuppressLintIntentionAction(selected.issue, selected.startElement);
        builder.listItem();
        builder.addLink(suppress.getText(), myLinkManager.createRunnableLink(() -> {
            myPopup.cancel();
            WriteCommandAction.runWriteCommandAction(selected.startElement.getProject(), () -> {
                suppress.invoke(selected.startElement.getProject(), null, myScreenView.getModel().getFile());
            });
        }));
        builder.endList();
        Issue issue = selected.issue;
        builder.addHeading("Priority: ", headerFontColor);
        builder.addHtml(String.format("%1$d / 10", issue.getPriority()));
        builder.newline();
        builder.addHeading("Category: ", headerFontColor);
        builder.add(issue.getCategory().getFullName());
        builder.newline();
        builder.addHeading("Severity: ", headerFontColor);
        builder.beginSpan();
        // Use converted level instead of *default* severity such that we match any user configured overrides
        HighlightDisplayLevel level = selected.level;
        builder.add(StringUtil.capitalize(level.getName().toLowerCase(Locale.US)));
        builder.endSpan();
        builder.newline();
        builder.addHeading("Explanation: ", headerFontColor);
        String description = issue.getBriefDescription(HTML);
        builder.addHtml(description);
        if (!description.isEmpty() && Character.isLetter(description.charAt(description.length() - 1))) {
            builder.addHtml(".");
        }
        builder.newline();
        String explanationHtml = issue.getExplanation(HTML);
        builder.addHtml(explanationHtml);
        List<String> moreInfo = issue.getMoreInfo();
        builder.newline();
        int count = moreInfo.size();
        if (count > 1) {
            builder.addHeading("More Info: ", headerFontColor);
            builder.beginList();
        }
        for (String uri : moreInfo) {
            if (count > 1) {
                builder.listItem();
            }
            builder.addLink(uri, uri);
        }
        if (count > 1) {
            builder.endList();
        }
        builder.newline();
    }
    builder.closeHtmlBody();
    try {
        myExplanationPane.read(new StringReader(builder.getHtml()), null);
        HtmlBuilderHelper.fixFontStyles(myExplanationPane);
        myExplanationPane.setCaretPosition(0);
    } catch (IOException ignore) {
    // can't happen for internal string reading
    }
}
Also used : AndroidLintInspectionBase(org.jetbrains.android.inspections.lint.AndroidLintInspectionBase) Issue(com.android.tools.lint.detector.api.Issue) HighlightDisplayLevel(com.intellij.codeHighlighting.HighlightDisplayLevel) HtmlBuilder(com.android.utils.HtmlBuilder) NlModel(com.android.tools.idea.uibuilder.model.NlModel) SuppressLintIntentionAction(com.android.tools.idea.lint.SuppressLintIntentionAction) AndroidLintQuickFix(org.jetbrains.android.inspections.lint.AndroidLintQuickFix) IOException(java.io.IOException) RelativePoint(com.intellij.ui.awt.RelativePoint) SuppressLintIntentionAction(com.android.tools.idea.lint.SuppressLintIntentionAction) IntentionAction(com.intellij.codeInsight.intention.IntentionAction) StringReader(java.io.StringReader) Editor(com.intellij.openapi.editor.Editor)

Aggregations

Issue (com.android.tools.lint.detector.api.Issue)14 NotNull (org.jetbrains.annotations.NotNull)6 Project (com.intellij.openapi.project.Project)5 VirtualFile (com.intellij.openapi.vfs.VirtualFile)5 Module (com.intellij.openapi.module.Module)4 XmlFile (com.intellij.psi.xml.XmlFile)4 File (java.io.File)4 ProblemData (org.jetbrains.android.inspections.lint.ProblemData)4 BuiltinIssueRegistry (com.android.tools.lint.checks.BuiltinIssueRegistry)3 LintDriver (com.android.tools.lint.client.api.LintDriver)3 LintRequest (com.android.tools.lint.client.api.LintRequest)3 AnalysisScope (com.intellij.analysis.AnalysisScope)3 HighlightDisplayLevel (com.intellij.codeHighlighting.HighlightDisplayLevel)3 HighlightDisplayKey (com.intellij.codeInsight.daemon.HighlightDisplayKey)3 PsiElement (com.intellij.psi.PsiElement)3 PsiFile (com.intellij.psi.PsiFile)3 LintIdeClient (com.android.tools.idea.lint.LintIdeClient)2 LintIdeIssueRegistry (com.android.tools.idea.lint.LintIdeIssueRegistry)2 LintIdeRequest (com.android.tools.idea.lint.LintIdeRequest)2 Scope (com.android.tools.lint.detector.api.Scope)2