@@ -517,9 +517,16 @@ <h1>Null-Safe Clang Playground</h1>
517517 </ button >
518518
519519 < select id ="examplesSelect " class ="examples-select ">
520- < option value ="null-check " selected > Null Check Example</ option >
521- < option value ="function-invalidates "> Function Invalidates Check</ option >
522- < option value ="nonnull-annotation "> _Nonnull Annotations</ option >
520+ < option value ="null-check " selected > 1. Basic Null Check</ option >
521+ < option value ="early-return "> 2. Early Return Pattern</ option >
522+ < option value ="pure-function "> 3. Pure Functions Preserve Narrowing</ option >
523+ < option value ="function-invalidates "> 4. Function Invalidates Narrowing</ option >
524+ < option value ="nonnull-annotation "> 5. _Nonnull Annotations</ option >
525+ < option value ="multi-level "> 6. Multi-Level Pointers</ option >
526+ < option value ="and-pattern "> 7. AND Pattern Narrowing</ option >
527+ < option value ="else-branch "> 8. Else Branch Narrowing</ option >
528+ < option value ="loop-narrowing "> 9. Loop Condition Narrowing</ option >
529+ < option value ="dereference-context "> 10. Dereference in Condition</ option >
523530 </ select >
524531
525532 < button id ="shareBtn " class ="btn btn-secondary ">
@@ -590,7 +597,8 @@ <h1>Null-Safe Clang Playground</h1>
590597 let editor = null ; // Monaco editor instance
591598
592599 const examples = {
593- 'null-check' : `void process(int* data) {
600+ 'null-check' : `// Flow-sensitive null checking catches bugs at compile-time
601+ void safe(int* data) {
594602 if (data) {
595603 *data = 42; // OK - data is non-null here
596604 }
@@ -599,29 +607,136 @@ <h1>Null-Safe Clang Playground</h1>
599607void unsafe(int* data) {
600608 *data = 42; // warning - data might be null!
601609}` ,
602- 'function-invalidates' : `void modify(int** ptr); // external function
610+
611+ 'early-return' : `// Early returns narrow type for rest of function
612+ void process(char* str) {
613+ if (!str) return; // guard clause
614+
615+ // str is now proven non-null
616+ *str = 'x'; // OK
617+ }
618+
619+ void multi_guard(char* p, char* q) {
620+ if (!p || !q) return;
621+
622+ *p = 'a'; // OK - both narrowed
623+ *q = 'b'; // OK
624+ }` ,
625+
626+ 'pure-function' : `// Pure functions don't invalidate narrowing
627+ int is_digit(char c) __attribute__((const));
628+
629+ void parse(const char* str) {
630+ if (is_digit(*str)) { // narrows str
631+ char val = *str - '0'; // OK - still narrowed
632+ }
633+ }` ,
634+
635+ 'function-invalidates' : `// Regular functions invalidate narrowing (side effects)
636+ void modify(void);
603637
604638void example(int* data) {
605639 if (data) {
606- *data = 1; // OK - checked
640+ *data = 1; // OK - narrowed
607641
608- modify(&data); // might modify data!
642+ modify(); // might modify data!
609643
610- *data = 2; // warning - data could be null now!
644+ *data = 2; // warning - no longer safe
611645 }
612646}` ,
613- 'nonnull-annotation' : `// Use _Nonnull to require non-null arguments
614- void process_nonnull(int * _Nonnull data) {
615- *data = 42; // OK - caller must pass non-null
647+
648+ 'nonnull-annotation' : `// _Nonnull forces callers to guarantee non-null
649+ void process(int * _Nonnull data) {
650+ *data = 42; // OK - guaranteed non-null
616651}
617652
618653void caller(void) {
619654 int* nullable = 0;
620655 int x = 10;
621656
622- process_nonnull(0); // error!
623- process_nonnull(nullable); // error!
624- process_nonnull(&x); // OK
657+ process(0); // error!
658+ process(nullable); // error!
659+ process(&x); // OK
660+ }` ,
661+
662+ 'multi-level' : `// Multi-level pointers check each level independently
663+ void example(int** pp) {
664+ if (pp && *pp) {
665+ **pp = 42; // OK - both levels narrowed
666+ }
667+ }
668+
669+ void triple(int*** ppp) {
670+ if (ppp && *ppp && **ppp) {
671+ ***ppp = 42; // OK - all 3 levels checked
672+ }
673+ }` ,
674+
675+ 'and-pattern' : `// AND expressions narrow in the then-block
676+ void simple(char* p, char* q) {
677+ if (p && q) {
678+ *p = 'x'; // OK - narrowed
679+ *q = 'y'; // OK - narrowed
680+ }
681+ }
682+
683+ // Dereference in AND is safe
684+ void deref_and(char* str) {
685+ if (str && *str == 'a') { // OK to deref
686+ *str = 'b'; // OK in body too
687+ }
688+ }` ,
689+
690+ 'else-branch' : `// Else branch gets opposite narrowing
691+ void example(int* p) {
692+ if (p) {
693+ *p = 42; // OK - known non-null
694+ } else {
695+ *p = 0; // warning - known NULL!
696+ }
697+ }
698+
699+ void negated(int* p) {
700+ if (!p) {
701+ *p = 1; // warning - known NULL
702+ } else {
703+ *p = 1; // OK - known non-null
704+ }
705+ }` ,
706+
707+ 'loop-narrowing' : `// Loop conditions narrow the body
708+ void while_loop(int* p) {
709+ while (p) {
710+ *p = 42; // OK - narrowed in body
711+ p = 0; // exit loop
712+ }
713+ *p = 0; // warning - nullable again
714+ }
715+
716+ void for_loop(int* p) {
717+ for (; p; p = 0) {
718+ *p = 42; // OK - narrowed in body
719+ }
720+ }` ,
721+
722+ 'dereference-context' : `// Dereference in condition is allowed
723+ void while_str(const char* ptr) {
724+ while (*ptr != '\\0') { // OK - can deref
725+ ptr++;
726+ }
727+ }
728+
729+ void if_check(const char* str) {
730+ if (*str == 'a') { // OK - can deref
731+ // process...
732+ }
733+ }
734+
735+ void for_check(const char* input) {
736+ const char* p;
737+ for (p = input; *p; p++) { // OK - can deref
738+ // process...
739+ }
625740}`
626741 } ;
627742
@@ -659,20 +774,14 @@ <h1>Null-Safe Clang Playground</h1>
659774 editor . onDidChangeModelContent ( ( ) => {
660775 clearTimeout ( diagnosticsTimeout ) ;
661776 diagnosticsTimeout = setTimeout ( async ( ) => {
662- if ( ! scriptUrl || isCompiling ) {
663- console . log ( '[Diagnostics] Skipped - scriptUrl:' , ! ! scriptUrl , 'isCompiling:' , isCompiling ) ;
664- return ;
665- }
777+ if ( ! scriptUrl || isCompiling ) return ;
666778 try {
667779 const code = getEditorValue ( ) ;
668- console . log ( '[Diagnostics] Running clang on code...' ) ;
669780 const result = await compileCode ( code , [ ] ) ;
670- console . log ( '[Diagnostics] stderr:' , result . stderr ) ;
671781 const diagnostics = parseDiagnostics ( result . stderr ) ;
672- console . log ( '[Diagnostics] Parsed markers:' , diagnostics ) ;
673782 monaco . editor . setModelMarkers ( editor . getModel ( ) , 'clang' , diagnostics ) ;
674783 } catch ( error ) {
675- console . error ( '[Diagnostics] Error:' , error ) ;
784+ // Silently fail - diagnostics are non-critical
676785 }
677786 } , 500 ) ;
678787 } ) ;
0 commit comments