提交 b9adb329 编写于 作者: C caikang.ck

fix Inconsistency results in different scans

上级 2ca345bf
......@@ -17,7 +17,6 @@ package com.alibaba.p3c.idea.action
import com.alibaba.p3c.idea.compatible.inspection.InspectionProfileService
import com.alibaba.p3c.idea.compatible.inspection.Inspections
import com.alibaba.p3c.idea.ep.InspectionActionExtensionPoint
import com.alibaba.p3c.idea.i18n.P3cBundle
import com.alibaba.p3c.idea.inspection.AliBaseInspection
import com.alibaba.p3c.idea.util.NumberConstants
......@@ -26,10 +25,10 @@ import com.intellij.analysis.AnalysisScope
import com.intellij.analysis.AnalysisUIOptions
import com.intellij.analysis.BaseAnalysisActionDialog
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.InspectionsBundle
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl
import com.intellij.codeInspection.ex.InspectionManagerEx
import com.intellij.codeInspection.ex.InspectionToolWrapper
import com.intellij.codeInspection.ui.InspectionResultsView
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
......@@ -38,9 +37,13 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.wm.ToolWindowId
import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileSystemItem
import com.intellij.psi.PsiManager
import java.awt.event.KeyEvent
......@@ -97,8 +100,10 @@ class AliInspectionAction : AnAction() {
val element = psiFile ?: psiElement
analysisScope.isIncludeTestSource = false
analysisScope.setSearchInLibraries(true)
createContext(toolWrappers, managerEx, element,
projectDir).doInspections(analysisScope)
createContext(
toolWrappers, managerEx, element,
projectDir, analysisScope
).doInspections(analysisScope)
}
private fun isBaseDir(file: VirtualFile, project: Project): Boolean {
......@@ -108,9 +113,11 @@ class AliInspectionAction : AnAction() {
return project.basePath == file.canonicalPath
}
private fun inspectForKeyEvent(project: Project, managerEx: InspectionManagerEx,
toolWrappers: List<InspectionToolWrapper<*, *>>, psiElement: PsiElement?, psiFile: PsiFile?,
virtualFile: VirtualFile?, analysisScope: AnalysisScope) {
private fun inspectForKeyEvent(
project: Project, managerEx: InspectionManagerEx,
toolWrappers: List<InspectionToolWrapper<*, *>>, psiElement: PsiElement?, psiFile: PsiFile?,
virtualFile: VirtualFile?, analysisScope: AnalysisScope
) {
var module: Module? = null
if (virtualFile != null && project.baseDir != virtualFile) {
module = ModuleUtilCore.findModuleForFile(virtualFile, project)
......@@ -118,8 +125,10 @@ class AliInspectionAction : AnAction() {
val uiOptions = AnalysisUIOptions.getInstance(project)
uiOptions.ANALYZE_TEST_SOURCES = false
val dialog = BaseAnalysisActionDialog("Select Analyze Scope", "Analyze Scope", project, analysisScope,
if (module != null) module.name else null, true, uiOptions, psiElement)
val dialog = BaseAnalysisActionDialog(
"Select Analyze Scope", "Analyze Scope", project, analysisScope,
module?.name, true, uiOptions, psiElement
)
if (!dialog.showAndGet()) {
return
......@@ -127,8 +136,10 @@ class AliInspectionAction : AnAction() {
val scope = dialog.getScope(uiOptions, analysisScope, project, module)
scope.setSearchInLibraries(true)
val element = psiFile ?: psiElement
createContext(toolWrappers, managerEx, element,
dialog.isProjectScopeSelected).doInspections(scope)
createContext(
toolWrappers, managerEx, element,
dialog.isProjectScopeSelected, scope
).doInspections(scope)
}
override fun update(e: AnActionEvent) {
......@@ -136,58 +147,63 @@ class AliInspectionAction : AnAction() {
}
companion object {
val logger = Logger.getInstance(AliInspectionAction::class.java)
private val logger = Logger.getInstance(AliInspectionAction::class.java)
fun createContext(toolWrapperList: List<InspectionToolWrapper<*, *>>,
managerEx: InspectionManagerEx, psiElement: PsiElement?, projectScopeSelected: Boolean):
GlobalInspectionContextImpl {
private fun getTitle(element: PsiElement?, isProjectScopeSelected: Boolean): String? {
if (element == null) {
return null
}
if (isProjectScopeSelected) {
return "Project"
}
if (element is PsiFileSystemItem) {
return VfsUtilCore.getRelativePath(element.virtualFile, element.project.baseDir)
}
return null
}
fun createContext(
toolWrapperList: List<InspectionToolWrapper<*, *>>,
managerEx: InspectionManagerEx, psiElement: PsiElement?,
projectScopeSelected: Boolean, scope: AnalysisScope
):
GlobalInspectionContextImpl {
// remove last same scope content
val project = managerEx.project
val title = getTitle(psiElement, projectScopeSelected)
val model = InspectionProfileService.createSimpleProfile(toolWrapperList, managerEx, psiElement)
title?.let {
model.name = it
}
val inspectionContext = createNewGlobalContext(
managerEx, projectScopeSelected)
managerEx, projectScopeSelected
)
InspectionProfileService.setExternalProfile(model, inspectionContext)
return inspectionContext
}
private fun createNewGlobalContext(managerEx: InspectionManagerEx,
projectScopeSelected: Boolean): GlobalInspectionContextImpl {
return object : GlobalInspectionContextImpl(managerEx.project, managerEx.contentManager) {
override fun runTools(scope: AnalysisScope, runGlobalToolsOnly: Boolean,
isOfflineInspections: Boolean) {
super.runTools(scope, runGlobalToolsOnly, isOfflineInspections)
if (myProgressIndicator.isCanceled) {
return
}
InspectionActionExtensionPoint.extension.extensions.forEach {
try {
it.doOnInspectionFinished(this, projectScopeSelected)
} catch(e: Exception) {
logger.warn(e)
}
}
}
val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.INSPECTION)
override fun close(noSuspiciousCodeFound: Boolean) {
super.close(noSuspiciousCodeFound)
InspectionActionExtensionPoint.extension.extensions.forEach {
try {
it.doOnClose(noSuspiciousCodeFound, project)
} catch(e: Exception) {
logger.warn(e)
}
}
if (toolWindow != null) {
val contentManager = toolWindow.contentManager
val contentTitle = title?.let {
InspectionsBundle.message("inspection.results.for.profile.toolwindow.title", it, scope.shortenName)
}
override fun addView(view: InspectionResultsView) {
super.addView(view)
InspectionActionExtensionPoint.extension.extensions.forEach {
try {
it.doOnView(view)
} catch(e: Exception) {
logger.warn(e)
}
}
val content = contentManager.contents.firstOrNull {
contentTitle != null && (it.tabName == contentTitle || it.tabName.endsWith(contentTitle))
}
content?.let {
contentManager.removeContent(content, true)
}
}
return inspectionContext
}
private fun createNewGlobalContext(
managerEx: InspectionManagerEx,
projectScopeSelected: Boolean
): GlobalInspectionContextImpl {
return PmdGlobalInspectionContextImpl(managerEx.project, managerEx.contentManager, projectScopeSelected)
}
}
}
package com.alibaba.p3c.idea.action
import com.alibaba.p3c.idea.component.AliProjectComponent
import com.alibaba.p3c.idea.ep.InspectionActionExtensionPoint
import com.alibaba.p3c.idea.inspection.AliLocalInspectionToolProvider
import com.alibaba.p3c.idea.inspection.PmdRuleInspectionIdentify
import com.alibaba.p3c.idea.pmd.AliPmdProcessor
import com.intellij.analysis.AnalysisScope
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl
import com.intellij.codeInspection.ui.InspectionResultsView
import com.intellij.concurrency.JobLauncher
import com.intellij.concurrency.JobLauncherImpl
import com.intellij.concurrency.SensitiveProgressWrapper
import com.intellij.diagnostic.ThreadDumper
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressIndicatorProvider
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectUtilCore
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.EmptyRunnable
import com.intellij.openapi.util.NotNullLazyValue
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.util.PsiUtilCore
import com.intellij.ui.content.ContentManager
import com.intellij.util.ExceptionUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.Processor
import com.intellij.util.ReflectionUtil
import com.intellij.util.containers.ContainerUtil
import gnu.trove.THashSet
import net.sourceforge.pmd.RuleViolation
import java.util.Queue
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import java.util.concurrent.Future
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit.SECONDS
/**
* @date 2020/06/19
* @author caikang
*/
class PmdGlobalInspectionContextImpl(
project: Project,
contentManager: NotNullLazyValue<ContentManager>,
private val projectScopeSelected: Boolean
) :
GlobalInspectionContextImpl(project, contentManager) {
private val logger = Logger.getInstance(PmdGlobalInspectionContextImpl::class.java)
override fun runTools(scope: AnalysisScope, runGlobalToolsOnly: Boolean, isOfflineInspections: Boolean) {
val usedTools = usedTools
val hasPmdTools = usedTools.any {
it.tool.tool is PmdRuleInspectionIdentify
}
if (hasPmdTools) {
val progressIndicator =
ProgressIndicatorProvider.getGlobalProgressIndicator()
?: throw IncorrectOperationException("Must be run under progress")
pmdNodeWarmUp(scope, progressIndicator, isOfflineInspections)
}
super.runTools(scope, runGlobalToolsOnly, isOfflineInspections)
if (myProgressIndicator.isCanceled) {
return
}
InspectionActionExtensionPoint.extension.extensions.forEach {
try {
it.doOnInspectionFinished(this, projectScopeSelected)
} catch (e: Exception) {
logger.warn(e)
}
}
}
private fun pmdNodeWarmUp(
scope: AnalysisScope,
progressIndicator: ProgressIndicator,
isOfflineInspections: Boolean
) {
val aliProjectComponent = project.getComponent(AliProjectComponent::class.java)
// run pmd inspection
val processor = Processor { file: PsiFile ->
ProgressManager.checkCanceled()
val readActionSuccess =
DumbService.getInstance(project).tryRunReadActionInSmartMode(
{
if (!file.isValid) {
return@tryRunReadActionInSmartMode true
}
val virtualFile = file.virtualFile
if (!scope.contains(virtualFile)) {
logger.info(file.name + "; scope: " + scope + "; " + virtualFile)
return@tryRunReadActionInSmartMode true
}
val path = virtualFile.canonicalPath?.toLowerCase() ?: ""
if (!path.endsWith(".java") && !path.endsWith(".vm")) {
return@tryRunReadActionInSmartMode true
}
doPmdProcess(file, aliProjectComponent, virtualFile)
true
}, "Inspect code is not available until indices are ready"
)
if (readActionSuccess == null || !readActionSuccess) {
throw ProcessCanceledException()
}
true
}
val headlessEnvironment = ApplicationManager.getApplication().isHeadlessEnvironment
val searchScope =
ApplicationManager.getApplication().runReadAction<SearchScope, RuntimeException> { scope.toSearchScope() }
val localScopeFiles: MutableSet<VirtualFile>? = if (searchScope is LocalSearchScope) THashSet() else null
val filesToInspect: BlockingQueue<PsiFile> = ArrayBlockingQueue(1000)
val iteratingIndicator: ProgressIndicator = SensitiveProgressWrapper(progressIndicator)
val startIterateScopeInBackground = ReflectionUtil.getDeclaredMethod(
javaClass.superclass,
"startIterateScopeInBackground",
AnalysisScope::class.java,
Collection::class.java,
headlessEnvironment.javaClass,
BlockingQueue::class.java,
ProgressIndicator::class.java
)
requireNotNull(startIterateScopeInBackground) {
"method GlobalInspectionContextImpl.startIterateScopeInBackground not found in this IDEA version"
}
val future: Future<*> = startIterateScopeInBackground.invoke(
this,
scope,
localScopeFiles,
headlessEnvironment,
filesToInspect,
iteratingIndicator
) as Future<*>
val dependentIndicators =
ReflectionUtil.getField(javaClass, this, List::class.java, "dependentIndicators")?.map {
it as ProgressIndicator
}?.toMutableList()
try {
val filesFailedToInspect: Queue<PsiFile> = LinkedBlockingQueue()
while (true) {
val disposable = Disposer.newDisposable()
val wrapper: ProgressIndicator = DaemonProgressIndicator()
dependentIndicators?.let {
it.add(wrapper)
}
try {
// avoid "attach listener"/"write action" race
ApplicationManager.getApplication().runReadAction {
wrapper.start()
ProgressIndicatorUtils.forceWriteActionPriority(wrapper, disposable)
// there is a chance we are racing with write action, in which case just registered listener might not be called, retry.
if (ApplicationManagerEx.getApplicationEx().isWriteActionPending) {
throw ProcessCanceledException()
}
}
// use wrapper here to cancel early when write action start but do not affect the original indicator
(JobLauncher.getInstance() as JobLauncherImpl).processQueue(
filesToInspect,
filesFailedToInspect,
wrapper,
PsiUtilCore.NULL_PSI_FILE,
processor
)
break
} catch (e: ProcessCanceledException) {
progressIndicator.checkCanceled()
assert(
isOfflineInspections || !ApplicationManager.getApplication().isReadAccessAllowed
) {
"""
Must be outside read action. PCE=
${ExceptionUtil.getThrowableText(e)}
""".trimIndent()
}
assert(
isOfflineInspections || !ApplicationManager.getApplication().isDispatchThread
) {
"""
Must be outside EDT. PCE=
${ExceptionUtil.getThrowableText(e)}
""".trimIndent()
}
// wait for write action to complete
ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance())
} finally {
dependentIndicators?.let {
it.remove(wrapper)
}
Disposer.dispose(disposable)
}
}
} finally {
iteratingIndicator.cancel() // tell file scanning thread to stop
filesToInspect.clear() // let file scanning thread a chance to put TOMBSTONE and complete
try {
future[30, SECONDS]
} catch (e: java.lang.Exception) {
logger.error(
"""
Thread dump:
${ThreadDumper.dumpThreadsToString()}
""".trimIndent(), e
)
}
}
ProgressManager.checkCanceled()
}
private fun doPmdProcess(
file: PsiFile,
aliProjectComponent: AliProjectComponent,
virtualFile: VirtualFile
) {
val url: String = ProjectUtilCore.displayUrlRelativeToProject(
virtualFile,
virtualFile.presentableUrl,
project,
true,
false
)
myProgressIndicator.text = "PMD Process in $url"
val violations =
AliPmdProcessor(AliLocalInspectionToolProvider.getRuleSets()).processFile(
file,
false
)
val fileContext = aliProjectComponent.getFileContext(virtualFile)
fileContext?.let { fc ->
val ruleViolations = ContainerUtil.createConcurrentSoftValueMap<String, List<RuleViolation>>()
for (entry in violations.groupBy {
it.rule.name
}) {
ruleViolations[entry.key] = entry.value
}
fc.ruleViolations = ruleViolations
}
}
override fun close(noSuspiciousCodeFound: Boolean) {
super.close(noSuspiciousCodeFound)
InspectionActionExtensionPoint.extension.extensions.forEach {
try {
it.doOnClose(noSuspiciousCodeFound, project)
} catch (e: Exception) {
logger.warn(e)
}
}
}
override fun addView(view: InspectionResultsView) {
super.addView(view)
InspectionActionExtensionPoint.extension.extensions.forEach {
try {
it.doOnView(view)
} catch (e: Exception) {
logger.warn(e)
}
}
}
}
\ No newline at end of file
......@@ -20,15 +20,21 @@ import com.alibaba.p3c.idea.config.P3cConfig
import com.alibaba.p3c.idea.i18n.P3cBundle
import com.alibaba.p3c.idea.inspection.AliPmdInspectionInvoker
import com.alibaba.p3c.idea.pmd.SourceCodeProcessor
import com.alibaba.p3c.idea.util.withLockNotInline
import com.alibaba.p3c.pmd.I18nResources
import com.alibaba.smartfox.idea.common.component.AliBaseProjectComponent
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileAdapter
import com.intellij.openapi.vfs.VirtualFileEvent
import com.intellij.openapi.vfs.VirtualFileListener
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.VirtualFileMoveEvent
import com.intellij.psi.PsiManager
import net.sourceforge.pmd.RuleViolation
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantReadWriteLock
/**
* @author caikang
......@@ -42,13 +48,16 @@ class AliProjectComponent(
private val javaExtension = ".java"
private val velocityExtension = ".vm"
private val lock = ReentrantReadWriteLock()
private val readLock = lock.readLock()
private val writeLock = lock.writeLock()
private val fileContexts = ConcurrentHashMap<String, FileContext>()
init {
listener = object : VirtualFileAdapter() {
override fun contentsChanged(event: VirtualFileEvent) {
val path = event.file.canonicalPath
if (path == null || !(path.endsWith(javaExtension) || path.endsWith(velocityExtension))) {
return
}
val path = getFilePath(event) ?: return
PsiManager.getInstance(project).findFile(event.file) ?: return
if (!p3cConfig.ruleCacheEnable) {
AliPmdInspectionInvoker.refreshFileViolationsCache(event.file)
......@@ -56,7 +65,34 @@ class AliProjectComponent(
if (!p3cConfig.astCacheEnable) {
SourceCodeProcessor.invalidateCache(path)
}
SourceCodeProcessor.invalidUserTrigger(path)
fileContexts[path]?.ruleViolations = null
}
override fun fileDeleted(event: VirtualFileEvent) {
val path = getFilePath(event)
path?.let {
SourceCodeProcessor.invalidateCache(it)
removeFileContext(it)
}
super.fileDeleted(event)
}
override fun fileMoved(event: VirtualFileMoveEvent) {
val path = getFilePath(event)
path?.let {
SourceCodeProcessor.invalidateCache(it)
removeFileContext(it)
}
super.fileMoved(event)
}
private fun getFilePath(event: VirtualFileEvent): String? {
val path = event.file.canonicalPath
if (path == null || !(path.endsWith(javaExtension) || path.endsWith(velocityExtension))) {
return null
}
return path
}
}
}
......@@ -80,4 +116,35 @@ class AliProjectComponent(
val analyticsGroupId = "com.alibaba.p3c.analytics.action_group"
val analyticsGroupText = "$analyticsGroupId.text"
}
data class FileContext(
val lock: ReentrantReadWriteLock,
var ruleViolations: Map<String, List<RuleViolation>>? = null
)
fun removeFileContext(path: String) {
fileContexts.remove(path)
}
fun getFileContext(virtualFile: VirtualFile?): FileContext? {
val file = virtualFile?.canonicalPath ?: return null
val result = readLock.withLockNotInline {
fileContexts[file]
}
if (result != null) {
return result
}
return writeLock.withLockNotInline {
val finalContext = fileContexts[file]
if (finalContext != null) {
return@withLockNotInline finalContext
}
val lock = ReentrantReadWriteLock()
FileContext(
lock = lock
).also {
fileContexts[file] = it
}
}
}
}
......@@ -38,8 +38,10 @@ import javassist.ClassPool
import javassist.CtField
import javassist.NotFoundException
import net.sourceforge.pmd.Rule
import net.sourceforge.pmd.RuleSet
import net.sourceforge.pmd.RuleSetFactory
import net.sourceforge.pmd.RuleSetNotFoundException
import net.sourceforge.pmd.RuleSets
import javax.annotation.Generated
/**
......@@ -70,15 +72,15 @@ class AliLocalInspectionToolProvider : InspectionToolProvider {
val ruleNames: MutableList<String> = Lists.newArrayList<String>()!!
private val CLASS_LIST = Lists.newArrayList<Class<LocalInspectionTool>>()
private val nativeInspectionToolClass = arrayListOf<Class<out LocalInspectionTool>>(
AliMissingOverrideAnnotationInspection::class.java,
AliAccessStaticViaInstanceInspection::class.java,
AliDeprecationInspection::class.java,
MapOrSetKeyShouldOverrideHashCodeEqualsInspection::class.java,
AliArrayNamingShouldHaveBracketInspection::class.java,
AliControlFlowStatementWithoutBracesInspection::class.java,
AliEqualsAvoidNullInspection::class.java,
AliLongLiteralsEndingWithLowercaseLInspection::class.java,
AliWrapperTypeEqualityInspection::class.java
AliMissingOverrideAnnotationInspection::class.java,
AliAccessStaticViaInstanceInspection::class.java,
AliDeprecationInspection::class.java,
MapOrSetKeyShouldOverrideHashCodeEqualsInspection::class.java,
AliArrayNamingShouldHaveBracketInspection::class.java,
AliControlFlowStatementWithoutBracesInspection::class.java,
AliEqualsAvoidNullInspection::class.java,
AliLongLiteralsEndingWithLowercaseLInspection::class.java,
AliWrapperTypeEqualityInspection::class.java
)
val javaShouldInspectChecker = object : ShouldInspectChecker {
override fun shouldInspect(file: PsiFile): Boolean {
......@@ -104,9 +106,9 @@ class AliLocalInspectionToolProvider : InspectionToolProvider {
val virtualFile = file.virtualFile
val index = ProjectRootManager.getInstance(file.project).fileIndex
return index.isInSource(virtualFile)
&& !index.isInTestSourceContent(virtualFile)
&& !index.isInLibraryClasses(virtualFile)
&& !index.isInLibrarySource(virtualFile)
&& !index.isInTestSourceContent(virtualFile)
&& !index.isInLibraryClasses(virtualFile)
&& !index.isInLibrarySource(virtualFile)
}
}
......@@ -173,11 +175,27 @@ class AliLocalInspectionToolProvider : InspectionToolProvider {
return result
}
private fun processForRuleSet(ruleSetName: String, shouldInspectChecker: ShouldInspectChecker): List<RuleInfo> {
fun getRuleSet(ruleSetName: String): RuleSet {
val factory = RuleSetFactory()
return factory.createRuleSet(ruleSetName.replace("/", "-"))
}
fun getRuleSetList(): List<RuleSet> {
return listOf(getRuleSet("java/ali-pmd"), getRuleSet("vm/ali-other"))
}
fun getRuleSets(): RuleSets {
return RuleSets().also { rs ->
for (ruleSet in getRuleSetList()) {
rs.addRuleSet(ruleSet)
}
}
}
private fun processForRuleSet(ruleSetName: String, shouldInspectChecker: ShouldInspectChecker): List<RuleInfo> {
val result = Lists.newArrayList<RuleInfo>()
try {
val ruleSet = factory.createRuleSet(ruleSetName.replace("/", "-"))
val ruleSet = getRuleSet(ruleSetName)
ruleSet.rules.mapTo(result) {
RuleInfo(it, shouldInspectChecker)
}
......
......@@ -15,6 +15,7 @@
*/
package com.alibaba.p3c.idea.inspection
import com.alibaba.p3c.idea.component.AliProjectComponent
import com.alibaba.p3c.idea.config.P3cConfig
import com.alibaba.p3c.idea.pmd.AliPmdProcessor
import com.alibaba.p3c.idea.util.DocumentUtils.calculateLineStart
......@@ -42,21 +43,30 @@ import java.util.concurrent.TimeUnit
* @date 2016/12/13
*/
class AliPmdInspectionInvoker(
private val psiFile: PsiFile,
private val manager: InspectionManager,
private val rule: Rule
private val psiFile: PsiFile,
private val manager: InspectionManager,
private val rule: Rule
) {
private val logger = Logger.getInstance(javaClass)
private var violations: List<RuleViolation> = emptyList()
fun doInvoke() {
fun doInvoke(isOnTheFly: Boolean) {
Thread.currentThread().contextClassLoader = javaClass.classLoader
val processor = AliPmdProcessor(rule)
val start = System.currentTimeMillis()
violations = processor.processFile(psiFile)
logger.debug("elapsed ${System.currentTimeMillis() - start}ms to" +
" to apply rule ${rule.name} for file ${psiFile.virtualFile.canonicalPath}")
val aliProjectComponent = manager.project.getComponent(AliProjectComponent::class.java)
val fileContext = aliProjectComponent.getFileContext(psiFile.virtualFile)
val ruleViolations = fileContext?.ruleViolations
violations = if (isOnTheFly || ruleViolations == null) {
processor.processFile(psiFile, isOnTheFly)
} else {
ruleViolations[rule.name] ?: emptyList()
}
logger.debug(
"elapsed ${System.currentTimeMillis() - start}ms to" +
" to apply rule ${rule.name} for file ${psiFile.virtualFile.canonicalPath}"
)
}
fun getRuleProblems(isOnTheFly: Boolean): Array<ProblemDescriptor>? {
......@@ -70,19 +80,25 @@ class AliPmdInspectionInvoker(
val document = FileDocumentManager.getInstance().getDocument(virtualFile) ?: continue
val offsets = if (rv.rule.name == RemoveCommentedCodeRule::class.java.simpleName) {
Offsets(calculateLineStart(document, rv.beginLine),
calculateLineStart(document, rv.endLine + 1) - 1)
Offsets(
calculateLineStart(document, rv.beginLine),
calculateLineStart(document, rv.endLine + 1) - 1
)
} else {
Offsets(calculateRealOffset(document, rv.beginLine, rv.beginColumn),
calculateRealOffset(document, rv.endLine, rv.endColumn))
Offsets(
calculateRealOffset(document, rv.beginLine, rv.beginColumn),
calculateRealOffset(document, rv.endLine, rv.endColumn)
)
}
val errorMessage = if (isOnTheFly) {
rv.description
} else {
"${rv.description} (line ${rv.beginLine})"
}
val problemDescriptor = ProblemsUtils.createProblemDescriptorForPmdRule(psiFile, manager,
isOnTheFly, rv.rule.name, errorMessage, offsets.start, offsets.end, rv.beginLine) ?: continue
val problemDescriptor = ProblemsUtils.createProblemDescriptorForPmdRule(
psiFile, manager,
isOnTheFly, rv.rule.name, errorMessage, offsets.start, offsets.end, rv.beginLine
) ?: continue
problemDescriptors.add(problemDescriptor)
}
return problemDescriptors.toTypedArray()
......@@ -90,51 +106,54 @@ class AliPmdInspectionInvoker(
companion object {
private lateinit var invokers: Cache<FileRule, AliPmdInspectionInvoker>
val smartFoxConfig = ServiceManager.getService(P3cConfig::class.java)!!
init {
reInitInvokers(smartFoxConfig.ruleCacheTime)
}
fun invokeInspection(psiFile: PsiFile?, manager: InspectionManager, rule: Rule,
isOnTheFly: Boolean): Array<ProblemDescriptor>? {
fun invokeInspection(
psiFile: PsiFile?, manager: InspectionManager, rule: Rule,
isOnTheFly: Boolean
): Array<ProblemDescriptor>? {
if (psiFile == null) {
return null
}
val virtualFile = psiFile.virtualFile ?: return null
if (!smartFoxConfig.ruleCacheEnable) {
val invoker = AliPmdInspectionInvoker(psiFile, manager, rule)
invoker.doInvoke()
invoker.doInvoke(isOnTheFly)
return invoker.getRuleProblems(isOnTheFly)
}
var invoker = invokers.getIfPresent(FileRule(virtualFile.canonicalPath!!, rule.name))
if (invoker == null) {
synchronized(virtualFile) {
invoker = invokers.getIfPresent(virtualFile.canonicalPath)
invoker = invokers.getIfPresent(virtualFile.canonicalPath!!)
if (invoker == null) {
invoker = AliPmdInspectionInvoker(psiFile, manager, rule)
invoker!!.doInvoke()
invokers.put(FileRule(virtualFile.canonicalPath!!, rule.name), invoker)
invoker!!.doInvoke(isOnTheFly)
invokers.put(FileRule(virtualFile.canonicalPath!!, rule.name), invoker!!)
}
}
}
return invoker!!.getRuleProblems(isOnTheFly)
}
private fun doInvokeIfPresent(filePath: String, rule: String) {
invokers.getIfPresent(FileRule(filePath, rule))?.doInvoke()
private fun doInvokeIfPresent(filePath: String, rule: String, isOnTheFly: Boolean) {
invokers.getIfPresent(FileRule(filePath, rule))?.doInvoke(isOnTheFly)
}
fun refreshFileViolationsCache(file: VirtualFile) {
AliLocalInspectionToolProvider.ruleNames.forEach {
doInvokeIfPresent(file.canonicalPath!!, it)
doInvokeIfPresent(file.canonicalPath!!, it, false)
}
}
fun reInitInvokers(expireTime: Long) {
invokers = CacheBuilder.newBuilder().maximumSize(500).expireAfterWrite(expireTime,
TimeUnit.MILLISECONDS).build<FileRule, AliPmdInspectionInvoker>()!!
invokers = CacheBuilder.newBuilder().maximumSize(500).expireAfterWrite(
expireTime,
TimeUnit.MILLISECONDS
).build<FileRule, AliPmdInspectionInvoker>()!!
}
}
}
......
......@@ -15,10 +15,12 @@
*/
package com.alibaba.p3c.idea.pmd
import com.alibaba.p3c.idea.component.AliProjectComponent
import com.google.common.base.Throwables
import com.intellij.openapi.application.ex.ApplicationUtil
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.psi.PsiFile
import net.sourceforge.pmd.PMDConfiguration
import net.sourceforge.pmd.PMDException
......@@ -32,12 +34,16 @@ import net.sourceforge.pmd.RulesetsFactoryUtils
import net.sourceforge.pmd.util.ResourceLoader
import java.io.IOException
import java.io.StringReader
import net.sourceforge.pmd.SourceCodeProcessor as PmdSourceCodeProcessor
/**
* @author caikang
* @date 2016/12/11
*/
class AliPmdProcessor(val rule: Rule) {
class AliPmdProcessor private constructor(val rule: Rule? = null, val ruleSets: RuleSets? = null) {
constructor(rule: Rule) : this(rule, null)
constructor(ruleSets: RuleSets) : this(null, ruleSets)
private val ruleSetFactory: RuleSetFactory
private val configuration = PMDConfiguration()
......@@ -45,30 +51,42 @@ class AliPmdProcessor(val rule: Rule) {
ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, ResourceLoader())
}
fun processFile(psiFile: PsiFile): List<RuleViolation> {
fun processFile(psiFile: PsiFile, isOnTheFly: Boolean): List<RuleViolation> {
configuration.setSourceEncoding(psiFile.virtualFile.charset.name())
configuration.inputPaths = psiFile.virtualFile.canonicalPath
val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile) ?: return emptyList()
if (document.lineCount > 10000) {
return emptyList()
}
val project = psiFile.project
val aliProjectComponent = project.getComponent(AliProjectComponent::class.java)
val fileContext = aliProjectComponent.getFileContext(psiFile.virtualFile) ?: return emptyList()
val ctx = RuleContext()
val processor = SourceCodeProcessor(configuration)
val niceFileName = psiFile.virtualFile.canonicalPath!!
val report = Report.createReport(ctx, niceFileName)
val ruleSets = RuleSets()
val processRuleSets = ruleSets ?: RuleSets().also { rs ->
val ruleSet = ruleSetFactory.createSingleRuleRuleSet(rule)
rs.addRuleSet(ruleSet)
}
val ruleSet = ruleSetFactory.createSingleRuleRuleSet(rule)
ruleSets.addRuleSet(ruleSet)
LOG.debug("Processing " + ctx.sourceCodeFilename)
try {
val reader = StringReader(document.text)
ctx.languageVersion = null
processor.processSourceCode(StringReader(document.text), ruleSets, ctx)
if (isOnTheFly) {
SourceCodeProcessor(configuration, document, fileContext, isOnTheFly).processSourceCode(
reader,
processRuleSets,
ctx
)
} else {
PmdSourceCodeProcessor(configuration).processSourceCode(reader, processRuleSets, ctx)
}
} catch (pmde: PMDException) {
LOG.debug("Error while processing file: $niceFileName", pmde.cause)
report.addError(Report.ProcessingError(pmde, niceFileName))
} catch (ioe: IOException) {
LOG.error("Unable to read source file: $niceFileName", ioe)
} catch (pce: ProcessCanceledException) {
throw pce
} catch (re: RuntimeException) {
val root = Throwables.getRootCause(re)
if (root !is ApplicationUtil.CannotRunReadActionException) {
......
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package com.alibaba.p3c.idea.pmd
import com.alibaba.p3c.idea.component.AliProjectComponent.FileContext
import com.alibaba.p3c.idea.config.P3cConfig
import com.alibaba.p3c.idea.util.withLockNotInline
import com.alibaba.p3c.idea.util.withTryLock
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Document
import net.sourceforge.pmd.PMD
import net.sourceforge.pmd.PMDConfiguration
import net.sourceforge.pmd.PMDException
......@@ -20,39 +26,16 @@ import net.sourceforge.pmd.lang.LanguageVersionHandler
import net.sourceforge.pmd.lang.Parser
import net.sourceforge.pmd.lang.ast.Node
import net.sourceforge.pmd.lang.ast.ParseException
import net.sourceforge.pmd.lang.xpath.Initializer
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.Reader
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.MILLISECONDS
class SourceCodeProcessor(private val configuration: PMDConfiguration) {
/**
* Processes the input stream against a rule set using the given input
* encoding.
*
* @param sourceCode
* The InputStream to analyze.
* @param ruleSets
* The collection of rules to process against the file.
* @param ctx
* The context in which PMD is operating.
* @throws PMDException
* if the input encoding is unsupported, the input stream could
* not be parsed, or other error is encountered.
* @see .processSourceCode
*/
@Throws(PMDException::class)
fun processSourceCode(sourceCode: InputStream, ruleSets: RuleSets, ctx: RuleContext) {
try {
InputStreamReader(sourceCode, configuration.sourceEncoding).use { streamReader -> processSourceCode(streamReader, ruleSets, ctx) }
} catch (e: IOException) {
throw PMDException("IO exception: " + e.message, e)
}
}
class SourceCodeProcessor(
private val configuration: PMDConfiguration,
private val document: Document,
private val fileContext: FileContext,
private val isOnTheFly: Boolean
) {
/**
* Processes the input stream against a rule set using the given input
......@@ -77,10 +60,6 @@ class SourceCodeProcessor(private val configuration: PMDConfiguration) {
@Throws(PMDException::class)
fun processSourceCode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) {
determineLanguage(ctx)
// make sure custom XPath functions are initialized
Initializer.initialize()
try {
ruleSets.start(ctx)
processSource(sourceCode, ruleSets, ctx)
......@@ -103,15 +82,60 @@ class SourceCodeProcessor(private val configuration: PMDConfiguration) {
}
}
private fun getRootNode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext): Node {
private fun getRootNode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext): Node? {
if (!smartFoxConfig.astCacheEnable) {
return parseNode(ctx, ruleSets, sourceCode)
}
val node = nodeCache.getIfPresent(ctx.sourceCodeFilename)
val node = getNode(ctx.sourceCodeFilename, isOnTheFly)
if (node != null) {
return node
}
return parseNode(ctx, ruleSets, sourceCode)
if (document.lineCount > 3000 && isOnTheFly) {
return null
}
val lock = fileContext.lock
val readLock = lock.readLock()
val writeLock = lock.writeLock()
val ruleName = ruleSets.allRules.joinToString(",") { it.name }
val fileName = ctx.sourceCodeFilename
val readAction = {
getNode(ctx.sourceCodeFilename, isOnTheFly)
}
val cacheNode = if (isOnTheFly) {
readLock.withTryLock(50, MILLISECONDS, readAction)
} else {
val start = System.currentTimeMillis()
LOG.info("rule:$ruleName,file:$fileName require read lock")
readLock.withLockNotInline(readAction).also {
LOG.info("rule:$ruleName,file:$fileName get result $it with read lock ,elapsed ${System.currentTimeMillis() - start}")
}
}
if (cacheNode != null) {
return cacheNode
}
val writeAction = {
val finalNode = getNode(ctx.sourceCodeFilename, isOnTheFly)
if (finalNode == null) {
val start = System.currentTimeMillis()
if (!isOnTheFly) {
LOG.info("rule:$ruleName,file:$fileName parse with write lock")
}
parseNode(ctx, ruleSets, sourceCode).also {
if (!isOnTheFly) {
LOG.info("rule:$ruleName,file:$fileName get result $it parse with write lock ,elapsed ${System.currentTimeMillis() - start}")
}
}
} else {
finalNode
}
}
return if (isOnTheFly) {
writeLock.withTryLock(50, MILLISECONDS, writeAction)!!
} else {
writeLock.withLockNotInline(
writeAction
)!!
}
}
private fun parseNode(ctx: RuleContext, ruleSets: RuleSets, sourceCode: Reader): Node {
......@@ -123,16 +147,19 @@ class SourceCodeProcessor(private val configuration: PMDConfiguration) {
val language = languageVersion.language
usesDFA(languageVersion, rootNode, ruleSets, language)
usesTypeResolution(languageVersion, rootNode, ruleSets, language)
nodeCache.put(ctx.sourceCodeFilename, rootNode)
onlyTheFlyCache.put(ctx.sourceCodeFilename, rootNode)
userTriggerNodeCache.put(ctx.sourceCodeFilename, rootNode)
return rootNode
}
private fun symbolFacade(rootNode: Node, languageVersionHandler: LanguageVersionHandler) {
TimeTracker.startOperation(TimedOperationCategory.SYMBOL_TABLE).use { to -> languageVersionHandler.getSymbolFacade(configuration.classLoader).start(rootNode) }
TimeTracker.startOperation(TimedOperationCategory.SYMBOL_TABLE)
.use { languageVersionHandler.getSymbolFacade(configuration.classLoader).start(rootNode) }
}
private fun resolveQualifiedNames(rootNode: Node, handler: LanguageVersionHandler) {
TimeTracker.startOperation(TimedOperationCategory.QUALIFIED_NAME_RESOLUTION).use { to -> handler.getQualifiedNameResolutionFacade(configuration.classLoader).start(rootNode) }
TimeTracker.startOperation(TimedOperationCategory.QUALIFIED_NAME_RESOLUTION)
.use { handler.getQualifiedNameResolutionFacade(configuration.classLoader).start(rootNode) }
}
// private ParserOptions getParserOptions(final LanguageVersionHandler
......@@ -153,8 +180,10 @@ class SourceCodeProcessor(private val configuration: PMDConfiguration) {
}
}
private fun usesTypeResolution(languageVersion: LanguageVersion, rootNode: Node, ruleSets: RuleSets,
language: Language) {
private fun usesTypeResolution(
languageVersion: LanguageVersion, rootNode: Node, ruleSets: RuleSets,
language: Language
) {
if (ruleSets.usesTypeResolution(language)) {
TimeTracker.startOperation(TimedOperationCategory.TYPE_RESOLUTION).use { to ->
......@@ -164,21 +193,22 @@ class SourceCodeProcessor(private val configuration: PMDConfiguration) {
}
}
private fun usesMultifile(rootNode: Node, languageVersionHandler: LanguageVersionHandler, ruleSets: RuleSets,
language: Language) {
private fun usesMultifile(
rootNode: Node, languageVersionHandler: LanguageVersionHandler, ruleSets: RuleSets,
language: Language
) {
if (ruleSets.usesMultifile(language)) {
TimeTracker.startOperation(TimedOperationCategory.MULTIFILE_ANALYSIS).use { to -> languageVersionHandler.multifileFacade.start(rootNode) }
TimeTracker.startOperation(TimedOperationCategory.MULTIFILE_ANALYSIS)
.use { languageVersionHandler.multifileFacade.start(rootNode) }
}
}
private fun processSource(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) {
val languageVersion = ctx.languageVersion
val languageVersionHandler = languageVersion.languageVersionHandler
val rootNode = getRootNode(sourceCode, ruleSets, ctx)
val rootNode = getRootNode(sourceCode, ruleSets, ctx) ?: return
resolveQualifiedNames(rootNode, languageVersionHandler)
symbolFacade(rootNode, languageVersionHandler)
val language = languageVersion.language
......@@ -201,21 +231,42 @@ class SourceCodeProcessor(private val configuration: PMDConfiguration) {
companion object {
val smartFoxConfig = ServiceManager.getService(P3cConfig::class.java)!!
private lateinit var nodeCache: Cache<String, Node>
private lateinit var onlyTheFlyCache: Cache<String, Node>
private lateinit var userTriggerNodeCache: Cache<String, Node>
private val LOG = Logger.getInstance(SourceCodeProcessor::class.java)
init {
reInitNodeCache(smartFoxConfig.astCacheTime)
}
fun reInitNodeCache(expireTime: Long) {
nodeCache = CacheBuilder.newBuilder().concurrencyLevel(16)
onlyTheFlyCache = CacheBuilder.newBuilder().concurrencyLevel(16)
.expireAfterWrite(expireTime, TimeUnit.MILLISECONDS)
.maximumSize(100)
.maximumSize(300)
.build<String, Node>()!!
userTriggerNodeCache = CacheBuilder.newBuilder().concurrencyLevel(16)
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(300)
.build<String, Node>()!!
}
fun invalidateCache(file: String) {
nodeCache.invalidate(file)
onlyTheFlyCache.invalidate(file)
userTriggerNodeCache.invalidate(file)
}
fun invalidUserTrigger(file: String) {
userTriggerNodeCache.invalidate(file)
}
fun invalidateAll() {
onlyTheFlyCache.invalidateAll()
userTriggerNodeCache.invalidateAll()
}
fun getNode(file: String, isOnTheFly: Boolean): Node? {
return if (isOnTheFly) onlyTheFlyCache.getIfPresent(file) else userTriggerNodeCache.getIfPresent(file)
}
}
}
package com.alibaba.p3c.idea.util
import com.intellij.openapi.progress.ProcessCanceledException
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.Lock
/**
* @date 2020/06/14
* @author caikang
*/
fun <T> Lock.withLockNotInline(action: () -> T?): T? {
lock()
try {
return action()
} finally {
unlock()
}
}
fun <T> Semaphore.withAcquire(action: () -> T?): T? {
acquire()
try {
return action()
} finally {
release()
}
}
fun <T> Lock.withTryLock(time: Long, timeUnit: TimeUnit, action: () -> T?): T? {
if (!tryLock(time, timeUnit)) {
throw ProcessCanceledException()
}
try {
return action()
} finally {
unlock()
}
}
fun <T> Semaphore.withTryAcquire(time: Long, timeUnit: TimeUnit, action: () -> T?): T? {
if (!tryAcquire(time, timeUnit)) {
throw ProcessCanceledException()
}
try {
return action()
} finally {
release()
}
}
\ No newline at end of file
......@@ -64,8 +64,8 @@ import javax.swing.JPanel
* @date 2017/05/04
*/
class AliCodeAnalysisCheckinHandler(
private val myProject: Project,
private val myCheckinPanel: CheckinProjectPanel
private val myProject: Project,
private val myCheckinPanel: CheckinProjectPanel
) : CheckinHandler() {
private val dialogTitle = "Alibaba Code Analyze"
private val cancelText = "&Cancel"
......@@ -106,16 +106,20 @@ class AliCodeAnalysisCheckinHandler(
return ServiceManager.getService(P3cConfig::class.java)
}
override fun beforeCheckin(executor: CommitExecutor?,
additionalDataConsumer: PairConsumer<Any, Any>): CheckinHandler.ReturnResult {
override fun beforeCheckin(
executor: CommitExecutor?,
additionalDataConsumer: PairConsumer<Any, Any>
): CheckinHandler.ReturnResult {
if (!getSettings().analysisBeforeCheckin) {
return CheckinHandler.ReturnResult.COMMIT
}
if (DumbService.getInstance(myProject).isDumb) {
if (Messages.showOkCancelDialog(myProject,
if (Messages.showOkCancelDialog(
myProject,
"Code analysis is impossible until indices are up-to-date", dialogTitle,
waitingText, commitText, null) == Messages.OK) {
waitingText, commitText, null
) == Messages.OK
) {
return CheckinHandler.ReturnResult.CANCEL
}
return CheckinHandler.ReturnResult.COMMIT
......@@ -124,12 +128,17 @@ class AliCodeAnalysisCheckinHandler(
val virtualFiles = CheckinHandlerUtil.filterOutGeneratedAndExcludedFiles(myCheckinPanel.virtualFiles, myProject)
val hasViolation = hasViolation(virtualFiles, myProject)
if (!hasViolation) {
BalloonNotifications.showSuccessNotification("No suspicious code found!",
myProject, "Analyze Finished")
BalloonNotifications.showSuccessNotification(
"No suspicious code found!",
myProject, "Analyze Finished"
)
return CheckinHandler.ReturnResult.COMMIT
}
if (Messages.showOkCancelDialog(myProject, "Found suspicious code,continue commit?",
dialogTitle, commitText, cancelText, null) == Messages.OK) {
if (Messages.showOkCancelDialog(
myProject, "Found suspicious code,continue commit?",
dialogTitle, commitText, cancelText, null
) == Messages.OK
) {
return CheckinHandler.ReturnResult.COMMIT
} else {
doAnalysis(myProject, virtualFiles.toTypedArray())
......@@ -139,55 +148,57 @@ class AliCodeAnalysisCheckinHandler(
fun doAnalysis(project: Project, virtualFiles: Array<VirtualFile>) {
val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx
val analysisScope = AnalysisScope(project,
ArrayList(Arrays.asList(*virtualFiles)))
val analysisScope = AnalysisScope(
project,
ArrayList(Arrays.asList(*virtualFiles))
)
val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection }
AliInspectionAction.createContext(tools, managerEx, null, false)
.doInspections(analysisScope)
AliInspectionAction.createContext(tools, managerEx, null, false, analysisScope)
.doInspections(analysisScope)
}
private fun hasViolation(virtualFiles: List<VirtualFile>, project: Project): Boolean {
ApplicationManager.getApplication().assertIsDispatchThread()
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
if (ApplicationManager.getApplication().isWriteAccessAllowed) throw RuntimeException(
"Must not run under write action")
"Must not run under write action"
)
val result = AtomicBoolean(false)
val exception = Ref.create<Exception>()
ProgressManager.getInstance().run(
object : Task.Modal(myProject, VcsBundle.message("checking.code.smells.progress.title"), true) {
override fun run(progress: ProgressIndicator) {
try {
val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection }
val inspectionManager = InspectionManager.getInstance(project)
val psiManager = PsiManager.getInstance(project)
val count = AtomicInteger(0)
val hasViolation = virtualFiles.asSequence().any {
file ->
ApplicationManager.getApplication().runReadAction(Computable {
val psiFile = psiManager.findFile(file) ?: return@Computable false
val curCount = count.incrementAndGet()
progress.text = file.canonicalPath
progress.fraction = curCount.toDouble() / virtualFiles.size.toDouble()
return@Computable tools.any {
progress.checkCanceled()
val tool = it.tool as LocalInspectionTool
val aliTool = tool as AliBaseInspection
progress.text2 = aliTool.ruleName()
val problems = tool.processFile(psiFile, inspectionManager)
problems.size > 0
}
})
object : Task.Modal(myProject, VcsBundle.message("checking.code.smells.progress.title"), true) {
override fun run(progress: ProgressIndicator) {
try {
val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection }
val inspectionManager = InspectionManager.getInstance(project)
val psiManager = PsiManager.getInstance(project)
val count = AtomicInteger(0)
val hasViolation = virtualFiles.asSequence().any { file ->
ApplicationManager.getApplication().runReadAction(Computable {
val psiFile = psiManager.findFile(file) ?: return@Computable false
val curCount = count.incrementAndGet()
progress.text = file.canonicalPath
progress.fraction = curCount.toDouble() / virtualFiles.size.toDouble()
return@Computable tools.any {
progress.checkCanceled()
val tool = it.tool as LocalInspectionTool
val aliTool = tool as AliBaseInspection
progress.text2 = aliTool.ruleName()
val problems = tool.processFile(psiFile, inspectionManager)
problems.size > 0
}
})
}
result.set(hasViolation)
} catch (e: ProcessCanceledException) {
result.set(false)
} catch (e: Exception) {
log.error(e)
exception.set(e)
}
result.set(hasViolation)
} catch (e: ProcessCanceledException) {
result.set(false)
} catch (e: Exception) {
log.error(e)
exception.set(e)
}
})
}
})
if (!exception.isNull) {
val t = exception.get()
ExceptionUtil.rethrowAllAsUnchecked(t)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册