@@ -435,3 +435,119 @@ fn assert_sequence_family(
435435 ) ;
436436 }
437437}
438+
439+ /// The compiler's answer and the fragments cut from the same file have to be
440+ /// about the same bytes of the same file, and three separate things have to
441+ /// line up for that: how the analysis names the file, whether it says anything
442+ /// about it, and whether what it says points where the scan read.
443+ ///
444+ /// Each is checked separately because a scan that loses any one of them looks
445+ /// identical from the outside — a full, successful, semantic run that reports
446+ /// nothing a compiler contributed. Which of the three went is the difference
447+ /// between a spelling fault, a compiler that answered about nothing, and
448+ /// offsets measured against a different copy of the file.
449+ #[ test]
450+ fn a_cpp_answer_is_about_the_bytes_the_scan_read ( ) {
451+ require_clang_helper ( ) ;
452+ let directory = tempfile:: tempdir ( ) . expect ( "temp dir" ) ;
453+ let root = codehelion_fixtures:: copy_cpp ( "overload-resolution" , directory. path ( ) )
454+ . expect ( "plant fixture" ) ;
455+ let report = scan ( & root) ;
456+ let run_id = report[ "run" ] [ "run_id" ]
457+ . as_i64 ( )
458+ . or_else ( || reports ( & report) . first ( ) ?[ "run" ] [ "run_id" ] . as_i64 ( ) )
459+ . expect ( "the scan records a run id" ) ;
460+
461+ let store = Store :: open ( & root. join ( ".codehelion/audit.db" ) ) . expect ( "open audit database" ) ;
462+ let source = root. join ( "src" ) . join ( "range_loop.cpp" ) ;
463+ let absolute = codehelion_core:: paths:: canonical ( & source) . expect ( "the planted source resolves" ) ;
464+ let text = std:: fs:: read_to_string ( & source) . expect ( "the planted source is readable" ) ;
465+
466+ let irs: Vec < _ > = store
467+ . run_compiler_units ( run_id)
468+ . expect ( "read compiler rows" )
469+ . into_iter ( )
470+ . filter_map ( |unit| match unit. outcome {
471+ CompilerOutcome :: Analyzed ( ir) => Some ( ir) ,
472+ CompilerOutcome :: Unavailable { .. } => None ,
473+ } )
474+ . collect ( ) ;
475+ assert ! ( !irs. is_empty( ) , "no unit of this tree was analyzed" ) ;
476+
477+ // How a reader holding the file asks about it, written by the same rule the
478+ // helper wrote its anchors with. Everything below is filed under this name.
479+ let named: Vec < String > = irs. iter ( ) . map ( |ir| ir. spelling ( & absolute) ) . collect ( ) ;
480+ let anchored: Vec < & str > = irs
481+ . iter ( )
482+ . flat_map ( |ir| {
483+ ir. calls
484+ . iter ( )
485+ . map ( |call| call. anchor . expansion . file . as_str ( ) )
486+ } )
487+ . collect ( ) ;
488+ assert ! (
489+ named. iter( ) . any( |name| anchored. contains( & name. as_str( ) ) ) ,
490+ "no answer is filed under the name a reader looks this file up by.\n \
491+ looked up as: {named:?}\n \
492+ answers are filed under: {:?}\n \
493+ anchored at: {:?}",
494+ anchored
495+ . iter( )
496+ . copied( )
497+ . collect:: <std:: collections:: BTreeSet <_>>( ) ,
498+ irs. iter( ) . map( |ir| & ir. anchored_at) . collect:: <Vec <_>>( )
499+ ) ;
500+
501+ let spelling = named
502+ . iter ( )
503+ . find ( |name| anchored. contains ( & name. as_str ( ) ) )
504+ . expect ( "one of the answers is about this file" ) ;
505+ let ir = irs
506+ . iter ( )
507+ . find ( |ir| {
508+ ir. calls
509+ . iter ( )
510+ . any ( |call| call. anchor . expansion . file == * spelling)
511+ } )
512+ . expect ( "the answer about this file" ) ;
513+
514+ // What it says about it. A standard call is the smallest thing every rule
515+ // over this fixture is built out of.
516+ let standard: Vec < _ > = ir
517+ . calls
518+ . iter ( )
519+ . filter ( |call| call. anchor . expansion . file == * spelling)
520+ . filter ( |call| call. api_name . as_deref ( ) == Some ( "std::push_back" ) )
521+ . collect ( ) ;
522+ assert ! (
523+ !standard. is_empty( ) ,
524+ "the compiler resolved no standard call in a file written out of them: {:?}" ,
525+ ir. calls
526+ . iter( )
527+ . filter( |call| call. anchor. expansion. file == * spelling)
528+ . filter_map( |call| call. api_name. as_deref( ) )
529+ . collect:: <std:: collections:: BTreeSet <_>>( )
530+ ) ;
531+ assert ! (
532+ ir. semantic_constructs
533+ . iter( )
534+ . any( |construct| construct. anchor. expansion. file == * spelling) ,
535+ "the compiler reported no construct in a file of range loops"
536+ ) ;
537+
538+ // And where. An offset is only an offset into some copy of the file, so it
539+ // is checked against the copy the scan read rather than assumed to be the
540+ // same one.
541+ for call in standard {
542+ let range = & call. anchor . expansion ;
543+ let start = usize:: try_from ( range. start_byte ) . expect ( "an offset within this file" ) ;
544+ let end = usize:: try_from ( range. end_byte ) . expect ( "an offset within this file" ) ;
545+ let written = text
546+ . get ( start..end)
547+ . unwrap_or_else ( || panic ! ( "{start}..{end} is outside the {} bytes read" , text. len( ) ) ) ;
548+ assert ! (
549+ written. contains( "push_back" ) ,
550+ "the offsets point at {written:?}, not at the call they were reported for"
551+ ) ;
552+ }
553+ }
0 commit comments