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(documentation): add exit code documentation #400

Merged
merged 2 commits into from
Mar 3, 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
110 changes: 110 additions & 0 deletions server/src/documentation/exit_code_documentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
export function generateExitCodeDocumentation(code: number): string | null {
const info = DATA[code]
if (!info) return null

return `${info.description}

**Phase**: ${info.origin}

Learn more about exit codes in documentation: https://docs.tact-lang.org/book/exit-codes/`
}

export const DATA: Record<number, {origin: string; description: string} | undefined> = {
"0": {
origin: "Compute and action phases",
description: "Standard successful execution exit code.",
},
"1": {
origin: "Compute phase",
description: "Alternative successful execution exit code. Reserved, but doesn’t occur.",
},
"2": {origin: "Compute phase", description: "Stack underflow."},
"3": {origin: "Compute phase", description: "Stack overflow."},
"4": {origin: "Compute phase", description: "Integer overflow."},
"5": {
origin: "Compute phase",
description: "Range check error — some integer is out of its expected range.",
},
"6": {origin: "Compute phase", description: "Invalid TVM opcode."},
"7": {origin: "Compute phase", description: "Type check error."},
"8": {origin: "Compute phase", description: "Cell overflow."},
"9": {origin: "Compute phase", description: "Cell underflow."},
"10": {origin: "Compute phase", description: "Dictionary error."},
"11": {
origin: "Compute phase",
description: "Described in TVM docs as “Unknown error, may be thrown by user programs”.",
},
"12": {
origin: "Compute phase",
description: "Fatal error. Thrown by TVM in situations deemed impossible.",
},
"13": {origin: "Compute phase", description: "Out of gas error."},
"-14": {
origin: "Compute phase",
description: "Same as 13. Negative, so that it cannot be faked.",
},
"14": {
origin: "Compute phase",
description: "VM virtualization error. Reserved, but never thrown.",
},
"32": {origin: "Action phase", description: "Action list is invalid."},
"33": {origin: "Action phase", description: "Action list is too long."},
"34": {origin: "Action phase", description: "Action is invalid or not supported."},
"35": {origin: "Action phase", description: "Invalid source address in outbound message."},
"36": {origin: "Action phase", description: "Invalid destination address in outbound message."},
"37": {origin: "Action phase", description: "Not enough Toncoin."},
"38": {origin: "Action phase", description: "Not enough extra currencies."},
"39": {
origin: "Action phase",
description: "Outbound message does not fit into a cell after rewriting.",
},
"40": {
origin: "Action phase",
description:
"Cannot process a message — not enough funds, the message is too large or its Merkle depth is too big.",
},
"41": {
origin: "Action phase",
description: "Library reference is null during library change action.",
},
"42": {origin: "Action phase", description: "Library change action error."},
"43": {
origin: "Action phase",
description:
"Exceeded maximum number of cells in the library or the maximum depth of the Merkle tree.",
},
"50": {origin: "Action phase", description: "Account state size exceeded limits."},
"128": {
origin: "Tact compiler (Compute phase)",
description: "Null reference exception. Configurable since Tact 1.6.",
},
"129": {origin: "Tact compiler (Compute phase)", description: "Invalid serialization prefix."},
"130": {
origin: "Tact compiler (Compute phase)",
description:
"Invalid incoming message — there’s no receiver for the opcode of the received message.",
},
"131": {
origin: "Tact compiler (Compute phase)",
description: "Constraints error. Reserved, but never thrown.",
},
"132": {
origin: "Tact compiler (Compute phase)",
description: "Access denied — someone other than the owner sent a message to the contract.",
},
"133": {
origin: "Tact compiler (Compute phase)",
description: "Contract stopped. Reserved, but never thrown.",
},
"134": {origin: "Tact compiler (Compute phase)", description: "Invalid argument."},
"135": {
origin: "Tact compiler (Compute phase)",
description: "Code of a contract was not found.",
},
"136": {origin: "Tact compiler (Compute phase)", description: "Invalid standard address."},
"137": {
origin: "Tact compiler (Compute phase)",
description:
"Masterchain support is not enabled for this contract. Removed since Tact 1.6.",
},
}
38 changes: 38 additions & 0 deletions server/src/e2e/suite/testcases/documentation/exit-codes.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
========================================================================
Exit code documentation for throw
========================================================================
fun foo() {
throw(<caret>134);
}
------------------------------------------------------------------------
Invalid argument.

**Phase**: Tact compiler (Compute phase)

Learn more about exit codes in documentation: https://docs.tact-lang.org/book/exit-codes/

========================================================================
Exit code documentation for throwIf
========================================================================
fun foo() {
throwIf(<caret>134, true);
}
------------------------------------------------------------------------
Invalid argument.

**Phase**: Tact compiler (Compute phase)

Learn more about exit codes in documentation: https://docs.tact-lang.org/book/exit-codes/

========================================================================
No exit code documentation for other functions
========================================================================
primitive Int;

fun bar(a: Int) {}

fun foo() {
bar(<caret>134);
}
------------------------------------------------------------------------
no documentation
34 changes: 34 additions & 0 deletions server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ import {setToolchain, setWorkspaceRoot, toolchain} from "@server/toolchain"
import {MistiInspection} from "@server/inspections/MistInspection"
import {DontUseTextReceiversInspection} from "@server/inspections/DontUseTextReceiversInspection"
import {ReplaceTextReceiverWithBinary} from "@server/intentions/ReplaceTextReceiverWithBinary"
import {generateExitCodeDocumentation} from "@server/documentation/exit_code_documentation"

/**
* Whenever LS is initialized.
Expand Down Expand Up @@ -567,6 +568,39 @@ connection.onInitialize(async (initParams: lsp.InitializeParams): Promise<lsp.In
}
}

// Hover documentation for 10 in `throwIf(10, cond)
if (hoverNode.type === "integer" && hoverNode.parent?.type === "argument") {
const call = hoverNode.parent.parent?.parent
if (!call) return null
if (call.type !== "static_call_expression") return null
const name = call.childForFieldName("name")?.text
if (!name) return null

if (
![
"throw",
"throwIf",
"throwUnless",
"nativeThrow",
"nativeThrowIf",
"nativeThrowUnless",
].includes(name)
) {
return null
}

const doc = generateExitCodeDocumentation(Number.parseInt(hoverNode.text))
if (doc === null) return null

return {
range: asLspRange(hoverNode),
contents: {
kind: "markdown",
value: doc,
},
}
}

if (
hoverNode.type !== "identifier" &&
hoverNode.type !== "type_identifier" &&
Expand Down