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
9 changes: 9 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ gen_data() {
cp target/upg/demo/alloc data -r
cp target/upg/demo/core data -r
cp target/upg/demo/std data -r

merge_unsafe_fns_json
}

merge_unsafe_fns_json() {
pushd data
# Merge all objects by key, and sort by key.
jq -s 'add' -S core/navi/unsafe_fns.json alloc/navi/unsafe_fns.json std/navi/unsafe_fns.json >core-alloc-std_unsafe_fns.json
popd
}

gen_data
1 change: 0 additions & 1 deletion src/adt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::utils::{FxHashMap, FxIndexSet, ThinVec};
use derive_more::Debug;
use rustc_public::{
CrateDef,
mir::ProjectionElem,
ty::{AdtDef, AdtKind},
};
Expand Down
10 changes: 3 additions & 7 deletions src/output/caller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
utils::FxIndexMap,
};
use rustc_middle::ty::TyCtxt;
use rustc_public::{CrateDef, DefId, mir::Safety, rustc_internal::internal, ty::FnDef};
use rustc_public::{CrateDef, DefId, rustc_internal::internal, ty::FnDef};
use serde::Serialize;

#[derive(Debug, Serialize)]
Expand All @@ -28,7 +28,7 @@ impl Caller {
Caller {
fn_def,
meta: utils::Meta::new(fn_def, tcx),
safe: is_safe(fn_def),
safe: utils::is_safe(fn_def),
callees: output_callee(info),
adts: Default::default(),
path: def_path(fn_def.def_id(), tcx, navi),
Expand Down Expand Up @@ -92,14 +92,10 @@ pub fn output_callee(finfo: &FnInfo) -> FxIndexMap<String, CalleeInfo> {
for (fn_def, info) in &finfo.callees {
let fn_def = *fn_def;
let callee_info = CalleeInfo {
safe: is_safe(fn_def),
safe: utils::is_safe(fn_def),
adt: Default::default(),
};
map.insert(info.non_instance_name.clone(), callee_info);
}
map
}

fn is_safe(fn_def: FnDef) -> bool {
matches!(fn_def.fn_sig().value.safety, Safety::Safe)
}
41 changes: 38 additions & 3 deletions src/output/fn_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,41 @@ use rustc_public::{mir::Body, ty::FnDef};
use serde::Serialize;

pub fn dump(map_fn: &FxIndexMap<FnDef, FnInfo>, tcx: TyCtxt, writer: &Writer) {
let mut unsafe_fns = FxIndexMap::<String, Unsafe>::with_capacity_and_hasher(
map_fn.len() / 4,
Default::default(),
);
let mut all_fns =
FxIndexSet::<FnDef>::with_capacity_and_hasher(map_fn.len() * 2, Default::default());
// Collect all functions from caller and direct callees.
for (caller, info) in map_fn {
all_fns.insert(*caller);

for (&caller, info) in map_fn {
// Collect all functions from caller and direct callees.
all_fns.insert(caller);
for &fn_def in info.callees.keys() {
all_fns.insert(fn_def);
}

// Collect all unsafe fns, including
// * unsafe caller
// * or safe fn with unsafe callees
let unsafe_caller = !utils::is_safe(caller);
let unsafe_callee = info.callees.keys().any(|&f| !utils::is_safe(f));
if unsafe_caller | unsafe_callee {
let fn_name = utils::name(caller, tcx);
let kind = match (unsafe_caller, unsafe_callee) {
(true, true) => Unsafe::Both,
(true, false) => Unsafe::Caller,
(false, true) => Unsafe::Callee,
(false, false) => unreachable!(),
};
unsafe_fns.insert(fn_name, kind);
}
}

// Sort unsafe fns.
unsafe_fns.sort_unstable_keys();
writer.dump_json("navi", "unsafe_fns", &unsafe_fns);

for fn_def in all_fns {
let doc = Documentation::new(fn_def, tcx);

Expand All @@ -39,6 +64,16 @@ pub fn dump(map_fn: &FxIndexMap<FnDef, FnInfo>, tcx: TyCtxt, writer: &Writer) {
}
}

#[derive(Debug, Serialize)]
pub enum Unsafe {
/// The function is unsafe, but no unsafe callees inside.
Caller,
/// At least one unsafe callee is called.
Callee,
/// The function is unsafe, and calles unsafe functions inside.
Both,
}

#[derive(Debug, Serialize)]
pub struct Source {
#[serde(flatten)]
Expand Down
12 changes: 11 additions & 1 deletion src/output/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use rustc_hir::def_id::DefId as IDefId;
use rustc_middle::ty::TyCtxt;
use rustc_public::{CrateDef, rustc_internal::internal, ty::Span};
use rustc_public::{
CrateDef,
mir::Safety,
rustc_internal::internal,
ty::{FnDef, Span},
};
use serde::Serialize;

use crate::info_mod::crate_name;
Expand Down Expand Up @@ -69,3 +74,8 @@ pub fn doc_internal(did: IDefId, tcx: TyCtxt) -> String {
}
buf
}

/// If the function is unsafe.
pub fn is_safe(fn_def: FnDef) -> bool {
matches!(fn_def.fn_sig().value.safety, Safety::Safe)
}
7 changes: 5 additions & 2 deletions ui/app/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ html, body {
.upg-node,
.upg-node-fn,
.upg-node-unsafe-fn {
@apply text-lg font-mono whitespace-nowrap;
@apply text-lg font-mono whitespace-nowrap border border-gray-400;
@variant dark {
@apply bg-gray-800 text-white border-gray-100;
};
Expand All @@ -141,7 +141,7 @@ html, body {
.upg-node-adt-fn-kind {
@apply text-gray-500 font-mono whitespace-nowrap bg-transparent;
}
.upg-node-adt { @apply font-bold text-xl dark:border-gray-100/50; }
.upg-node-adt { @apply font-bold text-xl border dark:border-gray-100/50; }
/* Unset bottom border if fields are connected to callees. */
.upg-node-adt-border-b-0 { @apply border-b-0; }
.upg-node-adt-fn-kind { @apply text-lg border-none; }
Expand Down Expand Up @@ -213,3 +213,6 @@ html, body {
margin-bottom: 0.1rem !important;
}
}

/* .fnLink { @apply font-mono; } */
.unsafeFnLink { @apply text-red-700 dark:text-red-400 hover:font-bold hover:text-red-500 transition-colors; }
20 changes: 13 additions & 7 deletions ui/app/components/UPG.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
<template>
<div class="upg-left">
<WidgetTopBar v-model:flowOpts="flowOpts" v-model:crate="crate" v-model="nodeItem" v-model:share="share"
:tags="tags" />
:tags="tags" :unsafeFns="unsafeFns" />
<Flow :nodeItem="nodeItem" :tags="tags" v-model:flowOpts="flowOpts" v-model:panelContent="panelContent"
v-model:adtOpts="adtOpts" />
</div>
<div class="upg-right">
<div class="upg-panel upg-panel-1">
<WidgetSelectPanel v-model="upPanel" v-model:panelContent="panelContent" :adtOpts="adtOpts" :tags="tags" />
<WidgetSelectPanel v-model="upPanel" v-model:panelContent="panelContent" :adtOpts="adtOpts" :tags="tags"
:unsafeFns="unsafeFns" />
</div>
<div class="upg-panel">
<WidgetSelectPanel v-model="downPanel" v-model:panelContent="panelContent" :adtOpts="adtOpts" :tags="tags" />
<WidgetSelectPanel v-model="downPanel" v-model:panelContent="panelContent" :adtOpts="adtOpts" :tags="tags"
:unsafeFns="unsafeFns" />
</div>
</div>
</template>

<script setup lang="ts">
import type { FlowOpts } from "~/lib/topbar"
import type { FlowOpts, UnsafeFns } from "~/lib/topbar"
import { type DataTags, } from '~/lib/output/tag';
import { Panel, toPanel, toPanelStr, type PanelContent } from "~/lib/panel"
import { Crate, FLOW_OPTS, defaultCrateItemQuery, tagURL, toCrate, toViewTypes } from "~/lib/topbar";
import { Crate, FLOW_OPTS, defaultCrateItemQuery, tagURL, toCrate, toViewTypes, unsafeFnsURL } from "~/lib/topbar";
import type { AdtOpts } from "~/lib/output/adt";

const router = useRouter();
Expand All @@ -46,9 +48,10 @@ function init() {
if (viewTypes) flowOpts_.view = viewTypes
}

const defaultCrate = Crate.alloc
return {
crate: krate ?? Crate.alloc,
item: (krate && item && item as string) ?? defaultCrateItemQuery(Crate.std),
crate: krate ?? defaultCrate,
item: (krate && item && item as string) ?? defaultCrateItemQuery(defaultCrate),
flowOpts: flowOpts_,
up: toPanel(query.up as string) ?? Panel.Src,
down: toPanel(query.down as string) ?? Panel.Doc,
Expand All @@ -60,11 +63,14 @@ const crate = ref<Crate>(initState.crate);
const nodeItem = ref<string>(initState.item);
watch(crate, root => nodeItem.value = defaultCrateItemQuery(root))

const unsafeFns = ref<UnsafeFns>({})
const tags = ref<DataTags>({ v_fn: {}, spec: {} });
watch(crate, val => {
$fetch(tagURL(val))
.then(text => tags.value = JSON.parse(text as string))
// .catch(err => console.log(err));
$fetch(unsafeFnsURL(val))
.then(text => unsafeFns.value = JSON.parse(text as string))
}, { immediate: true });

const panelContent = ref<PanelContent>({ nodeItem: nodeItem.value });
Expand Down
11 changes: 6 additions & 5 deletions ui/app/components/code/Adt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,32 @@
<div> Field Access: </div>
<div v-for="[field, v_data], idx in Object.entries(groupedFieldAccess)" class="adt-fn-group">
<div class="min-w-15 ml-5 font-mono">{{ idx }}-{{ field }}:</div>
<CodeAdtSearchFn v-for="data in v_data" :val="data" />
<CodeAdtSearchFn v-for="data in v_data" :val="data" :unsafeFns="unsafeFns" />
</div>

<div class="adt-fn-group">
<div> Construct Adt: </div>
<CodeAdtSearchFn :val="constructors" />
<CodeAdtSearchFn :val="constructors" :unsafeFns="unsafeFns" />
</div>

<div class="adt-fn-group">
<div>{{ group_access_self_as_arg.group }}</div>
<CodeAdtSearchFn v-for="data in group_access_self_as_arg.data" :val="data" />
<CodeAdtSearchFn v-for="data in group_access_self_as_arg.data" :val="data" :unsafeFns="unsafeFns" />
</div>

<div class="adt-fn-group">
<div>{{ group_access_self_as_locals.group }}</div>
<CodeAdtSearchFn v-for="data in group_access_self_as_locals.data" :val="data" />
<CodeAdtSearchFn v-for="data in group_access_self_as_locals.data" :val="data" :unsafeFns="unsafeFns" />
</div>
</div>
</template>

<script setup lang="ts">
import type { AdtPanelItem, DataAdt } from '~/lib/output/adt';
import { getTag, type DataTags } from '~/lib/output/tag';
import type { UnsafeFns } from '~/lib/topbar';

const props = defineProps<{ adt?: DataAdt, tags: DataTags }>()
const props = defineProps<{ adt?: DataAdt, tags: DataTags, unsafeFns: UnsafeFns }>()

function gen(v: string[] | undefined, kind: string, desc: string) {
const v_fn = v ? v.map(
Expand Down
8 changes: 4 additions & 4 deletions ui/app/components/code/adt/SearchFn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
</UButton>
</UTooltip>
<template #content>
<WidgetSearchFn :v_fn="val.v_fn" v-model="search" :title="val.desc" />
<WidgetSearchFn :v_fn="val.v_fn" :unsafeFns="unsafeFns" v-model="search" :title="val.desc" />
</template>
</UModal>
</template>

<script setup lang="ts">
import type { AdtPanelItem } from '~/lib/output/adt';
import type { Search } from '~/lib/topbar';
import type { Search, UnsafeFns } from '~/lib/topbar';

const props = defineProps<{ val: AdtPanelItem }>()
const search = ref<Search>({ withTags: false, text: "", page: 1, itemsPerPage: 20 })
const props = defineProps<{ val: AdtPanelItem, unsafeFns: UnsafeFns }>()
const search = ref<Search>({ withTags: false, unsafeOnly: false, text: "", page: 1, itemsPerPage: 20 })
</script>
15 changes: 11 additions & 4 deletions ui/app/components/widget/SearchFn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="text-lg font-bold">{{ title }}</div>
<ol class="list-decimal ml-3 list-inside" :start="view.start">
<li v-for="item in view.range" class="my-1">
<ULink :to="getLink(item.name, $route, $router)">
<ULink :to="getLink(item.name, $route, $router)" :class="isUnsafe(item.name) ? 'unsafeFnLink' : ''">
<span class="font-mono">{{ item.name }}</span>
</ULink>
<UBadge v-for="tag in item.tags" :label="tag" color="warning" variant="outline" class="ml-2" />
Expand All @@ -16,26 +16,33 @@
<div class="flex justify-end items-center gap-2">
<UInput v-model="search.text" placeholder="Search Name Or Tag" />

<UCheckbox v-model="search.unsafeOnly" label="Only Unsafe Fns" />
<UCheckbox v-model="search.withTags" label="Only With Tags" />
</div>
</div>
</template>

<script setup lang="ts">
import type { Search, SearchFnItem } from '~/lib/topbar';
import type { Search, SearchFnItem, UnsafeFns } from '~/lib/topbar';

const props = defineProps<{ v_fn: SearchFnItem[], title: string }>()
const props = defineProps<{ v_fn: SearchFnItem[], title: string, unsafeFns: UnsafeFns }>()

function isUnsafe(name: string): boolean {
return props.unsafeFns[name] ? true : false
}

const search = defineModel<Search>({ required: true })

const sort = (a: SearchFnItem, b: SearchFnItem) => a.name.localeCompare(b.name)
const fullFns = computed<SearchFnItem[]>(() => {
const original = props.v_fn

const filterUnsafe = search.value.unsafeOnly
const filterTags = search.value.withTags
const filterText = search.value.text ? search.value.text.toLowerCase() : ""
return (filterTags || filterText) ? original.filter(i => {
return (filterUnsafe || filterTags || filterText) ? original.filter(i => {
let ret = true
if (filterUnsafe) ret &&= isUnsafe(i.name)
if (filterTags) ret &&= i.tags.length !== 0
if (filterText) ret &&= (
i.name.toLowerCase().includes(filterText)
Expand Down
5 changes: 3 additions & 2 deletions ui/app/components/widget/SelectPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<CodeSrc v-else-if="selected === Panel.Mir" :src="mir.mir" :isWrapped="isWrapped" />
<CodeMarkdown v-else-if="selected === Panel.Doc" :doc="doc.doc" :isWrapped="isWrapped" />
<CodeMarkdown v-else-if="selected === Panel.Tag" :doc="tagDoc" :isWrapped="isWrapped" />
<CodeAdt v-else-if="selected === Panel.Adt" :adt="adtOpts.data" :tags="tags" />
<CodeAdt v-else-if="selected === Panel.Adt" :adt="adtOpts.data" :tags="tags" :unsafeFns="unsafeFns" />
</div>
</ClientOnly>
</template>
Expand All @@ -24,10 +24,11 @@ import { adtDoc, type AdtOpts } from "~/lib/output/adt";
import { getTagDoc, type DataTags, } from "~/lib/output/tag";
import { Panel, PANELS, type PanelContent } from "~/lib/panel"
import getLink from "~/utils/getLink";
import { type UnsafeFns } from "~/lib/topbar";

const selected = defineModel<Panel>();
const panelContent = defineModel<PanelContent>("panelContent", { required: true });
const props = defineProps<{ tags: DataTags, adtOpts: AdtOpts }>()
const props = defineProps<{ tags: DataTags, unsafeFns: UnsafeFns, adtOpts: AdtOpts }>()

const src = ref<Src>(EMPTY_SRC)
const mir = ref<Mir>(EMPTY_MIR)
Expand Down
2 changes: 1 addition & 1 deletion ui/app/components/widget/Tag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<div class="flex items-center gap-4">
<USelectMenu v-model="filterTagNames" multiple clear :items="tagNames" placeholder="Filter Tags"
variant="ghost" icon="tabler:search" />
<UCheckbox label="Toggle Function" color="secondary" v-model="showFunction" />
<UCheckbox label="Display Functions" color="secondary" v-model="showFunction" />
</div>
<div class="text-xs">
Used Tag Kind: {{ spec.stat.tag_cardinality }},
Expand Down
Loading