Skip to content

Commit 1f77317

Browse files
add strictTuples flag giving tuples known length
1 parent d03d107 commit 1f77317

File tree

27 files changed

+185
-69
lines changed

27 files changed

+185
-69
lines changed

src/compiler/checker.ts

+7
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ namespace ts {
6464
const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters;
6565
const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System;
6666
const strictNullChecks = compilerOptions.strictNullChecks === undefined ? compilerOptions.strict : compilerOptions.strictNullChecks;
67+
const strictTuples = compilerOptions.strictTuples === undefined ? compilerOptions.strict : compilerOptions.strictTuples;
6768
const noImplicitAny = compilerOptions.noImplicitAny === undefined ? compilerOptions.strict : compilerOptions.noImplicitAny;
6869
const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis;
6970

@@ -7161,6 +7162,12 @@ namespace ts {
71617162
property.type = typeParameter;
71627163
properties.push(property);
71637164
}
7165+
if (strictTuples) {
7166+
const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String);
7167+
lengthSymbol.type = getLiteralType(arity);
7168+
lengthSymbol.checkFlags = CheckFlags.Readonly;
7169+
properties.push(lengthSymbol);
7170+
}
71647171
const type = <GenericType & InterfaceTypeWithDeclaredMembers>createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
71657172
type.typeParameters = typeParameters;
71667173
type.outerTypeParameters = undefined;

src/compiler/commandLineParser.ts

+7
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,13 @@ namespace ts {
269269
category: Diagnostics.Strict_Type_Checking_Options,
270270
description: Diagnostics.Enable_strict_null_checks
271271
},
272+
{
273+
name: "strictTuples",
274+
type: "boolean",
275+
showInSimplifiedHelpView: true,
276+
category: Diagnostics.Strict_Type_Checking_Options,
277+
description: Diagnostics.Enable_strict_tuple_checks
278+
},
272279
{
273280
name: "noImplicitThis",
274281
type: "boolean",

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3302,6 +3302,10 @@
33023302
"category": "Message",
33033303
"code": 6185
33043304
},
3305+
"Enable strict tuple checks.": {
3306+
"category": "Message",
3307+
"code": 6187
3308+
},
33053309
"Variable '{0}' implicitly has an '{1}' type.": {
33063310
"category": "Error",
33073311
"code": 7005

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3622,6 +3622,7 @@ namespace ts {
36223622
sourceRoot?: string;
36233623
strict?: boolean;
36243624
strictNullChecks?: boolean; // Always combine with strict property
3625+
strictTuples?: boolean;
36253626
/* @internal */ stripInternal?: boolean;
36263627
suppressExcessPropertyErrors?: boolean;
36273628
suppressImplicitAnyIndexErrors?: boolean;

src/harness/unittests/configurationExtension.ts

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ namespace ts {
1616
strictNullChecks: false
1717
}
1818
},
19+
"/dev/tsconfig.strictTuples.json": {
20+
extends: "./tsconfig",
21+
compilerOptions: {
22+
strictTuples: false
23+
}
24+
},
1925
"/dev/configs/base.json": {
2026
compilerOptions: {
2127
allowJs: true,

src/harness/unittests/transpile.ts

+4
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ var x = 0;`, {
413413
options: { compilerOptions: { strictNullChecks: true }, fileName: "input.js", reportDiagnostics: true }
414414
});
415415

416+
transpilesCorrectly("Supports setting 'strictTuples'", "x;", {
417+
options: { compilerOptions: { strictTuples: true }, fileName: "input.js", reportDiagnostics: true }
418+
});
419+
416420
transpilesCorrectly("Supports setting 'stripInternal'", "x;", {
417421
options: { compilerOptions: { stripInternal: true }, fileName: "input.js", reportDiagnostics: true }
418422
});

src/server/protocol.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2448,6 +2448,7 @@ namespace ts.server.protocol {
24482448
sourceRoot?: string;
24492449
strict?: boolean;
24502450
strictNullChecks?: boolean;
2451+
strictTuples?: boolean;
24512452
suppressExcessPropertyErrors?: boolean;
24522453
suppressImplicitAnyIndexErrors?: boolean;
24532454
target?: ScriptTarget | ts.ScriptTarget;

tests/baselines/reference/genericTypeAliases.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ type Strange<T> = string; // Type parameter not used
4040
var s: Strange<number>;
4141
s = "hello";
4242

43-
interface Tuple<A, B> {
43+
interface AB<A, B> {
4444
a: A;
4545
b: B;
4646
}
4747

48-
type Pair<T> = Tuple<T, T>;
48+
type Pair<T> = AB<T, T>;
4949

5050
interface TaggedPair<T> extends Pair<T> {
5151
tag: string;

tests/baselines/reference/genericTypeAliases.symbols

+16-16
Original file line numberDiff line numberDiff line change
@@ -124,29 +124,29 @@ var s: Strange<number>;
124124
s = "hello";
125125
>s : Symbol(s, Decl(genericTypeAliases.ts, 38, 3))
126126

127-
interface Tuple<A, B> {
128-
>Tuple : Symbol(Tuple, Decl(genericTypeAliases.ts, 39, 12))
129-
>A : Symbol(A, Decl(genericTypeAliases.ts, 41, 16))
130-
>B : Symbol(B, Decl(genericTypeAliases.ts, 41, 18))
127+
interface AB<A, B> {
128+
>AB : Symbol(AB, Decl(genericTypeAliases.ts, 39, 12))
129+
>A : Symbol(A, Decl(genericTypeAliases.ts, 41, 13))
130+
>B : Symbol(B, Decl(genericTypeAliases.ts, 41, 15))
131131

132132
a: A;
133-
>a : Symbol(Tuple.a, Decl(genericTypeAliases.ts, 41, 23))
134-
>A : Symbol(A, Decl(genericTypeAliases.ts, 41, 16))
133+
>a : Symbol(AB.a, Decl(genericTypeAliases.ts, 41, 20))
134+
>A : Symbol(A, Decl(genericTypeAliases.ts, 41, 13))
135135

136136
b: B;
137-
>b : Symbol(Tuple.b, Decl(genericTypeAliases.ts, 42, 9))
138-
>B : Symbol(B, Decl(genericTypeAliases.ts, 41, 18))
137+
>b : Symbol(AB.b, Decl(genericTypeAliases.ts, 42, 9))
138+
>B : Symbol(B, Decl(genericTypeAliases.ts, 41, 15))
139139
}
140140

141-
type Pair<T> = Tuple<T, T>;
141+
type Pair<T> = AB<T, T>;
142142
>Pair : Symbol(Pair, Decl(genericTypeAliases.ts, 44, 1))
143143
>T : Symbol(T, Decl(genericTypeAliases.ts, 46, 10))
144-
>Tuple : Symbol(Tuple, Decl(genericTypeAliases.ts, 39, 12))
144+
>AB : Symbol(AB, Decl(genericTypeAliases.ts, 39, 12))
145145
>T : Symbol(T, Decl(genericTypeAliases.ts, 46, 10))
146146
>T : Symbol(T, Decl(genericTypeAliases.ts, 46, 10))
147147

148148
interface TaggedPair<T> extends Pair<T> {
149-
>TaggedPair : Symbol(TaggedPair, Decl(genericTypeAliases.ts, 46, 27))
149+
>TaggedPair : Symbol(TaggedPair, Decl(genericTypeAliases.ts, 46, 24))
150150
>T : Symbol(T, Decl(genericTypeAliases.ts, 48, 21))
151151
>Pair : Symbol(Pair, Decl(genericTypeAliases.ts, 44, 1))
152152
>T : Symbol(T, Decl(genericTypeAliases.ts, 48, 21))
@@ -157,17 +157,17 @@ interface TaggedPair<T> extends Pair<T> {
157157

158158
var p: TaggedPair<number>;
159159
>p : Symbol(p, Decl(genericTypeAliases.ts, 52, 3))
160-
>TaggedPair : Symbol(TaggedPair, Decl(genericTypeAliases.ts, 46, 27))
160+
>TaggedPair : Symbol(TaggedPair, Decl(genericTypeAliases.ts, 46, 24))
161161

162162
p.a = 1;
163-
>p.a : Symbol(Tuple.a, Decl(genericTypeAliases.ts, 41, 23))
163+
>p.a : Symbol(AB.a, Decl(genericTypeAliases.ts, 41, 20))
164164
>p : Symbol(p, Decl(genericTypeAliases.ts, 52, 3))
165-
>a : Symbol(Tuple.a, Decl(genericTypeAliases.ts, 41, 23))
165+
>a : Symbol(AB.a, Decl(genericTypeAliases.ts, 41, 20))
166166

167167
p.b = 2;
168-
>p.b : Symbol(Tuple.b, Decl(genericTypeAliases.ts, 42, 9))
168+
>p.b : Symbol(AB.b, Decl(genericTypeAliases.ts, 42, 9))
169169
>p : Symbol(p, Decl(genericTypeAliases.ts, 52, 3))
170-
>b : Symbol(Tuple.b, Decl(genericTypeAliases.ts, 42, 9))
170+
>b : Symbol(AB.b, Decl(genericTypeAliases.ts, 42, 9))
171171

172172
p.tag = "test";
173173
>p.tag : Symbol(TaggedPair.tag, Decl(genericTypeAliases.ts, 48, 41))

tests/baselines/reference/genericTypeAliases.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ s = "hello";
158158
>s : string
159159
>"hello" : "hello"
160160

161-
interface Tuple<A, B> {
162-
>Tuple : Tuple<A, B>
161+
interface AB<A, B> {
162+
>AB : AB<A, B>
163163
>A : A
164164
>B : B
165165

@@ -172,17 +172,17 @@ interface Tuple<A, B> {
172172
>B : B
173173
}
174174

175-
type Pair<T> = Tuple<T, T>;
176-
>Pair : Tuple<T, T>
175+
type Pair<T> = AB<T, T>;
176+
>Pair : AB<T, T>
177177
>T : T
178-
>Tuple : Tuple<A, B>
178+
>AB : AB<A, B>
179179
>T : T
180180
>T : T
181181

182182
interface TaggedPair<T> extends Pair<T> {
183183
>TaggedPair : TaggedPair<T>
184184
>T : T
185-
>Pair : Tuple<T, T>
185+
>Pair : AB<T, T>
186186
>T : T
187187

188188
tag: string;

tests/baselines/reference/nominalSubtypeCheckOfTypeParameter.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
//// [nominalSubtypeCheckOfTypeParameter.ts]
2-
interface Tuple<T, S> {
2+
interface BinaryTuple<T, S> {
33
first: T
4-
second: S
4+
second: S
55
}
66

77
interface Sequence<T> {
88
hasNext(): boolean
9-
pop(): T
10-
zip<S>(seq: Sequence<S>): Sequence<Tuple<T, S>>
9+
pop(): T
10+
zip<S>(seq: Sequence<S>): Sequence<BinaryTuple<T, S>>
1111
}
1212

1313
// error, despite the fact that the code explicitly says List<T> extends Sequence<T>, the current rules for infinitely expanding type references
1414
// perform nominal subtyping checks that allow variance for type arguments, but not nominal subtyping for the generic type itself
1515
interface List<T> extends Sequence<T> {
1616
getLength(): number
17-
zip<S>(seq: Sequence<S>): List<Tuple<T, S>>
17+
zip<S>(seq: Sequence<S>): List<BinaryTuple<T, S>>
1818
}
1919

2020

Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
=== tests/cases/conformance/types/typeRelationships/recursiveTypes/nominalSubtypeCheckOfTypeParameter.ts ===
2-
interface Tuple<T, S> {
3-
>Tuple : Symbol(Tuple, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 0))
4-
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 16))
5-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 18))
2+
interface BinaryTuple<T, S> {
3+
>BinaryTuple : Symbol(BinaryTuple, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 0))
4+
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 22))
5+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 24))
66

77
first: T
8-
>first : Symbol(Tuple.first, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 23))
9-
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 16))
8+
>first : Symbol(BinaryTuple.first, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 29))
9+
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 22))
1010

11-
second: S
12-
>second : Symbol(Tuple.second, Decl(nominalSubtypeCheckOfTypeParameter.ts, 1, 12))
13-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 18))
11+
second: S
12+
>second : Symbol(BinaryTuple.second, Decl(nominalSubtypeCheckOfTypeParameter.ts, 1, 12))
13+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 24))
1414
}
1515

1616
interface Sequence<T> {
@@ -20,20 +20,20 @@ interface Sequence<T> {
2020
hasNext(): boolean
2121
>hasNext : Symbol(Sequence.hasNext, Decl(nominalSubtypeCheckOfTypeParameter.ts, 5, 23))
2222

23-
pop(): T
23+
pop(): T
2424
>pop : Symbol(Sequence.pop, Decl(nominalSubtypeCheckOfTypeParameter.ts, 6, 22))
2525
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 5, 19))
2626

27-
zip<S>(seq: Sequence<S>): Sequence<Tuple<T, S>>
28-
>zip : Symbol(Sequence.zip, Decl(nominalSubtypeCheckOfTypeParameter.ts, 7, 14))
29-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 10))
30-
>seq : Symbol(seq, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 13))
27+
zip<S>(seq: Sequence<S>): Sequence<BinaryTuple<T, S>>
28+
>zip : Symbol(Sequence.zip, Decl(nominalSubtypeCheckOfTypeParameter.ts, 7, 12))
29+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 8))
30+
>seq : Symbol(seq, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 11))
3131
>Sequence : Symbol(Sequence, Decl(nominalSubtypeCheckOfTypeParameter.ts, 3, 1))
32-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 10))
32+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 8))
3333
>Sequence : Symbol(Sequence, Decl(nominalSubtypeCheckOfTypeParameter.ts, 3, 1))
34-
>Tuple : Symbol(Tuple, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 0))
34+
>BinaryTuple : Symbol(BinaryTuple, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 0))
3535
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 5, 19))
36-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 10))
36+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 8, 8))
3737
}
3838

3939
// error, despite the fact that the code explicitly says List<T> extends Sequence<T>, the current rules for infinitely expanding type references
@@ -47,15 +47,15 @@ interface List<T> extends Sequence<T> {
4747
getLength(): number
4848
>getLength : Symbol(List.getLength, Decl(nominalSubtypeCheckOfTypeParameter.ts, 13, 39))
4949

50-
zip<S>(seq: Sequence<S>): List<Tuple<T, S>>
50+
zip<S>(seq: Sequence<S>): List<BinaryTuple<T, S>>
5151
>zip : Symbol(List.zip, Decl(nominalSubtypeCheckOfTypeParameter.ts, 14, 23))
52-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 10))
53-
>seq : Symbol(seq, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 13))
52+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 8))
53+
>seq : Symbol(seq, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 11))
5454
>Sequence : Symbol(Sequence, Decl(nominalSubtypeCheckOfTypeParameter.ts, 3, 1))
55-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 10))
55+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 8))
5656
>List : Symbol(List, Decl(nominalSubtypeCheckOfTypeParameter.ts, 9, 1))
57-
>Tuple : Symbol(Tuple, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 0))
57+
>BinaryTuple : Symbol(BinaryTuple, Decl(nominalSubtypeCheckOfTypeParameter.ts, 0, 0))
5858
>T : Symbol(T, Decl(nominalSubtypeCheckOfTypeParameter.ts, 13, 15))
59-
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 10))
59+
>S : Symbol(S, Decl(nominalSubtypeCheckOfTypeParameter.ts, 15, 8))
6060
}
6161

tests/baselines/reference/nominalSubtypeCheckOfTypeParameter.types

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
=== tests/cases/conformance/types/typeRelationships/recursiveTypes/nominalSubtypeCheckOfTypeParameter.ts ===
2-
interface Tuple<T, S> {
3-
>Tuple : Tuple<T, S>
2+
interface BinaryTuple<T, S> {
3+
>BinaryTuple : BinaryTuple<T, S>
44
>T : T
55
>S : S
66

77
first: T
88
>first : T
99
>T : T
1010

11-
second: S
11+
second: S
1212
>second : S
1313
>S : S
1414
}
@@ -20,18 +20,18 @@ interface Sequence<T> {
2020
hasNext(): boolean
2121
>hasNext : () => boolean
2222

23-
pop(): T
23+
pop(): T
2424
>pop : () => T
2525
>T : T
2626

27-
zip<S>(seq: Sequence<S>): Sequence<Tuple<T, S>>
28-
>zip : <S>(seq: Sequence<S>) => Sequence<Tuple<T, S>>
27+
zip<S>(seq: Sequence<S>): Sequence<BinaryTuple<T, S>>
28+
>zip : <S>(seq: Sequence<S>) => Sequence<BinaryTuple<T, S>>
2929
>S : S
3030
>seq : Sequence<S>
3131
>Sequence : Sequence<T>
3232
>S : S
3333
>Sequence : Sequence<T>
34-
>Tuple : Tuple<T, S>
34+
>BinaryTuple : BinaryTuple<T, S>
3535
>T : T
3636
>S : S
3737
}
@@ -47,14 +47,14 @@ interface List<T> extends Sequence<T> {
4747
getLength(): number
4848
>getLength : () => number
4949

50-
zip<S>(seq: Sequence<S>): List<Tuple<T, S>>
51-
>zip : <S>(seq: Sequence<S>) => List<Tuple<T, S>>
50+
zip<S>(seq: Sequence<S>): List<BinaryTuple<T, S>>
51+
>zip : <S>(seq: Sequence<S>) => List<BinaryTuple<T, S>>
5252
>S : S
5353
>seq : Sequence<S>
5454
>Sequence : Sequence<T>
5555
>S : S
5656
>List : List<T>
57-
>Tuple : Tuple<T, S>
57+
>BinaryTuple : BinaryTuple<T, S>
5858
>T : T
5959
>S : S
6060
}

tests/baselines/reference/transpile/Supports setting strictTuples.js

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"strict": true /* Enable all strict type-checking options. */
2323
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
2424
// "strictNullChecks": true, /* Enable strict null checks. */
25+
// "strictTuples": true, /* Enable strict tuple checks. */
2526
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
2627
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
2728

tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"strict": true, /* Enable all strict type-checking options. */
2323
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
2424
// "strictNullChecks": true, /* Enable strict null checks. */
25+
// "strictTuples": true, /* Enable strict tuple checks. */
2526
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
2627
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
2728

tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"strict": true /* Enable all strict type-checking options. */
2323
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
2424
// "strictNullChecks": true, /* Enable strict null checks. */
25+
// "strictTuples": true, /* Enable strict tuple checks. */
2526
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
2627
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
2728

tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"strict": true /* Enable all strict type-checking options. */
2323
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
2424
// "strictNullChecks": true, /* Enable strict null checks. */
25+
// "strictTuples": true, /* Enable strict tuple checks. */
2526
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
2627
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
2728

0 commit comments

Comments
 (0)