import org.jetbrains.plugins.groovy.lang.psi.util.*;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyConstructorReference;
-import org.jetbrains.plugins.groovy.lang.resolve.ast.GeneratedConstructorCollector;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.GrTupleConstructorUtils;
import org.jetbrains.plugins.groovy.lang.resolve.ast.InheritConstructorContributor;
import org.jetbrains.plugins.groovy.transformations.immutable.GrImmutableUtils;
PsiAnnotation anno = typeDefinition.getAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR);
if (anno != null) {
- if (!TupleConstructorAnnotationChecker.isSuperCalledInPre(anno)) {
- PsiNameValuePair preAttribute = AnnotationUtil.findDeclaredAttribute(anno, "pre");
- Objects.requireNonNull(preAttribute);
+ PsiNameValuePair preAttribute = AnnotationUtil.findDeclaredAttribute(anno, "pre");
+ if (preAttribute != null && !TupleConstructorAnnotationChecker.isSuperCalledInPre(anno)) {
holder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("there.is.no.default.constructor.available.in.class.0", qName))
.range(preAttribute).create();
}
PsiAnnotation tupleConstructor = containingClass.getAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR);
if (tupleConstructor == null) return;
if (!Boolean.FALSE.equals(GrAnnotationUtil.inferBooleanAttribute(tupleConstructor, "defaults"))) return;
- List<String> excludes = GeneratedConstructorCollector.getIdentifierList(tupleConstructor, "excludes");
- List<String> includes = GeneratedConstructorCollector.getIdentifierList(tupleConstructor, "includes");
- if ((excludes != null && !excludes.contains(field.getName())) || (includes != null && includes.contains(field.getName()))) {
+ if (GrTupleConstructorUtils.isFieldAccepted(tupleConstructor, field)) {
myHolder.newAnnotation(HighlightSeverity.ERROR, GroovyBundle.message("initializers.are.forbidden.with.defaults"))
.range(initializer)
.create();
import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil
import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil.inferClosureAttribute
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames
-import org.jetbrains.plugins.groovy.lang.resolve.ast.GeneratedConstructorCollector
import org.jetbrains.plugins.groovy.lang.resolve.ast.contributor.SyntheticKeywordConstructorContributor.Companion.isSyntheticConstructorCall
+import org.jetbrains.plugins.groovy.lang.resolve.ast.getIdentifierList
class TupleConstructorAnnotationChecker : CustomAnnotationChecker() {
if (annotation.qualifiedName != GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR) {
return false
}
- val excludes = GeneratedConstructorCollector.getIdentifierList(annotation, "excludes")
+ val excludes = getIdentifierList(annotation, "excludes")
val includes = AnnotationUtil.findDeclaredAttribute(annotation, "includes")
if (includes != null && excludes != null && excludes.isNotEmpty()) {
registerIdentifierListError(holder, AnnotationUtil.findDeclaredAttribute(annotation, "excludes")!!)
-// 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.
+// Copyright 2000-2020 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.codeInspection.control.finalVar;
import com.intellij.codeInspection.LocalQuickFix;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ControlFlowBuilder;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.GrFieldControlFlowPolicy;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ResolvedVariableDescriptor;
+import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
+import org.jetbrains.plugins.groovy.lang.resolve.ast.GrTupleConstructorUtils;
import org.jetbrains.plugins.groovy.transformations.immutable.GrImmutableUtils;
import java.util.*;
if (isImmutableField(field)) return true;
+ if (isInitializedInTupleConstructor(field)) return true;
+
final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
final GrTypeDefinition aClass = ((GrTypeDefinition)field.getContainingClass());
return true;
}
+ private static boolean isInitializedInTupleConstructor(@NotNull GrField field) {
+ var containingClass = field.getContainingClass();
+ if (containingClass == null) {
+ return false;
+ }
+ PsiAnnotation anno = containingClass.getAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR);
+ if (anno == null) {
+ return false;
+ }
+ return GrTupleConstructorUtils.isFieldAccepted(anno, field);
+ }
+
private static boolean isImmutableField(@NotNull GrField field) {
GrModifierList fieldModifierList = field.getModifierList();
if (fieldModifierList != null && fieldModifierList.hasExplicitVisibilityModifiers()) return false;
String JAVA_UTIL_REGEX_MATCHER = "java.util.regex.Matcher";
String GROOVY_TRANSFORM_FIELD = "groovy.transform.Field";
String GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR = "groovy.transform.TupleConstructor";
+ String GROOVY_TRANSFORM_PROPERTY_OPTIONS = "groovy.transform.PropertyOptions";
String GROOVY_TRANSFORM_IMMUTABLE = "groovy.transform.Immutable";
String GROOVY_TRANSFORM_CANONICAL = "groovy.transform.Canonical";
String GROOVY_LANG_REFERENCE = "groovy.lang.Reference";
import com.intellij.psi.*
import com.intellij.psi.util.PropertyUtilBase
-import groovy.transform.Undefined
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter
-import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil
}
- fun accept(clazz: PsiClass, includePropertes: Boolean, includeBeans: Boolean, includeFields: Boolean) {
+ fun accept(clazz: PsiClass, includeProperties: Boolean, includeBeans: Boolean, includeFields: Boolean) {
val (properties, setters, fields) = getGroupedClassMembers(clazz)
fun addParameter(origin: PsiField) {
collector.add(lightParameter)
}
- if (includePropertes) {
+ if (includeProperties) {
for (property in properties) {
addParameter(property)
}
if (includeFields) {
for (field in fields) {
+ if (field.hasModifierProperty(GrModifier.FINAL) && field.initializer != null) continue
addParameter(field)
}
}
return Triple(properties, setters, fields)
}
- private fun String.isInternal(): Boolean = contains("$")
-
- @JvmStatic
- fun getIdentifierList(annotation: PsiAnnotation, attributeName: String): List<String>? {
- annotation.takeIf { it.hasAttribute(attributeName) } ?: return null
- val rawIdentifiers = GrAnnotationUtil.inferStringAttribute(annotation, attributeName)
- return rawIdentifiers?.split(',')?.mapNotNull { it.trim().takeUnless(CharSequence::isBlank) }?.toList()
- ?: GrAnnotationUtil.getStringArrayValue(annotation, attributeName, false)
- }
-
- private fun collectNamesOrderInformation(tupleConstructor: PsiAnnotation): Pair<(String) -> Boolean, List<String>?> {
-
- val excludes: List<String> = getIdentifierList(tupleConstructor, "excludes") ?: emptyList()
-
- val includes: List<String>? = getIdentifierList(tupleConstructor, "includes")
- ?.takeUnless { Undefined.isUndefined(it.singleOrNull()) }
-
- val allowInternalNames = GrAnnotationUtil.inferBooleanAttribute(tupleConstructor, "allNames") ?: false
-
- val filter: (String) -> Boolean = { name: String ->
- val internalFilter = allowInternalNames || !name.isInternal()
- val excludesFilter = !excludes.contains(name)
- val includesFilter = includes == null || includes.contains(name)
- internalFilter && excludesFilter && includesFilter
- }
- return filter to includes
- }
}
}
\ No newline at end of file
--- /dev/null
+// Copyright 2000-2020 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.
+@file:JvmName("GrTupleConstructorUtils")
+
+package org.jetbrains.plugins.groovy.lang.resolve.ast
+
+import com.intellij.psi.PsiAnnotation
+import groovy.transform.Undefined
+import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField
+import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil
+import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames
+
+fun getIdentifierList(annotation: PsiAnnotation, attributeName: String): List<String>? {
+ annotation.takeIf { it.hasAttribute(attributeName) } ?: return null
+ val rawIdentifiers = GrAnnotationUtil.inferStringAttribute(annotation, attributeName)
+ return rawIdentifiers?.split(',')?.mapNotNull { it.trim().takeUnless(CharSequence::isBlank) }?.toList()
+ ?: GrAnnotationUtil.getStringArrayValue(annotation, attributeName, false)
+}
+
+
+private fun String.isInternal(): Boolean = contains("$")
+
+internal fun collectNamesOrderInformation(tupleConstructor: PsiAnnotation): Pair<(String) -> Boolean, List<String>?> {
+
+ val excludes: List<String> = getIdentifierList(tupleConstructor, "excludes") ?: emptyList()
+
+ val includes: List<String>? = getIdentifierList(tupleConstructor, "includes")
+ ?.takeUnless { Undefined.isUndefined(it.singleOrNull()) }
+
+ val allowInternalNames = GrAnnotationUtil.inferBooleanAttribute(tupleConstructor, "allNames") ?: false
+
+ val filter: (String) -> Boolean = { name: String ->
+ val internalFilter = allowInternalNames || !name.isInternal()
+ val excludesFilter = !excludes.contains(name)
+ val includesFilter = includes == null || includes.contains(name)
+ internalFilter && excludesFilter && includesFilter
+ }
+ return filter to includes
+}
+
+
+fun isFieldAccepted(annotation: PsiAnnotation, field: GrField): Boolean {
+ if (field.hasModifierProperty(GrModifier.STATIC)) return false
+ if (field.isProperty) {
+ val hasCustomPropertyHandler = field.containingClass?.hasAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_PROPERTY_OPTIONS) ?: false
+ if (hasCustomPropertyHandler) return false
+ val includeProperties = GrAnnotationUtil.inferBooleanAttribute(annotation, "includeProperties") ?: true
+ if (!includeProperties) return false
+ }
+ else {
+ val includeFields = GrAnnotationUtil.inferBooleanAttribute(annotation, "includeFields") ?: false
+ if (!includeFields) return false
+ }
+ val (namesFilter, _) = collectNamesOrderInformation(annotation)
+ return namesFilter(field.name)
+}
+
-// 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-2020 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.
@file:JvmName("GrImmutableUtils")
package org.jetbrains.plugins.groovy.transformations.immutable
list.add(GrLightAnnotation(owner, alias, GROOVY_TRANSFORM_IMMUTABLE_OPTIONS, emptyMap()))
list.add(GrLightAnnotation(owner, alias, GROOVY_TRANSFORM_KNOWN_IMMUTABLE, emptyMap()))
list.add(GrLightAnnotation(owner, alias, GroovyCommonClassNames.GROOVY_TRANSFORM_TUPLE_CONSTRUCTOR, mapOf("defaults" to "false")))
+ list.add(GrLightAnnotation(owner, alias, GroovyCommonClassNames.GROOVY_TRANSFORM_PROPERTY_OPTIONS, emptyMap()))
}
fun isImmutable(field: GrField): Boolean {
@groovy.transform.TupleConstructor(defaults = false, includeSuperProperties = true)
class Rr extends NN {
- String actionType = ""
+ String actionType
long referrerCode;
boolean referrerUrl;
}
new Rr({}, 1)
new Rr({}, 1, true)
new Rr({}, 1, true, "")
- new Rr(actionType: {}, referrerUrl: true, referrerCode: 1)
+ new Rr(actionType: {}, referrerUrl: true, referrerCode: 1, prop: "a")
}"""
}
@groovy.transform.CompileStatic
@groovy.transform.TupleConstructor(pre = { super() }, <error>callSuper = true</error>)
class Rr extends NN {
+}"""
+ }
+
+
+ @Test
+ void 'final fields in constructor'() {
+ highlightingTest """
+@groovy.transform.CompileStatic
+@groovy.transform.TupleConstructor(includeFields = true)
+class Rr {
+ private final int a = 1
+ private final boolean b
+ String c
+}
+
+@groovy.transform.CompileStatic
+static void main(String[] args) {
+ new Rr<error>("", 2)</error>
+ new Rr("", true)
}"""
}
}