Avoid infinite cycle resolving generic type that refers itself

This commit improves type resolution for a unresolved generic type that
uses itself in its upper bound declaration.

Closes gh-16451
This commit is contained in:
Stephane Nicoll 2019-04-04 15:55:55 +02:00
parent 8a04e2cc86
commit fbb5ffe0a4
4 changed files with 112 additions and 4 deletions

View File

@ -248,15 +248,28 @@ class TypeUtils {
TypeMirror typeMirror = descriptor.resolveGeneric(t);
if (typeMirror != null) {
if (typeMirror instanceof TypeVariable) {
// Still unresolved, let's use upper bound
return visit(((TypeVariable) typeMirror).getUpperBound(), descriptor);
TypeVariable typeVariable = (TypeVariable) typeMirror;
// Still unresolved, let's use the upper bound, checking first if
// a cycle may exist
if (!hasCycle(typeVariable)) {
return visit(typeVariable.getUpperBound(), descriptor);
}
}
else {
return visit(typeMirror, descriptor);
}
}
// Unresolved generics, use upper bound
return visit(t.getUpperBound(), descriptor);
// Fallback to simple representation of the upper bound
return defaultAction(t.getUpperBound(), descriptor);
}
private boolean hasCycle(TypeVariable variable) {
TypeMirror upperBound = variable.getUpperBound();
if (upperBound instanceof DeclaredType) {
return ((DeclaredType) upperBound).getTypeArguments().stream()
.anyMatch((candidate) -> candidate.equals(variable));
}
return false;
}
@Override

View File

@ -46,8 +46,10 @@ import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint;
import org.springframework.boot.configurationsample.endpoint.SpecificEndpoint;
import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalEndpoint;
import org.springframework.boot.configurationsample.generic.AbstractGenericProperties;
import org.springframework.boot.configurationsample.generic.ComplexGenericProperties;
import org.springframework.boot.configurationsample.generic.SimpleGenericProperties;
import org.springframework.boot.configurationsample.generic.UnresolvedGenericProperties;
import org.springframework.boot.configurationsample.generic.UpperBoundGenericPojo;
import org.springframework.boot.configurationsample.incremental.BarProperties;
import org.springframework.boot.configurationsample.incremental.FooProperties;
import org.springframework.boot.configurationsample.incremental.RenamedBarProperties;
@ -524,6 +526,21 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata.getItems()).hasSize(3);
}
@Test
public void complexGenericProperties() {
ConfigurationMetadata metadata = compile(ComplexGenericProperties.class);
assertThat(metadata).has(
Metadata.withGroup("generic").fromSource(ComplexGenericProperties.class));
assertThat(metadata).has(
Metadata.withGroup("generic.test").ofType(UpperBoundGenericPojo.class)
.fromSource(ComplexGenericProperties.class));
assertThat(metadata).has(Metadata
.withProperty("generic.test.mappings",
"java.util.Map<java.lang.Enum<T>,java.lang.String>")
.fromSource(UpperBoundGenericPojo.class));
assertThat(metadata.getItems()).hasSize(3);
}
@Test
public void unresolvedGenericProperties() {
ConfigurationMetadata metadata = compile(AbstractGenericProperties.class,

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* 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
*
* https://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.
*/
package org.springframework.boot.configurationsample.generic;
import org.springframework.boot.configurationsample.ConfigurationProperties;
import org.springframework.boot.configurationsample.NestedConfigurationProperty;
/**
* More advanced generic setup.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("generic")
public class ComplexGenericProperties {
@NestedConfigurationProperty
private UpperBoundGenericPojo<Test> test = new UpperBoundGenericPojo<>();
public UpperBoundGenericPojo<Test> getTest() {
return this.test;
}
public enum Test {
ONE, TWO, THREE
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* 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
*
* https://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.
*/
package org.springframework.boot.configurationsample.generic;
import java.util.HashMap;
import java.util.Map;
/**
* A pojo with a complex generic signature.
*
* @author Stephane Nicoll
*/
public class UpperBoundGenericPojo<T extends Enum<T>> {
private final Map<T, String> mappings = new HashMap<>();
public Map<T, String> getMappings() {
return this.mappings;
}
}