Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wangsun300
p3c
提交
b9adb329
P
p3c
项目概览
wangsun300
/
p3c
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
p3c
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
b9adb329
编写于
6月 21, 2020
作者:
C
caikang.ck
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix Inconsistency results in different scans
上级
2ca345bf
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
730 addition
and
206 deletion
+730
-206
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt
...kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt
+70
-54
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/PmdGlobalInspectionContextImpl.kt
...alibaba/p3c/idea/action/PmdGlobalInspectionContextImpl.kt
+273
-0
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/AliProjectComponent.kt
...lin/com/alibaba/p3c/idea/component/AliProjectComponent.kt
+71
-4
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt
...aba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt
+32
-14
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt
...om/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt
+44
-25
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt
...c/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt
+28
-10
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt
...in/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt
+103
-52
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/withLockNotInline.kt
...ain/kotlin/com/alibaba/p3c/idea/util/withLockNotInline.kt
+51
-0
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt
...com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt
+58
-47
未找到文件。
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt
浏览文件 @
b9adb329
...
...
@@ -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
)
}
}
}
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/PmdGlobalInspectionContextImpl.kt
0 → 100644
浏览文件 @
b9adb329
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
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/AliProjectComponent.kt
浏览文件 @
b9adb329
...
...
@@ -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
}
}
}
}
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt
浏览文件 @
b9adb329
...
...
@@ -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
)
}
...
...
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt
浏览文件 @
b9adb329
...
...
@@ -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
>()
!!
}
}
}
...
...
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt
浏览文件 @
b9adb329
...
...
@@ -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
)
{
...
...
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt
浏览文件 @
b9adb329
/**
* 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
)
{
node
Cache
=
CacheBuilder
.
newBuilder
().
concurrencyLevel
(
16
)
onlyTheFly
Cache
=
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
)
}
}
}
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/withLockNotInline.kt
0 → 100644
浏览文件 @
b9adb329
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
idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt
浏览文件 @
b9adb329
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录