Skip to content

Commit 5cd1bd7

Browse files
committed
change the flags processing for property
1 parent e265e18 commit 5cd1bd7

File tree

10 files changed

+163
-53
lines changed

10 files changed

+163
-53
lines changed

src/ast/classconstant.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,17 @@ const ClassConstant = ConstantStatement.extends(
5353
* @return {void}
5454
*/
5555
ClassConstant.prototype.parseFlags = function (flags) {
56-
if (flags[0] === -1) {
56+
const getVis = flags[0][0];
57+
if (getVis === -1) {
5758
this.visibility = IS_UNDEFINED;
58-
} else if (flags[0] === null) {
59+
} else if (getVis === null) {
5960
/* istanbul ignore next */
6061
this.visibility = null;
61-
} else if (flags[0] === 0) {
62+
} else if (getVis === 0) {
6263
this.visibility = IS_PUBLIC;
63-
} else if (flags[0] === 1) {
64+
} else if (getVis === 1) {
6465
this.visibility = IS_PROTECTED;
65-
} else if (flags[0] === 2) {
66+
} else if (getVis === 2) {
6667
this.visibility = IS_PRIVATE;
6768
}
6869
this.final = flags[2] === 2;

src/ast/declaration.js

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@ const IS_PUBLIC = "public";
1313
const IS_PROTECTED = "protected";
1414
const IS_PRIVATE = "private";
1515

16-
const SET_VISIBILITY_MAP = {
17-
0: IS_PUBLIC,
18-
1: IS_PROTECTED,
19-
2: IS_PRIVATE,
20-
};
16+
const VISIBILITY_MAP = [IS_PUBLIC, IS_PROTECTED, IS_PRIVATE];
2117

2218
/**
2319
* A declaration statement (function, class, interface...)
@@ -48,23 +44,17 @@ Declaration.prototype.parseFlags = function (flags) {
4844
this.isFinal = flags[2] === 2;
4945
this.isReadonly = flags[3] === 1;
5046
if (this.kind !== "class") {
51-
if (flags[0] === -1) {
47+
const [getVis, setVis] = flags[0];
48+
if (getVis === -1) {
5249
this.visibility = IS_UNDEFINED;
53-
} else if (flags[0] === null) {
50+
} else if (getVis === null) {
5451
/* istanbul ignore next */
5552
this.visibility = null;
56-
} else if (flags[0] === 0) {
57-
this.visibility = IS_PUBLIC;
58-
} else if (flags[0] === 1) {
59-
this.visibility = IS_PROTECTED;
60-
} else if (flags[0] === 2) {
61-
this.visibility = IS_PRIVATE;
53+
} else {
54+
this.visibility = VISIBILITY_MAP[getVis];
6255
}
6356
this.isStatic = flags[1] === 1;
64-
this.visibilitySet =
65-
flags[4] !== undefined && flags[4] !== -1
66-
? SET_VISIBILITY_MAP[flags[4]]
67-
: null;
57+
this.visibilitySet = setVis !== -1 ? VISIBILITY_MAP[setVis] : null;
6858
}
6959
};
7060

src/ast/propertystatement.js

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@ const IS_PUBLIC = "public";
1313
const IS_PROTECTED = "protected";
1414
const IS_PRIVATE = "private";
1515

16-
const SET_VISIBILITY_MAP = {
17-
0: IS_PUBLIC,
18-
1: IS_PROTECTED,
19-
2: IS_PRIVATE,
20-
};
16+
const VISIBILITY_MAP = [IS_PUBLIC, IS_PROTECTED, IS_PRIVATE];
2117

2218
/**
2319
* Declares a properties into the current scope
@@ -46,23 +42,17 @@ const PropertyStatement = Statement.extends(
4642
* @return {void}
4743
*/
4844
PropertyStatement.prototype.parseFlags = function (flags) {
49-
if (flags[0] === -1) {
45+
const [getVis, setVis] = flags[0];
46+
if (getVis === -1) {
5047
this.visibility = IS_UNDEFINED;
51-
} else if (flags[0] === null) {
48+
} else if (getVis === null) {
5249
this.visibility = null;
53-
} else if (flags[0] === 0) {
54-
this.visibility = IS_PUBLIC;
55-
} else if (flags[0] === 1) {
56-
this.visibility = IS_PROTECTED;
57-
} else if (flags[0] === 2) {
58-
this.visibility = IS_PRIVATE;
50+
} else {
51+
this.visibility = VISIBILITY_MAP[getVis];
5952
}
6053

6154
this.isStatic = flags[1] === 1;
62-
this.visibilitySet =
63-
flags[4] !== undefined && flags[4] !== -1
64-
? SET_VISIBILITY_MAP[flags[4]]
65-
: null;
55+
this.visibilitySet = setVis !== -1 ? VISIBILITY_MAP[setVis] : null;
6656
};
6757

6858
module.exports = PropertyStatement;

src/ast/traitalias.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ module.exports = Node.extends(
3232
this.as = as;
3333
this.visibility = IS_UNDEFINED;
3434
if (flags) {
35-
if (flags[0] === 0) {
35+
const getVis = flags[0][0];
36+
if (getVis === 0) {
3637
this.visibility = IS_PUBLIC;
37-
} else if (flags[0] === 1) {
38+
} else if (getVis === 1) {
3839
this.visibility = IS_PROTECTED;
39-
} else if (flags[0] === 2) {
40+
} else if (getVis === 2) {
4041
this.visibility = IS_PRIVATE;
4142
}
4243
}

src/parser/class.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ module.exports = {
114114

115115
// check constant
116116
if (this.token === this.tok.T_CONST) {
117+
if (flags[0][1] !== -1) {
118+
this.raiseError("Cannot use asymmetric visibility on constants");
119+
}
117120
const constants = this.read_constant_list(flags, attrs);
118121
if (this.expect(";")) {
119122
this.next();
@@ -125,7 +128,7 @@ module.exports = {
125128
// jump over T_VAR then land on T_VARIABLE
126129
if (allow_variables && this.token === this.tok.T_VAR) {
127130
this.next().expect(this.tok.T_VARIABLE);
128-
flags[0] = null; // public (as null)
131+
flags[0][0] = null; // public (as null)
129132
flags[1] = 0; // non static var
130133
}
131134

@@ -267,14 +270,14 @@ module.exports = {
267270
/*
268271
* Read member flags
269272
* @return array
270-
* 1st index : -1 => no visibility, 0 => public, 1 => protected, 2 => private
273+
* 1st index : [get, set] visibility tuple
274+
* get/set: -1 => no visibility, 0 => public, 1 => protected, 2 => private
271275
* 2nd index : 0 => instance member, 1 => static member
272276
* 3rd index : 0 => normal, 1 => abstract member, 2 => final member
273277
* 4th index : 0 => no readonly, 1 => readonly
274-
* 5th index : -1 => no set modifier, 0 => public(set), 1 => protected(set), 2 => private(set)
275278
*/
276279
read_member_flags(asInterface) {
277-
const result = [-1, 0, 0, 0, -1];
280+
const result = [[-1, -1], 0, 0, 0];
278281
const seen = new Set();
279282
while (this.is("T_MEMBER_FLAGS")) {
280283
let idx = -1,
@@ -307,19 +310,19 @@ module.exports = {
307310
if (this.expect(")")) {
308311
this.next(); // consume ')'
309312
}
310-
if (seen.has(4)) {
313+
if (seen.has("set")) {
311314
this.error(); // set modifier already defined
312315
} else if (val !== -1) {
313-
seen.add(4);
314-
result[4] = val;
316+
seen.add("set");
317+
result[0][1] = val;
315318
}
316319
continue;
317320
}
318321
if (seen.has(idx)) {
319322
this.error();
320323
} else if (val !== -1) {
321324
seen.add(idx);
322-
result[idx] = val;
325+
result[0][0] = val;
323326
}
324327
continue;
325328
}
@@ -482,6 +485,9 @@ module.exports = {
482485

483486
// check constant
484487
if (this.token === this.tok.T_CONST) {
488+
if (flags[0][1] !== -1) {
489+
this.raiseError("Cannot use asymmetric visibility on constants");
490+
}
485491
const constants = this.read_constant_list(flags, attrs);
486492
if (this.expect(";")) {
487493
this.next();

test/snapshot/__snapshots__/asymmetric-visibility.test.js.snap

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
11
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

3+
exports[`asymmetric visibility asymmetric visibility on constant 1`] = `
4+
Program {
5+
"children": [
6+
Class {
7+
"attrGroups": [],
8+
"body": [
9+
ClassConstant {
10+
"attrGroups": [],
11+
"constants": [
12+
Constant {
13+
"kind": "constant",
14+
"name": Identifier {
15+
"kind": "identifier",
16+
"name": "BAR",
17+
},
18+
"value": Number {
19+
"kind": "number",
20+
"value": "1",
21+
},
22+
},
23+
],
24+
"final": false,
25+
"kind": "classconstant",
26+
"nullable": false,
27+
"type": null,
28+
"visibility": "public",
29+
},
30+
],
31+
"extends": null,
32+
"implements": null,
33+
"isAbstract": false,
34+
"isAnonymous": false,
35+
"isFinal": false,
36+
"isReadonly": false,
37+
"kind": "class",
38+
"name": Identifier {
39+
"kind": "identifier",
40+
"name": "Foo",
41+
},
42+
},
43+
],
44+
"errors": [
45+
Error {
46+
"expected": undefined,
47+
"kind": "error",
48+
"line": 1,
49+
"message": "Cannot use asymmetric visibility on constants on line 1",
50+
"token": undefined,
51+
},
52+
],
53+
"kind": "program",
54+
}
55+
`;
56+
357
exports[`asymmetric visibility constructor promotion malformed set keyword explicit 1`] = `
458
Program {
559
"children": [

test/snapshot/__snapshots__/trait.test.js.snap

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
11
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
22

3+
exports[`trait trait alias with visibility 1`] = `
4+
Program {
5+
"children": [
6+
Class {
7+
"attrGroups": [],
8+
"body": [
9+
TraitUse {
10+
"adaptations": [
11+
TraitAlias {
12+
"as": Identifier {
13+
"kind": "identifier",
14+
"name": "bar",
15+
},
16+
"kind": "traitalias",
17+
"method": "foo",
18+
"trait": null,
19+
"visibility": "public",
20+
},
21+
TraitAlias {
22+
"as": null,
23+
"kind": "traitalias",
24+
"method": "foo",
25+
"trait": null,
26+
"visibility": "protected",
27+
},
28+
],
29+
"kind": "traituse",
30+
"traits": [
31+
Name {
32+
"kind": "name",
33+
"name": "B",
34+
"resolution": "uqn",
35+
},
36+
],
37+
},
38+
],
39+
"extends": null,
40+
"implements": null,
41+
"isAbstract": false,
42+
"isAnonymous": false,
43+
"isFinal": false,
44+
"isReadonly": false,
45+
"kind": "class",
46+
"name": Identifier {
47+
"kind": "identifier",
48+
"name": "A",
49+
},
50+
},
51+
],
52+
"errors": [],
53+
"kind": "program",
54+
}
55+
`;
56+
357
exports[`trait trait name as identifier 1`] = `
458
Program {
559
"children": [

test/snapshot/asymmetric-visibility.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ describe("asymmetric visibility", () => {
127127
),
128128
).toMatchSnapshot();
129129
});
130+
it("asymmetric visibility on constant", () => {
131+
expect(
132+
parser.parseEval("class Foo { public private(set) const BAR = 1; }", {
133+
parser: { version: 804, suppressErrors: true },
134+
}),
135+
).toMatchSnapshot();
136+
});
130137
it("ignored below PHP 8.4", () => {
131138
expect(
132139
parser.parseEval("class Foo { public private(set) string $name; }", {

test/snapshot/trait.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,11 @@ describe("trait", function () {
44
it("trait name as identifier", function () {
55
expect(parser.parseEval("trait A {}")).toMatchSnapshot();
66
});
7+
it("trait alias with visibility", function () {
8+
expect(
9+
parser.parseEval(
10+
"class A { use B { foo as public bar; foo as protected; } }",
11+
),
12+
).toMatchSnapshot();
13+
});
714
});

types.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ declare module "php-parser" {
157157
/**
158158
* Generic flags parser
159159
*/
160-
parseFlags(flags: (number | null)[]): void;
160+
parseFlags(flags: [[number | null, number], number, number, number]): void;
161161
visibility: string;
162162
final: boolean;
163163
nullable: boolean;
@@ -223,7 +223,7 @@ declare module "php-parser" {
223223
/**
224224
* Generic flags parser
225225
*/
226-
parseFlags(flags: (number | null)[]): void;
226+
parseFlags(flags: [[number | null, number], number, number, number]): void;
227227
name: Identifier | string;
228228
visibilitySet: string | null;
229229
}
@@ -795,7 +795,7 @@ declare module "php-parser" {
795795
/**
796796
* Generic flags parser
797797
*/
798-
parseFlags(flags: (number | null)[]): void;
798+
parseFlags(flags: [[number | null, number], number, number, number]): void;
799799
properties: Property[];
800800
visibility: string | null;
801801
visibilitySet: string | null;

0 commit comments

Comments
 (0)