SSR: separate inspection are now fake

actual inspecting for all fake inspections is done in one main Structural Search inspection

GitOrigin-RevId: 722b44940aadb389e6966a23be56754b36269232
This commit is contained in:
Bas Leijdekkers
2019-12-17 09:02:33 +00:00
committed by intellij-monorepo-bot
parent f4d01ce645
commit d121cf3c9f
9 changed files with 157 additions and 142 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection;
import com.intellij.lang.annotation.ProblemGroup;
@@ -51,4 +51,17 @@ public interface ProblemDescriptor extends CommonProblemDescriptor {
void setProblemGroup(@Nullable ProblemGroup problemGroup);
boolean showTooltip();
/**
* To report this problem as a different inspection.
* Can be used to let one master inspection pretend it is several different fake inspections.
* @return the shortName of the fake inspection
* @see InspectionEP#shortName
*/
@Nullable
default String getFakeInspectionShortName() {
return null;
}
default void setFakeInspectionShortName(String shortName) {}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection;
import com.intellij.lang.annotation.ProblemGroup;
@@ -30,6 +30,7 @@ public class ProblemDescriptorBase extends CommonProblemDescriptorImpl implement
private TextAttributesKey myEnforcedTextAttributes;
private int myLineNumber = -1;
private ProblemGroup myProblemGroup;
private String myFakeInspectionShortName;
@Nullable private final Throwable myCreationTrace;
public ProblemDescriptorBase(@NotNull PsiElement startElement,
@@ -269,4 +270,14 @@ public class ProblemDescriptorBase extends CommonProblemDescriptorImpl implement
PsiElement element = getPsiElement();
return ProblemDescriptorUtil.renderDescriptionMessage(this, element);
}
@Override
public String getFakeInspectionShortName() {
return myFakeInspectionShortName;
}
@Override
public void setFakeInspectionShortName(String shortName) {
myFakeInspectionShortName = shortName;
}
}
@@ -518,6 +518,13 @@ public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass
@NotNull HighlightSeverity severity,
@NotNull ProblemDescriptor descriptor,
@NotNull PsiElement element, boolean ignoreSuppressed) {
final String fakeShortName = descriptor.getFakeInspectionShortName();
if (fakeShortName != null) {
final InspectionToolWrapper<?, ?> fakeTool = myProfileWrapper.getInspectionTool(fakeShortName, null);
assert fakeTool instanceof LocalInspectionToolWrapper;
toolWrapper = (LocalInspectionToolWrapper)fakeTool;
severity = myProfileWrapper.getErrorLevel(HighlightDisplayKey.find(fakeShortName), file).getSeverity();
}
LocalInspectionTool tool = toolWrapper.getTool();
if (ignoreSuppressed && SuppressionUtil.inspectionResultSuppressed(element, tool)) {
registerSuppressedElements(toolWrapper, element);
@@ -30,7 +30,7 @@ public class GotoInspectionModel extends SimpleChooseByNameModel {
public GotoInspectionModel(@NotNull Project project) {
super(project, IdeBundle.message("prompt.goto.inspection.enter.name"), "goto.inspection.help.id");
InspectionProfileImpl rootProfile = InspectionProfileManager.getInstance().getCurrentProfile();
InspectionProfileImpl rootProfile = InspectionProfileManager.getInstance(project).getCurrentProfile();
for (ScopeToolState state : rootProfile.getAllTools()) {
InspectionToolWrapper tool = LocalInspectionToolWrapper.findTool2RunInBatch(project, null, rootProfile, state.getTool());
if (tool != null) {
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 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.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.actions;
@@ -132,7 +118,7 @@ public class RunInspectionIntention implements IntentionAction, HighPriorityActi
public static InspectionProfileImpl createProfile(@NotNull InspectionToolWrapper toolWrapper,
@NotNull InspectionManagerEx managerEx,
@Nullable PsiElement psiElement) {
InspectionProfileImpl rootProfile = InspectionProfileManager.getInstance().getCurrentProfile();
InspectionProfileImpl rootProfile = InspectionProfileManager.getInstance(managerEx.getProject()).getCurrentProfile();
LinkedHashSet<InspectionToolWrapper<?, ?>> allWrappers = new LinkedHashSet<>();
allWrappers.add(toolWrapper);
rootProfile.collectDependentInspections(toolWrapper, allWrappers, managerEx.getProject());
@@ -2,6 +2,8 @@
package com.intellij.structuralsearch.inspection;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.GlobalInspectionContext;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
@@ -9,7 +11,7 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager;
import com.intellij.structuralsearch.inspection.highlightTemplate.SSBasedInspection;
import com.intellij.structuralsearch.inspection.highlightTemplate.StructuralSearchFullInspection;
import com.intellij.structuralsearch.inspection.highlightTemplate.StructuralSearchFakeInspection;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import org.jetbrains.annotations.NotNull;
@@ -18,7 +20,7 @@ import java.util.UUID;
/**
* @author Bas Leijdekkers
*/
public class InspectionProvider implements StartupActivity {
public class InspectionProvider implements StartupActivity.DumbAware {
@Override
public void runActivity(@NotNull Project project) {
@@ -57,13 +59,27 @@ public class InspectionProvider implements StartupActivity {
private static class StructuralSearchInspectionToolWrapper extends LocalInspectionToolWrapper {
StructuralSearchInspectionToolWrapper(Configuration configuration) {
super(new StructuralSearchFullInspection(configuration));
super(new StructuralSearchFakeInspection(configuration.getName(), configuration.getUuid()));
}
private StructuralSearchInspectionToolWrapper(@NotNull LocalInspectionTool tool) {
super(tool);
}
@NotNull
@Override
public LocalInspectionToolWrapper createCopy() {
return this;
return new StructuralSearchInspectionToolWrapper(new StructuralSearchFakeInspection((StructuralSearchFakeInspection)getTool()));
}
@Override
public void initialize(@NotNull GlobalInspectionContext context) {
super.initialize(context);
final InspectionProfileImpl profile = ((GlobalInspectionContextBase)context).getCurrentProfile();
final InspectionToolWrapper<?, ?> tool = profile.getInspectionTool(SSBasedInspection.SHORT_NAME, context.getProject());
assert tool != null;
final SSBasedInspection inspection = (SSBasedInspection)tool.getTool();
inspection.setSessionProfile(profile);
}
}
}
@@ -1,13 +1,16 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.structuralsearch.inspection.highlightTemplate;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.dupLocator.iterators.CountingNodeIterator;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.structuralsearch.MatchResult;
@@ -44,6 +47,7 @@ public class SSBasedInspection extends LocalInspectionTool {
public static final String SHORT_NAME = "SSBasedInspection";
private final List<Configuration> myConfigurations = ContainerUtil.createLockFreeCopyOnWriteList();
final Set<String> myProblemsReported = new HashSet<>(1);
private InspectionProfileImpl mySessionProfile = null;
@Override
public void writeSettings(@NotNull Element node) throws WriteExternalException {
@@ -70,16 +74,31 @@ public class SSBasedInspection extends LocalInspectionTool {
return SHORT_NAME;
}
public void setSessionProfile(InspectionProfileImpl profile) {
mySessionProfile = profile;
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
final Project project = holder.getManager().getProject();
if (myConfigurations.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
if (Registry.is("ssr.separate.inspections")) return PsiElementVisitor.EMPTY_VISITOR;
final List<Configuration> configurations;
final InspectionProfileImpl profile;
if (Registry.is("ssr.separate.inspections")) {
profile = (mySessionProfile != null) ? mySessionProfile : InspectionProfileManager.getInstance(project).getCurrentProfile();
configurations = ContainerUtil.filter(myConfigurations, x -> profile.isToolEnabled(HighlightDisplayKey.find(x.getUuid().toString())));
if (configurations.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
}
else {
profile = null;
configurations = myConfigurations;
}
final Matcher matcher = new Matcher(project);
final Map<Configuration, MatchContext> compiledOptions =
SSBasedInspectionCompiledPatternsCache.getCompiledOptions(myConfigurations, matcher);
SSBasedInspectionCompiledPatternsCache.getCompiledOptions(configurations, matcher);
if (compiledOptions.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
return new PsiElementVisitor() {
@@ -87,9 +106,12 @@ public class SSBasedInspection extends LocalInspectionTool {
final PsiElement element = matchResult.getMatch();
final String name = configuration.getName();
final LocalQuickFix fix = createQuickFix(project, matchResult, configuration);
holder.registerProblem(
holder.getManager().createProblemDescriptor(element, name, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)
);
final ProblemDescriptor descriptor =
holder.getManager().createProblemDescriptor(element, name, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
if (Registry.is("ssr.separate.inspections")) {
descriptor.setFakeInspectionShortName(configuration.getUuid().toString());
}
holder.registerProblem(descriptor);
return true;
};
@@ -103,7 +125,8 @@ public class SSBasedInspection extends LocalInspectionTool {
final MatchContext context = entry.getValue();
if (context == null) continue;
if (Matcher.checkIfShouldAttemptToMatch(context, matchedNodes)) {
if (Matcher.checkIfShouldAttemptToMatch(context, matchedNodes) &&
(profile == null || profile.isToolEnabled(HighlightDisplayKey.find(configuration.getUuid().toString()), element))) {
final int nodeCount = context.getPattern().getNodeCount();
try {
matcher.processMatchesInElement(context, configuration, new CountingNodeIterator(nodeCount, matchedNodes), processor);
@@ -0,0 +1,72 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.structuralsearch.inspection.highlightTemplate;
import com.intellij.codeInspection.LocalInspectionTool;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
/**
* @author Bas Leijdekkers
*/
public class StructuralSearchFakeInspection extends LocalInspectionTool {
private final String myShortName;
private String name = "";
@SuppressWarnings("unused")
public StructuralSearchFakeInspection() {
myShortName = "";
}
public StructuralSearchFakeInspection(String name, UUID uuid) {
this.name = name;
myShortName = uuid.toString();
}
public StructuralSearchFakeInspection(StructuralSearchFakeInspection copy) {
myShortName = copy.myShortName;
name = copy.name;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String getDisplayName() {
return name;
}
@NotNull
@Override
public String getShortName() {
return myShortName;
}
@NotNull
@Override
public String getID() {
return myShortName;
}
@Nullable
@Override
public String getMainToolId() {
return SSBasedInspection.SHORT_NAME;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String[] getGroupPath() {
return new String[]{"General", "Structural Search"};
}
@Nullable
@Override
public String getStaticDescription() {
return "no description provided";
}
}
@@ -1,113 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.structuralsearch.inspection.highlightTemplate;
import com.intellij.codeInspection.*;
import com.intellij.dupLocator.iterators.CountingNodeIterator;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.structuralsearch.MatchResult;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.SSRBundle;
import com.intellij.structuralsearch.StructuralSearchException;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
import com.intellij.structuralsearch.impl.matcher.predicates.ScriptSupport;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.structuralsearch.plugin.ui.UIUtil;
import com.intellij.util.PairProcessor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author Bas Leijdekkers
*/
public class StructuralSearchFullInspection extends LocalInspectionTool {
static final Object LOCK = new Object(); // hack to avoid race conditions in SSR
private final List<Configuration> myConfigurations = ContainerUtil.createLockFreeCopyOnWriteList();
private Map<Configuration, MatchContext> myCompiledOptionsCache = new HashMap<>();
final Set<String> myProblemsReported = new HashSet<>(1);
private final String myName;
private final String myShortName;
public StructuralSearchFullInspection(Configuration configuration) {
myName = configuration.getName();
myShortName = configuration.getUuid().toString();
myConfigurations.add(configuration);
}
@NotNull
@Override
public String getShortName() {
return myShortName;
}
@Nls(capitalization = Nls.Capitalization.Sentence)
@NotNull
@Override
public String[] getGroupPath() {
return new String[]{"General", "Structural Search"};
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) {
final PsiFile file = holder.getFile();
if (!(file.getFileType() instanceof LanguageFileType)) return PsiElementVisitor.EMPTY_VISITOR;
final Project project = holder.getManager().getProject();
if (myConfigurations.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
final Matcher matcher = new Matcher(project);
myCompiledOptionsCache = SSBasedInspectionCompiledPatternsCache.getCompiledOptions(myConfigurations, matcher, myCompiledOptionsCache);
if (myCompiledOptionsCache.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
return new PsiElementVisitor() {
final PairProcessor<MatchResult, Configuration> processor = (matchResult, configuration) -> {
final PsiElement element = matchResult.getMatch();
final String name = configuration.getName();
final LocalQuickFix fix = SSBasedInspection.createQuickFix(project, matchResult, configuration);
holder.registerProblem(
holder.getManager().createProblemDescriptor(element, name, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)
);
return true;
};
@Override
public void visitElement(@NotNull PsiElement element) {
if (LexicalNodesFilter.getInstance().accepts(element)) return;
synchronized (LOCK) {
final SsrFilteringNodeIterator matchedNodes = new SsrFilteringNodeIterator(element);
for (Map.Entry<Configuration, MatchContext> entry : myCompiledOptionsCache.entrySet()) {
final Configuration configuration = entry.getKey();
final MatchContext context = entry.getValue();
if (context == null) continue;
if (Matcher.checkIfShouldAttemptToMatch(context, matchedNodes)) {
final int nodeCount = context.getPattern().getNodeCount();
try {
matcher.processMatchesInElement(context, configuration, new CountingNodeIterator(nodeCount, matchedNodes), processor);
}
catch (StructuralSearchException e) {
if (myProblemsReported.add(configuration.getName())) { // don't overwhelm the user with messages
final String message = e.getMessage().replace(ScriptSupport.UUID, "");
UIUtil.SSR_NOTIFICATION_GROUP.createNotification(NotificationType.ERROR)
.setContent(SSRBundle.message("inspection.script.problem", message, configuration.getName()))
.setImportant(true)
.notify(element.getProject());
}
}
matchedNodes.reset();
}
}
}
}
};
}
}