semantifold 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/build/src/backends/effects.js +3 -1
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +56 -3
- package/build/src/backends/python.js +11 -7
- package/build/src/backends/scalars.js +2 -2
- package/build/src/backends/shared.d.ts +2 -1
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +210 -8
- package/build/src/backends/writer.d.ts +18 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +40 -4
- package/build/src/frontends/java.d.ts +1 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +14 -11
- package/build/src/frontends/javascript-typescript.d.ts +1 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +17 -17
- package/build/src/frontends/php.d.ts +5 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +159 -31
- package/build/src/frontends/python.d.ts.map +1 -1
- package/build/src/frontends/python.js +37 -7
- package/build/src/frontends/ruby.d.ts +1 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +12 -9
- package/build/src/frontends/types.d.ts +9 -2
- package/build/src/frontends/types.d.ts.map +1 -1
- package/build/src/frontends/types.js +26 -9
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +30 -6
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +4 -2
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +3 -1
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +37 -4
- package/build/src/semantic/types.d.ts +140 -9
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +37 -6
- package/build/src/semantic/validate.d.ts +31 -2
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +347 -28
- package/docs/adr-bounded-subtyping.md +180 -0
- package/docs/goals.md +1 -1
- package/docs/language-support.md +5 -4
- package/docs/testing.md +2 -2
- package/package.json +1 -1
- package/src/backends/effects.js +1 -0
- package/src/backends/php.js +56 -2
- package/src/backends/python.js +10 -4
- package/src/backends/scalars.js +1 -1
- package/src/backends/shared.js +211 -7
- package/src/backends/writer.js +41 -3
- package/src/frontends/java.js +13 -10
- package/src/frontends/javascript-typescript.js +16 -16
- package/src/frontends/php.js +175 -31
- package/src/frontends/python.js +39 -5
- package/src/frontends/ruby.js +11 -8
- package/src/frontends/types.js +26 -8
- package/src/language-registry.js +29 -5
- package/src/mapping.js +3 -1
- package/src/semantic/effects.js +1 -0
- package/src/semantic/provenance.js +34 -3
- package/src/semantic/types.js +39 -5
- package/src/semantic/validate.js +378 -27
|
@@ -16,7 +16,7 @@ import {documentedValueType, instantiatedRecordFieldType, iterationBindingType,
|
|
|
16
16
|
/** @typedef {NonNullable<import("@babel/parser").ParseResult<import("@babel/types").File>["tokens"]>[number]} BabelToken */
|
|
17
17
|
/** @typedef {{byStart: Map<number, BabelToken>, tokens: BabelToken[]}} BabelTokenIndex */
|
|
18
18
|
/** @typedef {{name: string, nameLocation: import("../semantic/types.js").SourceLocation, parameters: import("../semantic/types.js").Parameter[], returnType: import("../semantic/types.js").SemanticFunctionReturnType, typeParameters?: import("../semantic/types.js").TypeParameter[], location: import("../semantic/types.js").SourceLocation}} JavaScriptFunctionSignature */
|
|
19
|
-
/** @typedef {{bindings: Map<string, import("../semantic/types.js").SemanticBindingType>, catchBindingNames?: Set<string>, catchBindingNodes?: Map<string, import("../semantic/types.js").CatchBinding>, classes: Map<string, import("../semantic/types.js").ClassDeclaration>, currentClass?: import("../semantic/types.js").ClassDeclaration, errors: Map<string, import("../semantic/types.js").ErrorDeclaration>, errorNames: Map<string, import("../semantic/types.js").ErrorDeclaration>, functions: Map<string, JavaScriptFunctionSignature>, lexicalValueNames?: Set<string>, orderedMapDeclarations?: Set<object>, recordNames: Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>, records: Map<string, import("../semantic/types.js").RecordDeclaration>, returnType?: import("../semantic/types.js").SemanticFunctionReturnType, typeParameters?: Map<string, import("../semantic/types.js").TypeParameter>, valueRecordNames: Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration>}} JavaScriptConversionContext */
|
|
19
|
+
/** @typedef {{bindings: Map<string, import("../semantic/types.js").SemanticBindingType>, catchBindingNames?: Set<string>, catchBindingNodes?: Map<string, import("../semantic/types.js").CatchBinding>, classes: Map<string, import("../semantic/types.js").ClassDeclaration>, currentClass?: import("../semantic/types.js").ClassDeclaration, errors: Map<string, import("../semantic/types.js").ErrorDeclaration>, errorNames: Map<string, import("../semantic/types.js").ErrorDeclaration>, functions: Map<string, JavaScriptFunctionSignature>, lexicalValueNames?: Set<string>, orderedMapDeclarations?: Set<object>, recordNames: Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>, records: Map<string, import("../semantic/types.js").RecordDeclaration>, returnType?: import("../semantic/types.js").SemanticFunctionReturnType, typeParameters?: Map<string, import("../semantic/types.js").TypeParameter>, valueRecordNames: Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration>}} JavaScriptConversionContext */
|
|
20
20
|
|
|
21
21
|
/** @type {WeakMap<object, BabelTokenIndex>} */
|
|
22
22
|
const nodeTokens = new WeakMap()
|
|
@@ -875,7 +875,7 @@ function convertLocalStatement(node, language, filename, source, context) {
|
|
|
875
875
|
* @param {string} name - Local name.
|
|
876
876
|
* @param {string} filename - Source filename.
|
|
877
877
|
* @param {string} source - Complete source.
|
|
878
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
878
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
879
879
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
880
880
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
881
881
|
*/
|
|
@@ -1393,7 +1393,7 @@ function classJsdocTypeParameters(node, ownerId, filename, source) {
|
|
|
1393
1393
|
* @param {import("../semantic/types.js").SourceLocation} location - Source location.
|
|
1394
1394
|
* @param {string} source - Complete parser input.
|
|
1395
1395
|
* @param {import("../semantic/types.js").SourceLocation} [typeLocation] - Exact type token location.
|
|
1396
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1396
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1397
1397
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1398
1398
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
1399
1399
|
*/
|
|
@@ -1411,7 +1411,7 @@ function convertType(sourceType, language, subject, location, source, typeLocati
|
|
|
1411
1411
|
* @param {import("../semantic/types.js").SourceLocation} location - Function location.
|
|
1412
1412
|
* @param {string} source - Complete parser input.
|
|
1413
1413
|
* @param {import("../semantic/types.js").SourceLocation} [typeLocation] - Exact annotation location.
|
|
1414
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1414
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1415
1415
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1416
1416
|
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic return type.
|
|
1417
1417
|
*/
|
|
@@ -1428,7 +1428,7 @@ function convertReturnType(sourceType, language, subject, location, source, type
|
|
|
1428
1428
|
* @param {import("../semantic/types.js").SourceLocation} ownerLocation - Owning declaration location.
|
|
1429
1429
|
* @param {string} filename - Source filename.
|
|
1430
1430
|
* @param {string} source - Complete source.
|
|
1431
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1431
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1432
1432
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1433
1433
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
1434
1434
|
*/
|
|
@@ -1448,7 +1448,7 @@ function convertTypeScriptType(annotation, subject, ownerLocation, filename, sou
|
|
|
1448
1448
|
* @param {import("../semantic/types.js").SourceLocation} ownerLocation - Owning declaration.
|
|
1449
1449
|
* @param {string} filename - Source filename.
|
|
1450
1450
|
* @param {string} source - Complete source.
|
|
1451
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1451
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1452
1452
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1453
1453
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic value type.
|
|
1454
1454
|
*/
|
|
@@ -1467,7 +1467,7 @@ function convertTypeScriptValueTypeNode(typeNode, subject, ownerLocation, filena
|
|
|
1467
1467
|
}
|
|
1468
1468
|
if (typeNode.type == "TSTypeReference" && typeNode.typeName.type == "Identifier" &&
|
|
1469
1469
|
recordNames.has(typeNode.typeName.name)) {
|
|
1470
|
-
const declaration = /** @type {import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration} */ (recordNames.get(typeNode.typeName.name))
|
|
1470
|
+
const declaration = /** @type {import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration} */ (recordNames.get(typeNode.typeName.name))
|
|
1471
1471
|
|
|
1472
1472
|
if (declaration.kind == "EffectResourceDeclaration") {
|
|
1473
1473
|
if (typeNode.typeParameters) return unsupportedSyntax("typescript", "generic owned resource type", nodeLocation(typeNode, filename, source))
|
|
@@ -1549,7 +1549,7 @@ function convertTypeScriptValueTypeNode(typeNode, subject, ownerLocation, filena
|
|
|
1549
1549
|
* @param {import("../semantic/types.js").SourceLocation} ownerLocation - Function location.
|
|
1550
1550
|
* @param {string} filename - Source filename.
|
|
1551
1551
|
* @param {string} source - Complete source.
|
|
1552
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1552
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1553
1553
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
1554
1554
|
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic return type.
|
|
1555
1555
|
*/
|
|
@@ -1596,7 +1596,7 @@ function typeScriptTypeParameters(declaration, ownerId, filename, source) {
|
|
|
1596
1596
|
* @param {"javascript" | "typescript"} language - Frontend language.
|
|
1597
1597
|
* @param {string} filename - Source filename.
|
|
1598
1598
|
* @param {string} source - Complete source.
|
|
1599
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1599
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1600
1600
|
* @param {string} ownerId - Stable function identity.
|
|
1601
1601
|
* @returns {JavaScriptFunctionSignature} Semantic function signature.
|
|
1602
1602
|
*/
|
|
@@ -1653,7 +1653,7 @@ function convertFunctionSignature(node, language, filename, source, recordNames,
|
|
|
1653
1653
|
* @param {import("@babel/types").FunctionDeclaration} node - Babel function.
|
|
1654
1654
|
* @param {JavaScriptFunctionSignature} signature - Converted signature.
|
|
1655
1655
|
* @param {Map<string, JavaScriptFunctionSignature>} functions - Module signatures.
|
|
1656
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by name.
|
|
1656
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by name.
|
|
1657
1657
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration>} valueRecordNames - Nominal declarations available in value positions.
|
|
1658
1658
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
1659
1659
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
@@ -1783,7 +1783,7 @@ function convertJavaScriptError(node, declaration, language, filename, source) {
|
|
|
1783
1783
|
* Predeclares the bounded native private-field reference-class profile and all callable signatures.
|
|
1784
1784
|
* @param {import("@babel/types").ClassDeclaration} node - Parser class.
|
|
1785
1785
|
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Predeclared class identity.
|
|
1786
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Nominal types by name.
|
|
1786
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Nominal types by name.
|
|
1787
1787
|
* @param {"javascript" | "typescript"} language - Source language.
|
|
1788
1788
|
* @param {string} filename - Source filename.
|
|
1789
1789
|
* @param {string} source - Complete source.
|
|
@@ -1874,7 +1874,7 @@ function predeclareReferenceClass(node, declaration, nominalNames, language, fil
|
|
|
1874
1874
|
* Converts one reference class body after every class signature is complete.
|
|
1875
1875
|
* @param {import("@babel/types").ClassDeclaration} node - Parser class.
|
|
1876
1876
|
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Complete predeclared class signature.
|
|
1877
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Nominal types by name.
|
|
1877
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Nominal types by name.
|
|
1878
1878
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration>} valueNames - Nominal constructors by name.
|
|
1879
1879
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
|
|
1880
1880
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Classes by identity.
|
|
@@ -1916,7 +1916,7 @@ function convertReferenceClassBodies(node, declaration, nominalNames, valueNames
|
|
|
1916
1916
|
* Reads one constructor or method signature without admitting overloads or modifiers.
|
|
1917
1917
|
* @param {import("@babel/types").ClassMethod} node - Parser method.
|
|
1918
1918
|
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Declaring class.
|
|
1919
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Nominal types.
|
|
1919
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Nominal types.
|
|
1920
1920
|
* @param {"javascript" | "typescript"} language - Source language.
|
|
1921
1921
|
* @param {string} filename - Source filename.
|
|
1922
1922
|
* @param {string} source - Complete source.
|
|
@@ -1972,7 +1972,7 @@ function convertClassCallableSignature(node, declaration, nominalNames, language
|
|
|
1972
1972
|
* Converts one exact TypeScript readonly constructor-parameter record profile.
|
|
1973
1973
|
* @param {import("@babel/types").ClassDeclaration} node - Class declaration.
|
|
1974
1974
|
* @param {import("../semantic/types.js").RecordDeclaration} declaration - Predeclared nominal identity.
|
|
1975
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - All nominal declarations by name.
|
|
1975
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - All nominal declarations by name.
|
|
1976
1976
|
* @param {string} filename - Source filename.
|
|
1977
1977
|
* @param {string} source - Complete source.
|
|
1978
1978
|
* @returns {import("../semantic/types.js").RecordDeclaration} Semantic record declaration.
|
|
@@ -2027,7 +2027,7 @@ function convertTypeScriptRecord(node, declaration, recordNames, filename, sourc
|
|
|
2027
2027
|
* Converts one exact JSDoc constructor/readonly-assignment/freeze record profile.
|
|
2028
2028
|
* @param {import("@babel/types").ClassDeclaration} node - Class declaration.
|
|
2029
2029
|
* @param {import("../semantic/types.js").RecordDeclaration} declaration - Predeclared nominal identity.
|
|
2030
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by name.
|
|
2030
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by name.
|
|
2031
2031
|
* @param {string} filename - Source filename.
|
|
2032
2032
|
* @param {string} source - Complete source.
|
|
2033
2033
|
* @returns {import("../semantic/types.js").RecordDeclaration} Semantic record declaration.
|
|
@@ -2359,7 +2359,7 @@ export function parseJavaScriptTypeScript({capabilities = [], filename, language
|
|
|
2359
2359
|
fields: [], id: `class:${index}`, kind: /** @type {const} */ ("ClassDeclaration"),
|
|
2360
2360
|
location: nodeLocation(node, filename, source), methods: [], name: node.id?.name ?? ""
|
|
2361
2361
|
}))
|
|
2362
|
-
const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} */ (
|
|
2362
|
+
const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} */ (
|
|
2363
2363
|
new Map(program?.records ?? []))
|
|
2364
2364
|
for (const resource of capabilities.flatMap(({resources}) => resources)) recordNames.set(resource.name, resource)
|
|
2365
2365
|
const valueRecordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration>} */ (
|
package/src/frontends/php.js
CHANGED
|
@@ -12,7 +12,7 @@ import {hasExactEffectSupport} from "../backends/effects.js"
|
|
|
12
12
|
import {hasOnlyUnicodeScalars, scalarType} from "../semantic/scalars.js"
|
|
13
13
|
import {typeContainsAnyVariable} from "../semantic/generics.js"
|
|
14
14
|
import {requireSourceReturnType, requireSourceScalarType, sourceScalarType} from "./scalars.js"
|
|
15
|
-
import {capabilityFunctionSignatures, documentedValueType, instantiatedRecordFieldType, iterationBindingType, iterationOperandType, knownCallParameterTypes, knownCallReturnType, optionalType, orderedMapTypeFromMap, ownedResourceType, preservesGenericOptionalEvidence, recordType, referenceType, sameValueType} from "./types.js"
|
|
15
|
+
import {capabilityFunctionSignatures, documentedValueType, instantiatedRecordFieldType, interfaceType, iterationBindingType, iterationOperandType, knownCallParameterTypes, knownCallReturnType, optionalType, orderedMapTypeFromMap, ownedResourceType, preservesGenericOptionalEvidence, recordType, referenceType, sameValueType} from "./types.js"
|
|
16
16
|
const parser = new PhpParser.Engine({
|
|
17
17
|
ast: {withPositions: true},
|
|
18
18
|
parser: {extractDoc: true, suppressErrors: false}
|
|
@@ -43,11 +43,12 @@ const phpBinaryOperations = new Map([
|
|
|
43
43
|
* @property {Map<string, import("../semantic/types.js").CatchBinding>} [catchBindingNodes] - Exact catch binding nodes by visible name.
|
|
44
44
|
* @property {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
45
45
|
* @property {import("../semantic/types.js").ClassDeclaration} [currentClass] - Enclosing reference class.
|
|
46
|
+
* @property {Map<string, import("../semantic/types.js").InterfaceDeclaration>} interfaces - Interface declarations by identity.
|
|
46
47
|
* @property {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
|
|
47
48
|
* @property {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Error declarations by identity.
|
|
48
49
|
* @property {Map<string, PhpFunctionSignature>} functions - Explicit module function signatures.
|
|
49
50
|
* @property {Set<object>} [orderedMapDeclarations] - Exact local declarations selected by parser-owned pair iteration.
|
|
50
|
-
* @property {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
51
|
+
* @property {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
51
52
|
* @property {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
52
53
|
* @property {import("../semantic/types.js").SemanticFunctionReturnType | undefined} returnType - Enclosing return type.
|
|
53
54
|
* @property {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
@@ -325,6 +326,9 @@ function convertExpression(node, filename, source, context, expectedType, preser
|
|
|
325
326
|
const declaration = context.recordNames.get(name.name)
|
|
326
327
|
|
|
327
328
|
if (!declaration) return unsupportedSyntax("php", "construction of an unknown nominal class", nodeLocation(construction.what, filename, source))
|
|
329
|
+
if (declaration.kind == "InterfaceDeclaration") {
|
|
330
|
+
return unsupportedSyntax("php", "construction of an interface", nodeLocation(construction.what, filename, source))
|
|
331
|
+
}
|
|
328
332
|
if (declaration.kind == "ClassDeclaration") {
|
|
329
333
|
const unsupportedArgument = construction.arguments.find((argument) =>
|
|
330
334
|
argument.kind == "namedargument" || argument.kind == "variadic" || Reflect.get(argument, "byref") === true)
|
|
@@ -382,6 +386,9 @@ function convertExpression(node, filename, source, context, expectedType, preser
|
|
|
382
386
|
if (receiverType?.kind == "ReferenceType") {
|
|
383
387
|
return unsupportedSyntax("php", "private reference-class field access", location)
|
|
384
388
|
}
|
|
389
|
+
if (receiverType?.kind == "InterfaceType") {
|
|
390
|
+
return unsupportedSyntax("php", "interface receiver member access", location)
|
|
391
|
+
}
|
|
385
392
|
const receiverRecord = receiverType?.kind == "OptionalType" ? receiverType.valueType : receiverType
|
|
386
393
|
const declaration = receiverRecord?.kind == "RecordType" ? context.records.get(receiverRecord.declarationId) : undefined
|
|
387
394
|
|
|
@@ -510,8 +517,10 @@ function convertExpression(node, filename, source, context, expectedType, preser
|
|
|
510
517
|
const member = lookup.offset.kind == "identifier" ? /** @type {import("php-parser").Identifier} */ (lookup.offset) : undefined
|
|
511
518
|
const receiverType = knownExpressionType(lookup.what, context)
|
|
512
519
|
|
|
513
|
-
if (member && typeof member.name == "string" && receiverType?.kind == "ReferenceType") {
|
|
514
|
-
const method =
|
|
520
|
+
if (member && typeof member.name == "string" && (receiverType?.kind == "ReferenceType" || receiverType?.kind == "InterfaceType")) {
|
|
521
|
+
const method = receiverType.kind == "ReferenceType"
|
|
522
|
+
? context.classes.get(receiverType.declarationId)?.methods.find((candidate) => candidate.name == member.name)
|
|
523
|
+
: context.interfaces.get(receiverType.declarationId)?.methods.find((candidate) => candidate.name == member.name)
|
|
515
524
|
const unsupportedArgument = call.arguments.find((argument) =>
|
|
516
525
|
argument.kind == "namedargument" || argument.kind == "variadic" || Reflect.get(argument, "byref") === true)
|
|
517
526
|
|
|
@@ -632,6 +641,9 @@ function knownExpressionType(node, context) {
|
|
|
632
641
|
if (receiver?.kind == "ReferenceType" && member && typeof member.name == "string") {
|
|
633
642
|
return context.classes.get(receiver.declarationId)?.methods.find((method) => method.name == member.name)?.returnType
|
|
634
643
|
}
|
|
644
|
+
if (receiver?.kind == "InterfaceType" && member && typeof member.name == "string") {
|
|
645
|
+
return context.interfaces.get(receiver.declarationId)?.methods.find((method) => method.name == member.name)?.returnType
|
|
646
|
+
}
|
|
635
647
|
}
|
|
636
648
|
}
|
|
637
649
|
if (node.kind == "offsetlookup") {
|
|
@@ -648,7 +660,9 @@ function knownExpressionType(node, context) {
|
|
|
648
660
|
|
|
649
661
|
if (declaration?.id) return declaration.kind == "ClassDeclaration"
|
|
650
662
|
? {declarationId: declaration.id, kind: "ReferenceType"}
|
|
651
|
-
:
|
|
663
|
+
: declaration.kind == "InterfaceDeclaration"
|
|
664
|
+
? {declarationId: declaration.id, kind: "InterfaceType"}
|
|
665
|
+
: {declarationId: declaration.id, kind: "RecordType"}
|
|
652
666
|
}
|
|
653
667
|
if (node.kind == "propertylookup") {
|
|
654
668
|
const read = /** @type {import("php-parser").PropertyLookup} */ (node)
|
|
@@ -756,7 +770,7 @@ function convertReturn(node, filename, source, context) {
|
|
|
756
770
|
* @param {string} name - Local name.
|
|
757
771
|
* @param {string} filename - Source filename.
|
|
758
772
|
* @param {string} source - Complete source.
|
|
759
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
773
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
760
774
|
* @param {Map<string, import("../semantic/types.js").TypeParameter>} [typeParameters] - Declaration-scoped parameters.
|
|
761
775
|
* @returns {{immutable: boolean, type: import("../semantic/types.js").SemanticValueType} | undefined} Metadata when present.
|
|
762
776
|
*/
|
|
@@ -876,7 +890,7 @@ function convertLocalStatement(node, filename, source, context) {
|
|
|
876
890
|
const inferredType = knownExpressionType(assignment.right, context)
|
|
877
891
|
|
|
878
892
|
if (inferredType?.kind == "RecordType" || inferredType?.kind == "ReferenceType" ||
|
|
879
|
-
inferredType?.kind == "OwnedResourceType") {
|
|
893
|
+
inferredType?.kind == "InterfaceType" || inferredType?.kind == "OwnedResourceType") {
|
|
880
894
|
const initializer = convertExpression(assignment.right, filename, source, context, inferredType)
|
|
881
895
|
|
|
882
896
|
context.bindings.set(variable.name, inferredType)
|
|
@@ -1195,7 +1209,7 @@ function convertBlock(node, filename, source, context) {
|
|
|
1195
1209
|
* @param {import("../semantic/types.js").SourceLocation} location - Source location.
|
|
1196
1210
|
* @param {string} filename - Source filename.
|
|
1197
1211
|
* @param {string} source - Complete source.
|
|
1198
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1212
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1199
1213
|
* @returns {import("../semantic/types.js").SemanticValueType} Semantic type.
|
|
1200
1214
|
*/
|
|
1201
1215
|
function convertType(sourceType, subject, location, filename, source, recordNames = new Map()) {
|
|
@@ -1211,9 +1225,11 @@ function convertType(sourceType, subject, location, filename, source, recordName
|
|
|
1211
1225
|
|
|
1212
1226
|
if (declaration?.id) return declaration.kind == "ClassDeclaration"
|
|
1213
1227
|
? referenceType(declaration.id, typeLocation)
|
|
1214
|
-
: declaration.kind == "
|
|
1215
|
-
?
|
|
1216
|
-
:
|
|
1228
|
+
: declaration.kind == "InterfaceDeclaration"
|
|
1229
|
+
? interfaceType(declaration.id, typeLocation)
|
|
1230
|
+
: declaration.kind == "EffectResourceDeclaration"
|
|
1231
|
+
? ownedResourceType(declaration.id, typeLocation)
|
|
1232
|
+
: recordType(declaration.id, typeLocation)
|
|
1217
1233
|
|
|
1218
1234
|
return requireSourceScalarType("php", typeName, subject, location, typeLocation)
|
|
1219
1235
|
}
|
|
@@ -1331,7 +1347,7 @@ function nullableType(valueType, sourceType, ownerStart, filename, source) {
|
|
|
1331
1347
|
* @param {import("../semantic/types.js").SourceLocation} location - Function location.
|
|
1332
1348
|
* @param {string} filename - Source filename.
|
|
1333
1349
|
* @param {string} source - Complete source.
|
|
1334
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1350
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} [recordNames] - Nominal declarations by source name.
|
|
1335
1351
|
* @returns {import("../semantic/types.js").SemanticFunctionReturnType} Semantic return type.
|
|
1336
1352
|
*/
|
|
1337
1353
|
function convertReturnType(sourceType, subject, location, filename, source, recordNames = new Map()) {
|
|
@@ -1344,9 +1360,11 @@ function convertReturnType(sourceType, subject, location, filename, source, reco
|
|
|
1344
1360
|
|
|
1345
1361
|
if (declaration?.id) return declaration.kind == "ClassDeclaration"
|
|
1346
1362
|
? referenceType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1347
|
-
: declaration.kind == "
|
|
1348
|
-
?
|
|
1349
|
-
:
|
|
1363
|
+
: declaration.kind == "InterfaceDeclaration"
|
|
1364
|
+
? interfaceType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1365
|
+
: declaration.kind == "EffectResourceDeclaration"
|
|
1366
|
+
? ownedResourceType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1367
|
+
: recordType(declaration.id, nodeLocation(sourceType, filename, source))
|
|
1350
1368
|
|
|
1351
1369
|
return requireSourceReturnType("php", typeName, subject, location, nodeLocation(sourceType, filename, source))
|
|
1352
1370
|
}
|
|
@@ -1356,7 +1374,7 @@ function convertReturnType(sourceType, subject, location, filename, source, reco
|
|
|
1356
1374
|
* @param {import("php-parser").Function} node - PHP function node.
|
|
1357
1375
|
* @param {string} filename - Source filename.
|
|
1358
1376
|
* @param {string} source - Complete source.
|
|
1359
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1377
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1360
1378
|
* @param {string} ownerId - Stable function identity.
|
|
1361
1379
|
* @returns {PhpFunctionSignature} Typed function signature.
|
|
1362
1380
|
*/
|
|
@@ -1457,7 +1475,7 @@ function convertFunctionSignature(node, filename, source, recordNames, ownerId)
|
|
|
1457
1475
|
* @param {string | undefined} nativeType - Native PHP type name.
|
|
1458
1476
|
* @param {boolean} nativeNullable - Whether the native carrier has PHP's nullable marker.
|
|
1459
1477
|
* @param {import("../semantic/types.js").SemanticValueType} documentedType - Exact semantic document type.
|
|
1460
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} records - Visible nominal declarations by name.
|
|
1478
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} records - Visible nominal declarations by name.
|
|
1461
1479
|
* @returns {boolean} Whether the native carrier agrees without changing meaning.
|
|
1462
1480
|
*/
|
|
1463
1481
|
function phpDocumentedNativeTypeMatches(nativeType, nativeNullable, documentedType, records) {
|
|
@@ -1482,6 +1500,9 @@ function phpDocumentedNativeTypeMatches(nativeType, nativeNullable, documentedTy
|
|
|
1482
1500
|
if (documentedType.kind == "ReferenceType") {
|
|
1483
1501
|
return nativeType !== undefined && records.get(nativeType)?.id == documentedType.declarationId
|
|
1484
1502
|
}
|
|
1503
|
+
if (documentedType.kind == "InterfaceType") {
|
|
1504
|
+
return nativeType !== undefined && records.get(nativeType)?.id == documentedType.declarationId
|
|
1505
|
+
}
|
|
1485
1506
|
|
|
1486
1507
|
return false
|
|
1487
1508
|
}
|
|
@@ -1491,16 +1512,17 @@ function phpDocumentedNativeTypeMatches(nativeType, nativeNullable, documentedTy
|
|
|
1491
1512
|
* @param {import("php-parser").Function} node - PHP function node.
|
|
1492
1513
|
* @param {PhpFunctionSignature} signature - Preconverted explicit signature.
|
|
1493
1514
|
* @param {Map<string, PhpFunctionSignature>} functions - Module function signatures.
|
|
1494
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1515
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} recordNames - Nominal declarations by source name.
|
|
1495
1516
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Record declarations by identity.
|
|
1496
1517
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
1518
|
+
* @param {Map<string, import("../semantic/types.js").InterfaceDeclaration>} interfaces - Interface declarations by identity.
|
|
1497
1519
|
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Error declarations by source name.
|
|
1498
1520
|
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Error declarations by identity.
|
|
1499
1521
|
* @param {string} filename - Source filename.
|
|
1500
1522
|
* @param {string} source - Complete source.
|
|
1501
1523
|
* @returns {import("../semantic/types.js").FunctionDeclaration} Semantic function.
|
|
1502
1524
|
*/
|
|
1503
|
-
function convertFunction(node, signature, functions, recordNames, records, classes, errorNames, errors, filename, source) {
|
|
1525
|
+
function convertFunction(node, signature, functions, recordNames, records, classes, interfaces, errorNames, errors, filename, source) {
|
|
1504
1526
|
if (!node.body) return unsupportedSyntax("php", "function body", signature.location)
|
|
1505
1527
|
const context = {
|
|
1506
1528
|
bindings: new Map(signature.parameters.map((parameter) => [parameter.name, parameter.type])),
|
|
@@ -1516,7 +1538,7 @@ function convertFunction(node, signature, functions, recordNames, records, class
|
|
|
1516
1538
|
}
|
|
1517
1539
|
|
|
1518
1540
|
return withParserRanges({
|
|
1519
|
-
body: convertBlock(node.body, filename, source, context),
|
|
1541
|
+
body: convertBlock(node.body, filename, source, {...context, interfaces}),
|
|
1520
1542
|
kind: "FunctionDeclaration",
|
|
1521
1543
|
location: signature.location,
|
|
1522
1544
|
name: signature.name,
|
|
@@ -1613,7 +1635,7 @@ function convertPhpError(node, declaration, filename, source) {
|
|
|
1613
1635
|
/**
|
|
1614
1636
|
* Reads one exact public PHP constructor or instance-method signature.
|
|
1615
1637
|
* @param {import("php-parser").Method} node - Parser-owned method.
|
|
1616
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1638
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1617
1639
|
* @param {string} ownerId - Stable callable identity.
|
|
1618
1640
|
* @param {boolean} constructor - Whether this is the sole constructor.
|
|
1619
1641
|
* @param {string} filename - Source filename.
|
|
@@ -1696,13 +1718,106 @@ function convertPhpClassCallableSignature(node, nominalNames, ownerId, construct
|
|
|
1696
1718
|
return {location, name, nameLocation: nodeLocation(nameNode, filename, source), parameters, returnType}
|
|
1697
1719
|
}
|
|
1698
1720
|
|
|
1721
|
+
/**
|
|
1722
|
+
* Converts the signature-only PHP interface profile.
|
|
1723
|
+
* @param {import("php-parser").Class} node - Parser interface.
|
|
1724
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration} declaration - Predeclared interface identity.
|
|
1725
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1726
|
+
* @param {string} filename - Source filename.
|
|
1727
|
+
* @param {string} source - Complete source.
|
|
1728
|
+
* @returns {import("../semantic/types.js").InterfaceDeclaration} Semantic interface.
|
|
1729
|
+
*/
|
|
1730
|
+
function convertPhpInterface(node, declaration, nominalNames, filename, source) {
|
|
1731
|
+
const location = nodeLocation(node, filename, source)
|
|
1732
|
+
const parents = /** @type {readonly import("php-parser").Name[]} */ (/** @type {unknown} */ (node.extends ?? []))
|
|
1733
|
+
|
|
1734
|
+
if (node.isAnonymous || node.attrGroups.length) return unsupportedSyntax("php", "malformed interface declaration", location)
|
|
1735
|
+
const parentReferences = parents.map((parent) => {
|
|
1736
|
+
if (parent.kind != "name" || parent.resolution != "uqn" || typeof parent.name != "string") {
|
|
1737
|
+
return unsupportedSyntax("php", "qualified or dynamic interface parent", nodeLocation(parent, filename, source))
|
|
1738
|
+
}
|
|
1739
|
+
const parentDeclaration = nominalNames.get(parent.name)
|
|
1740
|
+
|
|
1741
|
+
if (!parentDeclaration?.id || parentDeclaration.kind != "InterfaceDeclaration") {
|
|
1742
|
+
return unsupportedSyntax("php", `interface parent '${parent.name}' is not a declared interface`, nodeLocation(parent, filename, source))
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
return interfaceType(/** @type {string} */ (parentDeclaration.id), nodeLocation(parent, filename, source))
|
|
1746
|
+
})
|
|
1747
|
+
const methodNodes = /** @type {import("php-parser").Method[]} */ (node.body.filter((member) => member.kind == "method"))
|
|
1748
|
+
|
|
1749
|
+
if (methodNodes.length != node.body.length) {
|
|
1750
|
+
return unsupportedSyntax("php", "interface member outside one signature-only public method",
|
|
1751
|
+
nodeLocation(node.body.find((member) => member.kind != "method") ?? node, filename, source))
|
|
1752
|
+
}
|
|
1753
|
+
declaration.methods = methodNodes.map((method, index) => convertPhpInterfaceMethodSignature(method, nominalNames,
|
|
1754
|
+
`${declaration.id}:method:${index}`, filename, source))
|
|
1755
|
+
const nameNode = typeof node.name == "string" ? node : node.name
|
|
1756
|
+
|
|
1757
|
+
return withParserRanges({
|
|
1758
|
+
...declaration,
|
|
1759
|
+
...(parentReferences.length > 0 ? {extends: parentReferences} : {})
|
|
1760
|
+
}, {name: nodeLocation(nameNode, filename, source)})
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* Reads one exact signature-only PHP interface method.
|
|
1765
|
+
* @param {import("php-parser").Method} node - Parser interface method.
|
|
1766
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1767
|
+
* @param {string} ownerId - Stable interface method identity.
|
|
1768
|
+
* @param {string} filename - Source filename.
|
|
1769
|
+
* @param {string} source - Complete source.
|
|
1770
|
+
* @returns {import("../semantic/types.js").InterfaceMethodDeclaration} Semantic interface method.
|
|
1771
|
+
*/
|
|
1772
|
+
function convertPhpInterfaceMethodSignature(node, nominalNames, ownerId, filename, source) {
|
|
1773
|
+
const location = nodeLocation(node, filename, source)
|
|
1774
|
+
const name = typeof node.name == "string" ? node.name : node.name.name
|
|
1775
|
+
|
|
1776
|
+
if (node.visibility != "public" || node.isStatic || node.isAbstract || node.isFinal || Reflect.get(node, "isReadonly") ||
|
|
1777
|
+
node.byref || node.nullable || node.attrGroups.length || node.body !== null) {
|
|
1778
|
+
return unsupportedSyntax("php", "interface method outside one public signature-only declaration", location)
|
|
1779
|
+
}
|
|
1780
|
+
const documented = functionDocumentedTypes(/** @type {import("php-parser").Function} */ (/** @type {unknown} */ (node)),
|
|
1781
|
+
filename, source, ownerId)
|
|
1782
|
+
|
|
1783
|
+
if (documented.parameters.size > 0 || documented.returnType || documented.typeParameters?.length) {
|
|
1784
|
+
return unsupportedSyntax("php", "documented interface method type annotation", location)
|
|
1785
|
+
}
|
|
1786
|
+
if (!node.type) return missingType("php", `Interface method '${name}' return`, location)
|
|
1787
|
+
const parameters = node.arguments.map((parameter) => {
|
|
1788
|
+
const parameterLocation = nodeLocation(parameter, filename, source)
|
|
1789
|
+
const parameterName = typeof parameter.name == "string" ? parameter.name : parameter.name.name
|
|
1790
|
+
|
|
1791
|
+
if (parameter.byref || parameter.variadic || parameter.value || parameter.flags || parameter.readonly || parameter.nullable ||
|
|
1792
|
+
parameter.hooks.length || parameter.attrGroups.length) {
|
|
1793
|
+
return unsupportedSyntax("php", "noncanonical interface method parameter", parameterLocation)
|
|
1794
|
+
}
|
|
1795
|
+
if (!parameter.type) return missingType("php", `Parameter '${parameterName}'`, parameterLocation)
|
|
1796
|
+
const type = convertType(parameter.type, `Parameter '${parameterName}'`, parameterLocation, filename, source, nominalNames)
|
|
1797
|
+
|
|
1798
|
+
if (type.kind == "OptionalType") return unsupportedSyntax("php", "optional interface method parameter", parameterLocation)
|
|
1799
|
+
const nameNode = typeof parameter.name == "string" ? parameter : parameter.name
|
|
1800
|
+
|
|
1801
|
+
return withParserRanges({kind: /** @type {const} */ ("Parameter"), location: parameterLocation,
|
|
1802
|
+
name: parameterName, type}, {name: nodeLocation(nameNode, filename, source)})
|
|
1803
|
+
})
|
|
1804
|
+
const returnType = convertReturnType(node.type, `Interface method '${name}' return`, location, filename, source, nominalNames)
|
|
1805
|
+
|
|
1806
|
+
if (returnType.kind == "OptionalType") return unsupportedSyntax("php", "optional interface method return", location)
|
|
1807
|
+
const nameNode = typeof node.name == "string" ? node : node.name
|
|
1808
|
+
|
|
1809
|
+
return withParserRanges({kind: /** @type {const} */ ("InterfaceMethodDeclaration"), location, name, parameters, returnType},
|
|
1810
|
+
{name: nodeLocation(nameNode, filename, source)})
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1699
1813
|
/**
|
|
1700
1814
|
* Converts the bounded final/private-field PHP reference-class profile.
|
|
1701
1815
|
* @param {import("php-parser").Class} node - Parser class.
|
|
1702
1816
|
* @param {import("../semantic/types.js").ClassDeclaration} declaration - Predeclared class identity.
|
|
1703
|
-
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1817
|
+
* @param {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} nominalNames - Visible nominal declarations.
|
|
1704
1818
|
* @param {Map<string, import("../semantic/types.js").RecordDeclaration>} records - Records by identity.
|
|
1705
1819
|
* @param {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Classes by identity.
|
|
1820
|
+
* @param {Map<string, import("../semantic/types.js").InterfaceDeclaration>} interfaces - Interfaces by identity.
|
|
1706
1821
|
* @param {Map<string, PhpFunctionSignature>} functions - Top-level signatures.
|
|
1707
1822
|
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Errors by name.
|
|
1708
1823
|
* @param {Map<string, import("../semantic/types.js").ErrorDeclaration>} errors - Errors by identity.
|
|
@@ -1710,12 +1825,28 @@ function convertPhpClassCallableSignature(node, nominalNames, ownerId, construct
|
|
|
1710
1825
|
* @param {string} source - Complete source.
|
|
1711
1826
|
* @returns {import("../semantic/types.js").ClassDeclaration} Semantic class.
|
|
1712
1827
|
*/
|
|
1713
|
-
function convertPhpReferenceClass(node, declaration, nominalNames, records, classes, functions, errorNames, errors,
|
|
1828
|
+
function convertPhpReferenceClass(node, declaration, nominalNames, records, classes, interfaces, functions, errorNames, errors,
|
|
1714
1829
|
filename, source) {
|
|
1715
1830
|
const location = nodeLocation(node, filename, source)
|
|
1716
1831
|
|
|
1717
|
-
if (node.isAnonymous || !node.isFinal || node.isReadonly || node.isAbstract || node.extends || node.
|
|
1718
|
-
|
|
1832
|
+
if (node.isAnonymous || !node.isFinal || node.isReadonly || node.isAbstract || node.extends || node.attrGroups.length) {
|
|
1833
|
+
return unsupportedSyntax("php", "reference class inheritance or noncanonical modifier", location)
|
|
1834
|
+
}
|
|
1835
|
+
const implementedReferences = /** @type {readonly import("php-parser").Name[]} */ (/** @type {unknown} */ (node.implements ?? []))
|
|
1836
|
+
.map((implemented) => {
|
|
1837
|
+
const implementedLocation = nodeLocation(implemented, filename, source)
|
|
1838
|
+
|
|
1839
|
+
if (implemented.kind != "name" || implemented.resolution != "uqn" || typeof implemented.name != "string") {
|
|
1840
|
+
return unsupportedSyntax("php", "qualified or dynamic interface implementation", implementedLocation)
|
|
1841
|
+
}
|
|
1842
|
+
const implementedDeclaration = nominalNames.get(implemented.name)
|
|
1843
|
+
|
|
1844
|
+
if (!implementedDeclaration?.id || implementedDeclaration.kind != "InterfaceDeclaration") {
|
|
1845
|
+
return unsupportedSyntax("php", `implemented interface '${implemented.name}' is not a declared interface`, implementedLocation)
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
return interfaceType(/** @type {string} */ (implementedDeclaration.id), implementedLocation)
|
|
1849
|
+
})
|
|
1719
1850
|
const fieldStatements = node.body.filter((member) => member.kind == "propertystatement")
|
|
1720
1851
|
const methodNodes = /** @type {import("php-parser").Method[]} */ (node.body.filter((member) => member.kind == "method"))
|
|
1721
1852
|
const constructors = methodNodes.filter((method) => (typeof method.name == "string" ? method.name : method.name.name) == "__construct")
|
|
@@ -1756,8 +1887,9 @@ function convertPhpReferenceClass(node, declaration, nominalNames, records, clas
|
|
|
1756
1887
|
parameters: signature.parameters,
|
|
1757
1888
|
returnType: /** @type {import("../semantic/types.js").SemanticFunctionReturnType} */ (signature.returnType)
|
|
1758
1889
|
}, {name: signature.nameLocation}))
|
|
1890
|
+
if (implementedReferences.length > 0) declaration.implements = implementedReferences
|
|
1759
1891
|
const base = {bindings: new Map(), classes, currentClass: declaration, errorNames, errors, functions,
|
|
1760
|
-
recordNames: nominalNames, records}
|
|
1892
|
+
interfaces, recordNames: nominalNames, records}
|
|
1761
1893
|
|
|
1762
1894
|
declaration.constructor.body = convertBlock(/** @type {import("php-parser").Block} */ (constructorNode.body), filename, source,
|
|
1763
1895
|
{...base, bindings: new Map(declaration.constructor.parameters.map((parameter) => [parameter.name, parameter.type])),
|
|
@@ -2007,6 +2139,7 @@ export function parsePhp({capabilities = [], filename, source, program: programC
|
|
|
2007
2139
|
|
|
2008
2140
|
body = namespace.children.filter((node) => node.kind != "usegroup" && !isPhpRequireOnce(node))
|
|
2009
2141
|
}
|
|
2142
|
+
const interfaceNodes = /** @type {import("php-parser").Class[]} */ (body.filter((node) => node.kind == "interface"))
|
|
2010
2143
|
const classNodes = /** @type {import("php-parser").Class[]} */ (body.filter((node) => node.kind == "class"))
|
|
2011
2144
|
const errorNodes = classNodes.filter((node) => node.extends?.kind == "name" &&
|
|
2012
2145
|
["RuntimeException", "\\RuntimeException"].includes(node.extends.name))
|
|
@@ -2038,7 +2171,12 @@ export function parsePhp({capabilities = [], filename, source, program: programC
|
|
|
2038
2171
|
location: nodeLocation(node, filename, source), methods: [],
|
|
2039
2172
|
name: typeof node.name == "string" ? node.name : node.name.name
|
|
2040
2173
|
}))
|
|
2041
|
-
const
|
|
2174
|
+
const interfaceDeclarations = interfaceNodes.map((node, index) => ({
|
|
2175
|
+
id: `interface:${index}`, kind: /** @type {const} */ ("InterfaceDeclaration"),
|
|
2176
|
+
location: nodeLocation(node, filename, source), methods: [],
|
|
2177
|
+
name: typeof node.name == "string" ? node.name : node.name.name
|
|
2178
|
+
}))
|
|
2179
|
+
const recordNames = /** @type {Map<string, import("../semantic/types.js").RecordDeclaration | import("../semantic/types.js").ClassDeclaration | import("../semantic/types.js").EffectResourceDeclaration | import("../semantic/types.js").InterfaceDeclaration>} */ (
|
|
2042
2180
|
new Map(programContext?.records ?? []))
|
|
2043
2181
|
for (const resource of capabilities.flatMap(({resources}) => resources)) recordNames.set(resource.name, resource)
|
|
2044
2182
|
const errorNames = new Map(programContext?.errors ?? [])
|
|
@@ -2050,10 +2188,12 @@ export function parsePhp({capabilities = [], filename, source, program: programC
|
|
|
2050
2188
|
const errors = errorNodes.map((node, index) => convertPhpError(node, errorDeclarations[index], filename, source))
|
|
2051
2189
|
for (const declaration of recordDeclarations) recordNames.set(declaration.name, declaration)
|
|
2052
2190
|
for (const declaration of classDeclarations) recordNames.set(declaration.name, declaration)
|
|
2191
|
+
for (const declaration of interfaceDeclarations) recordNames.set(declaration.name, declaration)
|
|
2053
2192
|
const recordsById = new Map([...recordNames.values()]
|
|
2054
2193
|
.filter((declaration) => declaration.kind == "RecordDeclaration")
|
|
2055
2194
|
.map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2056
2195
|
const classesById = new Map(classDeclarations.map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2196
|
+
const interfacesById = new Map(interfaceDeclarations.map((declaration) => [/** @type {string} */ (declaration.id), declaration]))
|
|
2057
2197
|
const records = recordNodes.map((node, index) => convertPhpRecord(node, recordDeclarations[index],
|
|
2058
2198
|
/** @type {Map<string, import("../semantic/types.js").RecordDeclaration>} */ (/** @type {unknown} */ (recordNames)), filename, source))
|
|
2059
2199
|
const functionNodes = /** @type {import("php-parser").Function[]} */ (body.filter((node) => node.kind == "function"))
|
|
@@ -2068,12 +2208,14 @@ export function parsePhp({capabilities = [], filename, source, program: programC
|
|
|
2068
2208
|
...(declaration.typeParameters ? {typeParameters: declaration.typeParameters} : {})
|
|
2069
2209
|
}]))
|
|
2070
2210
|
for (const signature of signatures) functionSignatures.set(signature.name, signature)
|
|
2211
|
+
const interfaces = interfaceNodes.map((node, index) => convertPhpInterface(node, interfaceDeclarations[index],
|
|
2212
|
+
recordNames, filename, source))
|
|
2071
2213
|
const classes = referenceClassNodes.map((node, index) => convertPhpReferenceClass(node, classDeclarations[index],
|
|
2072
|
-
recordNames, recordsById, classesById, functionSignatures, errorNames, errorsById, filename, source))
|
|
2214
|
+
recordNames, recordsById, classesById, interfacesById, functionSignatures, errorNames, errorsById, filename, source))
|
|
2073
2215
|
const functions = functionNodes.map((node, index) =>
|
|
2074
|
-
convertFunction(node, signatures[index], functionSignatures, recordNames, recordsById, classesById,
|
|
2075
|
-
filename, source))
|
|
2076
|
-
const executableNodes = body.filter((node) => !["class", "function", "declare", "noop"].includes(node.kind))
|
|
2216
|
+
convertFunction(node, signatures[index], functionSignatures, recordNames, recordsById, classesById, interfacesById,
|
|
2217
|
+
errorNames, errorsById, filename, source))
|
|
2218
|
+
const executableNodes = body.filter((node) => !["class", "function", "interface", "declare", "noop"].includes(node.kind))
|
|
2077
2219
|
const location = moduleLocation(filename, source)
|
|
2078
2220
|
|
|
2079
2221
|
if (programContext && !programContext.isEntry && executableNodes.length > 0) {
|
|
@@ -2090,6 +2232,7 @@ export function parsePhp({capabilities = [], filename, source, program: programC
|
|
|
2090
2232
|
errorNames,
|
|
2091
2233
|
errors: errorsById,
|
|
2092
2234
|
functions: functionSignatures,
|
|
2235
|
+
interfaces: interfacesById,
|
|
2093
2236
|
orderedMapDeclarations: orderedMapDeclarationsForPhp(executableNodes),
|
|
2094
2237
|
recordNames,
|
|
2095
2238
|
records: recordsById,
|
|
@@ -2115,6 +2258,7 @@ export function parsePhp({capabilities = [], filename, source, program: programC
|
|
|
2115
2258
|
location,
|
|
2116
2259
|
...(errors.length > 0 ? {errors} : {}),
|
|
2117
2260
|
...(classes.length > 0 ? {classes} : {}),
|
|
2261
|
+
...(interfaces.length > 0 ? {interfaces} : {}),
|
|
2118
2262
|
...(records.length > 0 ? {records} : {})
|
|
2119
2263
|
}
|
|
2120
2264
|
}
|