Skip to content

Commit

Permalink
Addressing instance methods in external annotations (#165)
Browse files Browse the repository at this point in the history
## What Changed?

This PR expands the capabilities of the resolution code used for
external markers and the `--analyze` command line argument to handle
more complex Rust paths, such as `<Type as Trait>::method`.

We reuse rustcs own parser to interpret the user-supplied string as a
"path".

## Why Does It Need To?

Currently external markers and the `--analyze` command line argument are
limited to simple paths (e.g. `std::vec::Vec`) and cannot target methods
of a trait impl. This PR removes that limitation.

## Checklist

- [x] Need to handle generics on the self-ty in the qualified case or at
least error.
- [x] Need to handle the qualified case without a trait
- [x] Need to re-enable `otype` annotation.
- [x] Above description has been filled out so that upon quash merge we
have a
  good record of what changed.
- [x] New functions, methods, types are documented. Old documentation is
updated
  if necessary
- [ ] Documentation in Notion has been updated
- [ ] Tests for new behaviors are provided
  - [ ] New test suites (if any) ave been added to the CI tests (in
`.github/workflows/rust.yml`) either as compiler test or integration
test.
*Or* justification for their omission from CI has been provided in this
PR
    description.
  • Loading branch information
JustusAdam committed Aug 29, 2024
1 parent df19b1e commit abb1bb1
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 164 deletions.
54 changes: 26 additions & 28 deletions crates/paralegal-flow/src/ann/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ use super::{
ExceptionAnnotation, MarkerAnnotation, MarkerRefinement, MarkerRefinementKind, VerificationHash,
};
use crate::{
utils,
utils::{write_sep, Print, TinyBitSet},
utils::{resolve::def_path_res, TinyBitSet},
Symbol,
};
use paralegal_spdg::Identifier;

use rustc_ast::{self as ast, token, tokenstream};
use rustc_ast::{self as ast, token, tokenstream, ExprKind};
use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
use rustc_parse::parser as rustc_parser;
use token::*;
use tokenstream::*;

Expand Down Expand Up @@ -240,32 +240,30 @@ pub fn tiny_bitset(i: I) -> R<TinyBitSet> {
pub(crate) fn otype_ann_match(ann: &ast::AttrArgs, tcx: TyCtxt) -> Result<Vec<DefId>, String> {
match ann {
ast::AttrArgs::Delimited(dargs) => {
let mut p = nom::multi::separated_list0(
assert_token(TokenKind::Comma),
nom::multi::separated_list0(
assert_token(TokenKind::ModSep),
nom::combinator::map(identifier, |i| i.to_string()),
),
);
p(I::from_stream(&dargs.tokens))
.map_err(|err: nom::Err<_>| format!("parser failed with error {err:?}"))?
.1
.into_iter()
.map(|strs| {
let segment_vec = strs.iter().map(AsRef::as_ref).collect::<Vec<&str>>();
Ok(utils::resolve::def_path_res(tcx, &segment_vec)
.map_err(|err| {
format!(
"Could not resolve {}: {err:?}",
Print(|f| write_sep(f, "::", &segment_vec, |elem, f| f
.write_str(elem)))
)
})?
.def_id())
})
.collect()
let mut parser =
rustc_parser::Parser::new(&tcx.sess.parse_sess, dargs.tokens.clone(), None);
std::iter::from_fn(|| {
if parser.token.kind == TokenKind::Eof {
return None;
}
let ExprKind::Path(qself, path) = &parser.parse_expr().ok()?.kind else {
return Some(Result::Err(format!(
"Expected path expression, got {:?}",
dargs.tokens
)));
};
if parser.token.kind != TokenKind::Eof {
parser.expect(&TokenKind::Comma).ok()?;
}
Some(
def_path_res(tcx, qself.as_deref(), &path.segments)
.map_err(|err| format!("Failed resolution: {err:?}",))
.map(|d| d.def_id()),
)
})
.collect()
}
_ => Result::Err("Expected delimoted annotation".to_owned()),
_ => Result::Err("Expected delimited annotation".to_owned()),
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/paralegal-flow/src/discover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl<'tcx> CollectingVisitor<'tcx> {
.filter_map(|path| {
let def_id = expect_resolve_string_to_def_id(tcx, path, opts.relaxed())?;
if !def_id.is_local() {
tcx.sess.span_err(tcx.def_span(def_id), "found an external function as analysis target. Analysis targets are required to be local.");
tcx.sess.span_err(tcx.def_span(def_id), format!("found an external function {def_id:?} as analysis target. Analysis targets are required to be local."));
return None;
}
Some(FnToAnalyze {
Expand Down
1 change: 1 addition & 0 deletions crates/paralegal-flow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ extern crate rustc_interface;
extern crate rustc_macros;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
extern crate rustc_parse;
extern crate rustc_query_system;
extern crate rustc_serialize;
extern crate rustc_session;
Expand Down
22 changes: 14 additions & 8 deletions crates/paralegal-flow/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rustc_interface::interface;
use crate::{
ann::dump_markers,
desc::{Identifier, ProgramDescription},
utils::Print,
HashSet, EXTRA_RUSTC_ARGS,
};
use std::hash::{Hash, Hasher};
Expand All @@ -22,6 +23,7 @@ use std::{

use paralegal_spdg::{
traverse::{generic_flows_to, EdgeSelection},
utils::write_sep,
DefInfo, EdgeInfo, Endpoint, Node, TypeId, SPDG,
};

Expand Down Expand Up @@ -202,7 +204,7 @@ impl InlineTestBuilder {
pub fn new(input: impl Into<String>) -> Self {
Self {
input: input.into(),
ctrl_name: "main".into(),
ctrl_name: "crate::main".into(),
}
}

Expand Down Expand Up @@ -240,12 +242,8 @@ impl InlineTestBuilder {
}

let args = crate::Args::try_from(
TopLevelArgs::parse_from([
"".into(),
"--analyze".into(),
format!("crate::{}", self.ctrl_name),
])
.args,
TopLevelArgs::parse_from(["".into(), "--analyze".into(), self.ctrl_name.to_string()])
.args,
)
.unwrap();

Expand Down Expand Up @@ -321,6 +319,7 @@ pub trait HasGraph<'g>: Sized + Copy {
}

fn ctrl_hashed(self, name: &str) -> Endpoint {
let name = name.strip_prefix("crate::").unwrap_or(name);
let candidates = self
.graph()
.desc
Expand All @@ -330,7 +329,14 @@ pub trait HasGraph<'g>: Sized + Copy {
.map(|(id, _)| *id)
.collect::<Vec<_>>();
match candidates.as_slice() {
[] => panic!("Could not find controller '{name}'"),
[] => panic!(
"Could not find controller '{name}'. Known controllers are {}",
Print(|fmt| {
write_sep(fmt, ", ", self.graph().desc.controllers.values(), |c, f| {
f.write_str(c.name.as_str())
})
})
),
[ctrl] => *ctrl,
more => panic!("Too many matching controllers, found candidates: {more:?}"),
}
Expand Down
Loading

0 comments on commit abb1bb1

Please sign in to comment.