@@ -53,18 +53,19 @@ void ProcessFile(string sourceFile, FrameworkIdentifier framework, string framew
5353 var code = File . ReadAllText ( sourceFile ) ;
5454
5555 var frameworkSymbols = framework . Directives ;
56- var sharedSymbols = GetSharedSymbols ( ) ;
56+ var featureSymbols = GetFeatureSymbols ( ) ;
57+ var parseSymbols = GetParseSymbols ( ) ;
5758
58- // Parse with ALL symbols ( framework + shared)
59- // This makes all code active (both framework and feature code)
60- var allSymbols = frameworkSymbols . Concat ( sharedSymbols ) . ToList ( ) ;
59+ // Parse with framework + parse symbols
60+ // This makes code active where those symbols are used
61+ var allSymbols = frameworkSymbols . Concat ( parseSymbols ) . ToList ( ) ;
6162 var parseOptions = CSharpParseOptions . Default . WithPreprocessorSymbols ( allSymbols ) ;
6263 var syntaxTree = CSharpSyntaxTree . ParseText ( code , parseOptions ) ;
6364
6465 // Get the root and remove only framework-related directives
6566 // Feature directives will be kept
6667 var root = syntaxTree . GetRoot ( ) ;
67- var processedRoot = RemoveFrameworkDirectives ( root , sharedSymbols ) ;
68+ var processedRoot = RemoveFrameworkDirectives ( root , featureSymbols ) ;
6869
6970 // Check if the file has any meaningful code
7071 if ( ! HasMeaningfulCode ( processedRoot ) )
@@ -148,14 +149,16 @@ or ConversionOperatorDeclarationSyntax
148149 return hasNonPolyfillTypes ;
149150 }
150151
151- static SyntaxNode RemoveFrameworkDirectives ( SyntaxNode root , List < string > sharedSymbols )
152+ static SyntaxNode RemoveFrameworkDirectives ( SyntaxNode root , List < string > featureSymbols )
152153 {
153154 // Remove only framework-related preprocessor directives while keeping feature directives
154- var rewriter = new DirectiveRemovalRewriter ( sharedSymbols ) ;
155+ var rewriter = new DirectiveRemovalRewriter ( featureSymbols ) ;
155156 return rewriter . Visit ( root ) ;
156157 }
157158
158- List < string > GetSharedSymbols ( ) =>
159+ // Symbols to define during parsing (makes code active)
160+ // Excludes symbols that typically appear negated (like !RefsBclMemory)
161+ List < string > GetParseSymbols ( ) =>
159162 [
160163 "FeatureMemory" ,
161164 "FeatureRuntimeInformation" ,
@@ -167,7 +170,27 @@ List<string> GetSharedSymbols() =>
167170 "AllowUnsafeBlocks" ,
168171 "FeatureValueTask" ,
169172 "FeatureValueTuple" ,
170- "FeatureCompression"
173+ "FeatureCompression" ,
174+ "PolyUseEmbeddedAttribute"
175+ ] ;
176+
177+ // All feature symbols (for IsFrameworkRelated check)
178+ // Includes symbols that appear negated - we still want to recognize them as feature flags
179+ List < string > GetFeatureSymbols ( ) =>
180+ [
181+ "FeatureMemory" ,
182+ "FeatureRuntimeInformation" ,
183+ "PolyEnsure" ,
184+ "PolyArgumentExceptions" ,
185+ "PolyPublic" ,
186+ "FeatureHttp" ,
187+ "PolyNullability" ,
188+ "AllowUnsafeBlocks" ,
189+ "FeatureValueTask" ,
190+ "FeatureValueTuple" ,
191+ "FeatureCompression" ,
192+ "PolyUseEmbeddedAttribute" ,
193+ "RefsBclMemory"
171194 ] ;
172195
173196 List < FrameworkIdentifier > GetAllTargetFrameworks ( ) =>
@@ -480,6 +503,7 @@ partial class DirectiveRemovalRewriter(List<string> sharedSymbols) : CSharpSynta
480503 {
481504 readonly HashSet < string > sharedSymbols = sharedSymbols . ToHashSet ( ) ;
482505 readonly Stack < bool > directiveStack = new ( ) ;
506+ bool removeNextDisabledText ; // Set when we remove a non-taken #else/#elif
483507
484508 readonly HashSet < string > frameworkRelatedKeywords =
485509 [
@@ -541,9 +565,10 @@ SyntaxTriviaList ProcessTrivia(SyntaxTriviaList triviaList)
541565 // Check if this disabled text is part of a framework conditional
542566 // Only check the INNERMOST directive (Peek), not any outer ones
543567 var insideFrameworkBlock = directiveStack . Count > 0 && directiveStack . Peek ( ) ;
544- if ( insideFrameworkBlock )
568+ if ( insideFrameworkBlock || removeNextDisabledText )
545569 {
546- continue ; // Remove disabled text from framework blocks
570+ removeNextDisabledText = false ; // Reset the flag
571+ continue ; // Remove disabled text
547572 }
548573 // Keep disabled text from feature blocks (though there shouldn't be any since we parse with all symbols)
549574 }
@@ -576,41 +601,63 @@ bool ShouldRemoveDirective(SyntaxTrivia trivia)
576601 {
577602 var isFrameworkRelated = IsFrameworkRelated ( ifDirective . Condition . ToString ( ) ) ;
578603
579- // If the directive is inactive, always remove it (regardless of whether it's framework-related)
580- // Inactive directives mean the code will never be compiled for this target framework
581- if ( ! ifDirective . IsActive )
604+ // If this branch wasn't taken (condition was false), remove it entirely
605+ // BranchTaken indicates whether the condition evaluated to true
606+ if ( ! ifDirective . BranchTaken )
582607 {
583608 directiveStack . Push ( true ) ; // Mark for removal so matching endif is also removed
584609 return true ;
585610 }
586611
587- // For active directives , only remove if framework-related
612+ // For taken branches , only remove if framework-related
588613 directiveStack . Push ( isFrameworkRelated ) ;
589614 return isFrameworkRelated ;
590615 }
591616
592617 // Check if it's an #elif directive
593618 if ( structure is ElifDirectiveTriviaSyntax elifDirective )
594619 {
595- // #elif is part of the current directive block
620+ // If we're already removing this directive block, continue removing
596621 if ( directiveStack . Count > 0 && directiveStack . Peek ( ) )
597622 {
623+ // Keep stack as-is so matching #endif is also removed
624+ // Content after #elif (if taken) is active syntax tree, not disabled text
598625 return true ;
599626 }
600627
601- return false ;
628+ // If #if branch was taken, this elif is inactive - remove it
629+ if ( ! elifDirective . BranchTaken )
630+ {
631+ removeNextDisabledText = true ; // Remove the disabled text that follows
632+ return true ;
633+ }
634+
635+ // This elif branch was taken and #if wasn't being removed
636+ // Check if it's framework-related
637+ var elifFrameworkRelated = IsFrameworkRelated ( elifDirective . Condition . ToString ( ) ) ;
638+ return elifFrameworkRelated ;
602639 }
603640
604641 // Check if it's an #else directive
605- if ( structure is ElseDirectiveTriviaSyntax )
642+ if ( structure is ElseDirectiveTriviaSyntax elseDirective )
606643 {
607- // #else is part of the current directive block
644+ // If we're already removing this directive block, continue removing
608645 if ( directiveStack . Count > 0 && directiveStack . Peek ( ) )
609646 {
647+ // Keep stack as-is so matching #endif is also removed
648+ // Content after #else (if taken) is active syntax tree, not disabled text
610649 return true ;
611650 }
612651
613- return false ;
652+ // If previous branch was taken, this else is inactive - remove directive and content
653+ if ( ! elseDirective . BranchTaken )
654+ {
655+ removeNextDisabledText = true ; // Remove the disabled text that follows
656+ return true ;
657+ }
658+
659+ // This else branch was taken - remove the directive but keep content
660+ return true ;
614661 }
615662
616663 // Check if it's an #endif directive
0 commit comments