diff --git a/litho-it/src/test/java/com/facebook/litho/specmodels/model/PropValidationTest.java b/litho-it/src/test/java/com/facebook/litho/specmodels/model/PropValidationTest.java index 2d312ae1190..9907754eddd 100644 --- a/litho-it/src/test/java/com/facebook/litho/specmodels/model/PropValidationTest.java +++ b/litho-it/src/test/java/com/facebook/litho/specmodels/model/PropValidationTest.java @@ -24,6 +24,7 @@ import com.facebook.litho.annotations.ResType; import com.facebook.litho.specmodels.internal.ImmutableList; import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import java.util.ArrayList; @@ -49,6 +50,8 @@ public void setup() { when(mPropModel2.getName()).thenReturn("name2"); when(mPropModel1.getTypeName()).thenReturn(TypeName.BOOLEAN); when(mPropModel2.getTypeName()).thenReturn(TypeName.INT); + when(mPropModel1.getTypeSpec()).thenReturn(new TypeSpec(TypeName.BOOLEAN)); + when(mPropModel2.getTypeSpec()).thenReturn(new TypeSpec(TypeName.INT)); when(mPropModel1.isOptional()).thenReturn(false); when(mPropModel2.isOptional()).thenReturn(false); when(mPropModel1.getResType()).thenReturn(ResType.NONE); @@ -179,17 +182,51 @@ public void testPropMarkedOverrideCommonButNotCommon() { @Test public void testPropWithReservedType() { - when(mPropModel1.getTypeName()).thenReturn(ClassNames.COMPONENT_LAYOUT); + when(mPropModel1.getTypeSpec()) + .thenReturn( + new TypeSpec.DeclaredTypeSpec( + ClassNames.COMPONENT_BUILDER, + ClassNames.COMPONENT_BUILDER.toString(), + () -> new TypeSpec(TypeName.OBJECT), + ImmutableList.of(), + ImmutableList.of())); List validationErrors = PropValidation.validate( mSpecModel, PropValidation.COMMON_PROP_NAMES, PropValidation.VALID_COMMON_PROPS); assertThat(validationErrors).hasSize(1); assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject1); - assertThat(validationErrors.get(0).message).isEqualTo( - "Props may not be declared with the following argument types: " + - "[com.facebook.litho.ComponentLayout, " + - "com.facebook.litho.Component.Builder]."); + assertThat(validationErrors.get(0).message) + .isEqualTo( + "Props may not be declared with argument type: com.facebook.litho.Component.Builder or its inherited types."); + } + + @Test + public void testPropInheritedFromReservedType() { + when(mPropModel1.getTypeSpec()) + .thenReturn( + new TypeSpec.DeclaredTypeSpec( + ClassName.bestGuess("com.facebook.litho.Text.Builder"), + ClassName.bestGuess("com.facebook.litho.Text.Builder").toString(), + () -> + new TypeSpec.DeclaredTypeSpec( + ClassNames.COMPONENT_BUILDER, + ClassNames.COMPONENT_BUILDER.toString(), + () -> new TypeSpec(TypeName.OBJECT), + ImmutableList.of(), + ImmutableList.of()), + ImmutableList.of(), + ImmutableList.of())); + + List validationErrors = + PropValidation.validate( + mSpecModel, PropValidation.COMMON_PROP_NAMES, PropValidation.VALID_COMMON_PROPS); + assertThat(validationErrors).hasSize(1); + assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject1); + assertThat(validationErrors.get(0).message) + .isEqualTo( + "Props may not be declared with argument type: com.facebook.litho.Component.Builder or its inherited types. " + + "com.facebook.litho.Text.Builder is an inherited type of com.facebook.litho.Component.Builder"); } @Test diff --git a/litho-processor/src/main/java/com/facebook/litho/specmodels/model/PropValidation.java b/litho-processor/src/main/java/com/facebook/litho/specmodels/model/PropValidation.java index e7b36a433a5..3f324e73964 100644 --- a/litho-processor/src/main/java/com/facebook/litho/specmodels/model/PropValidation.java +++ b/litho-processor/src/main/java/com/facebook/litho/specmodels/model/PropValidation.java @@ -366,7 +366,6 @@ static List validate( } } - TypeName argumentType = null; if (prop.hasVarArgs()) { TypeName typeName = prop.getTypeName(); if (typeName instanceof ParameterizedTypeName) { @@ -377,7 +376,6 @@ static List validate( prop.getRepresentedObject(), prop.getName() + " is a variable argument, and thus should be a List<> type.")); } - argumentType = parameterizedTypeName.typeArguments.get(0); } else { validationErrors.add( new SpecModelValidationError( @@ -385,17 +383,29 @@ static List validate( prop.getName() + " is a variable argument, and thus requires a parameterized List type.")); } - } else { - argumentType = prop.getTypeName(); } - if (ILLEGAL_PROP_TYPES.contains(argumentType)) { - validationErrors.add( - new SpecModelValidationError( - prop.getRepresentedObject(), - "Props may not be declared with the following argument types: " - + ILLEGAL_PROP_TYPES - + ".")); + TypeSpec typeSpec = prop.getTypeSpec(); + for (TypeName illegalPropType : ILLEGAL_PROP_TYPES) { + + if (typeSpec.isSameDeclaredType(illegalPropType)) { + validationErrors.add( + new SpecModelValidationError( + prop.getRepresentedObject(), + "Props may not be declared with argument type: " + + illegalPropType + + " or its inherited types.")); + } else if (typeSpec.isSubType(illegalPropType)) { + validationErrors.add( + new SpecModelValidationError( + prop.getRepresentedObject(), + "Props may not be declared with argument type: " + + illegalPropType + + " or its inherited types. " + + typeSpec.getTypeName() + + " is an inherited type of " + + illegalPropType)); + } } if (!prop.isOptional() && prop.hasDefault(specModel.getPropDefaults())) {