mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-218377 Support required named params
GitOrigin-RevId: 518cca90081b6567fabed432658c3f66c8b5a3d3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
38b39b952f
commit
db3160961d
@@ -74,6 +74,7 @@ cannot.apply.constructor=Constructor ''{0}'' in ''{1}'' cannot be applied to ''{
|
||||
cannot.apply.default.constructor=Cannot apply default constructor for class ''{0}''
|
||||
cannot.instantiate.abstract.class=Cannot instantiate abstract class ''{0}''
|
||||
cannot.instantiate.interface=Cannot instantiate interface ''{0}''
|
||||
missing.required.named.parameter=Missing required ''{0}'' named parameter
|
||||
loss.of.precision=Possible loss of precision from ''{0}'' to ''{1}''
|
||||
cannot.assign=Cannot assign ''{0}'' to ''{1}''
|
||||
cannot.cast=Cannot cast ''{0}'' to ''{1}''
|
||||
|
||||
+35
-2
@@ -1,15 +1,19 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.annotator
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiModifier
|
||||
import org.jetbrains.plugins.groovy.GroovyBundle
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil
|
||||
import org.jetbrains.plugins.groovy.transformations.immutable.hasImmutableAnnotation
|
||||
import org.jetbrains.plugins.groovy.transformations.immutable.isImmutable
|
||||
import org.jetbrains.plugins.groovy.transformations.impl.namedVariant.collectAllParamsFromNamedVariantMethod
|
||||
import org.jetbrains.plugins.groovy.transformations.impl.namedVariant.collectNamedParams
|
||||
|
||||
/**
|
||||
* Check features introduced in groovy 2.5
|
||||
@@ -20,7 +24,10 @@ class GroovyAnnotator25(private val holder: AnnotationHolder) : GroovyElementVis
|
||||
collectAllParamsFromNamedVariantMethod(method).groupBy { it.first }.filter { it.value.size > 1 }.forEach { (name, parameters) ->
|
||||
val parametersList = parameters.joinToString { "'${it.second.name}'" }
|
||||
parameters.drop(1).map { (_, parameter) -> parameter }.forEach {
|
||||
holder.createErrorAnnotation(it.nameIdentifierGroovy, GroovyBundle.message("duplicating.named.parameter", name, parametersList))
|
||||
val nameIdentifier = it.nameIdentifier
|
||||
if (nameIdentifier != null) {
|
||||
holder.createErrorAnnotation(nameIdentifier, GroovyBundle.message("duplicating.named.parameter", name, parametersList))
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitMethod(method)
|
||||
@@ -37,4 +44,30 @@ class GroovyAnnotator25(private val holder: AnnotationHolder) : GroovyElementVis
|
||||
holder.createErrorAnnotation(field.nameIdentifierGroovy, GroovyBundle.message("field.should.be.immutable", field.name))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCallExpression(callExpression: GrCallExpression) {
|
||||
checkRequiredNamedArguments(callExpression)
|
||||
|
||||
super.visitCallExpression(callExpression)
|
||||
}
|
||||
|
||||
private fun checkRequiredNamedArguments(callExpression: GrCallExpression) {
|
||||
val namedArguments = callExpression.namedArguments.mapNotNull { it.labelName }.toSet()
|
||||
if (namedArguments.isEmpty()) return
|
||||
|
||||
val resolveResult = callExpression.advancedResolve()
|
||||
val method = resolveResult.element as? PsiMethod ?: return
|
||||
|
||||
val parameters = method.parameterList.parameters
|
||||
val mapParameter = (if (parameters.isNotEmpty()) parameters[0] else null) ?: return
|
||||
|
||||
val requiredNamedParams = collectNamedParams(mapParameter).filter { it.required }
|
||||
|
||||
requiredNamedParams.forEach {
|
||||
if (!namedArguments.contains(it.name) && PsiUtil.isCompileStatic(callExpression)) {
|
||||
val message = GroovyBundle.message("missing.required.named.parameter", it.name)
|
||||
holder.createErrorAnnotation(callExpression, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -9,19 +9,16 @@ import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.transformations.impl.namedVariant.NamedParamData;
|
||||
import org.jetbrains.plugins.groovy.transformations.impl.namedVariant.NamedParamsUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author ven
|
||||
@@ -105,8 +102,8 @@ public class GroovyPresentationUtil {
|
||||
}
|
||||
|
||||
private static boolean presentNamedParameters(@NotNull StringBuilder buffer, @NotNull GrParameter parameter) {
|
||||
List<Pair<String, PsiType>> pairs = NamedParamsUtil.collectNamedParams(parameter);
|
||||
StringUtil.join(pairs, pair -> pair.getFirst() + ": " + pair.getSecond().getPresentableText(), ", ", buffer);
|
||||
List<NamedParamData> pairs = NamedParamsUtil.collectNamedParams(parameter);
|
||||
StringUtil.join(pairs, namedParam -> namedParam.getName() + ": " + namedParam.getType().getPresentableText(), ", ", buffer);
|
||||
return !pairs.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -1,9 +1,10 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.transformations.impl.namedVariant
|
||||
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.plugins.groovy.extensions.GroovyNamedArgumentProvider
|
||||
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor
|
||||
import org.jetbrains.plugins.groovy.extensions.impl.NamedArgumentDescriptorImpl
|
||||
import org.jetbrains.plugins.groovy.extensions.impl.TypeCondition
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall
|
||||
@@ -19,6 +20,9 @@ class GroovyNamedVariantArgumentProvider : GroovyNamedArgumentProvider() {
|
||||
val parameters = method.parameterList.parameters
|
||||
val mapParameter = (if (parameters.isNotEmpty()) parameters[0] else null) ?: return
|
||||
|
||||
collectNamedParams(mapParameter).forEach { result[it.first] = TypeCondition(it.second) }
|
||||
collectNamedParams(mapParameter).forEach {
|
||||
val type = it.type
|
||||
result[it.name] = if (type != null) TypeCondition(type, it.navigation) else NamedArgumentDescriptorImpl(it.navigation)
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -1,7 +1,14 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.transformations.impl.namedVariant
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiParameter
|
||||
import com.intellij.psi.PsiType
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter
|
||||
|
||||
internal data class NamedParamData(val name: String, val type: PsiType?, val origin: GrParameter)
|
||||
data class NamedParamData(
|
||||
val name: String,
|
||||
val type: PsiType?,
|
||||
val origin: PsiParameter,
|
||||
val navigation: PsiElement,
|
||||
val required: Boolean = false
|
||||
)
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.transformations.impl.namedVariant
|
||||
|
||||
import com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT
|
||||
@@ -34,6 +34,7 @@ class NamedVariantTransformationSupport : AstTransformationSupport {
|
||||
modifierList.addAnnotation(GROOVY_TRANSFORM_NAMED_PARAM).let {
|
||||
it.addAttribute("type", namedParam.type?.presentableText ?: JAVA_LANG_OBJECT)
|
||||
it.addAttribute("value", "\"${namedParam.name}\"")
|
||||
it.addAttribute("required", "${namedParam.required}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-10
@@ -9,9 +9,9 @@ import com.intellij.psi.util.PropertyUtilBase
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.getArrayValue
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils
|
||||
@@ -25,7 +25,7 @@ const val GROOVY_TRANSFORM_NAMED_PARAMS = "groovy.transform.NamedParams"
|
||||
const val GROOVY_TRANSFORM_NAMED_DELEGATE = "groovy.transform.NamedDelegate"
|
||||
|
||||
|
||||
fun collectNamedParams(mapParameter: PsiParameter): List<Pair<String, PsiType>> {
|
||||
fun collectNamedParams(mapParameter: PsiParameter): List<NamedParamData> {
|
||||
if (!mapParameter.type.equalsToText(CommonClassNames.JAVA_UTIL_MAP)) return emptyList()
|
||||
|
||||
val annotations = mapParameter
|
||||
@@ -34,25 +34,26 @@ fun collectNamedParams(mapParameter: PsiParameter): List<Pair<String, PsiType>>
|
||||
?.getArrayValue { it as? GrAnnotation }
|
||||
|
||||
if (annotations != null) {
|
||||
return annotations.mapNotNull(::constructNamedParameter)
|
||||
return annotations.mapNotNull { constructNamedParameter(it, mapParameter) }
|
||||
}
|
||||
|
||||
return mapParameter.annotations.mapNotNull(::constructNamedParameter)
|
||||
return mapParameter.annotations.mapNotNull{ constructNamedParameter(it, mapParameter) }
|
||||
}
|
||||
|
||||
private fun constructNamedParameter(annotation: PsiAnnotation): Pair<String, PsiType>? {
|
||||
private fun constructNamedParameter(annotation: PsiAnnotation, owner: PsiParameter): NamedParamData? {
|
||||
if(annotation.qualifiedName != GROOVY_TRANSFORM_NAMED_PARAM) return null
|
||||
val attributeLiteral = annotation.findAttributeValue("value")
|
||||
val name = (attributeLiteral as? GrLiteral)?.value as? String ?: return null
|
||||
val classValue = annotation.findAttributeValue("type") as? GrExpression ?: return null
|
||||
val type = ResolveUtil.getClassReferenceFromExpression(classValue) ?: return null
|
||||
return name to type
|
||||
val required = GrAnnotationUtil.inferBooleanAttribute(annotation, "required") ?: false
|
||||
return NamedParamData(name, type, owner, annotation, required)
|
||||
}
|
||||
|
||||
/**
|
||||
* The order of the parameters is preserved as it is in the code
|
||||
*/
|
||||
fun collectAllParamsFromNamedVariantMethod(method: GrMethod): List<Pair<String, GrParameter>> {
|
||||
fun collectAllParamsFromNamedVariantMethod(method: GrMethod): List<Pair<String, PsiParameter>> {
|
||||
val namedParams = collectNamedParamsFromNamedVariantMethod(method).groupBy { it.origin }
|
||||
return method.parameterList.parameters.flatMap { parameter ->
|
||||
namedParams[parameter]?.let {
|
||||
@@ -72,14 +73,15 @@ internal fun collectNamedParamsFromNamedVariantMethod(method: GrMethod): List<Na
|
||||
val namedParamsAnn = PsiImplUtil.getAnnotation(parameter, GROOVY_TRANSFORM_NAMED_PARAM)
|
||||
if (namedParamsAnn != null) {
|
||||
val name = AnnotationUtil.getDeclaredStringAttributeValue(namedParamsAnn, "value") ?: parameter.name
|
||||
result.add(NamedParamData(name, type, parameter))
|
||||
val required = GrAnnotationUtil.inferBooleanAttribute(namedParamsAnn, "required") ?: false
|
||||
result.add(NamedParamData(name, type, parameter, namedParamsAnn, required))
|
||||
continue
|
||||
}
|
||||
|
||||
PsiImplUtil.getAnnotation(parameter, GROOVY_TRANSFORM_NAMED_DELEGATE) ?: continue
|
||||
val psiAnnotation = PsiImplUtil.getAnnotation(parameter, GROOVY_TRANSFORM_NAMED_DELEGATE) ?: continue
|
||||
val parameterClass = (type as? PsiClassType)?.resolve() ?: continue
|
||||
getProperties(parameterClass).forEach { (propertyName, propertyType) ->
|
||||
result.add(NamedParamData(propertyName, propertyType, parameter))
|
||||
result.add(NamedParamData(propertyName, propertyType, parameter, psiAnnotation))
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
+6
@@ -29,6 +29,12 @@ class Groovy25HighlightingTest extends LightGroovyTestCase implements Highlighti
|
||||
highlightingTest()
|
||||
}
|
||||
|
||||
void 'test named params required'() { highlightingTest() }
|
||||
|
||||
void 'test several absent required named params'() { highlightingTest() }
|
||||
|
||||
void 'test required named param in named variant'() { highlightingTest() }
|
||||
|
||||
void 'test named delegate without properties'() { highlightingTest() }
|
||||
|
||||
void 'test immutable fields'() { highlightingTest() }
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.NamedParam
|
||||
import groovy.transform.NamedParams
|
||||
|
||||
@CompileStatic
|
||||
void namedParams(@NamedParams([
|
||||
@NamedParam(value = 'first', type = String, required = true),
|
||||
@NamedParam(value = 'last', type = Integer)
|
||||
]) Map args, int i) {
|
||||
|
||||
print args.last
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
def m() {
|
||||
<error descr="Missing required 'first' named parameter">namedParams(1, last: 1)</error>
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.NamedParam
|
||||
import groovy.transform.NamedVariant
|
||||
|
||||
@CompileStatic
|
||||
@NamedVariant
|
||||
void namedParams(int i, @NamedParam(value = 'param', required = true) Integer n) {
|
||||
print i
|
||||
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
def m() {
|
||||
<error descr="Missing required 'param' named parameter">namedParams(1, a :1)</error>
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.NamedParam
|
||||
import groovy.transform.NamedParams
|
||||
|
||||
@CompileStatic
|
||||
void namedParams(@NamedParams([
|
||||
@NamedParam(value = 'first', type = String, required = true),
|
||||
@NamedParam(value = 'last', type = Integer, required = true)
|
||||
]) Map args, int i) {
|
||||
|
||||
print args.last
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
def m() {
|
||||
<error descr="Missing required 'first' named parameter"><error descr="Missing required 'last' named parameter">namedParams(1, param: 1)</error></error>
|
||||
}
|
||||
Reference in New Issue
Block a user