Skip to content

Commit

Permalink
ownership rules
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodesdev committed Jun 14, 2024
1 parent 569108f commit ec1e2f6
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions challenges/challenges.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,18 @@
"created_at": "2024-06-13T00:00:00Z",
"updated_at": "2024-06-13T00:00:00Z"
},
{
"id": 36,
"title": "Ownership Rules",
"slug": "ownership-rules",
"short_description": "Identify and fix ownership rule violations in Rust code.",
"language": "RUST",
"difficulty": "EASY",
"track": "RUST_BASICS",
"tags": ["ownership", "borrowing", "fixing errors"],
"created_at": "2024-06-14T00:00:00Z",
"updated_at": "2024-06-14T00:00:00Z"
},
{
"id": 2,
"title": "Character counting string",
Expand Down
9 changes: 9 additions & 0 deletions challenges/ownership-rules/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "ownership-rules"
version = "0.1.0"
edition = "2021"

[dependencies]

[dev-dependencies]
syntest = { path = "../../crates/syntest" }
11 changes: 11 additions & 0 deletions challenges/ownership-rules/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub fn calculate_and_modify() -> (String, usize) {
let mut s = String::from("hello");
let length = s.len();

let s2 = &s;
println!("{}", s2);

s.push_str(", world");

(s, length)
}
11 changes: 11 additions & 0 deletions challenges/ownership-rules/src/starter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub fn calculate_and_modify() -> (String, usize) {
let mut s = String::from("hello");
let length = s.len();

let s2 = &s;
s.push_str(", world");

println!("{}", s2); // uses an old reference that has been changed `s2`

(s, length)
}
108 changes: 108 additions & 0 deletions challenges/ownership-rules/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#[cfg(test)]
mod tests {
use std::fs;

use ownership_rules::calculate_and_modify;
use syntest::{visit::Visit, ExprMethodCall, Syntest};

#[derive(Debug)]
struct Visitor {
push_str_calls: Vec<ExprMethodCall>,
}

impl Visitor {
fn new(fn_name: &str, code: &str) -> Self {
let file = syntest::parse_file(code).expect("Failed to parse file");

let mut visitor = Self {
push_str_calls: Vec::new(),
};

let items = file
.items
.iter()
.find_map(|item| {
if let syntest::Item::Fn(fn_item) = item {
if fn_item.sig.ident == fn_name {
Some(fn_item)
} else {
None
}
} else {
None
}
})
.expect(&format!("Function {} not found", fn_name));

visitor.visit_item_fn(items);

visitor
}
}

impl<'ast> Visit<'ast> for Visitor {
fn visit_expr_method_call(&mut self, method_call: &'ast ExprMethodCall) {
if method_call.method == "push_str" {
self.push_str_calls.push(method_call.clone());
}
}
}

#[test]
fn test_calculate_and_modify() {
let (s, length) = calculate_and_modify();
assert_eq!(s, "hello, world");
assert_eq!(length, 5);
}

#[test]
fn test_ownership_violations() {
{
let code = fs::read_to_string("src/lib.rs").expect("Failed to read file");
let visitor = Visitor::new("calculate_and_modify", &code);

assert_eq!(
visitor.push_str_calls.len(),
1,
"Expected push_str to be called"
);

// check the macro to be used `println!`
let syntest = Syntest::new("calculate_and_modify", "src/lib.rs");
let macros = syntest.mac.macros();
let macro_call = &macros[0];

assert_eq!(macros.len(), 1, "Expected println! to be called");
assert_eq!(macro_call.name, "println", "Expected println! to be called");
assert_eq!(
macro_call.tokens.get(0).unwrap(),
"\"{}\"",
"Expected println! to be called"
);
assert_eq!(
macro_call.tokens.get(2).unwrap(),
"s2",
"Expected println! to be called with s2"
);
}

// Local tests outside users code
{
let code = r#"
pub fn calculate_and_modify() -> (String, usize) {
let mut s = String::from("hello");
let length = s.len();
s.push_str(", world");
(s, length)
}"#;

let visitor = Visitor::new("calculate_and_modify", code);

assert_eq!(
visitor.push_str_calls.len(),
1,
"Expected push_str to be called"
);
}
}
}

0 comments on commit ec1e2f6

Please sign in to comment.