2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInspection.dataFlow
18 import com.intellij.lang.LighterAST
19 import com.intellij.lang.LighterASTNode
20 import com.intellij.psi.PsiMethod
21 import com.intellij.psi.impl.source.JavaLightStubBuilder
22 import com.intellij.psi.impl.source.PsiFileImpl
23 import com.intellij.psi.impl.source.PsiMethodImpl
24 import com.intellij.psi.impl.source.tree.JavaElementType
25 import com.intellij.psi.impl.source.tree.JavaElementType.*
26 import com.intellij.psi.impl.source.tree.LightTreeUtil
27 import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor
28 import com.intellij.util.gist.GistManager
35 private val gist = GistManager.getInstance().newPsiFileGist("contractInference", 0, MethodDataExternalizer) { file ->
36 indexFile(file.node.lighterAST)
39 private fun indexFile(tree: LighterAST): Map<Int, MethodData> {
40 val result = HashMap<Int, MethodData>()
42 object : RecursiveLighterASTNodeWalkingVisitor(tree) {
45 override fun visitNode(element: LighterASTNode) {
46 if (element.tokenType === METHOD) {
47 calcData(tree, element)?.let { data -> result[methodIndex] = data }
51 if (JavaLightStubBuilder.isCodeBlockWithoutStubs(element)) return
53 super.visitNode(element)
55 }.visitNode(tree.root)
60 private fun calcData(tree: LighterAST, method: LighterASTNode): MethodData? {
61 val body = LightTreeUtil.firstChildOfType(tree, method, CODE_BLOCK) ?: return null
62 val statements = ContractInferenceInterpreter.getStatements(body, tree)
64 val contracts = ContractInferenceInterpreter(tree, method, body).inferContracts(statements)
66 val nullityVisitor = NullityInference.NullityInferenceVisitor(tree, body)
67 val purityVisitor = PurityInference.PurityInferenceVisitor(tree, body)
68 for (statement in statements) {
69 walkMethodBody(tree, statement) { nullityVisitor.visitNode(it); purityVisitor.visitNode(it) }
72 return createData(body, contracts, nullityVisitor.result, purityVisitor.result)
75 private fun walkMethodBody(tree: LighterAST, root: LighterASTNode, processor: (LighterASTNode) -> Unit) {
76 object : RecursiveLighterASTNodeWalkingVisitor(tree) {
77 override fun visitNode(element: LighterASTNode) {
78 val type = element.tokenType
79 if (type === CLASS || type === FIELD || type == METHOD || type == ANNOTATION_METHOD || type === LAMBDA_EXPRESSION) return
82 super.visitNode(element)
87 private fun createData(body: LighterASTNode,
88 contracts: List<PreContract>,
89 nullity: NullityInferenceResult?,
90 purity: PurityInferenceResult?): MethodData? {
91 if (nullity == null && purity == null && !contracts.isNotEmpty()) return null
93 return MethodData(nullity, purity, contracts, body.startOffset, body.endOffset)
96 fun getIndexedData(method: PsiMethod): MethodData? {
97 if (method !is PsiMethodImpl || !InferenceFromSourceUtil.shouldInferFromSource(method)) return null
99 return gist.getFileData(method.containingFile)?.get(methodIndex(method))
102 private fun methodIndex(method: PsiMethodImpl): Int {
103 val file = method.containingFile as PsiFileImpl
104 val stubTree = file.stubTree ?: file.calcStubTree()
105 return stubTree.plainList.filter { it.stubType == JavaElementType.METHOD }.map { it.psi }.indexOf(method)