diff options
author | Vitek Karas <vitek.karas@microsoft.com> | 2021-07-30 01:05:12 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-30 01:05:12 +0300 |
commit | c1d83c9ca5953f7e09812a48e2bddf90efbea0c5 (patch) | |
tree | 3cf0267bd416696778324d691336f5f0e89b0957 /test/Mono.Linker.Tests.Cases | |
parent | 0cb9250a903cfc90cbac602ed79c0cbc588d8d3f (diff) |
Warn when accessing DAM annotated method via reflection (#2145)
* Warn when accessing DAM annotated method via reflection
Any access to DAM annotated method must be validated by the trimmer. Normal calls and such are handled already, but direct reflection access or other indirect accesses can lead to trimmer allowing potential invocation of DAM annotated method without validating the annotations. This change will warn whenever such potential "leak" happens.
This applies to method parameter, method return value and field annotations.
Uses the same infra as RUC already just adds additional checks.
Tests for the new cases.
* Formatting
* Only warn on virtual methods with annotate return value
Annotated return value is mostly not problematic (the caller only "Reads" from it, which is always safe). The only case where it's problematic is if something would override the method at runtime (ref emit) in which case the trimmer can't validate the new code that it fulfills the return value requirements. But that can only happen if the method is virtual.
* PR feedback
* PR feedback
* Remove test code.
Diffstat (limited to 'test/Mono.Linker.Tests.Cases')
3 files changed, 562 insertions, 17 deletions
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs b/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs new file mode 100644 index 000000000..f94b9a458 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs @@ -0,0 +1,553 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + class AnnotatedMembersAccessedViaReflection + { + public static void Main () + { + AnnotatedField.Test (); + AnnotatedMethodParameters.Test (); + AnnotatedMethodReturnValue.Test (); + AnnotatedProperty.Test (); + AnnotatedGenerics.Test (); + AnnotationOnGenerics.Test (); + AnnotationOnInteropMethod.Test (); + AccessThroughLdToken.Test (); + } + + class AnnotatedField + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type _annotatedField; + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + static void Reflection () + { + typeof (AnnotatedField).GetField ("_annotatedField").SetValue (null, typeof (TestType)); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionSuppressedByRUC () + { + typeof (AnnotatedField).GetField ("_annotatedField").SetValue (null, typeof (TestType)); + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + static void ReflectionReadOnly () + { + typeof (AnnotatedField).GetField ("_annotatedField").GetValue (null); + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (AnnotatedField))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (AnnotatedField))] + static void DynamicDependencySuppressedByRUC () + { + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + [DynamicDependency (nameof (_annotatedField), typeof (AnnotatedField))] + static void DynamicDependencyByName () + { + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedField).RequiresPublicFields (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedField).RequiresPublicFields (); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + Reflection (); + ReflectionSuppressedByRUC (); + ReflectionReadOnly (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicDependencyByName (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + } + } + + class AnnotatedMethodParameters + { + public static void MethodWithSingleAnnotatedParameter ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + + class AttributeWithConstructorWithAnnotation : Attribute + { + public AttributeWithConstructorWithAnnotation ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + static void Reflection () + { + typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter)).Invoke (null, null); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionSuppressedByRUC () + { + typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter)).Invoke (null, null); + } + + // Should not warn, there's nothing wrong about this + [AttributeWithConstructorWithAnnotation (typeof (TestType))] + static void AnnotatedAttributeConstructor () + { + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodParameters))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodParameters))] + static void DynamicDependencySuppressedByRUC () + { + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + [DynamicDependency (nameof (MethodWithSingleAnnotatedParameter), typeof (AnnotatedMethodParameters))] + static void DynamicDependencyByName () + { + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedMethodParameters).RequiresPublicMethods (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedMethodParameters).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + static void Ldftn () + { + var _ = new Action<Type> (AnnotatedMethodParameters.MethodWithSingleAnnotatedParameter); + } + + interface IWithAnnotatedMethod + { + public void AnnotatedMethod ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] Type type); + } + + [ExpectedWarning ("IL2111", nameof (IWithAnnotatedMethod.AnnotatedMethod))] + static void Ldvirtftn () + { + IWithAnnotatedMethod instance = null; + var _ = new Action<Type> (instance.AnnotatedMethod); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + Reflection (); + ReflectionSuppressedByRUC (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicDependencyByName (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + Ldftn (); + Ldvirtftn (); + } + } + + class AnnotatedMethodReturnValue + { + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type StaticMethodWithAnnotatedReturnValue () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type InstanceMethodWithAnnotatedReturnValue () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public virtual Type VirtualMethodWithAnnotatedReturnValue () => null; + + // Only virtual methods should warn - the problem is only possible if something overrides a virtual method. + // Getting an annotated value in itself is not dangerous in any way. + + static void ReflectionOnStatic () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (StaticMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + static void ReflectionOnInstance () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (InstanceMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + static void ReflectionOnVirtual () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (VirtualMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionOnVirtualSuppressedByRUC () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (VirtualMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodReturnValue))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencySuppressedByRUC () + { + } + + [DynamicDependency (nameof (StaticMethodWithAnnotatedReturnValue), typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencyByNameOnStatic () + { + } + + [DynamicDependency (nameof (InstanceMethodWithAnnotatedReturnValue), typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencyByNameOnInstance () + { + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + [DynamicDependency (nameof (VirtualMethodWithAnnotatedReturnValue), typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencyByNameOnVirtual () + { + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedMethodReturnValue).RequiresPublicMethods (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedMethodReturnValue).RequiresPublicMethods (); + } + + static void LdftnOnStatic () + { + var _ = new Func<Type> (AnnotatedMethodReturnValue.StaticMethodWithAnnotatedReturnValue); + } + + static void LdftnOnInstance () + { + var _ = new Func<Type> ((new AnnotatedMethodReturnValue ()).InstanceMethodWithAnnotatedReturnValue); + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + static void LdftnOnVirtual () + { + var _ = new Func<Type> ((new AnnotatedMethodReturnValue ()).VirtualMethodWithAnnotatedReturnValue); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + ReflectionOnStatic (); + ReflectionOnInstance (); + ReflectionOnVirtual (); + ReflectionOnVirtualSuppressedByRUC (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicDependencyByNameOnStatic (); + DynamicDependencyByNameOnInstance (); + DynamicDependencyByNameOnVirtual (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + LdftnOnStatic (); + LdftnOnInstance (); + LdftnOnVirtual (); + } + } + + class AnnotatedProperty + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] + public static Type PropertyWithAnnotation { get; set; } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + public static Type PropertyWithAnnotationGetterOnly { get => null; } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + public virtual Type VirtualPropertyWithAnnotationGetterOnly { get => null; } + + class AttributeWithPropertyWithAnnotation : Attribute + { + public AttributeWithPropertyWithAnnotation () { } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + public Type PropertyWithAnnotation { get; set; } + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation))] + static void ReflectionOnPropertyItself () + { + typeof (AnnotatedProperty).GetProperty (nameof (PropertyWithAnnotation)); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionOnPropertyItselfSuppressedByRUC () + { + typeof (AnnotatedProperty).GetProperty (nameof (PropertyWithAnnotation)); + } + + static void ReflectionOnPropertyWithGetterOnly () + { + typeof (AnnotatedProperty).GetProperty (nameof (PropertyWithAnnotationGetterOnly)); + } + + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly))] + static void ReflectionOnPropertyWithGetterOnlyOnVirtual () + { + typeof (AnnotatedProperty).GetProperty (nameof (VirtualPropertyWithAnnotationGetterOnly)); + } + + static void ReflectionOnGetter () + { + typeof (AnnotatedProperty).GetMethod ("get_" + nameof (PropertyWithAnnotation)); + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation) + ".set")] + static void ReflectionOnSetter () + { + typeof (AnnotatedProperty).GetMethod ("set_" + nameof (PropertyWithAnnotation)); + } + + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly) + ".get")] + static void ReflectionOnVirtualGetter () + { + typeof (AnnotatedProperty).GetMethod ("get_" + nameof (VirtualPropertyWithAnnotationGetterOnly)); + } + + // Should not warn - there's nothing wrong with this + [AttributeWithPropertyWithAnnotation (PropertyWithAnnotation = typeof (TestType))] + static void AnnotatedAttributeProperty () + { + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation) + ".set")] + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly) + ".get")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (AnnotatedProperty))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (AnnotatedProperty))] + static void DynamicDependencySuppressedByRUC () + { + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation) + ".set")] + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly) + ".get")] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedProperty).RequiresPublicProperties (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedProperty).RequiresPublicProperties (); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + ReflectionOnPropertyItself (); + ReflectionOnPropertyItselfSuppressedByRUC (); + ReflectionOnPropertyWithGetterOnly (); + ReflectionOnPropertyWithGetterOnlyOnVirtual (); + ReflectionOnGetter (); + ReflectionOnSetter (); + ReflectionOnVirtualGetter (); + AnnotatedAttributeProperty (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + } + } + + // Annotation on generic parameter + class AnnotatedGenerics + { + public static void GenericWithAnnotation< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] T> () + { } + + static void ReflectionOnly () + { + // Should not warn - there's nothing wrong with asking for MethodInfo alone + typeof (AnnotatedGenerics).GetMethod (nameof (GenericWithAnnotation)); + } + + // Similarly to direct reflection - no warning expected + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedGenerics))] + static void DynamicDependency () + { + } + + // Similarly to direct reflection - no warning expected + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedGenerics).RequiresPublicMethods (); + } + + // This should produce IL2071 https://github.com/mono/linker/issues/2144 + [ExpectedWarning ("IL2070", "MakeGenericMethod")] + static void InstantiateGeneric (Type type = null) + { + // This should warn due to MakeGenericMethod - in this case the generic parameter is unannotated type + typeof (AnnotatedGenerics).GetMethod (nameof (GenericWithAnnotation)).MakeGenericMethod (type); + } + + public static void Test () + { + ReflectionOnly (); + DynamicDependency (); + DynamicallyAccessedMembers (); + InstantiateGeneric (); + } + } + + // Annotation on non-generic parameter but on generic methods + class AnnotationOnGenerics + { + class GenericWithAnnotatedMethod<T> + { + public static void AnnotatedMethod ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + } + + public static void GenericMethodWithAnnotation<T> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + + [ExpectedWarning ("IL2111", nameof (GenericWithAnnotatedMethod<TestType>.AnnotatedMethod))] + public static void GenericTypeWithStaticMethodViaLdftn () + { + var _ = new Action<Type> (GenericWithAnnotatedMethod<TestType>.AnnotatedMethod); + } + + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] + public static void GenericMethodWithAnnotationReflection () + { + typeof (AnnotationOnGenerics).GetMethod (nameof (GenericMethodWithAnnotation)); + } + + public static void GenericMethodWithAnnotationDirectCall () + { + // Should not warn, nothing wrong about this + GenericMethodWithAnnotation<TestType> (typeof (TestType)); + } + + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] + public static void GenericMethodWithAnnotationViaLdftn () + { + var _ = new Action<Type> (GenericMethodWithAnnotation<TestType>); + } + + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] + public static void GenericMethodDynamicallyAccessedMembers () + { + typeof (AnnotationOnGenerics).RequiresPublicMethods (); + } + + public static void Test () + { + GenericTypeWithStaticMethodViaLdftn (); + GenericMethodWithAnnotationReflection (); + GenericMethodWithAnnotationDirectCall (); + GenericMethodWithAnnotationViaLdftn (); + GenericMethodDynamicallyAccessedMembers (); + } + } + + class AnnotationOnInteropMethod + { + struct ValueWithAnnotatedField + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type _typeField; + } + + [ExpectedWarning ("IL2110", nameof (ValueWithAnnotatedField._typeField))] + [DllImport ("nonexistent")] + static extern ValueWithAnnotatedField GetValueWithAnnotatedField (); + + [ExpectedWarning ("IL2110", nameof (ValueWithAnnotatedField._typeField))] + [DllImport ("nonexistent")] + static extern void AcceptValueWithAnnotatedField (ValueWithAnnotatedField value); + + public static void Test () + { + GetValueWithAnnotatedField (); + AcceptValueWithAnnotatedField (default (ValueWithAnnotatedField)); + } + } + + class AccessThroughLdToken + { + public virtual Type PropertyWithLdToken { + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + get { + return null; + } + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithLdToken))] + public static void Test () + { + Expression<Func<Type>> getter = () => (new AccessThroughLdToken ()).PropertyWithLdToken; + } + } + + class TestType { } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs b/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs index c21c32b1d..25107c005 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs @@ -13,6 +13,10 @@ namespace Mono.Linker.Tests.Cases.DataFlow { [SkipKeptItemsValidation] [SandboxDependency ("Dependencies/TestSystemTypeBase.cs")] + + // Suppress warnings about accessing methods with annotations via reflection - the test below does that a LOT + // (The test accessed these methods through DynamicallyAccessedMembers annotations which is effectively the same reflection access) + [UnconditionalSuppressMessage ("test", "IL2111")] class VirtualMethodHierarchyDataflowAnnotationValidation { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs index 142e82dd5..b4b71d5eb 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs @@ -118,21 +118,6 @@ namespace Mono.Linker.Tests.Cases.Reflection class TestUnknownType { [Kept] - public static void PublicMethod () - { - } - - [Kept] - protected static void ProtectedMethod () - { - } - - [Kept] - private static void PrivateMethod () - { - } - - [Kept] [UnrecognizedReflectionAccessPattern (typeof (Expression), nameof (Expression.Call), new Type[] { typeof (Type), typeof (string), typeof (Type[]), typeof (Expression[]) }, messageCode: "IL2072")] public static void Test () @@ -148,13 +133,13 @@ namespace Mono.Linker.Tests.Cases.Reflection [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] static Type GetUnknownType () { - return typeof (TestUnknownType); + return typeof (TestType); } [Kept] static Type TriggerUnrecognizedPattern () { - return typeof (TestUnknownType); + return typeof (TestType); } } @@ -355,5 +340,8 @@ namespace Mono.Linker.Tests.Cases.Reflection [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] static Type GetUnknownTypeWithRequrements () { return null; } } + + [Kept] + class TestType { } } } |