Pointcut Expressions
This chapter explains the pointcut expression language used 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