This is an automated email from the ASF dual-hosted git repository.

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit a24f1c7300afc20bb857cb634650d89bf39a7faa
Author: Paul King <[email protected]>
AuthorDate: Wed May 6 20:22:32 2026 +1000

    GROOVY-11998: Better support of intersection types (copilot review)
---
 ARCHITECTURE.md                                    | 22 +++++++++++-----------
 .../groovy/ast/IntersectionTypeClassNode.java      |  4 ++--
 .../asm/sc/AbstractFunctionalInterfaceWriter.java  |  2 +-
 .../classgen/asm/sc/StaticTypesClosureWriter.java  |  2 +-
 .../groovy/runtime/IntersectionCastSupport.java    |  2 +-
 .../groovy/tools/javac/JavaStubGenerator.java      |  2 +-
 .../groovy/transform/RecordBaseASTStubber.java     |  2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   |  2 +-
 src/spec/doc/core-differences-java.adoc            |  2 +-
 src/spec/doc/core-semantics.adoc                   |  2 +-
 src/test-resources/core/Groovydoc_02x.groovy       |  4 ++--
 src/test/groovy/bugs/Groovy11967.groovy            |  2 +-
 .../transform/stc/IntersectionCastSTCTest.groovy   |  6 +++---
 .../antlr4/IntersectionCastParserTest.groovy       |  2 --
 .../groovy/tools/groovydoc/TagRenderer.java        |  4 ++--
 .../groovydoc/antlr4/GroovydocJavaVisitor.java     |  2 +-
 .../groovy/tools/groovydoc/GroovyDocToolTest.java  |  4 ++--
 .../testfiles/ScriptWithMarkdownTopLevelDoc.groovy |  2 +-
 18 files changed, 33 insertions(+), 35 deletions(-)

diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 5c4a35d93c..4f6723d4e5 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -63,17 +63,17 @@ and exposed as the
 [`CompilePhase`](src/main/java/org/codehaus/groovy/control/CompilePhase.java)
 enum that AST transformations and customizers attach to:
 
-| # | Phase | What happens | Driver classes |
-|---|---|---|---|
-| 1 | `INITIALIZATION` | Source files opened, `CompilationUnit` configured, 
customizers applied | `CompilationUnit`, `CompilerConfiguration` |
-| 2 | `PARSING` | ANTLR4 lexer + parser produce a CST (parse tree) | 
`Antlr4ParserPlugin`, `GroovyLangLexer`, `GroovyLangParser` |
-| 3 | `CONVERSION` | CST → AST (`ModuleNode` / `ClassNode` / `MethodNode` / 
...) | `AstBuilder` |
-| 4 | `SEMANTIC_ANALYSIS` | Class resolution, import handling, validity checks 
the grammar can't catch | `ResolveVisitor`, `StaticImportVisitor`, 
`AnnotationConstantsVisitor` |
-| 5 | `CANONICALIZATION` | Fill in the AST: synthesised members, generic 
types, most local AST transforms run here | `ASTTransformationVisitor`, 
`GenericsVisitor` |
+| # | Phase | What happens                                                     
                       | Driver classes |
+|---|---|-----------------------------------------------------------------------------------------|---|
+| 1 | `INITIALIZATION` | Source files opened, `CompilationUnit` configured, 
customizers applied                  | `CompilationUnit`, 
`CompilerConfiguration` |
+| 2 | `PARSING` | ANTLR4 lexer + parser produce a CST (parse tree)             
                           | `Antlr4ParserPlugin`, `GroovyLangLexer`, 
`GroovyLangParser` |
+| 3 | `CONVERSION` | CST → AST (`ModuleNode` / `ClassNode` / `MethodNode` / 
...)                             | `AstBuilder` |
+| 4 | `SEMANTIC_ANALYSIS` | Class resolution, import handling, validity checks 
the grammar can't catch              | `ResolveVisitor`, `StaticImportVisitor`, 
`AnnotationConstantsVisitor` |
+| 5 | `CANONICALIZATION` | Fill in the AST: synthesized members, generic 
types, most local AST transforms run here | `ASTTransformationVisitor`, 
`GenericsVisitor` |
 | 6 | `INSTRUCTION_SELECTION` | Optimisations and instruction-set selection; 
`@CompileStatic` / `@TypeChecked` run here | `OptimizerVisitor`, 
`StaticTypeCheckingVisitor` |
-| 7 | `CLASS_GENERATION` | AST → bytecode in memory | `AsmClassGenerator`, 
`Verifier`, classes under `classgen/asm/` |
-| 8 | `OUTPUT` | Write generated `.class` files | `CompilationUnit` output 
stage |
-| 9 | `FINALIZATION` | Cleanup, `Janitor` callbacks | `CompilationUnit`, 
`Janitor` |
+| 7 | `CLASS_GENERATION` | AST → bytecode in memory                            
                                    | `AsmClassGenerator`, `Verifier`, classes 
under `classgen/asm/` |
+| 8 | `OUTPUT` | Write generated `.class` files                                
                          | `CompilationUnit` output stage |
+| 9 | `FINALIZATION` | Cleanup, `Janitor` callbacks                            
                                | `CompilationUnit`, `Janitor` |
 
 Each phase iterates over all `SourceUnit`s before the next phase
 begins. AST transformations declare which phase they run in; the
@@ -140,7 +140,7 @@ verbatim keeps the reference precise; paraphrasing tends to 
drift.
 
 - `org.codehaus.groovy.classgen.AsmClassGenerator` walks the AST and
   emits bytecode via ASM. Supporting visitors run here too:
-  `Verifier` (synthesises bridge methods, accessors, default
+  `Verifier` (synthesizes bridge methods, accessors, default
   constructors), `EnumVisitor`, `EnumCompletionVisitor`,
   `InnerClassVisitor`, `InnerClassCompletionVisitor`,
   `VariableScopeVisitor`, `ReturnAdder`.
diff --git 
a/src/main/java/org/codehaus/groovy/ast/IntersectionTypeClassNode.java 
b/src/main/java/org/codehaus/groovy/ast/IntersectionTypeClassNode.java
index 906481d832..2b443fa7f0 100644
--- a/src/main/java/org/codehaus/groovy/ast/IntersectionTypeClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/IntersectionTypeClassNode.java
@@ -35,7 +35,7 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
  *
  * <p>Distinct from the implicit lowest-upper-bound nodes that
  * {@link org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor}
- * synthesises during inference: an instance of this class records the ordered
+ * synthesizes during inference: an instance of this class records the ordered
  * list of components exactly as written by the user. That ordering is needed
  * for cast-conversion checks, error messages and (in later phases) bytecode
  * generation via {@code LambdaMetafactory.altMetafactory} markers.
@@ -48,7 +48,7 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
  * interfaces array contains only interface components and the superclass is
  * the (at most one) class component.
  *
- * @since 5.0.0
+ * @since 6.0.0
  */
 public final class IntersectionTypeClassNode extends ClassNode {
 
diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
index 76eac91cb9..ff74bee97b 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/AbstractFunctionalInterfaceWriter.java
@@ -90,7 +90,7 @@ public interface AbstractFunctionalInterfaceWriter {
      * {@code LambdaMetafactory.altMetafactory} via {@code FLAG_MARKERS} so the
      * generated lambda implements every component interface at runtime.
      *
-     * @since 5.0.0
+     * @since 6.0.0
      */
     default void writeFunctionalInterfaceIndy(final MethodVisitor 
methodVisitor,
                                               final String samMethodName, 
final String invokedTypeDescriptor,
diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
index 24201148ea..2ae86527df 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
@@ -90,7 +90,7 @@ public class StaticTypesClosureWriter extends ClosureWriter {
             if (closureClass.implementsInterface(marker)) continue;
             // Only add interfaces with no abstract methods (true markers). For
             // interfaces that declare unimplemented abstract methods, we'd
-            // have to synthesise method bodies — out of scope here, fall back
+            // have to synthesize method bodies — out of scope here, fall back
             // to the runtime proxy path in IntersectionCastSupport.asType.
             if (hasAbstractMethods(marker)) continue;
             closureClass.addInterface(marker);
diff --git 
a/src/main/java/org/codehaus/groovy/runtime/IntersectionCastSupport.java 
b/src/main/java/org/codehaus/groovy/runtime/IntersectionCastSupport.java
index 06eec2654b..1306676b33 100644
--- a/src/main/java/org/codehaus/groovy/runtime/IntersectionCastSupport.java
+++ b/src/main/java/org/codehaus/groovy/runtime/IntersectionCastSupport.java
@@ -35,7 +35,7 @@ import java.util.Map;
  * reference (those cases are handled at compile time via
  * {@code LambdaMetafactory.altMetafactory} markers).
  *
- * @since 5.0.0
+ * @since 6.0.0
  */
 public final class IntersectionCastSupport {
 
diff --git 
a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java 
b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index a7a5c13819..b3e89c8a41 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -542,7 +542,7 @@ public class JavaStubGenerator {
     }
 
     private void printMethods(final PrintWriter out, final ClassNode 
classNode, final boolean isEnum, final boolean isRecordStub) {
-        // For native record stubs, let javac auto-synthesise the canonical
+        // For native record stubs, let javac auto-synthesize the canonical
         // constructor; we cannot reliably emit a placeholder body that
         // satisfies record component definite-assignment rules. Non-canonical
         // user-declared constructors are not visible in the stub (known
diff --git 
a/src/main/java/org/codehaus/groovy/transform/RecordBaseASTStubber.java 
b/src/main/java/org/codehaus/groovy/transform/RecordBaseASTStubber.java
index be2b529f00..dc41f865ff 100644
--- a/src/main/java/org/codehaus/groovy/transform/RecordBaseASTStubber.java
+++ b/src/main/java/org/codehaus/groovy/transform/RecordBaseASTStubber.java
@@ -94,7 +94,7 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
  * <p><b>Native records.</b> When the class would compile as a native JVM
  * record (target {@code >= JDK16} and {@code mode != EMULATE}), the stub
  * generator already renders {@code record Foo(...)} syntax via the
- * back-channel introduced by GROOVY-11974, and {@code javac} synthesises
+ * back-channel introduced by GROOVY-11974, and {@code javac} synthesizes
  * the canonical constructor and component accessors itself. This stubber
  * detects that case via
  * {@link RecordTypeASTTransformation#wouldBeNativeRecord} and bails out;
diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index f3cb833c23..5606a97d3e 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1628,7 +1628,7 @@ out:    if ((samParameterTypes.length == 1 && 
isOrImplements(samParameterTypes[0
     }
 
     /**
-     * Build a synthesised ListExpression of {@code rhs.getAt(i)} accesses, 
one per LHS slot,
+     * Build a synthesized ListExpression of {@code rhs.getAt(i)} accesses, 
one per LHS slot,
      * tagged with the inferred element type. Used by {@link 
#typeCheckMultipleAssignmentPositional}
      * when the RHS isn't a literal list/range/Tuple-typed expression. Returns 
the original
      * RHS unchanged if its static type doesn't support {@code getAt(int)}.
diff --git a/src/spec/doc/core-differences-java.adoc 
b/src/spec/doc/core-differences-java.adoc
index a3725ce055..47b5bfd9a2 100644
--- a/src/spec/doc/core-differences-java.adoc
+++ b/src/spec/doc/core-differences-java.adoc
@@ -244,7 +244,7 @@ or other marker interfaces, just like in Java:
 
 [source,groovy]
 ----
-Runnable r = (Runnable & Serializable) () -> println('hi') // serialisable 
lambda
+Runnable r = (Runnable & Serializable) () -> println('hi') // serializable 
lambda
 ----
 
 Groovy additionally accepts the `as` form, where parentheses are required 
around
diff --git a/src/spec/doc/core-semantics.adoc b/src/spec/doc/core-semantics.adoc
index ab9bebac5a..caa17a6771 100644
--- a/src/spec/doc/core-semantics.adoc
+++ b/src/spec/doc/core-semantics.adoc
@@ -793,7 +793,7 @@ 
include::../test/CoercionTest.groovy[tags=intersection_as_coercion_marker,indent
 ----
 
 For closure literals and maps, `as` builds a
-{@link groovy.util.ProxyGenerator} aggregate that implements every interface
+`groovy.util.ProxyGenerator` aggregate that implements every interface
 component:
 
 [source,groovy]
diff --git a/src/test-resources/core/Groovydoc_02x.groovy 
b/src/test-resources/core/Groovydoc_02x.groovy
index 16e18c4501..8cb100208f 100644
--- a/src/test-resources/core/Groovydoc_02x.groovy
+++ b/src/test-resources/core/Groovydoc_02x.groovy
@@ -25,7 +25,7 @@
 //
 // Known runtime-doc gaps (pre-existing, independent of GROOVY-8877):
 //   * Script-level /**@: a leading /**@ on a bare script is not attached to
-//     the synthesised Script class — no AST node to claim it.
+//     the synthesized Script class — no AST node to claim it.
 //   * @Field lift: /**@ before an @Field declaration is lost when the AST
 //     transform lifts the local into a FieldNode; the annotation isn't
 //     carried across.
@@ -36,7 +36,7 @@
  */
 void m() {}
 
-// /**@ on a script's top-level method reaches the synthesised method.
+// /**@ on a script's top-level method reaches the synthesized method.
 def mMethod = this.class.getDeclaredMethods().find { it.name == 'm' }
 assert mMethod != null
 assert mMethod.groovydoc.isPresent()
diff --git a/src/test/groovy/bugs/Groovy11967.groovy 
b/src/test/groovy/bugs/Groovy11967.groovy
index b3f893072a..8a7b60c783 100644
--- a/src/test/groovy/bugs/Groovy11967.groovy
+++ b/src/test/groovy/bugs/Groovy11967.groovy
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test
 import static groovy.test.GroovyAssert.assertScript
 
 /**
- * Regression coverage for the synthesised lower-arity bridge constructor that
+ * Regression coverage for the synthesized lower-arity bridge constructor that
  * {@code @CompileStatic} emits for a constructor with a default-valued list
  * parameter. The bridge inlines the default {@code [...]} literal as a
  * {@code new ArrayList(n)} followed by {@code .add(...)} calls; when the
diff --git 
a/src/test/groovy/groovy/transform/stc/IntersectionCastSTCTest.groovy 
b/src/test/groovy/groovy/transform/stc/IntersectionCastSTCTest.groovy
index ba9186c10c..e7e01bd6fe 100644
--- a/src/test/groovy/groovy/transform/stc/IntersectionCastSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/IntersectionCastSTCTest.groovy
@@ -39,9 +39,9 @@ import org.junit.jupiter.api.Test
 /**
  * Tests for static type checking of intersection-cast targets (GROOVY-11998 
PR2).
  *
- * These tests stop compilation at {@link Phases#SEMANTIC_ANALYSIS} so they
- * exercise resolution and STC without relying on bytecode generation, which
- * is delivered in subsequent phases.
+ * These tests stop compilation at {@link Phases#INSTRUCTION_SELECTION} so they
+ * exercise resolution and STC without producing class files, which keeps the
+ * tests focused on resolution/STC behavior delivered in this PR.
  */
 final class IntersectionCastSTCTest {
 
diff --git 
a/src/test/groovy/org/apache/groovy/parser/antlr4/IntersectionCastParserTest.groovy
 
b/src/test/groovy/org/apache/groovy/parser/antlr4/IntersectionCastParserTest.groovy
index 5e6a54db0d..ae278f787d 100644
--- 
a/src/test/groovy/org/apache/groovy/parser/antlr4/IntersectionCastParserTest.groovy
+++ 
b/src/test/groovy/org/apache/groovy/parser/antlr4/IntersectionCastParserTest.groovy
@@ -22,7 +22,6 @@ import org.codehaus.groovy.ast.ClassNode
 import org.codehaus.groovy.ast.IntersectionTypeClassNode
 import org.codehaus.groovy.ast.ModuleNode
 import org.codehaus.groovy.ast.expr.CastExpression
-import org.codehaus.groovy.ast.expr.ClassExpression
 import org.codehaus.groovy.ast.expr.DeclarationExpression
 import org.codehaus.groovy.ast.expr.Expression
 import org.codehaus.groovy.ast.expr.BinaryExpression
@@ -36,7 +35,6 @@ import org.junit.jupiter.api.Test
 import static org.junit.jupiter.api.Assertions.assertNotNull
 import static org.junit.jupiter.api.Assertions.assertTrue
 import static org.junit.jupiter.api.Assertions.assertEquals
-import static org.junit.jupiter.api.Assertions.assertThrows
 
 /**
  * Tests for the parser-level handling of intersection types in cast 
expressions
diff --git 
a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/TagRenderer.java
 
b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/TagRenderer.java
index f8d7384ee4..9122c9abcf 100644
--- 
a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/TagRenderer.java
+++ 
b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/TagRenderer.java
@@ -60,7 +60,7 @@ import java.util.regex.PatternSyntaxException;
  *
  * <p>Supported block tags: {@code @see}, {@code @param}, {@code @return},
  * {@code @throws} / {@code @exception}, {@code @since}, {@code @author},
- * {@code @version}, {@code @default}, plus synthesised {@code typeparam}
+ * {@code @version}, {@code @default}, plus synthesized {@code typeparam}
  * (from {@code @param <T>}). Unknown block tags fall through to a generic
  * {@code <DL>} rendering.
  *
@@ -110,7 +110,7 @@ final class TagRenderer {
         COLLATED_TAGS.put("author", "Authors");
         COLLATED_TAGS.put("version", "Version");
         COLLATED_TAGS.put("default", "Default");
-        // typeparam is synthesised from `@param <T> desc`
+        // typeparam is synthesized from `@param <T> desc`
         COLLATED_TAGS.put("typeparam", "Type Parameters");
     }
 
diff --git 
a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
 
b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
index b967353c0f..49e673e65d 100644
--- 
a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
+++ 
b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovydocJavaVisitor.java
@@ -190,7 +190,7 @@ public class GroovydocJavaVisitor
         currentClassDoc.add(fieldDoc);
         applyJavadocComment(n.getJavadocComment(), fieldDoc);
         n.getDefaultValue().ifPresent(defValue -> {
-            // For Markdown-form comments (no `*` line prefix), the synthesised
+            // For Markdown-form comments (no `*` line prefix), the synthesized
             // @default tag goes on a bare line; traditional /** */ form keeps
             // the `* ` prefix for visual parity with existing continuation 
lines.
             String prefix = fieldDoc.isMarkdown() ? "\n@default " : "\n* 
@default ";
diff --git 
a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
 
b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
index 9a1e936da0..52a9a9b283 100644
--- 
a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
+++ 
b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
@@ -371,7 +371,7 @@ public class GroovyDocToolTest extends GroovyTestCase {
         String fixtureSourcePath = "src/test/resources/docfiles-fixture";
         String pkg = "org/codehaus/groovy/tools/groovydoc/testfiles/docfiles";
 
-        // Synthesise a class on the fly that uses {@snippet file=... 
region=...}
+        // Synthesize a class on the fly that uses {@snippet file=... 
region=...}
         // Actually, just use a testfile in src/test/groovy that we add to 
sourcepath.
         // Here we reuse a class with an inline doc reference via the 
resource-dir.
         Path tmp = Files.createTempDirectory("snippet-region-");
@@ -1613,7 +1613,7 @@ public class GroovyDocToolTest extends GroovyTestCase {
         String klass = "EnumWithAbstractMethodAndConstantBodies";
         Properties props = new Properties();
         // phase 7 = CLASS_GENERATION, by which point the per-constant 
anonymous
-        // inner classes have been synthesised. The fix must keep them out of
+        // inner classes have been synthesized. The fix must keep them out of
         // the doc output at any phase.
         props.put("phaseOverride", "7");
         GroovyDocTool tool = makeHtmltool(new ArrayList<>(), null, props);
diff --git 
a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/ScriptWithMarkdownTopLevelDoc.groovy
 
b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/ScriptWithMarkdownTopLevelDoc.groovy
index 81dc549148..a97bb0a5dc 100644
--- 
a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/ScriptWithMarkdownTopLevelDoc.groovy
+++ 
b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/ScriptWithMarkdownTopLevelDoc.groovy
@@ -18,7 +18,7 @@
  */
 
 /// GROOVY-11542 + GROOVY-8877 fixture: a **Markdown** script-level doc that
-/// should be lifted to the synthesised Script class via the same rules used
+/// should be lifted to the synthesized Script class via the same rules used
 /// for traditional Javadoc comments.
 package org.codehaus.groovy.tools.groovydoc.testfiles
 

Reply via email to