Skip to content
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
30 changes: 30 additions & 0 deletions graph/javascript/engine/call-edge-generation/calls.dl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,36 @@ unresolved_covered(ce) :- call_site("client", ck, _, _, _, _, ce, _, _), call_ki
unresolved_receiver(ce, cn, ck, "other_form") :- call_site("client", ck, cn, _, _, _, ce, _, _),
!call_resolved(ce), !unresolved_covered(ce).

// ── library_receiver(CallExpr, Specifier) ───────────────────────────────────
// An unresolved member call whose receiver is a PACKAGE'S value: a binding imported
// from a package no staged IR declares, a member of one, what calling or constructing
// one returns, and a const holding any of those — `request(app).get('/x').expect(200)`,
// `new grpc.Metadata().get(k)`, `const md = new Metadata(); md.get(k)`. The receiver's
// origin is known, so the site is not an untyped `x.get()`: a project method of the
// same name is not what it calls. It stays unresolved (the package is not staged), and
// consumers list it apart from the name matches on unknown receivers rather than
// among them. Read off the source, never the value flow, so it cannot invent a value.
// A call handed a project INSTANCE is left out: a wrapper, a proxy or a mock helper
// may return the very object it was given (`mocked(repo).get(1)`).
// A package is named by a bare specifier: a relative or absolute path that reached no
// file (`require('./not-here')`) is a missing PROJECT module, and an alias (`@/x`, `~/x`,
// `#x`) may map onto the project's own source.
package_import(imp, spec) :- import_decl(_, spec, _, _, _, _, out, _, imp), import_outcome_is_package(out),
!match("[.#~/].*", spec), !match("@/.*", spec).
library_value(e, spec) :- expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), var_import(_, imp, v),
package_import(imp, spec).
library_value(e, spec) :- expr_kind(_, "MODULE_EDGE_CALL", _, e), expr_module_edge(_, imp, e), package_import(imp, spec).
library_value(e, spec) :- expr_kind(_, k, _, e), access_kind_reads_member(k), expr_child(_, e, "ACCESS_TARGET", _, r),
library_value(r, spec).
library_value(e, spec) :- expr_kind(_, k, _, e), (k = "CALL" ; k = "NEW"), expr_child(_, e, role, _, c),
(role = "CALLEE" ; role = "RECEIVER"), library_value(c, spec), !call_passes_project_instance(e).
library_value(e, spec) :- expr_kind(_, "AWAIT", _, e), expr_child(_, e, _, _, c), library_value(c, spec).
library_value(e, spec) :- expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), !var_import(_, _, v),
var_init("client", _, i, v), !variable_reassigned(v), library_value(i, spec).
call_passes_project_instance(e) :- call_arg(e, _, a), expr_value(a, "inst", _).
library_receiver(ce, spec) :- call_site("client", ck, _, "SYNTACTIC", _, _, ce, _, _), call_kind_is_member_form(ck),
!call_resolved(ce), expr_child(_, ce, "RECEIVER", _, r), library_value(r, spec).

// ── unresolved_value_callee(CallExpr, Binding) ──────────────────────────────
// An unresolved call whose callee is a VALUE, not a name: `cb()` on a parameter,
// `f()` on a loop variable or a local, `table[k]()`, `eval(s)`. Nothing about its
Expand Down
4 changes: 4 additions & 0 deletions graph/javascript/souffle/decls_all.dl
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
.decl call_resolved(c0:symbol)
.decl unresolved_receiver(c0:symbol, c1:symbol, c2:symbol, c3:symbol)
.decl unresolved_covered(c0:symbol)
.decl package_import(c0:symbol, c1:symbol)
.decl library_value(c0:symbol, c1:symbol)
.decl call_passes_project_instance(c0:symbol)
.decl library_receiver(c0:symbol, c1:symbol)
.decl value_callee_open(c0:symbol, c1:symbol)
.decl unresolved_value_callee(c0:symbol, c1:symbol)
.decl callee_builds_code(c0:symbol)
Expand Down
1 change: 1 addition & 0 deletions graph/javascript/souffle/export_manifest.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ client_calls_client client-to-client-calls.csv
client_calls_lib client-to-lib-calls.csv
client_calls_lib_detail client-to-lib-navigation.csv
unresolved_receiver unresolved-receiver.csv
library_receiver library-receiver.csv
method_has_unknown_call method-has-unknown-call.csv
unresolved_value_callee unresolved-value-callee.csv
entry_point entry-point.csv
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';
// A member call whose receiver is a value of a package no IR declares: what calling it,
// constructing from it, or chaining on it returns is the package's object, so the
// project's own `get` is not what these call (library_receiver in the diagnostics).
const request = require('never-staged');
const wire = require('alpha/missing');
class Repo { get(id) { return id; } }
class Service {
constructor(repo) { this.repo = repo; }
load(id) { return this.repo.get(id); }
}
async function probe(app) {
await request(app).get('/x').expect(200);
const res = await request(app).get('/y');
const md = new wire.Metadata();
md.get('k');
return res.body;
}
// controls: none of these is a package's value
function untyped(obj) { return obj.get(1); }
function wrapped(repo) { return request.mocked(new Repo()).get(1); }
function reassigned() {
let c = request.agent();
c = untyped;
return c.get(2);
}
module.exports = { Repo, Service, probe, untyped, wrapped, reassigned };
3 changes: 3 additions & 0 deletions graph/test/javascript/expected/08-library-dependency.diag
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import_cause app.js:2:13 dep not_staged
import_cause app.js:3:17 dep not_staged
import_cause app.js:3:9 dep not_staged
library_receiver app.js:5:1 c.get dep
library_receiver app.js:6:1 new Client().request dep
library_receiver app.js:8:1 dep.helper dep
package_entry probe6-app . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved app.js:4:11 FUNCTION_CALL dep callee_untyped
unresolved app.js:5:1 METHOD_CALL get receiver_untyped
Expand Down
28 changes: 28 additions & 0 deletions graph/test/javascript/expected/28-uninstalled-dependency.diag
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ import_cause app.js:6:14 alpha/missing not_staged
import_cause app.js:7:14 never-staged not_staged
import_cause app.mjs:1:10 alpha not_staged
import_cause app.mjs:2:17 alpha not_staged
import_cause receivers.js:5:17 never-staged not_staged
import_cause receivers.js:6:14 alpha/missing not_staged
library_receiver app.js:10:3 alpha.createClient alpha
library_receiver app.js:10:3 alpha.createClient().run alpha
library_receiver app.js:13:3 gone.anything alpha/missing
library_receiver app.js:14:3 nope.thing never-staged
library_receiver app.mjs:3:1 createClient().run alpha
library_receiver app.mjs:4:1 m.createClient alpha
library_receiver app.mjs:4:1 m.createClient().run alpha
library_receiver receivers.js:13:9 request(app).get never-staged
library_receiver receivers.js:13:9 request(app).get('/x').expect never-staged
library_receiver receivers.js:14:21 request(app).get never-staged
library_receiver receivers.js:16:3 md.get alpha/missing
library_receiver receivers.js:21:33 request.mocked never-staged
library_receiver receivers.js:23:11 request.agent never-staged
package_entry case26-app . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved app.js:10:3 METHOD_CALL createClient receiver_untyped
unresolved app.js:10:3 METHOD_CALL run receiver_untyped
Expand All @@ -16,3 +31,16 @@ unresolved app.mjs:3:1 FUNCTION_CALL createClient callee_untyped
unresolved app.mjs:3:1 METHOD_CALL run receiver_untyped
unresolved app.mjs:4:1 METHOD_CALL createClient receiver_untyped
unresolved app.mjs:4:1 METHOD_CALL run receiver_untyped
unresolved receivers.js:10:21 METHOD_CALL get receiver_untyped
unresolved receivers.js:13:9 FUNCTION_CALL request callee_untyped
unresolved receivers.js:13:9 METHOD_CALL expect receiver_untyped
unresolved receivers.js:13:9 METHOD_CALL get receiver_untyped
unresolved receivers.js:14:21 FUNCTION_CALL request callee_untyped
unresolved receivers.js:14:21 METHOD_CALL get receiver_untyped
unresolved receivers.js:15:14 CONSTRUCTOR_CALL Metadata receiver_untyped
unresolved receivers.js:16:3 METHOD_CALL get receiver_untyped
unresolved receivers.js:20:32 METHOD_CALL get receiver_untyped
unresolved receivers.js:21:33 METHOD_CALL get receiver_untyped
unresolved receivers.js:21:33 METHOD_CALL mocked receiver_untyped
unresolved receivers.js:23:11 METHOD_CALL agent receiver_untyped
unresolved receivers.js:25:10 METHOD_CALL get member_absent
14 changes: 14 additions & 0 deletions graph/test/javascript/expected/28-uninstalled-dependency.edges
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ app.mjs:3:1 FUNCTION_CALL createClient -> ambiguous_unknown -
app.mjs:3:1 METHOD_CALL createClient().run -> ambiguous_unknown -
app.mjs:4:1 METHOD_CALL m.createClient -> ambiguous_unknown -
app.mjs:4:1 METHOD_CALL m.createClient().run -> ambiguous_unknown -
receivers.js:10:21 METHOD_CALL this.repo.get -> ambiguous_unknown -
receivers.js:13:9 FUNCTION_CALL request -> ambiguous_unknown -
receivers.js:13:9 METHOD_CALL request(app).get -> ambiguous_unknown -
receivers.js:13:9 METHOD_CALL request(app).get('/x').expect -> ambiguous_unknown -
receivers.js:14:21 FUNCTION_CALL request -> ambiguous_unknown -
receivers.js:14:21 METHOD_CALL request(app).get -> ambiguous_unknown -
receivers.js:15:14 CONSTRUCTOR_CALL wire.Metadata -> ambiguous_unknown -
receivers.js:16:3 METHOD_CALL md.get -> ambiguous_unknown -
receivers.js:20:32 METHOD_CALL obj.get -> ambiguous_unknown -
receivers.js:21:33 METHOD_CALL request.mocked -> ambiguous_unknown -
receivers.js:21:33 METHOD_CALL request.mocked(new Repo()).get -> ambiguous_unknown -
receivers.js:21:48 CONSTRUCTOR_CALL Repo -> implicit_constructor -
receivers.js:23:11 METHOD_CALL request.agent -> ambiguous_unknown -
receivers.js:25:10 METHOD_CALL c.get -> ambiguous_unknown -
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import_cause app.js:6:14 alpha/missing staged_unreached
import_cause app.js:7:14 never-staged not_staged
import_cause receivers.js:5:17 never-staged not_staged
import_cause receivers.js:6:14 alpha/missing staged_unreached
library_receiver app.js:13:3 gone.anything alpha/missing
library_receiver app.js:14:3 nope.thing never-staged
library_receiver receivers.js:13:9 request(app).get never-staged
library_receiver receivers.js:13:9 request(app).get('/x').expect never-staged
library_receiver receivers.js:14:21 request(app).get never-staged
library_receiver receivers.js:16:3 md.get alpha/missing
library_receiver receivers.js:21:33 request.mocked never-staged
library_receiver receivers.js:23:11 request.agent never-staged
package_entry alpha . [] MAIN lib/index.js RESOLVED -> lib:lib/index.js
package_entry alpha . [import] EXPORTS lib/index.mjs RESOLVED -> lib:lib/index.mjs
package_entry alpha . [require] EXPORTS lib/index.js RESOLVED -> lib:lib/index.js
Expand All @@ -9,3 +19,16 @@ package_entry case26-app . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved app.js:13:3 METHOD_CALL anything receiver_untyped
unresolved app.js:14:3 METHOD_CALL thing receiver_untyped
unresolved app.mjs:2:17 DYNAMIC_IMPORT_CALL dynamic
unresolved receivers.js:10:21 METHOD_CALL get receiver_untyped
unresolved receivers.js:13:9 FUNCTION_CALL request callee_untyped
unresolved receivers.js:13:9 METHOD_CALL expect receiver_untyped
unresolved receivers.js:13:9 METHOD_CALL get receiver_untyped
unresolved receivers.js:14:21 FUNCTION_CALL request callee_untyped
unresolved receivers.js:14:21 METHOD_CALL get receiver_untyped
unresolved receivers.js:15:14 CONSTRUCTOR_CALL Metadata receiver_untyped
unresolved receivers.js:16:3 METHOD_CALL get receiver_untyped
unresolved receivers.js:20:32 METHOD_CALL get receiver_untyped
unresolved receivers.js:21:33 METHOD_CALL get receiver_untyped
unresolved receivers.js:21:33 METHOD_CALL mocked receiver_untyped
unresolved receivers.js:23:11 METHOD_CALL agent receiver_untyped
unresolved receivers.js:25:10 METHOD_CALL get member_absent
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ app.mjs:3:1 FUNCTION_CALL createClient -> boundary_lib lib:lib/index.mjs:2:1
app.mjs:3:1 METHOD_CALL createClient().run -> boundary_lib lib:lib/index.mjs:1:21 run
app.mjs:4:1 METHOD_CALL m.createClient -> boundary_lib lib:lib/index.mjs:2:1 createClient
app.mjs:4:1 METHOD_CALL m.createClient().run -> boundary_lib lib:lib/index.mjs:1:21 run
receivers.js:10:21 METHOD_CALL this.repo.get -> ambiguous_unknown -
receivers.js:13:9 FUNCTION_CALL request -> ambiguous_unknown -
receivers.js:13:9 METHOD_CALL request(app).get -> ambiguous_unknown -
receivers.js:13:9 METHOD_CALL request(app).get('/x').expect -> ambiguous_unknown -
receivers.js:14:21 FUNCTION_CALL request -> ambiguous_unknown -
receivers.js:14:21 METHOD_CALL request(app).get -> ambiguous_unknown -
receivers.js:15:14 CONSTRUCTOR_CALL wire.Metadata -> ambiguous_unknown -
receivers.js:16:3 METHOD_CALL md.get -> ambiguous_unknown -
receivers.js:20:32 METHOD_CALL obj.get -> ambiguous_unknown -
receivers.js:21:33 METHOD_CALL request.mocked -> ambiguous_unknown -
receivers.js:21:33 METHOD_CALL request.mocked(new Repo()).get -> ambiguous_unknown -
receivers.js:21:48 CONSTRUCTOR_CALL Repo -> implicit_constructor -
receivers.js:23:11 METHOD_CALL request.agent -> ambiguous_unknown -
receivers.js:25:10 METHOD_CALL c.get -> ambiguous_unknown -
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
app.js:11:3 CONSTRUCTOR_CALL Mine SYNTHESIZED_OK
app.js:16:1 FUNCTION_CALL main EXACT app.js:9:1
receivers.js:21:48 CONSTRUCTOR_CALL Repo SYNTHESIZED_OK
# defects: 0
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import_cause app.js:7:18 kit/internal/secret not_staged
import_cause app.js:8:16 kit/feat/deep/none not_staged
import_cause app.mjs:1:10 kit/mjs/gamma not_staged
import_cause app.mjs:2:10 kit/feat/alpha not_staged
library_receiver app.js:9:19 createApp().start kit
library_receiver app.js:9:66 internal.secret kit/internal/secret
library_receiver app.js:9:85 nested.x kit/feat/deep/none
package_entry case30-app . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved app.js:9:19 FUNCTION_CALL createApp callee_untyped
unresolved app.js:9:19 METHOD_CALL start receiver_untyped
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import_cause app.js:7:18 kit/internal/secret staged_unreached
import_cause app.js:8:16 kit/feat/deep/none staged_unreached
library_receiver app.js:9:66 internal.secret kit/internal/secret
library_receiver app.js:9:85 nested.x kit/feat/deep/none
package_entry case30-app . [] DEFAULT_INDEX index.js MISSING_FILE -> -
package_entry kit . [default] EXPORTS src/index.js RESOLVED -> lib:src/index.js
package_entry kit . [node.require] EXPORTS src/index.js RESOLVED -> lib:src/index.js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import_cause main.js:2:14 walker not_staged
import_cause main.js:37:136 events builtin
import_cause main.js:3:15 https builtin
import_cause main.js:4:9 stream builtin
library_receiver main.js:43:25 walk.register walker
library_receiver main.js:43:48 walk.register walker
package_entry options-callbacks . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved main.js:10:28 FUNCTION_CALL walk callee_untyped
unresolved main.js:11:89 FUNCTION_CALL walk callee_untyped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import_cause main.js:17:16 assert builtin
import_cause main.js:37:136 events builtin
import_cause main.js:3:15 https builtin
import_cause main.js:4:9 stream builtin
library_receiver main.js:43:25 walk.register walker
library_receiver main.js:43:48 walk.register walker
package_entry options-callbacks . [] DEFAULT_INDEX index.js MISSING_FILE -> -
package_entry walker . [] MAIN index.js RESOLVED -> lib:index.js
unresolved main.js:12:86 METHOD_CALL destroy no_target
Expand Down
2 changes: 2 additions & 0 deletions graph/test/javascript/expected/48-umd-library.diag
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import_cause app.js:3:13 umdlib not_staged
import_cause app.js:4:9 umdlib not_staged
library_receiver app.js:5:1 umd.format umdlib
library_receiver app.js:7:1 new umd.Formatter().render umdlib
package_entry case48-app . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved app.js:5:1 METHOD_CALL format receiver_untyped
unresolved app.js:6:1 FUNCTION_CALL format callee_untyped
Expand Down
1 change: 1 addition & 0 deletions graph/test/javascript/expected/60-ts-emit-helpers.diag
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import_cause main.js:5:9 tspkg not_staged
library_receiver main.js:7:26 new Client().send tspkg
package_entry 60-ts-emit-helpers . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved main.js:7:26 CONSTRUCTOR_CALL Client callee_untyped
unresolved main.js:7:26 METHOD_CALL send receiver_untyped
1 change: 1 addition & 0 deletions graph/test/javascript/expected/63-jsx-wrapper-lazy.diag
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import_cause app.jsx:3:10 thing-kit not_staged
import_cause defmemo.jsx:1:10 react not_staged
import_cause parts.jsx:1:10 react not_staged
import_cause parts.jsx:1:16 react not_staged
library_receiver app.jsx:11:16 React.memo react
package_entry @axiomcode/code-graph . [] MAIN dist/reason.js NOT_STAGED -> -
unresolved app.jsx:10:18 FUNCTION_CALL observer callee_untyped
unresolved app.jsx:11:16 METHOD_CALL memo receiver_untyped
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import_cause components/List.svelte:2:12 svelte not_staged
import_cause components/Price.vue:4:10 vue not_staged
import_cause main.js:2:10 vue not_staged
library_receiver main.js:5:1 createApp(Price).mount vue
package_entry single-file-component-scripts . [] DEFAULT_INDEX index.js MISSING_FILE -> -
unresolved components/Legacy.vue:7:26 METHOD_CALL $emit member_absent
unresolved components/List.svelte:5:3 FUNCTION_CALL onMount callee_untyped
Expand Down
4 changes: 4 additions & 0 deletions graph/test/javascript/expected/74-orm-model-factory.diag
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import_cause models.js:21:17 other-orm not_staged
import_cause models.js:7:18 mongoose not_staged
library_receiver models.js:12:1 docSchema.static mongoose
library_receiver models.js:13:1 docSchema.static mongoose
library_receiver models.js:15:1 docSchema.method mongoose
library_receiver models.js:28:10 mongoose.model mongoose
package_entry @axiomcode/code-graph . [] MAIN dist/reason.js NOT_STAGED -> -
unresolved models.js:10:19 CONSTRUCTOR_CALL Schema callee_untyped
unresolved models.js:11:51 METHOD_CALL find receiver_untyped
Expand Down
4 changes: 4 additions & 0 deletions graph/test/javascript/tools/normalize_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ def raw(path, width):
for f in raw(os.path.join(out, 'unresolved-value-callee.csv'), 2):
pos, _, text = site.get(f[0], ('?', '?', '?'))
lines.add('value_callee %s %s %s' % (pos, text, f[1]))
# library_receiver(CallExpr, Specifier): the receiver is a package's value, so a same-named project method is not the callee
for f in raw(os.path.join(out, 'library-receiver.csv'), 2):
pos, _, text = site.get(f[0], ('?', '?', '?'))
lines.add('library_receiver %s %s %s' % (pos, text, f[1]))
# package_entry(Prov, Package, Subpath, Condition, Source, Target, Outcome, ModuleHash)
for f in raw(os.path.join(out, 'package-entry.csv'), 8):
lines.add('package_entry %s %s [%s] %s %s %s -> %s' % (
Expand Down
Loading
Loading