add assert if no accessors found for binding

disabled for inspection since all inspections serialized by platform by default but not all inspections are state-full
This commit is contained in:
Vladimir Krivosheev
2018-07-19 13:14:09 +02:00
parent f8b6ef85dc
commit da96d65c9d
7 changed files with 153 additions and 63 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2017 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-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.
package com.intellij.codeInspection;
import com.intellij.lang.injection.InjectedLanguageManager;
@@ -6,6 +6,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.util.xmlb.annotations.Property;
import org.intellij.lang.annotations.Language;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NonNls;
@@ -14,9 +15,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author max
*/
@Property(assertIfNoBindings = false)
public abstract class LocalInspectionTool extends InspectionProfileEntry {
public static final LocalInspectionTool[] EMPTY_ARRAY = new LocalInspectionTool[0];
@@ -1,24 +1,12 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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.
package com.intellij.configurationStore.xml
import com.intellij.configurationStore.AState
import com.intellij.configurationStore.deserialize
import com.intellij.openapi.components.BaseState
import com.intellij.util.loadElement
import com.intellij.util.xmlb.annotations.MapAnnotation
import com.intellij.util.xmlb.annotations.Property
import com.intellij.util.xmlb.annotations.Tag
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
@@ -39,6 +27,35 @@ class KotlinXmlSerializerTest {
</bean>""", data)
}
@Tag("profile-state")
private class VisibleTreeState : BaseState() {
internal var foo by string()
}
private class VisibleTreeStateComponent : BaseState() {
// we do not support private accessors
@get:Property(surroundWithTag = false)
@get:MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
var profileNameToState by map<String, VisibleTreeState>()
fun getVisibleTreeState(profile: String) = profileNameToState.getOrPut(profile) {
incrementModificationCount()
VisibleTreeState()
}
}
@Test fun `private map`() {
val data = VisibleTreeStateComponent()
data.getVisibleTreeState("new")
testSerializer("""
<VisibleTreeStateComponent>
<entry key="new">
<profile-state />
</entry>
</VisibleTreeStateComponent>""", data)
}
@Test fun floatProperty() {
val state = AState()
state.floatProperty = 3.4f
@@ -41,7 +41,7 @@ internal class XmlSerializerTest {
@Test fun annotatedInternalVar() {
class Bean {
@MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
internal var PLACES_MAP = TreeMap<String, String>()
var PLACES_MAP = TreeMap<String, String>()
}
val data = Bean()
@@ -61,19 +61,27 @@ internal class XmlSerializerTest {
}
}
@Test fun emptyBeanSerialization() {
// commented for now, error level changed to WARN
// @Test fun emptyBeanSerialization() {
// class EmptyBean
//
// try {
// testSerializer("<EmptyBean />", EmptyBean())
// }
// catch (e: AssertionError) {
// assertThat(e.message).startsWith("no accessors for class ")
// }
//
// Assertions.fail("error expected")
// }
@Test fun `supress no accessors warn`() {
@Property(assertIfNoBindings = false)
class EmptyBean
testSerializer("<EmptyBean />", EmptyBean())
}
@Tag("Bean")
private class EmptyBeanWithCustomName
@Test fun emptyBeanSerializationWithCustomName() {
testSerializer("<Bean />", EmptyBeanWithCustomName())
}
@Test fun publicFieldSerialization() {
val bean = BeanWithPublicFields()
@@ -107,17 +115,43 @@ internal class XmlSerializerTest {
}
private class BeanWithSubBean {
var BEAN1: EmptyBeanWithCustomName? = EmptyBeanWithCustomName()
var BEAN2: BeanWithPublicFields? = BeanWithPublicFields()
var bean1: BeanWithPublicFields? = BeanWithPublicFields()
var bean2: BeanWithPublicFields? = BeanWithPublicFields()
}
@Test fun subBeanSerialization() {
val bean = BeanWithSubBean()
testSerializer("<BeanWithSubBean>\n" + " <option name=\"BEAN1\">\n" + " <Bean />\n" + " </option>\n" + " <option name=\"BEAN2\">\n" + " <BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + " </BeanWithPublicFields>\n" + " </option>\n" + "</BeanWithSubBean>", bean)
bean.BEAN2!!.INT_V = 2
bean.BEAN2!!.STRING_V = "bye"
testSerializer("""<BeanWithSubBean>
<option name="bean1">
<BeanWithPublicFields>
<option name="INT_V" value="1" />
<option name="STRING_V" value="hello" />
</BeanWithPublicFields>
</option>
<option name="bean2">
<BeanWithPublicFields>
<option name="INT_V" value="1" />
<option name="STRING_V" value="hello" />
</BeanWithPublicFields>
</option>
</BeanWithSubBean>""", bean)
bean.bean2!!.INT_V = 2
bean.bean2!!.STRING_V = "bye"
testSerializer("<BeanWithSubBean>\n" + " <option name=\"BEAN1\">\n" + " <Bean />\n" + " </option>\n" + " <option name=\"BEAN2\">\n" + " <BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"2\" />\n" + " <option name=\"STRING_V\" value=\"bye\" />\n" + " </BeanWithPublicFields>\n" + " </option>\n" + "</BeanWithSubBean>", bean)
testSerializer("""<BeanWithSubBean>
<option name="bean1">
<BeanWithPublicFields>
<option name="INT_V" value="1" />
<option name="STRING_V" value="hello" />
</BeanWithPublicFields>
</option>
<option name="bean2">
<BeanWithPublicFields>
<option name="INT_V" value="2" />
<option name="STRING_V" value="bye" />
</BeanWithPublicFields>
</option>
</BeanWithSubBean>""", bean)
}
@Test fun subBeanSerializationAndSkipDefaults() {
@@ -140,7 +174,7 @@ internal class XmlSerializerTest {
@Tag("bean")
class BeanWithSubBeanWithEquals {
@Suppress("unused")
var bean1: EmptyBeanWithCustomName = EmptyBeanWithCustomName()
var bean1: BeanWithPublicFields = BeanWithPublicFields()
var bean2: BeanWithEquals = BeanWithEquals()
}
@@ -155,17 +189,26 @@ internal class XmlSerializerTest {
@Test fun nullFieldValue() {
val bean1 = BeanWithPublicFields()
testSerializer("<BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" value=\"hello\" />\n" + "</BeanWithPublicFields>", bean1)
testSerializer("""<BeanWithPublicFields>
<option name="INT_V" value="1" />
<option name="STRING_V" value="hello" />
</BeanWithPublicFields>""", bean1)
bean1.STRING_V = null
testSerializer("<BeanWithPublicFields>\n" + " <option name=\"INT_V\" value=\"1\" />\n" + " <option name=\"STRING_V\" />\n" + "</BeanWithPublicFields>", bean1)
testSerializer("""<BeanWithPublicFields>
<option name="INT_V" value="1" />
<option name="STRING_V" />
</BeanWithPublicFields>""", bean1)
val bean2 = BeanWithSubBean()
bean2.BEAN1 = null
bean2.BEAN2 = null
bean2.bean1 = null
bean2.bean2 = null
testSerializer("<BeanWithSubBean>\n" + " <option name=\"BEAN1\" />\n" + " <option name=\"BEAN2\" />\n" + "</BeanWithSubBean>", bean2)
testSerializer("""<BeanWithSubBean>
<option name="bean1" />
<option name="bean2" />
</BeanWithSubBean>""", bean2)
}
private data class BeanWithOption(@OptionTag("path") var PATH: String? = null)
@@ -270,9 +313,13 @@ internal class XmlSerializerTest {
@Suppress("unused")
@Transient
fun getValue(): String = "foo"
var foo: String? = null
}
testSerializer("<Bean />", Bean())
testSerializer("""<Bean>
<option name="foo" />
</Bean>""", Bean())
}
@Test fun propertyWithoutTagWithPrimitiveType() {
@@ -447,10 +494,18 @@ internal class XmlSerializerTest {
val element = BeanWithJDOMElement()
element.STRING_V = "a"
element.actions = Element("x").addContent(Element("a")).addContent(Element("b"))
assertSerializer(element, "<BeanWithJDOMElement>\n" + " <option name=\"STRING_V\" value=\"a\" />\n" + " <actions>\n" + " <a />\n" + " <b />\n" + " </actions>\n" + "</BeanWithJDOMElement>", null)
assertSerializer(element, """<BeanWithJDOMElement>
<option name="STRING_V" value="a" />
<actions>
<a />
<b />
</actions>
</BeanWithJDOMElement>""", null)
element.actions = null
assertSerializer(element, "<BeanWithJDOMElement>\n" + " <option name=\"STRING_V\" value=\"a\" />\n" + "</BeanWithJDOMElement>", null)
assertSerializer(element, """<BeanWithJDOMElement>
<option name="STRING_V" value="a" />
</BeanWithJDOMElement>""", null)
}
@Test fun deserializeJDOMElementField() {
@@ -541,7 +596,7 @@ internal class XmlSerializerTest {
testSerializer("<bean>\n <condition expression=\"2+2\" />\n" + "</bean>", bean)
}
@Test fun `no_wrap`() {
@Test fun `no wrap`() {
@Tag("bean")
class Bean {
@Property(flat = true)
@@ -3,6 +3,7 @@ package com.intellij.openapi.extensions.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.*;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.pico.CachingConstructorInjectionComponentAdapter;
@@ -170,7 +171,7 @@ public class ExtensionsAreaImpl implements ExtensionsArea {
adapter = new ExtensionComponentAdapter(implClass, extensionElement, myPicoContainer, pluginDescriptor, shouldDeserializeInstance(extensionElement));
}
else {
adapter = new ExtensionComponentAdapter(extensionPoint.getClassName(), extensionElement, myPicoContainer, pluginDescriptor, true);
adapter = new ExtensionComponentAdapter(extensionPoint.getClassName(), extensionElement, myPicoContainer, pluginDescriptor, !JDOMUtil.isEmpty(extensionElement));
}
myPicoContainer.registerComponent(adapter);
((ExtensionPointImpl)extensionPoint).registerExtensionAdapter(adapter);
@@ -182,7 +183,7 @@ public class ExtensionsAreaImpl implements ExtensionsArea {
// has custom attributes
for (Attribute attribute : extensionElement.getAttributes()) {
final String name = attribute.getName();
if (!"implementation".equals(name) && !"id".equals(name) && !"order".equals(name)) {
if (!"implementation".equals(name) && !"id".equals(name) && !"order".equals(name) && !"os".equals(name)) {
return true;
}
}
@@ -117,7 +117,8 @@ fun PersistentStateComponent<*>.serializeStateInto(element: Element) {
state?.let { serializeObjectInto(it, element) }
}
fun serializeObjectInto(o: Any, target: Element) {
@JvmOverloads
fun serializeObjectInto(o: Any, target: Element, filter: SerializationFilter? = null) {
if (o is Element) {
val iterator = o.children.iterator()
for (child in iterator) {
@@ -134,7 +135,7 @@ fun serializeObjectInto(o: Any, target: Element) {
}
val binding = serializer.getClassBinding(o.javaClass)
(binding as BeanBinding).serializeInto(o, target, if (o is BaseState) null else getDefaultSerializationFilter())
(binding as BeanBinding).serializeInto(o, target, if (o is BaseState) null else (filter ?: getDefaultSerializationFilter()))
}
private val serializer = object : XmlSerializerImpl.XmlSerializerBase() {
@@ -306,8 +306,8 @@ public class BeanBinding extends NotNullDeserializeBinding {
}
}
if (accessors.isEmpty()) {
LOG.error("no accessors for " + aClass);
if (accessors.isEmpty() && !isAssertBindings(aClass)) {
LOG.warn("no accessors for " + aClass);
}
ourAccessorCache.put(aClass, accessors);
@@ -315,11 +315,23 @@ public class BeanBinding extends NotNullDeserializeBinding {
return accessors;
}
// avoid assert for OCInspections.Properties
private static boolean isAssertBindings(@NotNull Class<?> aClass) {
do {
Property property = aClass.getAnnotation(Property.class);
if (property != null && !property.assertIfNoBindings()) {
return true;
}
}
while ((aClass = aClass.getSuperclass()) != null);
return false;
}
private static class NameAndIsSetter {
final String name;
final boolean isSetter;
public NameAndIsSetter(String name, boolean isSetter) {
private NameAndIsSetter(String name, boolean isSetter) {
this.name = name;
this.isSetter = isSetter;
}
@@ -462,26 +474,31 @@ public class BeanBinding extends NotNullDeserializeBinding {
@NotNull
private static Binding createBinding(@NotNull MutableAccessor accessor, @NotNull Serializer serializer, @NotNull Property.Style propertyStyle) {
Binding binding = serializer.getBinding(accessor);
if (binding instanceof JDOMElementBinding) {
return binding;
}
Attribute attribute = accessor.getAnnotation(Attribute.class);
if (attribute != null) {
return new AttributeBinding(accessor, attribute);
}
Tag tag = accessor.getAnnotation(Tag.class);
if (tag != null) {
return new TagBinding(accessor, tag);
}
Text text = accessor.getAnnotation(Text.class);
if (text != null) {
return new TextBinding(accessor);
}
OptionTag optionTag = accessor.getAnnotation(OptionTag.class);
if (optionTag != null && optionTag.converter() != Converter.class) {
return new OptionTagBinding(accessor, optionTag);
}
Binding binding = serializer.getBinding(accessor);
if (binding instanceof JDOMElementBinding) {
return binding;
}
Tag tag = accessor.getAnnotation(Tag.class);
if (tag != null) {
return new TagBinding(accessor, tag);
}
if (binding instanceof CompactCollectionBinding) {
return new AccessorBindingWrapper(accessor, binding, false, Property.Style.OPTION_TAG);
}
@@ -509,8 +526,6 @@ public class BeanBinding extends NotNullDeserializeBinding {
return new TagBinding(accessor, xCollection.propertyElementName());
}
OptionTag optionTag = accessor.getAnnotation(OptionTag.class);
if (optionTag == null) {
XMap xMap = accessor.getAnnotation(XMap.class);
if (xMap != null) {
@@ -40,4 +40,6 @@ public @interface Property {
* Applicable only on class level.
*/
Style style() default Style.OPTION_TAG;
boolean assertIfNoBindings() default true;
}