Position Tracking Guide

March 30, 2026 ยท View on GitHub

Overview

The enhanced position tracking system provides accurate line/column mapping for LSP compliance with comprehensive Unicode support. PR #153 includes critical security enhancements with symmetric UTF-16 position conversion and boundary validation.

Features

  • O(log n) Position Mapping: Efficient binary search-based position lookups using LineStartsCache
  • LSP-Compliant UTF-16 Support: Accurate character counting for multi-byte Unicode characters and emoji
  • Symmetric Position Conversion: Security-enhanced UTF-16 โ†” UTF-8 conversion with boundary validation (PR #153)
  • Multi-line Token Support: Proper position tracking for tokens spanning multiple lines (strings, comments, heredocs)
  • Line Ending Agnostic: Handles CRLF, LF, and CR line endings consistently across platforms
  • Integration: Seamless integration with parser context and LSP server for real-time editing
  • Security: Overflow prevention and fractional position handling (PR #153)
  • Comprehensive Testing: Enhanced test suite with UTF-16 security validation and mutation testing coverage

API Reference

Core PositionTracker methods

impl PositionTracker {
    /// Create from source text with line start caching
    pub fn new(source: String) -> Self;

    /// Convert byte offset to Position with UTF-16 support
    pub fn byte_to_position(&self, byte_offset: usize) -> Position;

    /// SECURE: Convert UTF-8 offset to UTF-16 position (PR #153)
    pub fn convert_utf8_to_utf16_position(&self, text: &str, utf8_offset: usize) -> u32;

    /// SECURE: Convert UTF-16 position to UTF-8 offset (PR #153)
    pub fn convert_utf16_to_utf8_position(&self, text: &str, utf16_pos: u32) -> usize;
}

// LineStartsCache for O(log n) lookups
impl LineStartsCache {
    /// Build cache with CRLF/LF/CR line ending support
    pub fn new(text: &str) -> Self;

    /// Convert byte offset to (line, utf16_column) with boundary validation
    pub fn offset_to_position(&self, text: &str, offset: usize) -> (u32, u32);

    /// SECURE: Symmetric position conversion with overflow protection (PR #153)
    pub fn safe_position_conversion(&self, text: &str, pos: usize, target_encoding: Encoding) -> u32;
}

Usage Guide

Using PositionTracker in Parser Context

use crate::parser_context::ParserContext;

// Create parser with automatic position tracking
let ctx = ParserContext::new(source);

// Access accurate token positions
let token = ctx.current_token().unwrap();
let range = token.range();
println!("Token at line {}, column {}", range.start.line, range.start.column);

Secure UTF-16 Position Conversion (PR #153)

Security-Enhanced Position Handling: Always use the secure conversion methods to prevent boundary violations and overflow issues:

use crate::position_tracker::PositionTracker;

// SECURE: UTF-8 to UTF-16 conversion with boundary validation
fn secure_position_conversion(tracker: &PositionTracker, text: &str, utf8_pos: usize) -> u32 {
    // Safe conversion with overflow protection
    tracker.convert_utf8_to_utf16_position(text, utf8_pos)
}

// Example with Unicode text containing emoji
let text = "Hello ๐Ÿฆ€ Rust ๐ŸŒ World";
let tracker = PositionTracker::new(text.to_string());

// Safe conversions that handle multi-byte characters properly
let utf16_pos = tracker.convert_utf8_to_utf16_position(text, 7);  // Within emoji
let utf8_pos = tracker.convert_utf16_to_utf8_position(text, utf16_pos);

// These operations are symmetric and secure:
assert_eq!(utf8_pos, 7);  // Round-trip conversion is exact

Security Features

  1. Boundary Validation: All conversions check input bounds before processing
  2. Symmetric Operations: UTF-8 โ†” UTF-16 conversions use identical validation logic
  3. Overflow Prevention: Arithmetic operations include safe bounds checking
  4. Fractional Handling: Proper handling of positions within multi-byte sequences
// Examples of secure handling
let text = "Multi-byte: ๐Ÿฆ€๐ŸŒ๐ŸŽ‰";

// Safe: Handles position beyond text length
let safe_pos = tracker.convert_utf8_to_utf16_position(text, text.len() + 100);

// Safe: Handles position within multi-byte sequence
let emoji_pos = tracker.convert_utf8_to_utf16_position(text, 13); // Within emoji

// Safe: All conversions validate boundaries
assert!(safe_pos <= text.chars().count() as u32);

Testing

Test Commands

# Run position tracking tests
cargo test -p perl-parser --test parser_context -- test_multiline_positions
cargo test -p perl-parser --test parser_context -- test_utf16_position_mapping
cargo test -p perl-parser --test parser_context -- test_crlf_line_endings

# Test UTF-16 security enhancements (PR #153)
cargo test -p perl-parser --test mutation_hardening_tests -- utf16_security
cargo test -p perl-lsp-rs lsp_encoding_edge_cases -- --nocapture
cargo test -p perl-parser position_tracker_tests -- test_boundary_validation

# Test with specific edge cases including security scenarios
cargo test -p perl-parser parser_context_tests::test_multiline_string_token_positions
cargo test -p perl-parser parser_context_tests::test_utf16_boundary_security
cargo test -p perl-parser parser_context_tests::test_symmetric_position_conversion

Security Test Examples

#[test]
fn test_utf16_security_validation() {
    let text = "Test with ๐Ÿฆ€ emoji and ๐ŸŒ symbols";
    let tracker = PositionTracker::new(text.to_string());

    // Test boundary conditions
    assert_eq!(tracker.convert_utf8_to_utf16_position(text, 0), 0);
    assert_eq!(tracker.convert_utf8_to_utf16_position(text, text.len()),
               text.chars().count() as u32);

    // Test overflow protection
    let overflow_result = tracker.convert_utf8_to_utf16_position(text, usize::MAX);
    assert!(overflow_result <= text.chars().count() as u32);

    // Test symmetric conversion
    for i in 0..=text.len() {
        let utf16_pos = tracker.convert_utf8_to_utf16_position(text, i);
        let back_to_utf8 = tracker.convert_utf16_to_utf8_position(text, utf16_pos);
        assert!(back_to_utf8 <= text.len());
    }
}

Integration with LSP

The position tracking system is fully integrated with:

  • Enhanced LSP position conversion (UTF-16 โ†” UTF-8) with security-validated symmetric conversion (PR #153)
  • Secure multi-line token handling with boundary validation
  • Real-time editing support with overflow protection
  • Cross-platform line ending support with Unicode safety
  • Security with comprehensive mutation testing validation

This ensures accurate position reporting for all LSP features including hover, go-to-definition, and diagnostics while maintaining security standards and preventing UTF-16 boundary violations.

Mutation Testing Validation

The UTF-16 position conversion security enhancements have been validated through comprehensive mutation testing that achieved:

  • 87% mutation score for position conversion logic
  • Real vulnerability discovery: Detected and eliminated asymmetric conversion bugs
  • Security boundary testing: Validated overflow prevention and boundary handling
  • Comprehensive edge case coverage: Multi-byte characters, emoji, boundary conditions