Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat(all): add support for get methods with explicit ID #358

Merged
merged 1 commit into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/src/documentation/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function generateDocFor(node: NamedNode, place: SyntaxNode): Promis
if (!ownerPresentation) return null // not possible in correct code

const actualId = func.computeMethodId()
const actualIdPresentation = `Method ID: \`0x${actualId.toString(16)}\`\n\n`
const actualIdPresentation = `Method ID: \`${actualId}\`\n\n`

const idPresentation = func.isGetMethod ? actualIdPresentation : ""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,57 @@ some cool documentation here
Called when a binary message of type `Msg` is sent to the contract

Learn more in documentation: https://docs.tact-lang.org/book/receive/

========================================================================
get function without method ID
========================================================================
contract Foo {
get fun <caret>foo() {}
}
------------------------------------------------------------------------
```tact
contract Foo
get fun foo()
```
Method ID: `0x103fc`

========================================================================
get function with method ID
========================================================================
contract Foo {
get(0x10000) fun <caret>foo() {}
}
------------------------------------------------------------------------
```tact
contract Foo
get(0x10000) fun foo()
```
Method ID: `0x10000`

========================================================================
get function with complex method ID
========================================================================
contract Foo {
get(0x10000 + 10 * 10) fun <caret>foo() {}
}
------------------------------------------------------------------------
```tact
contract Foo
get(0x10000 + 10 * 10) fun foo()
```
Method ID: `0x10000 + 10 * 10`

========================================================================
get function with constant method ID
========================================================================
const METHOD_ID: Int = 100;

contract Foo {
get(METHOD_ID) fun <caret>foo() {}
}
------------------------------------------------------------------------
```tact
contract Foo
get(METHOD_ID) fun foo()
```
Method ID: `METHOD_ID`
2 changes: 1 addition & 1 deletion server/src/e2e/suite/testcases/documentation/require.test
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Checks the condition and throws an error with an exit code generated from the er

The generated exit code is guaranteed to be outside the common 0−255 range reserved for TVM and Tact contract errors, which makes it possible to distinguish exit codes from `require()` and any other standard exit codes.


```tact
fun examples() {
// now() has to return a value greater than 1000,
Expand Down
19 changes: 19 additions & 0 deletions server/src/e2e/suite/testcases/inlayHints/contract.test
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ contract Foo {
}
}

========================================================================
Don't show method ID for method with explicit ID
========================================================================
primitive Int;

contract Foo {
get(0x1000) fun bar(){
const a: Int;
}
}
------------------------------------------------------------------------
primitive Int;

contract Foo {
get(0x1000) fun bar(){
const a: Int;
}
}

========================================================================
Exit codes
========================================================================
Expand Down
4 changes: 2 additions & 2 deletions server/src/inlays/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,16 +460,16 @@ export function collect(
if (type === "storage_function" && hints.showMethodId) {
const func = new Fun(n, file)
if (!func.isGetMethod) return true
if (func.hasExplicitMethodId) return true

const modifiers = n.childForFieldName("attributes")
if (!modifiers) return true

const actualId = func.computeMethodId()
const actualIdHex = actualId.toString(16)

result.push({
kind: InlayHintKind.Type,
label: `(0x${actualIdHex})`,
label: `(${actualId})`,
position: {
line: modifiers.endPosition.row,
character: modifiers.endPosition.column,
Expand Down
31 changes: 29 additions & 2 deletions server/src/psi/Decls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,28 @@ export class Fun extends NamedNode {
return this.modifiers().includes("get")
}

public get hasExplicitMethodId(): boolean {
// check for
// get(0x1000) fun foo() {}
// ^^^^^^^^ this
const attributes = this.node.childForFieldName("attributes")
if (!attributes) return false
const getAttrs = attributes.children.find(attr => attr?.type === "get_attribute")
if (!getAttrs) return false
return getAttrs.children.some(it => it?.text === "(")
}

public get getExplicitMethodId(): SyntaxNode | null {
// find
// get(0x1000) fun foo() {}
// ^^^^^^ this
const attributes = this.node.childForFieldName("attributes")
if (!attributes) return null
const getAttrs = attributes.children.find(attr => attr?.type === "get_attribute")
if (!getAttrs) return null
return getAttrs.childForFieldName("value")
}

public returnType(): Expression | null {
const result = this.node.childForFieldName("result")
if (!result) return null
Expand Down Expand Up @@ -363,8 +385,13 @@ export class Fun extends NamedNode {
return attr
}

public computeMethodId(): number {
return (crc16(Buffer.from(this.name())) & 0xff_ff) | 0x1_00_00
public computeMethodId(): string {
const explicitId = this.getExplicitMethodId
if (explicitId) {
return explicitId.text
}

return "0x" + ((crc16(Buffer.from(this.name())) & 0xff_ff) | 0x1_00_00).toString(16)
}

public computeGasConsumption(gas: {loopGasCoefficient: number}): GasConsumption {
Expand Down
7 changes: 5 additions & 2 deletions tree-sitter-tact/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,10 @@ module.exports = grammar({
field("body", alias($.block_statement, $.function_body)),
),

function_attributes: (_) =>
function_attributes: ($) =>
repeat1(
choice(
"get",
$.get_attribute,
"mutates",
"extends",
"virtual",
Expand All @@ -354,6 +354,9 @@ module.exports = grammar({
),
),

get_attribute: ($) =>
seq("get", optional(seq("(", field("value", $._expression), ")"))),

parameter_list: ($) => seq("(", commaSepWithTrailing($.parameter), ")"),

parameter: ($) =>
Expand Down