Pointcut Expressions
This chapter explains the pointcut expression language used in Phase 3 for automatic aspect matching, including syntax, semantics, and advanced patterns.
What Are Pointcuts?
Pointcuts are expressions that select join points (function calls) where aspects should be applied:
Pointcut Expression → Selects Functions → Aspects Applied
Example:
--aspect-pointcut "execution(pub fn *(..))"
# Selects all public functions
Pointcut Syntax
Execution Pointcut
Matches function execution based on signature:
execution(VISIBILITY fn NAME(PARAMETERS))
Components:
VISIBILITY:pub,pub(crate), or omit for anyfn: Keyword (required)NAME: Function name or*wildcardPARAMETERS:(..)for any parameters
Examples:
# All public functions
execution(pub fn *(..))
# Specific function
execution(pub fn fetch_user(..))
# All functions (any visibility)
execution(fn *(..))
# Private functions
execution(fn *(..) where !pub)
Within Pointcut
Matches functions within a specific module:
within(MODULE_PATH)
Examples:
# All functions in api module
within(api)
# Nested module
within(api::handlers)
# Full path
within(crate::api)
Call Pointcut
Matches function calls (caller perspective):
call(FUNCTION_NAME)
Examples:
# Any call to database::query
call(database::query)
# Any call to functions starting with "fetch_"
call(fetch_*)
Pattern Matching
Wildcard Matching
Use * to match any name:
# All functions
execution(fn *(..))
# All functions starting with "get_"
execution(fn get_*(..))
# All functions in any submodule
within(*::handlers)
Visibility Matching
# Public functions
execution(pub fn *(..))
# Crate-visible functions
execution(pub(crate) fn *(..))
# Private functions (no visibility keyword)
execution(fn *(..) where !pub)
Module Path Matching
# Exact module
within(api)
# Module prefix
within(api::*)
# Nested modules
within(crate::api::handlers)
Implementation Details
Pointcut Data Structure
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, PartialEq)]
pub enum Pointcut {
Execution(ExecutionPattern),
Within(String),
Call(String),
And(Box<Pointcut>, Box<Pointcut>),
Or(Box<Pointcut>, Box<Pointcut>),
Not(Box<Pointcut>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ExecutionPattern {
pub visibility: Option<VisibilityKind>,
pub name: String, // "*" for wildcard
pub async_fn: Option<bool>,
}
}
Parsing Execution Patterns
#![allow(unused)]
fn main() {
impl Pointcut {
pub fn parse_execution(expr: &str) -> Result<Self, ParseError> {
// expr = "execution(pub fn *(..))
// Remove "execution(" and trailing ")"
let inner = expr
.strip_prefix("execution(")
.and_then(|s| s.strip_suffix(")"))
.ok_or(ParseError::InvalidSyntax)?;
// Parse: "pub fn *(..)"
let parts: Vec<&str> = inner.split_whitespace().collect();
let mut visibility = None;
let mut name = "*".to_string();
let mut async_fn = None;
let mut i = 0;
// Check for visibility
if parts.get(i) == Some(&"pub") {
visibility = Some(VisibilityKind::Public);
i += 1;
// Check for pub(crate)
if parts.get(i).map(|s| s.starts_with("(crate)")).unwrap_or(false) {
visibility = Some(VisibilityKind::Crate);
i += 1;
}
}
// Check for async
if parts.get(i) == Some(&"async") {
async_fn = Some(true);
i += 1;
}
// Expect "fn"
if parts.get(i) != Some(&"fn") {
return Err(ParseError::MissingFnKeyword);
}
i += 1;
// Get function name
if let Some(name_part) = parts.get(i) {
// Remove trailing "(..)" if present
name = name_part.trim_end_matches("(..)").to_string();
}
Ok(Pointcut::Execution(ExecutionPattern {
visibility,
name,
async_fn,
}))
}
}
}
Matching Algorithm
#![allow(unused)]
fn main() {
impl PointcutMatcher {
pub fn matches(&self, pointcut: &Pointcut, func: &FunctionMetadata) -> bool {
match pointcut {
Pointcut::Execution(pattern) => {
self.matches_execution(pattern, func)
}
Pointcut::Within(module) => {
self.matches_within(module, func)
}
Pointcut::Call(name) => {
self.matches_call(name, func)
}
Pointcut::And(p1, p2) => {
self.matches(p1, func) && self.matches(p2, func)
}
Pointcut::Or(p1, p2) => {
self.matches(p1, func) || self.matches(p2, func)
}
Pointcut::Not(p) => {
!self.matches(p, func)
}
}
}
fn matches_execution(
&self,
pattern: &ExecutionPattern,
func: &FunctionMetadata
) -> bool {
// Check visibility
if let Some(required_vis) = &pattern.visibility {
if &func.visibility != required_vis {
return false;
}
}
// Check async
if let Some(required_async) = pattern.async_fn {
if func.is_async != required_async {
return false;
}
}
// Check name
if pattern.name != "*" {
if !self.matches_name(&pattern.name, &func.simple_name) {
return false;
}
}
true
}
fn matches_name(&self, pattern: &str, name: &str) -> bool {
if pattern == "*" {
return true;
}
// Wildcard matching
if pattern.ends_with("*") {
let prefix = pattern.trim_end_matches('*');
return name.starts_with(prefix);
}
if pattern.starts_with("*") {
let suffix = pattern.trim_start_matches('*');
return name.ends_with(suffix);
}
// Exact match
pattern == name
}
fn matches_within(&self, module: &str, func: &FunctionMetadata) -> bool {
// Check if function is within specified module
// Handle wildcard
if module.ends_with("::*") {
let prefix = module.trim_end_matches("::*");
return func.module_path.starts_with(prefix);
}
// Exact match or prefix match
func.module_path == module ||
func.module_path.starts_with(&format!("{}::", module)) ||
func.qualified_name.contains(&format!("::{}", module))
}
}
}
Boolean Combinators
Combine pointcuts with logical operators:
AND Combinator
# Public functions in api module
execution(pub fn *(..)) && within(api)
Implementation:
#![allow(unused)]
fn main() {
Pointcut::And(
Box::new(Pointcut::Execution(/* pub fn *(..) */)),
Box::new(Pointcut::Within("api".to_string())),
)
}
OR Combinator
# Functions in api or handlers modules
within(api) || within(handlers)
Implementation:
#![allow(unused)]
fn main() {
Pointcut::Or(
Box::new(Pointcut::Within("api".to_string())),
Box::new(Pointcut::Within("handlers".to_string())),
)
}
NOT Combinator
# All functions except in tests module
execution(fn *(..)) && !within(tests)
Implementation:
#![allow(unused)]
fn main() {
Pointcut::And(
Box::new(Pointcut::Execution(/* fn *(..) */)),
Box::new(Pointcut::Not(
Box::new(Pointcut::Within("tests".to_string())),
)),
)
}
Practical Examples
Example 1: API Logging
Apply logging to all public API functions:
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn *(..)) && within(api)" \
--aspect-apply "LoggingAspect::new()"
Matches:
#![allow(unused)]
fn main() {
// ✓ Matched
pub mod api {
pub fn fetch_user(id: u64) -> User { }
pub fn save_user(user: User) -> Result<()> { }
}
// ✗ Not matched (not in api module)
pub fn helper() { }
// ✗ Not matched (private)
mod api {
fn internal() { }
}
}
Example 2: Async Function Timing
Time all async functions:
aspect-rustc-driver \
--aspect-pointcut "execution(pub async fn *(..))" \
--aspect-apply "TimingAspect::new()"
Matches:
#![allow(unused)]
fn main() {
// ✓ Matched
pub async fn fetch_data() -> Data { }
// ✗ Not matched (not async)
pub fn sync_function() { }
// ✗ Not matched (private)
async fn private_async() { }
}
Example 3: Database Transaction Management
Apply transactions to all database operations:
aspect-rustc-driver \
--aspect-pointcut "within(database::ops)" \
--aspect-apply "TransactionalAspect::new()"
Matches:
#![allow(unused)]
fn main() {
// ✓ Matched
mod database {
mod ops {
pub fn insert(data: Data) -> Result<()> { }
fn delete(id: u64) -> Result<()> { } // Also matched
}
}
// ✗ Not matched
mod database {
pub fn connect() -> Connection { }
}
}
Example 4: Security for Admin Functions
Apply authorization to admin functions:
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn admin_*(..))" \
--aspect-apply "AuthorizationAspect::require_role(\"admin\")"
Matches:
#![allow(unused)]
fn main() {
// ✓ Matched
pub fn admin_delete_user(id: u64) { }
pub fn admin_grant_permissions(user: User) { }
// ✗ Not matched
pub fn user_profile() { }
pub fn admin() { } // Exact match, not prefix
}
Example 5: Exclude Test Code
Apply aspects to all code except tests:
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn *(..)) && !within(tests)" \
--aspect-apply "MetricsAspect::new()"
Matches:
#![allow(unused)]
fn main() {
// ✓ Matched
pub fn production_code() { }
mod api {
pub fn handler() { } // ✓ Matched
}
// ✗ Not matched
mod tests {
pub fn test_something() { }
}
}
Command-Line Usage
Single Pointcut
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn *(..))" \
main.rs
Multiple Pointcuts
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn *(..))" \
--aspect-pointcut "within(api)" \
--aspect-pointcut "within(handlers)" \
main.rs
Each pointcut is evaluated independently.
With Aspect Application
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn *(..))" \
--aspect-apply "LoggingAspect::new()" \
main.rs
Configuration File
Create aspect-config.toml:
[[pointcuts]]
pattern = "execution(pub fn *(..))"
aspects = ["LoggingAspect::new()"]
[[pointcuts]]
pattern = "within(api)"
aspects = ["TimingAspect::new()", "SecurityAspect::new()"]
[options]
verbose = true
output = "target/aspect-analysis.txt"
Use with:
aspect-rustc-driver --aspect-config aspect-config.toml main.rs
Advanced Patterns
Combining Multiple Criteria
# Public async functions in api module, except tests
execution(pub async fn *(..)) && within(api) && !within(api::tests)
Prefix and Suffix Matching
# Functions starting with "get_"
execution(fn get_*(..))
# Functions ending with "_handler"
execution(fn *_handler(..))
Module Hierarchies
# All submodules of api
within(api::*)
# Specific nested module
within(crate::services::api::handlers)
Visibility Variants
# Public and crate-visible
execution(pub fn *(..)) || execution(pub(crate) fn *(..))
# Only truly public
execution(pub fn *(..)) && !execution(pub(crate) fn *(..))
Pointcut Library
Common pointcut patterns for reuse:
All Public API
--aspect-pointcut "execution(pub fn *(..)) && (within(api) || within(handlers))"
All Database Operations
--aspect-pointcut "within(database) || call(query) || call(execute)"
All HTTP Handlers
--aspect-pointcut "execution(pub async fn *_handler(..))"
All Admin Functions
--aspect-pointcut "execution(pub fn admin_*(..)) || within(admin)"
Production Code Only
--aspect-pointcut "execution(fn *(..)) && !within(tests) && !within(benches)"
Performance Considerations
Pointcut Evaluation Cost
#![allow(unused)]
fn main() {
// Fast: Simple checks
execution(pub fn *(..)) // O(1) visibility check
// Medium: String matching
execution(fn get_*(..)) // O(n) prefix check
// Slow: Complex combinators
(execution(...) && within(...)) || (!execution(...)) // Multiple checks
}
Optimization strategy:
- Evaluate cheapest checks first
- Short-circuit on failure
- Cache results when possible
Compilation Impact
Simple pointcut: +1% compile time
Complex pointcut: +3% compile time
Multiple pointcuts: +2% per pointcut
Still negligible compared to total compilation.
Testing Pointcuts
Dry Run Mode
aspect-rustc-driver \
--aspect-pointcut "execution(pub fn *(..))" \
--aspect-dry-run \
--aspect-output matches.txt \
main.rs
Outputs matched functions without applying aspects.
Verification
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_execution_pointcut() {
let pointcut = Pointcut::parse_execution("execution(pub fn *(..))").unwrap();
let func = FunctionMetadata {
simple_name: "test_func".to_string(),
visibility: VisibilityKind::Public,
// ...
};
let matcher = PointcutMatcher::new(vec![pointcut]);
assert!(matcher.matches(&pointcut, &func));
}
#[test]
fn test_within_pointcut() {
let pointcut = Pointcut::Within("api".to_string());
let func = FunctionMetadata {
module_path: "crate::api".to_string(),
// ...
};
let matcher = PointcutMatcher::new(vec![pointcut]);
assert!(matcher.matches(&pointcut, &func));
}
}
}
Error Handling
Invalid Syntax
$ aspect-rustc-driver --aspect-pointcut "invalid syntax"
error: Failed to parse pointcut expression
--> invalid syntax
|
| Expected: execution(PATTERN) or within(MODULE)
Missing Components
$ aspect-rustc-driver --aspect-pointcut "execution(*(..))
error: Missing 'fn' keyword in execution pointcut
--> execution(*(..))
|
| Expected: execution([pub] fn NAME(..))
Unsupported Features
$ aspect-rustc-driver --aspect-pointcut "args(i32, String)"
error: 'args' pointcut not yet supported
--> Use execution(...) or within(...) instead
Future Enhancements
Planned Features
-
Parameter Matching
execution(fn *(id: u64, ..)) -
Return Type Matching
execution(fn *(..) -> Result<T, E>) -
Annotation Matching
execution(@deprecated fn *(..)) -
Call-Site Matching
call(database::query) && within(api) -
Field Access
get(User.email) || set(User.*)
Key Takeaways
- Pointcuts select functions automatically - No manual annotations
- Three main types - execution, within, call
- Wildcards enable flexible matching -
*matches anything - Boolean combinators - AND, OR, NOT for complex logic
- Compile-time evaluation - Zero runtime cost
- Extensible design - Easy to add new pointcut types
- Production-ready - Handles real Rust code
Next Steps
- See Architecture for system overview
- See How It Works for implementation details
- See Breakthrough for the technical journey
Related Chapters:
- Chapter 10.1: Architecture - System design
- Chapter 10.2: How It Works - MIR extraction
- Chapter 10.4: Breakthrough - Technical achievement