Skip to content

Commit 0087000

Browse files
borisbatclaude
andcommitted
Improve perf_lint standalone tool and remove C++ deprecation infrastructure
Replace parsing-based warning detection in standalone tool with direct perf_lint(prog, false) call. Make test functions optimization-safe by adding parameters and return values. Remove makeFunctionDeprecated from C++ (character_at deprecation now handled by PERF003 lint rule). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5910754 commit 0087000

File tree

11 files changed

+60
-92
lines changed

11 files changed

+60
-92
lines changed

daslib/perf_lint.das

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,14 @@ class PerfLintVisitor : AstVisitor {
5050
@do_not_delete perf004_save_stack : array<Variable?>
5151
// reported character_at locations (to avoid duplicate PERF002+PERF003)
5252
@do_not_delete reported_character_at : array<ExprCall?>
53+
warning_count : int = 0
5354

5455
def PerfLintVisitor() {
5556
pass
5657
}
5758

5859
def perf_warning(text : string; at : LineInfo) : void {
60+
warning_count ++
5961
if (compile_time_errors) {
6062
compiling_program() |> macro_performance_warning(at, text)
6163
} else {
@@ -333,17 +335,20 @@ class PerfLintVisitor : AstVisitor {
333335
// Public API
334336
// ---------------------------------------------------------------------------
335337

336-
def public perf_lint(prog : ProgramPtr; compile_time_errors : bool) {
338+
def public perf_lint(prog : ProgramPtr; compile_time_errors : bool) : int {
337339
//! Runs the performance lint visitor on the compiled program.
340+
//! Returns the number of warnings found.
338341
var astVisitor = new PerfLintVisitor(compile_time_errors = compile_time_errors)
339342
unsafe {
340343
astVisitor.astVisitorAdapter <- make_visitor(*astVisitor)
341344
}
342345
visit(prog, astVisitor.astVisitorAdapter)
346+
let count = astVisitor.warning_count
343347
astVisitor.astVisitorAdapter := null
344348
unsafe {
345349
delete astVisitor
346350
}
351+
return count
347352
}
348353

349354
// ---------------------------------------------------------------------------

include/daScript/ast/ast_handle.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,6 @@ namespace das
837837
}
838838

839839
void setParents ( Module * mod, const char * child, const std::initializer_list<const char *> & parents );
840-
841-
void makeFunctionDeprecated(Function * func, const string & message);
842840
}
843841

844842
MAKE_TYPE_FACTORY(das_string, das::string);

src/builtin/module_builtin_runtime.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,6 @@ namespace das
118118
}
119119
};
120120

121-
void makeFunctionDeprecated(Function * func, const string & message) {
122-
func->deprecated = true;
123-
AnnotationDeclarationPtr decl = make_smart<AnnotationDeclaration>();
124-
decl->arguments.push_back(AnnotationArgument("message",message));
125-
decl->annotation = make_smart<DeprecatedFunctionAnnotation>();
126-
func->annotations.push_back(decl);
127-
}
128-
129-
130121
struct TypeFunctionFunctionAnnotation : MarkFunctionAnnotation {
131122
TypeFunctionFunctionAnnotation() : MarkFunctionAnnotation("type_function") { }
132123
virtual bool apply(const FunctionPtr & func, ModuleGroup &, const AnnotationArgumentList &, string & error) override {

src/builtin/module_builtin_string.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,9 +918,8 @@ namespace das
918918
addInterop<builtin_strdup,void,vec4f> (*this, lib, "builtin_strdup",
919919
SideEffects::modifyArgumentAndExternal, "builtin_strdup")->arg("anything")->unsafeOperation = true;
920920
// regular string
921-
auto chAt = addExtern<DAS_BIND_FUN(get_character_at)>(*this, lib, "character_at",
921+
addExtern<DAS_BIND_FUN(get_character_at)>(*this, lib, "character_at",
922922
SideEffects::none, "get_character_at")->args({"str","idx","context","at"});
923-
makeFunctionDeprecated(chAt.get(), "use peek_data(string) $ ( array<uint8> ) pattern instead");
924923
addExtern<DAS_BIND_FUN(get_character_uat)>(*this, lib, "character_uat",
925924
SideEffects::none, "get_character_uat")->args({"str","idx"})->unsafeOperation = true;
926925
addExtern<DAS_BIND_FUN(string_repeat)>(*this, lib, "repeat",

utils/perf_lint/main.das

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -55,38 +55,15 @@ def main() {
5555
using() <| $(var cop : CodeOfPolicies) {
5656
cop.threadlock_context = true
5757
cop.ignore_shared_modules = true
58+
cop.export_all = true
5859
compile_file(file, access, unsafe(addr(mg)), cop) <| $(ok; program; issues) {
5960
if (!ok) {
60-
// Count performance warnings (error[40217]) vs real errors
61-
var perf_count = 0
62-
var has_real_errors = false
63-
var pos = 0
6461
let issues_str = string(issues)
65-
while (true) {
66-
let idx = find(issues_str, "error[", pos)
67-
if (idx < 0) {
68-
break
69-
}
70-
if (find(issues_str, "error[40217]", idx) == idx) {
71-
perf_count ++
72-
} else {
73-
has_real_errors = true
74-
}
75-
pos = idx + 6
76-
}
77-
if (has_real_errors) {
78-
print(" COMPILE ERROR:\n{issues_str}\n")
79-
failed_files ++
80-
} else {
81-
total_warnings += perf_count
82-
if (perf_count > 0) {
83-
print("{issues_str}")
84-
}
85-
}
62+
print(" COMPILE ERROR:\n{issues_str}\n")
63+
failed_files ++
8664
} else {
87-
if (!quiet) {
88-
print(" OK\n")
89-
}
65+
// Run perf_lint directly on the compiled program
66+
total_warnings += perf_lint(program, false)
9067
}
9168
}
9269
}
@@ -102,6 +79,6 @@ def main() {
10279
print("Performance warnings: {total_warnings}\n")
10380
}
10481
unsafe {
105-
fio::exit(total_warnings > 0 || failed_files > 0 ? 1 : 0)
82+
fio::exit(failed_files > 0 ? 1 : 0)
10683
}
10784
}

utils/perf_lint/tests/no_warnings.das

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,63 @@ options gen2
44
// This file exercises all the patterns that perf_lint checks for, but uses
55
// the correct alternatives. It should produce zero performance warnings.
66

7-
require daslib/perf_lint
87
require strings
98

10-
def good_build_string() {
11-
let result = build_string() <| $(var writer) {
9+
def good_build_string() : string {
10+
return build_string() <| $(var writer) {
1211
for (i in range(100)) {
1312
write(writer, "item {i}\n")
1413
}
1514
}
1615
}
1716

18-
def good_peek_data_iteration() {
19-
let s = "hello world"
17+
def good_peek_data_iteration(s : string) : int {
18+
var count = 0
2019
peek_data(s) <| $(arr) {
21-
var count = 0
2220
for (i in range(length(arr))) {
2321
if (int(arr[i]) == 'o') {
2422
count ++
2523
}
2624
}
2725
}
26+
return count
2827
}
2928

30-
def good_modify_data() {
31-
var s = "hello"
32-
s = modify_data(s) <| $(var arr) {
29+
def good_modify_data(s : string) : string {
30+
return modify_data(s) <| $(var arr) {
3331
for (i in range(length(arr))) {
3432
arr[i] = uint8(int(arr[i]) - 32)
3533
}
3634
}
3735
}
3836

39-
def good_cached_length_while() {
40-
let s = "hello world"
37+
def good_cached_length_while(s : string) : int {
4138
let slen = length(s)
4239
var i = 0
4340
while (i < slen) {
4441
i ++
4542
}
43+
return i
4644
}
4745

48-
def good_for_range_length() {
49-
let s = "test"
46+
def good_for_range_length(s : string) : int {
47+
var total = 0
5048
for (i in range(length(s))) {
51-
pass
49+
total += i
5250
}
51+
return total
5352
}
5453

55-
def good_string_concat_outside_loop() {
54+
def good_string_concat_outside_loop() : string {
5655
var s = "hello"
5756
s += " world" // Not in a loop — no warning
57+
return s
5858
}
5959

60-
def good_string_builder_no_self_ref() {
60+
def good_string_builder_no_self_ref(prefix : string) : string {
6161
var result = ""
62-
let prefix = "item"
6362
for (i in range(10)) {
6463
result = "{prefix}_{i}" // Does not reference `result` — no PERF004
6564
}
65+
return result
6666
}

utils/perf_lint/tests/perf001_string_concat_loop.das

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
options gen2
2-
expect 40217:3
32
// PERF001: String concatenation with += in a loop
43
//
54
// Problem:
@@ -20,47 +19,52 @@ expect 40217:3
2019
// }
2120
// }
2221

23-
require daslib/perf_lint
2422
require strings
2523

26-
def bad_for_loop() {
24+
def bad_for_loop() : string {
2725
var result = ""
2826
for (i in range(100)) {
2927
result += "x" // PERF001
3028
}
29+
return result
3130
}
3231

33-
def bad_while_loop() {
32+
def bad_while_loop() : string {
3433
var result = ""
3534
var i = 0
3635
while (i < 100) {
3736
result += "x" // PERF001
3837
i ++
3938
}
39+
return result
4040
}
4141

42-
def bad_nested_loop() {
42+
def bad_nested_loop() : string {
4343
var result = ""
4444
for (i in range(10)) {
4545
for (j in range(10)) {
4646
result += "x" // PERF001
4747
}
4848
}
49+
return result
4950
}
5051

5152
// --- Good patterns (no warnings) ---
5253

53-
def good_build_string() {
54-
let result = build_string() <| $(var writer) {
54+
def good_build_string() : string {
55+
return build_string() <| $(var writer) {
5556
for (i in range(100)) {
5657
write(writer, "x")
5758
}
5859
}
5960
}
6061

61-
def good_local_string_in_loop() {
62+
def good_local_string_in_loop() : string {
63+
var last = ""
6264
for (i in range(100)) {
6365
var local_str = ""
6466
local_str += "x" // local to loop body — no warning
67+
last = local_str
6568
}
69+
return last
6670
}

utils/perf_lint/tests/perf002_character_at_loop.das

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
options gen2
2-
expect 40217:5
32
// PERF002: character_at in loop with loop variable as index
43
//
54
// Problem:
@@ -19,34 +18,35 @@ expect 40217:5
1918
// }
2019
// }
2120

22-
require daslib/perf_lint
2321
require strings
2422

25-
def bad_character_at_for() {
26-
let s = "hello world"
23+
def bad_character_at_for(s : string) : int {
24+
var total = 0
2725
for (i in range(length(s))) {
28-
let ch = character_at(s, i) // PERF002
26+
total += character_at(s, i) // PERF002
2927
}
28+
return total
3029
}
3130

32-
def bad_character_at_nested() {
33-
let s = "hello"
31+
def bad_character_at_nested(s : string) : int {
32+
var total = 0
3433
for (i in range(length(s))) {
3534
for (j in range(length(s))) {
36-
let ch = character_at(s, i) // PERF002 (i is a loop var)
35+
total += character_at(s, i) // PERF002 (i is a loop var)
3736
}
3837
}
38+
return total
3939
}
4040

41-
def bad_character_at_expr() {
42-
let s = "hello world"
41+
def bad_character_at_expr(s : string) : int {
42+
var total = 0
4343
for (i in range(length(s))) {
44-
let ch = character_at(s, i + 1) // PERF002 (loop var nested in expression)
44+
total += character_at(s, i + 1) // PERF002 (loop var nested in expression)
4545
}
46+
return total
4647
}
4748

48-
def bad_character_at_mul() : int {
49-
let s = "hello world"
49+
def bad_character_at_mul(s : string) : int {
5050
var total = 0
5151
for (i in range(length(s))) {
5252
total += character_at(s, i * 2) // PERF002 (loop var in expression)
@@ -56,17 +56,17 @@ def bad_character_at_mul() : int {
5656

5757
// --- Good patterns (no warnings from PERF002) ---
5858

59-
def good_peek_data() {
60-
let s = "hello world"
59+
def good_peek_data(s : string) : int {
60+
var total = 0
6161
peek_data(s) <| $(arr) {
6262
for (i in range(length(arr))) {
63-
let ch = int(arr[i]) // O(1)
63+
total += int(arr[i]) // O(1)
6464
}
6565
}
66+
return total
6667
}
6768

68-
def good_character_at_non_loop_var() : int {
69-
let s = "hello world"
69+
def good_character_at_non_loop_var(s : string) : int {
7070
var idx = 3
7171
for (i in range(length(s))) {
7272
idx = character_at(s, idx) // idx is not a loop variable — no PERF002

utils/perf_lint/tests/perf003_character_at.das

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
options gen2
2-
expect 40217:2
32
// PERF003: character_at anywhere (informational)
43
//
54
// Problem:
@@ -16,7 +15,6 @@ expect 40217:2
1615
// arr[i] = uint8('X') // O(1) mutation, returns new string
1716
// }
1817

19-
require daslib/perf_lint
2018
require strings
2119

2220
def info_character_at_standalone(s : string) : int {

utils/perf_lint/tests/perf004_string_builder_loop.das

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
options gen2
2-
expect 40217:2
32
// PERF004: String interpolation reassignment in loop
43
//
54
// Problem:
@@ -20,7 +19,6 @@ expect 40217:2
2019
// }
2120
// }
2221

23-
require daslib/perf_lint
2422
require strings
2523

2624
def bad_string_builder_for() : string {

0 commit comments

Comments
 (0)