You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A Rust import that points inside the same crate is classified as an I/O
import whenever one of its path segments happens to match a name in the I/O
table. use crate::request::Foo; becomes a network import because request is the Node HTTP client, use super::http::fetch; becomes network
because of Dart's http, and use self::fs::helper; becomes filesystem.
The leading crate, self or super segment says the path is local code, but
nothing reads it.
This builds the candidate names the classifier tries, and it includes every
interior :: segment of the path.
Mechanism
_candidate_variants (io_boundaries.py:56-86) is deliberately generous so
one function can serve every language. For a :: path it adds each interior
segment on its own (out.update(colon_parts), io_boundaries.py:79), so crate::request::Foo yields crate, request, Foo and the progressive
prefixes.
classify_io_kind (ingestion/external_systems/io_kind.py:180-189) is an
exact, case-insensitive lookup, not a prefix match. So the candidate request hits "request" in _NETWORK_NAMES (io_kind.py:88, the Node
package), http hits io_kind.py:100 (the Dart package), and fs hits io_kind.py:121. Nothing in the table is Rust-specific for these; the Rust
HTTP crates are only reqwest, hyper and similar (io_kind.py:97).
_classify_import (io_boundaries.py:110-122) takes the first candidate
that resolves and then binds every identifier in the statement to that kind:
use crate::request::Foo; {'crate': 'network', 'request': 'network', 'Foo': 'network'}
use super::http::fetch; {'super': 'network', 'http': 'network', 'fetch': 'network'}
use self::fs::helper; {'self': 'filesystem', 'fs': 'filesystem', 'helper': 'filesystem'}
use reqwest::Client; {'reqwest': 'network', 'Client': 'network'}
The last line is the control: a real external HTTP crate stays network.
With the _classify_import body from PR #2871 patched into a scratch copy, the
first three lines are unchanged: its longest-variant-first order still reaches request, http and fs.
This is not only cosmetic. The Rust dialect treats any db name in the file
as a database import (complexity/perf_walk.py:238, has_db_import = any(k == "db" for k in io_names.values())) and then flags
every .execute(...) call as a db sink (perf/dialects/rust.py:179). A local
module named after a database crate is enough:
usecrate::postgres::Row;fnf(items:Vec<i32>,c:&Conn){for i in items { c.execute(i);}}
The same function without the use line produces no hit.
Impact
Rust projects routinely have modules called http, fs, request, postgres or drift. A use crate::... or use super::... of such a module
silently turns the file into one that "imports a database" or "imports a
network client", which produces io_in_loop findings on unrelated .execute(...) calls. The wrong kind also lands in io_boundary_names for the
file.
Done looks like
_classify_import (io_boundaries.py:89) returns (None, []) for a Rust
path whose first segment is crate, self or super, so nothing in the
statement is bound. A leading :: (::std::fs), std and core stay
external. Keep this scoped to the Rust use shape: do not special-case the
words self or super for Python, Java or other languages that share this
function.
What must NOT change: use reqwest::Client;, use std::fs::File; and use sqlx::Pool; still classify as before. The shared table in io_kind.py is not edited, since request, http and fs are right for the
ecosystems that own them.
Because stored perf findings change, bump HEALTH_ANALYZER_VERSION
(analysis/health/engine.py:382) with a comment in the version history above
it, as earlier classification changes (v29, v30) did.
Why this is a good first issue: one function, one new early return, the table
and the sink logic are untouched, and the repro is five lines.
Worth checking in the same pass: pub(crate) use and use crate::{fs::a, db::b}
(a brace group with a local prefix), which should both come out empty.
Tests
tests/unit/health/test_perf_io_in_loop.py (it already drives walk_file
for Rust): use crate::request::Foo;, use super::http::fetch; and use self::fs::helper; give an empty io_boundary_names.
Same file: use crate::postgres::Row; followed by a for loop calling c.execute(i) produces no io_in_loop; the same loop with use sqlx::PgPool; still produces one.
Control: use reqwest::Client; still gives {'reqwest': 'network', 'Client': 'network'}.
Summary
A Rust import that points inside the same crate is classified as an I/O
import whenever one of its path segments happens to match a name in the I/O
table.
use crate::request::Foo;becomes anetworkimport becauserequestis the Node HTTP client,use super::http::fetch;becomesnetworkbecause of Dart's
http, anduse self::fs::helper;becomesfilesystem.The leading
crate,selforsupersegment says the path is local code, butnothing reads it.
packages/core/src/repowise/core/analysis/health/perf/io_boundaries.py:107-108This builds the candidate names the classifier tries, and it includes every
interior
::segment of the path.Mechanism
_candidate_variants(io_boundaries.py:56-86) is deliberately generous soone function can serve every language. For a
::path it adds each interiorsegment on its own (
out.update(colon_parts),io_boundaries.py:79), socrate::request::Fooyieldscrate,request,Fooand the progressiveprefixes.
classify_io_kind(ingestion/external_systems/io_kind.py:180-189) is anexact, case-insensitive lookup, not a prefix match. So the candidate
requesthits"request"in_NETWORK_NAMES(io_kind.py:88, the Nodepackage),
httphitsio_kind.py:100(the Dart package), andfshitsio_kind.py:121. Nothing in the table is Rust-specific for these; the RustHTTP crates are only
reqwest,hyperand similar (io_kind.py:97)._classify_import(io_boundaries.py:110-122) takes the first candidatethat resolves and then binds every identifier in the statement to that kind:
crate,selfandsuperare not in_IMPORT_KW(io_boundaries.py:37-39),so they get bound as well.
Rust reaches this through
node_type in ("using_directive", "use_declaration")at
io_boundaries.py:192.Repro
On
main(68d6c1d):The last line is the control: a real external HTTP crate stays
network.With the
_classify_importbody from PR #2871 patched into a scratch copy, thefirst three lines are unchanged: its longest-variant-first order still reaches
request,httpandfs.This is not only cosmetic. The Rust dialect treats any
dbname in the fileas a database import (
complexity/perf_walk.py:238,has_db_import = any(k == "db" for k in io_names.values())) and then flagsevery
.execute(...)call as a db sink (perf/dialects/rust.py:179). A localmodule named after a database crate is enough:
The same function without the
useline produces no hit.Impact
Rust projects routinely have modules called
http,fs,request,postgresordrift. Ause crate::...oruse super::...of such a modulesilently turns the file into one that "imports a database" or "imports a
network client", which produces
io_in_loopfindings on unrelated.execute(...)calls. The wrong kind also lands inio_boundary_namesfor thefile.
Done looks like
_classify_import(io_boundaries.py:89) returns(None, [])for a Rustpath whose first segment is
crate,selforsuper, so nothing in thestatement is bound. A leading
::(::std::fs),stdandcorestayexternal. Keep this scoped to the Rust
useshape: do not special-case thewords
selforsuperfor Python, Java or other languages that share thisfunction.
_classify_import. Start after that PR merges and apply the check to therewritten function. The grouped-use issue (Rust grouped
use {a::x, b::y}gives every imported name the first module's I/O kind #2894) will later applythe same check per leaf; for this issue checking the statement's first path
is enough.
use reqwest::Client;,use std::fs::File;anduse sqlx::Pool;still classify as before. The shared table inio_kind.pyis not edited, sincerequest,httpandfsare right for theecosystems that own them.
HEALTH_ANALYZER_VERSION(
analysis/health/engine.py:382) with a comment in the version history aboveit, as earlier classification changes (v29, v30) did.
and the sink logic are untouched, and the repro is five lines.
pub(crate) useanduse crate::{fs::a, db::b}(a brace group with a local prefix), which should both come out empty.
Tests
tests/unit/health/test_perf_io_in_loop.py(it already driveswalk_filefor Rust):
use crate::request::Foo;,use super::http::fetch;anduse self::fs::helper;give an emptyio_boundary_names.use crate::postgres::Row;followed by aforloop callingc.execute(i)produces noio_in_loop; the same loop withuse sqlx::PgPool;still produces one.use reqwest::Client;still gives{'reqwest': 'network', 'Client': 'network'}.