From 2586907598301f3250b701d7976d5e0c132e7a13 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 18 Feb 2025 08:15:00 -0500 Subject: [PATCH] fix(callgraph): Incorrect processing of `StateWrite` for cells (#279) Closes #278 --- CHANGELOG.md | 1 + src/detectors/builtin/ensurePrgSeed.ts | 10 +-- src/internals/ir/callGraph.ts | 48 +++++-------- src/internals/tact/stdlib.ts | 64 +++++++++++++++++ src/internals/tact/util.ts | 39 +---------- test/all/syntax.expected.callgraph.dot | 55 ++++++++------- test/all/syntax.expected.callgraph.json | 91 +++++++++++++++---------- test/all/syntax.expected.callgraph.mmd | 55 ++++++++------- test/all/syntax.expected.cfg.dot | 38 ++++++----- test/all/syntax.expected.cfg.json | 62 ++++++++++------- test/all/syntax.expected.cfg.mmd | 29 ++++---- test/all/syntax.expected.out | 34 ++++----- test/all/syntax.tact | 6 +- 13 files changed, 299 insertions(+), 233 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 387a1ddc..9a18837a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `UnboundMap`: False positive: Issue [#262](https://github.com/nowarp/misti/issues/262) - Internal Errors Printed to `stderr` Instead of JSON Output: Issue [#263](https://github.com/nowarp/misti/issues/263) - `CellBounds`: Infinite recursion: PR [#272](https://github.com/nowarp/misti/pull/272) +- Callgraph: Incorrect processing of `Effect.StateWrite` for cells: PR [#279](https://github.com/nowarp/misti/pull/279) ## [0.6.2] - 2024-12-25 diff --git a/src/detectors/builtin/ensurePrgSeed.ts b/src/detectors/builtin/ensurePrgSeed.ts index 8a795a73..2b385bfe 100644 --- a/src/detectors/builtin/ensurePrgSeed.ts +++ b/src/detectors/builtin/ensurePrgSeed.ts @@ -1,8 +1,8 @@ import { CompilationUnit } from "../../internals/ir"; import { forEachExpression, - PRG_INIT_NAMES, - PRG_NATIVE_USE_NAMES, + PRG_INIT_FUNCTIONS, + PRG_NATIVE_USE_FUNCTIONS, } from "../../internals/tact"; import { MistiTactWarning, Severity } from "../../internals/warnings"; import { AstDetector } from "../detector"; @@ -46,10 +46,10 @@ export class EnsurePrgSeed extends AstDetector { (acc, node) => { forEachExpression(node, (expr) => { if (expr.kind === "static_call") { - if (PRG_INIT_NAMES.has(idText(expr.function))) { + if (PRG_INIT_FUNCTIONS.has(idText(expr.function))) { acc.hasInitializer = true; } - if (PRG_NATIVE_USE_NAMES.has(idText(expr.function))) { + if (PRG_NATIVE_USE_FUNCTIONS.has(idText(expr.function))) { acc.uses.push(expr); } } @@ -70,7 +70,7 @@ export class EnsurePrgSeed extends AstDetector { `PRG seed should be initialized before using ${idText(use.function)}`, use.loc, { - suggestion: `Use ${Array.from(PRG_INIT_NAMES) + suggestion: `Use ${Array.from(PRG_INIT_FUNCTIONS) .map((name) => "`" + name + "`") .join( ", ", diff --git a/src/internals/ir/callGraph.ts b/src/internals/ir/callGraph.ts index bb280597..6b0cc1ac 100644 --- a/src/internals/ir/callGraph.ts +++ b/src/internals/ir/callGraph.ts @@ -3,16 +3,19 @@ import { AstStore } from "./astStore"; import { IdxGenerator } from "./indices"; import { MistiContext } from "../../"; import { Logger } from "../../internals/logger"; -import { findInExpressions, forEachExpression } from "../tact/iterators"; import { - DATETIME_NAMES, isSelfAccess, isSendCall, isSelf, - PRG_INIT_NAMES, - PRG_NATIVE_USE_NAMES, - PRG_SAFE_USE_NAMES, -} from "../tact/util"; + DATETIME_FUNCTIONS, + PRG_INIT_FUNCTIONS, + PRG_NATIVE_USE_FUNCTIONS, + PRG_SAFE_USE_FUNCTIONS, + MAP_MUTATING_METHODS, + STRING_MUTATING_METHODS, + BUILDER_MUTATING_METHODS, +} from "../tact"; +import { findInExpressions, forEachExpression } from "../tact/iterators"; import { AstFunctionDef, AstReceiver, @@ -413,14 +416,14 @@ export class CallGraph { if (!funcNode) return; if (expr.kind === "static_call") { const functionName = idText(expr.function); - if (DATETIME_NAMES.has(functionName)) + if (DATETIME_FUNCTIONS.has(functionName)) funcNode.addEffect(Effect.AccessDatetime); else if ( - PRG_NATIVE_USE_NAMES.has(functionName) || - PRG_SAFE_USE_NAMES.has(functionName) + PRG_NATIVE_USE_FUNCTIONS.has(functionName) || + PRG_SAFE_USE_FUNCTIONS.has(functionName) ) funcNode.addEffect(Effect.PrgUse); - else if (PRG_INIT_NAMES.has(functionName)) + else if (PRG_INIT_FUNCTIONS.has(functionName)) funcNode.addEffect(Effect.PrgSeedInit); } if (isSendCall(expr)) funcNode.addEffect(Effect.Send); @@ -518,25 +521,6 @@ function isContractStateWrite(stmt: AstStatement): boolean { ) { return isSelfAccess(stmt.path); } - - // https://docs.tact-lang.org/book/maps/ - const MAP_MUTATING_OPERATIONS = new Set(["set", "del", "replace"]); - // For slices, cells, builders: - // https://github.com/tact-lang/tact/blob/08133e8418f3c6dcb49229b45cfeb7dd261bbe1f/stdlib/std/cells.tact#L75 - const CELL_MUTATING_OPERATIONS = new Set([ - "loadRef", - "loadBits", - "loadInt", - "loadUint", - "loadBool", - "loadBit", - "loadCoins", - "loadAddress", - "skipBits", - ]); - // Strings: - // https://github.com/tact-lang/tact/blob/08133e8418f3c6dcb49229b45cfeb7dd261bbe1f/stdlib/std/text.tact#L18 - const STRING_MUTATING_OPERATIONS = new Set(["append"]); return ( null !== findInExpressions( @@ -544,9 +528,9 @@ function isContractStateWrite(stmt: AstStatement): boolean { (expr) => expr.kind === "method_call" && isSelf(expr.self) && - (MAP_MUTATING_OPERATIONS.has(idText(expr.method)) || - STRING_MUTATING_OPERATIONS.has(idText(expr.method)) || - CELL_MUTATING_OPERATIONS.has(idText(expr.method))), + (MAP_MUTATING_METHODS.has(idText(expr.method)) || + STRING_MUTATING_METHODS.has(idText(expr.method)) || + BUILDER_MUTATING_METHODS.has(idText(expr.method))), ) ); } diff --git a/src/internals/tact/stdlib.ts b/src/internals/tact/stdlib.ts index b7f74d28..bcabdb42 100644 --- a/src/internals/tact/stdlib.ts +++ b/src/internals/tact/stdlib.ts @@ -1,8 +1,72 @@ +/** + * This module contains definitions from the Tact stdlib and logic for + * accessing the stdlib in Misti. + * + * It should be reviewed before each Tact update to determine if updates are needed. + * + * @packageDocumentation + */ + import { MistiContext } from "../context"; import { hasSubdirs } from "../util"; import { SrcInfo } from "@tact-lang/compiler/dist/grammar/ast"; import path from "path"; +/** + * Stdlib functions that access datetime functions. + */ +export const DATETIME_FUNCTIONS = new Set(["now", "timestamp"]); + +/** + * Stdlib functions that initialize PRG seed. + */ +export const PRG_INIT_FUNCTIONS = new Set([ + "nativePrepareRandom", + "nativeRandomize", + "nativeRandomizeLt", +]); + +/** + * Native stdlib functions that use PRG. + */ +export const PRG_NATIVE_USE_FUNCTIONS = new Set([ + "nativeRandom", + "nativeRandomInterval", +]); + +/** + * Safe Tact wrapper functions that use PRG. + */ +export const PRG_SAFE_USE_FUNCTIONS = new Set(["random", "randomInt"]); + +/** + * Map methods that mutate state. + * See: https://docs.tact-lang.org/book/maps/ + */ +export const MAP_MUTATING_METHODS = new Set(["set", "del", "replace"]); + +/** + * Builder methods mutating state. + * https://github.com/tact-lang/tact/blob/08133e8418f3c6dcb49229b45cfeb7dd261bbe1f/stdlib/std/cells.tact#L75 + */ +export const BUILDER_MUTATING_METHODS = new Set([ + "storeRef", + "storeBits", + "storeInt", + "storeUint", + "storeBool", + "storeBit", + "storeCoins", + "storeAddress", + "skipBits", +]); + +/** + * String mutating methods. + * https://github.com/tact-lang/tact/blob/08133e8418f3c6dcb49229b45cfeb7dd261bbe1f/stdlib/std/text.tact#L18 + */ +export const STRING_MUTATING_METHODS = new Set(["append"]); + /** * A mandatory part of the file path to stdlib if using the default path. */ diff --git a/src/internals/tact/util.ts b/src/internals/tact/util.ts index f673f971..0f798bdb 100644 --- a/src/internals/tact/util.ts +++ b/src/internals/tact/util.ts @@ -1,6 +1,7 @@ import { forEachExpression } from "./iterators"; import { evalToType } from "../../internals/tact/"; import { unreachable } from "../util"; +import { MAP_MUTATING_METHODS, BUILDER_MUTATING_METHODS } from "./stdlib"; import { AstComparator } from "@tact-lang/compiler/dist/"; import { AstExpression, @@ -23,25 +24,6 @@ import { prettyPrint } from "@tact-lang/compiler/dist/prettyPrinter"; import { Interval as RawInterval } from "ohm-js"; import * as path from "path"; -/** Stdlib functions that access datetime functions. */ -export const DATETIME_NAMES = new Set(["now", "timestamp"]); - -/** Stdlib functions that initialize PRG seed. */ -export const PRG_INIT_NAMES = new Set([ - "nativePrepareRandom", - "nativeRandomize", - "nativeRandomizeLt", -]); - -/** Native stdlib functions that use PRG. */ -export const PRG_NATIVE_USE_NAMES = new Set([ - "nativeRandom", - "nativeRandomInterval", -]); - -/** Safe Tact wrapper functions that use PRG. */ -export const PRG_SAFE_USE_NAMES = new Set(["random", "randomInt"]); - /** * Creates a concise string representation of `SrcInfo`. */ @@ -104,28 +86,13 @@ export function isSelfAccess(expr: AstExpression): boolean { * @returns True iff `call` is a stdlib method mutating its receiver. */ export function isStdlibMutationMethod(call: AstMethodCall): boolean { - // https://docs.tact-lang.org/book/maps - const mapMutationOperations = ["set", "del"]; - // See: stdlib/std/cells.tact - const builderMutationOperations = [ - "storeInt", - "storeUint", - "storeBool", - "storeBit", - "storeCoins", - "storeRef", - "storeSlice", - "storeBuilder", - "storeAddress", - "storeMaybeRef", - ]; const methodName = idText(call.method); return ( // Filter out contract calls e.g.: `self.set(/*...*/)` !isSelf(call.self) && // TODO: This should be rewritten when we have types in AST - (mapMutationOperations.includes(methodName) || - builderMutationOperations.includes(methodName)) + (MAP_MUTATING_METHODS.has(methodName) || + BUILDER_MUTATING_METHODS.has(methodName)) ); } diff --git a/test/all/syntax.expected.callgraph.dot b/test/all/syntax.expected.callgraph.dot index 6349625d..c146cd5e 100644 --- a/test/all/syntax.expected.callgraph.dot +++ b/test/all/syntax.expected.callgraph.dot @@ -17,37 +17,40 @@ digraph "CallGraph" { [Send,StateRead]"]; node_28 [label="fun funcWithStateRead() [StateRead]"]; - node_29 [label="fun funcWithStateWrite() + node_29 [label="fun funcWithStateWrite1() [StateWrite]"]; - node_30 [label="fun funcWithMultipleEffects() + node_30 [label="fun funcWithStateWrite2(s: Slice): Address"]; + node_31 [label="fun funcWithMultipleEffects() [AccessDatetime,PrgUse,PrgSeedInit]"]; - node_31 [label="receive()"]; - node_32 [label="dump"]; - node_33 [label="emptyMap"]; - node_34 [label="m::set"]; - node_35 [label="s::loadRefEx"]; - node_36 [label="getA"]; - node_37 [label="sender"]; - node_38 [label="newAddress"]; - node_39 [label="now"]; - node_40 [label="nativeRandomizeLt"]; - node_41 [label="beginString"]; - node_42 [label="a::append"]; - node_17 -> node_32; - node_18 -> node_33; - node_19 -> node_32; - node_20 -> node_33; + node_32 [label="receive()"]; + node_33 [label="dump"]; + node_34 [label="emptyMap"]; + node_35 [label="m::set"]; + node_36 [label="s::loadRefEx"]; + node_37 [label="getA"]; + node_38 [label="sender"]; + node_39 [label="newAddress"]; + node_40 [label="s::loadAddress"]; + node_41 [label="now"]; + node_42 [label="nativeRandomizeLt"]; + node_43 [label="beginString"]; + node_44 [label="a::append"]; + node_17 -> node_33; + node_18 -> node_34; + node_19 -> node_33; node_20 -> node_34; - node_20 -> node_34; - node_20 -> node_34; - node_21 -> node_35; - node_24 -> node_36; - node_26 -> node_37; + node_20 -> node_35; + node_20 -> node_35; + node_20 -> node_35; + node_21 -> node_36; + node_24 -> node_37; + node_26 -> node_38; node_27 -> node_15; - node_29 -> node_38; - node_30 -> node_39; - node_30 -> node_12; + node_29 -> node_39; node_30 -> node_40; node_31 -> node_41; + node_31 -> node_12; node_31 -> node_42; + node_32 -> node_43; + node_32 -> node_44; } diff --git a/test/all/syntax.expected.callgraph.json b/test/all/syntax.expected.callgraph.json index 5fb3fbb5..c1577c94 100644 --- a/test/all/syntax.expected.callgraph.json +++ b/test/all/syntax.expected.callgraph.json @@ -58,7 +58,7 @@ { "idx": 12, "inEdges": [ - 14 + 15 ], "outEdges": [] }, @@ -174,21 +174,28 @@ "idx": 30, "inEdges": [], "outEdges": [ - 13, - 14, - 15 + 13 ] }, { "idx": 31, "inEdges": [], "outEdges": [ - 16, - 17 + 14, + 15, + 16 ] }, { "idx": 32, + "inEdges": [], + "outEdges": [ + 17, + 18 + ] + }, + { + "idx": 33, "inEdges": [ 1, 3 @@ -196,7 +203,7 @@ "outEdges": [] }, { - "idx": 33, + "idx": 34, "inEdges": [ 2, 4 @@ -204,7 +211,7 @@ "outEdges": [] }, { - "idx": 34, + "idx": 35, "inEdges": [ 5, 6, @@ -213,112 +220,119 @@ "outEdges": [] }, { - "idx": 35, + "idx": 36, "inEdges": [ 8 ], "outEdges": [] }, { - "idx": 36, + "idx": 37, "inEdges": [ 9 ], "outEdges": [] }, { - "idx": 37, + "idx": 38, "inEdges": [ 10 ], "outEdges": [] }, { - "idx": 38, + "idx": 39, "inEdges": [ 12 ], "outEdges": [] }, { - "idx": 39, + "idx": 40, "inEdges": [ 13 ], "outEdges": [] }, { - "idx": 40, + "idx": 41, "inEdges": [ - 15 + 14 ], "outEdges": [] }, { - "idx": 41, + "idx": 42, "inEdges": [ 16 ], "outEdges": [] }, { - "idx": 42, + "idx": 43, "inEdges": [ 17 ], "outEdges": [] + }, + { + "idx": 44, + "inEdges": [ + 18 + ], + "outEdges": [] } ], "edges": [ { "idx": 1, "src": 17, - "dst": 32 + "dst": 33 }, { "idx": 2, "src": 18, - "dst": 33 + "dst": 34 }, { "idx": 3, "src": 19, - "dst": 32 + "dst": 33 }, { "idx": 4, "src": 20, - "dst": 33 + "dst": 34 }, { "idx": 5, "src": 20, - "dst": 34 + "dst": 35 }, { "idx": 6, "src": 20, - "dst": 34 + "dst": 35 }, { "idx": 7, "src": 20, - "dst": 34 + "dst": 35 }, { "idx": 8, "src": 21, - "dst": 35 + "dst": 36 }, { "idx": 9, "src": 24, - "dst": 36 + "dst": 37 }, { "idx": 10, "src": 26, - "dst": 37 + "dst": 38 }, { "idx": 11, @@ -328,32 +342,37 @@ { "idx": 12, "src": 29, - "dst": 38 + "dst": 39 }, { "idx": 13, "src": 30, - "dst": 39 + "dst": 40 }, { "idx": 14, - "src": 30, - "dst": 12 + "src": 31, + "dst": 41 }, { "idx": 15, - "src": 30, - "dst": 40 + "src": 31, + "dst": 12 }, { "idx": 16, "src": 31, - "dst": 41 + "dst": 42 }, { "idx": 17, - "src": 31, - "dst": 42 + "src": 32, + "dst": 43 + }, + { + "idx": 18, + "src": 32, + "dst": 44 } ] } \ No newline at end of file diff --git a/test/all/syntax.expected.callgraph.mmd b/test/all/syntax.expected.callgraph.mmd index 7b6e45d7..19c970df 100644 --- a/test/all/syntax.expected.callgraph.mmd +++ b/test/all/syntax.expected.callgraph.mmd @@ -16,36 +16,39 @@ graph TD [Send,StateRead]"] node_28["fun funcWithStateRead() [StateRead]"] - node_29["fun funcWithStateWrite() + node_29["fun funcWithStateWrite1() [StateWrite]"] - node_30["fun funcWithMultipleEffects() + node_30["fun funcWithStateWrite2(s: Slice): Address"] + node_31["fun funcWithMultipleEffects() [AccessDatetime,PrgUse,PrgSeedInit]"] - node_31["receive()"] - node_32["dump"] - node_33["emptyMap"] - node_34["m::set"] - node_35["s::loadRefEx"] - node_36["getA"] - node_37["sender"] - node_38["newAddress"] - node_39["now"] - node_40["nativeRandomizeLt"] - node_41["beginString"] - node_42["a::append"] - node_17 --> node_32 - node_18 --> node_33 - node_19 --> node_32 - node_20 --> node_33 + node_32["receive()"] + node_33["dump"] + node_34["emptyMap"] + node_35["m::set"] + node_36["s::loadRefEx"] + node_37["getA"] + node_38["sender"] + node_39["newAddress"] + node_40["s::loadAddress"] + node_41["now"] + node_42["nativeRandomizeLt"] + node_43["beginString"] + node_44["a::append"] + node_17 --> node_33 + node_18 --> node_34 + node_19 --> node_33 node_20 --> node_34 - node_20 --> node_34 - node_20 --> node_34 - node_21 --> node_35 - node_24 --> node_36 - node_26 --> node_37 + node_20 --> node_35 + node_20 --> node_35 + node_20 --> node_35 + node_21 --> node_36 + node_24 --> node_37 + node_26 --> node_38 node_27 --> node_15 - node_29 --> node_38 - node_30 --> node_39 - node_30 --> node_12 + node_29 --> node_39 node_30 --> node_40 node_31 --> node_41 + node_31 --> node_12 node_31 --> node_42 + node_32 --> node_43 + node_32 --> node_44 diff --git a/test/all/syntax.expected.cfg.dot b/test/all/syntax.expected.cfg.dot index 53051280..007d2e0f 100644 --- a/test/all/syntax.expected.cfg.dot +++ b/test/all/syntax.expected.cfg.dot @@ -112,9 +112,9 @@ digraph "syntax" { subgraph "cluster_TestContractT__receive_external_fallback_1722" { label="TestContractT__receive_external_fallback_1722"; } - subgraph "cluster_EffectTest__init_1981" { - label="EffectTest__init_1981"; - "EffectTest__init_1981_110" [label="self.addr = sender()",style=filled,fillcolor="#66A7DB"]; + subgraph "cluster_EffectTest__init_1991" { + label="EffectTest__init_1991"; + "EffectTest__init_1991_110" [label="self.addr = sender()",style=filled,fillcolor="#66A7DB"]; } subgraph "cluster_EffectTest__funcWithSend" { label="EffectTest__funcWithSend"; @@ -126,25 +126,29 @@ digraph "syntax" { label="EffectTest__funcWithStateRead"; "EffectTest__funcWithStateRead_113" [label="let value = self.addr",style=filled,fillcolor="#66A7DB"]; } - subgraph "cluster_EffectTest__funcWithStateWrite" { - label="EffectTest__funcWithStateWrite"; - "EffectTest__funcWithStateWrite_114" [label="self.addr = newAddress(0, 0)",style=filled,fillcolor="#66A7DB"]; + subgraph "cluster_EffectTest__funcWithStateWrite1" { + label="EffectTest__funcWithStateWrite1"; + "EffectTest__funcWithStateWrite1_114" [label="self.addr = newAddress(0, 0)",style=filled,fillcolor="#66A7DB"]; + } + subgraph "cluster_EffectTest__funcWithStateWrite2" { + label="EffectTest__funcWithStateWrite2"; + "EffectTest__funcWithStateWrite2_115" [label="return s.loadAddress()",style=filled,fillcolor="#66A7DB"]; } subgraph "cluster_EffectTest__funcWithMultipleEffects" { label="EffectTest__funcWithMultipleEffects"; - "EffectTest__funcWithMultipleEffects_115" [label="let currentTime: Int = now()"]; - "EffectTest__funcWithMultipleEffects_116" [label="let randValue: Int = random(1, 100)"]; - "EffectTest__funcWithMultipleEffects_117" [label="if (1 > 42)"]; - "EffectTest__funcWithMultipleEffects_118" [label="nativeRandomizeLt()",style=filled,fillcolor="#66A7DB"]; - "EffectTest__funcWithMultipleEffects_115" -> "EffectTest__funcWithMultipleEffects_116"; + "EffectTest__funcWithMultipleEffects_116" [label="let currentTime: Int = now()"]; + "EffectTest__funcWithMultipleEffects_117" [label="let randValue: Int = random(1, 100)"]; + "EffectTest__funcWithMultipleEffects_118" [label="if (1 > 42)"]; + "EffectTest__funcWithMultipleEffects_119" [label="nativeRandomizeLt()",style=filled,fillcolor="#66A7DB"]; "EffectTest__funcWithMultipleEffects_116" -> "EffectTest__funcWithMultipleEffects_117"; "EffectTest__funcWithMultipleEffects_117" -> "EffectTest__funcWithMultipleEffects_118"; + "EffectTest__funcWithMultipleEffects_118" -> "EffectTest__funcWithMultipleEffects_119"; } - subgraph "cluster_EffectTest__receive_internal_fallback_1980" { - label="EffectTest__receive_internal_fallback_1980"; - "EffectTest__receive_internal_fallback_1980_119" [label="let a = beginString()"]; - "EffectTest__receive_internal_fallback_1980_120" [label="a.append(\"f\")",style=filled,fillcolor="#66A7DB"]; - "EffectTest__receive_internal_fallback_1980_119" -> "EffectTest__receive_internal_fallback_1980_120"; + subgraph "cluster_EffectTest__receive_internal_fallback_1990" { + label="EffectTest__receive_internal_fallback_1990"; + "EffectTest__receive_internal_fallback_1990_120" [label="let a = beginString()"]; + "EffectTest__receive_internal_fallback_1990_121" [label="a.append(\"f\")",style=filled,fillcolor="#66A7DB"]; + "EffectTest__receive_internal_fallback_1990_120" -> "EffectTest__receive_internal_fallback_1990_121"; } -"116" -> "26"; +"117" -> "26"; } diff --git a/test/all/syntax.expected.cfg.json b/test/all/syntax.expected.cfg.json index ea456ac4..635a9c31 100644 --- a/test/all/syntax.expected.cfg.json +++ b/test/all/syntax.expected.cfg.json @@ -648,7 +648,7 @@ "nodes": [ { "id": 107, - "stmtID": 1998, + "stmtID": 2008, "srcEdges": [], "dstEdges": [] } @@ -719,7 +719,7 @@ "name": "EffectTest", "methods": [ { - "name": "EffectTest.init_1981", + "name": "EffectTest.init_1991", "cfg": { "nodes": [ { @@ -777,7 +777,7 @@ } }, { - "name": "EffectTest.funcWithStateWrite", + "name": "EffectTest.funcWithStateWrite1", "cfg": { "nodes": [ { @@ -791,20 +791,34 @@ } }, { - "name": "EffectTest.funcWithMultipleEffects", + "name": "EffectTest.funcWithStateWrite2", "cfg": { "nodes": [ { "id": 115, - "stmtID": 1955, + "stmtID": 1958, + "srcEdges": [], + "dstEdges": [] + } + ], + "edges": [] + } + }, + { + "name": "EffectTest.funcWithMultipleEffects", + "cfg": { + "nodes": [ + { + "id": 116, + "stmtID": 1965, "srcEdges": [], "dstEdges": [ 101 ] }, { - "id": 116, - "stmtID": 1962, + "id": 117, + "stmtID": 1972, "srcEdges": [ 101 ], @@ -813,8 +827,8 @@ ] }, { - "id": 117, - "stmtID": 1969, + "id": 118, + "stmtID": 1979, "srcEdges": [ 102 ], @@ -823,8 +837,8 @@ ] }, { - "id": 118, - "stmtID": 1968, + "id": 119, + "stmtID": 1978, "srcEdges": [ 103 ], @@ -834,37 +848,37 @@ "edges": [ { "id": 101, - "src": 115, - "dst": 116 - }, - { - "id": 102, "src": 116, "dst": 117 }, { - "id": 103, + "id": 102, "src": 117, "dst": 118 + }, + { + "id": 103, + "src": 118, + "dst": 119 } ] } }, { - "name": "EffectTest.receive_internal_fallback_1980", + "name": "EffectTest.receive_internal_fallback_1990", "cfg": { "nodes": [ { - "id": 119, - "stmtID": 1974, + "id": 120, + "stmtID": 1984, "srcEdges": [], "dstEdges": [ 104 ] }, { - "id": 120, - "stmtID": 1979, + "id": 121, + "stmtID": 1989, "srcEdges": [ 104 ], @@ -874,8 +888,8 @@ "edges": [ { "id": 104, - "src": 119, - "dst": 120 + "src": 120, + "dst": 121 } ] } diff --git a/test/all/syntax.expected.cfg.mmd b/test/all/syntax.expected.cfg.mmd index 3b284d9e..4b72455e 100644 --- a/test/all/syntax.expected.cfg.mmd +++ b/test/all/syntax.expected.cfg.mmd @@ -102,8 +102,8 @@ subgraph TestContractT__test end subgraph TestContractT__receive_external_fallback_1722 end -subgraph EffectTest__init_1981 - EffectTest__init_1981_110["self.addr = sender()"]:::exitNode +subgraph EffectTest__init_1991 + EffectTest__init_1991_110["self.addr = sender()"]:::exitNode end subgraph EffectTest__funcWithSend EffectTest__funcWithSend_111["let amount: Int = 100"] @@ -113,20 +113,23 @@ end subgraph EffectTest__funcWithStateRead EffectTest__funcWithStateRead_113["let value = self.addr"]:::exitNode end -subgraph EffectTest__funcWithStateWrite - EffectTest__funcWithStateWrite_114["self.addr = newAddress(0, 0)"]:::exitNode +subgraph EffectTest__funcWithStateWrite1 + EffectTest__funcWithStateWrite1_114["self.addr = newAddress(0, 0)"]:::exitNode +end +subgraph EffectTest__funcWithStateWrite2 + EffectTest__funcWithStateWrite2_115["return s.loadAddress()"]:::exitNode end subgraph EffectTest__funcWithMultipleEffects - EffectTest__funcWithMultipleEffects_115["let currentTime: Int = now()"] - EffectTest__funcWithMultipleEffects_116["let randValue: Int = random(1, 100)"] - EffectTest__funcWithMultipleEffects_117["if (1 > 42)"] - EffectTest__funcWithMultipleEffects_118["nativeRandomizeLt()"]:::exitNode - EffectTest__funcWithMultipleEffects_115 --> EffectTest__funcWithMultipleEffects_116 + EffectTest__funcWithMultipleEffects_116["let currentTime: Int = now()"] + EffectTest__funcWithMultipleEffects_117["let randValue: Int = random(1, 100)"] + EffectTest__funcWithMultipleEffects_118["if (1 > 42)"] + EffectTest__funcWithMultipleEffects_119["nativeRandomizeLt()"]:::exitNode EffectTest__funcWithMultipleEffects_116 --> EffectTest__funcWithMultipleEffects_117 EffectTest__funcWithMultipleEffects_117 --> EffectTest__funcWithMultipleEffects_118 + EffectTest__funcWithMultipleEffects_118 --> EffectTest__funcWithMultipleEffects_119 end -subgraph EffectTest__receive_internal_fallback_1980 - EffectTest__receive_internal_fallback_1980_119["let a = beginString()"] - EffectTest__receive_internal_fallback_1980_120["a.append('f')"]:::exitNode - EffectTest__receive_internal_fallback_1980_119 --> EffectTest__receive_internal_fallback_1980_120 +subgraph EffectTest__receive_internal_fallback_1990 + EffectTest__receive_internal_fallback_1990_120["let a = beginString()"] + EffectTest__receive_internal_fallback_1990_121["a.append('f')"]:::exitNode + EffectTest__receive_internal_fallback_1990_120 --> EffectTest__receive_internal_fallback_1990_121 end diff --git a/test/all/syntax.expected.out b/test/all/syntax.expected.out index 034c8812..3a992475 100644 --- a/test/all/syntax.expected.out +++ b/test/all/syntax.expected.out @@ -8,35 +8,35 @@ Help: Consider removing the variable See: https://nowarp.io/tools/misti/docs/detectors/NeverAccessedVariables [MEDIUM] NeverAccessedVariables: Variable currentTime is never accessed -test/all/syntax.tact:125:9: - 124 | fun funcWithMultipleEffects() { -> 125 | let currentTime: Int = now(); +test/all/syntax.tact:128:9: + 127 | fun funcWithMultipleEffects() { +> 128 | let currentTime: Int = now(); ^ - 126 | let randValue: Int = random(1, 100); + 129 | let randValue: Int = random(1, 100); Help: Consider removing the variable See: https://nowarp.io/tools/misti/docs/detectors/NeverAccessedVariables [MEDIUM] NeverAccessedVariables: Variable randValue is never accessed -test/all/syntax.tact:126:9: - 125 | let currentTime: Int = now(); -> 126 | let randValue: Int = random(1, 100); +test/all/syntax.tact:129:9: + 128 | let currentTime: Int = now(); +> 129 | let randValue: Int = random(1, 100); ^ - 127 | if (1 > 42) { + 130 | if (1 > 42) { Help: Consider removing the variable See: https://nowarp.io/tools/misti/docs/detectors/NeverAccessedVariables [MEDIUM] FalseCondition: Condition always evaluates to false -test/all/syntax.tact:127:13: - 126 | let randValue: Int = random(1, 100); -> 127 | if (1 > 42) { +test/all/syntax.tact:130:13: + 129 | let randValue: Int = random(1, 100); +> 130 | if (1 > 42) { ^ - 128 | nativeRandomizeLt(); + 131 | nativeRandomizeLt(); Help: Consider removing it if there is no logic error See: https://nowarp.io/tools/misti/docs/detectors/FalseCondition [LOW] ZeroAddress: Using zero address test/all/syntax.tact:119:35: - 118 | fun funcWithStateWrite() { + 118 | fun funcWithStateWrite1() { > 119 | self.addr = newAddress(0, 0); ^ 120 | } @@ -179,10 +179,10 @@ Help: Consider using augmented assignment instead: sum += value See: https://nowarp.io/tools/misti/docs/detectors/PreferAugmentedAssign [INFO] AsmIsUsed: asm function is used -test/all/syntax.tact:139:1: - 138 | -> 139 | asm(-> 1 0) extends mutates fun loadRefEx(self: Slice): Cell { LDREF } +test/all/syntax.tact:143:1: + 142 | // Checks if we add asm functions to IR entries correctly +> 143 | asm(-> 1 0) extends mutates fun loadRefEx(self: Slice): Cell { LDREF } ^ - 140 | fun callAsm(s: Slice): Cell { return s.loadRefEx(); } + 144 | fun callAsm(s: Slice): Cell { return s.loadRefEx(); } Help: Using TVM assembly is a potentially dangerous operation that requires additional review See: https://nowarp.io/tools/misti/docs/detectors/AsmIsUsed \ No newline at end of file diff --git a/test/all/syntax.tact b/test/all/syntax.tact index 185808bf..5738a3f5 100644 --- a/test/all/syntax.tact +++ b/test/all/syntax.tact @@ -115,9 +115,12 @@ contract EffectTest { } // Effect.StateWrite - fun funcWithStateWrite() { + fun funcWithStateWrite1() { self.addr = newAddress(0, 0); } + fun funcWithStateWrite2(s: Slice): Address { + return s.loadAddress(); + } // Multiple effects: // Effect.AccessDatetime + Effect.PrgUse + Effect.SeedInit @@ -136,5 +139,6 @@ contract EffectTest { } } +// Checks if we add asm functions to IR entries correctly asm(-> 1 0) extends mutates fun loadRefEx(self: Slice): Cell { LDREF } fun callAsm(s: Slice): Cell { return s.loadRefEx(); }