@@ -95,6 +95,8 @@ void ProcessFile(string sourceFile, FrameworkIdentifier framework, string framew
9595 var lines = processedCode . Split ( '\n ' ) ;
9696 var filteredLines = lines . Where ( line => ! line . TrimStart ( ) . StartsWith ( "//Link:" , StringComparison . OrdinalIgnoreCase ) ) ;
9797 processedCode = string . Join ( "\n " , filteredLines ) ;
98+ // Simplify mixed #if conditions by removing always-true framework parts
99+ processedCode = SimplifyPreprocessorConditions ( processedCode , frameworkSymbols ) ;
98100 var codeWithHeader = $ "// <auto-generated />\n #pragma warning disable\n { processedCode } ";
99101 File . WriteAllText ( outputPath , codeWithHeader ) ;
100102
@@ -156,6 +158,121 @@ static SyntaxNode RemoveFrameworkDirectives(SyntaxNode root, List<string> featur
156158 return rewriter . Visit ( root ) ;
157159 }
158160
161+ // Simplify preprocessor conditions by removing always-true framework parts
162+ // For example: "#if !NET8_0_OR_GREATER && FeatureX" becomes "#if FeatureX" when NET8_0_OR_GREATER is not defined
163+ static string SimplifyPreprocessorConditions ( string code , List < string > definedFrameworkSymbols )
164+ {
165+ var lines = code . Split ( '\n ' ) ;
166+ var result = new List < string > ( ) ;
167+
168+ foreach ( var line in lines )
169+ {
170+ var trimmed = line . TrimStart ( ) ;
171+ if ( trimmed . StartsWith ( "#if " ) || trimmed . StartsWith ( "#elif " ) )
172+ {
173+ var simplified = SimplifyConditionLine ( line , definedFrameworkSymbols ) ;
174+ result . Add ( simplified ) ;
175+ }
176+ else
177+ {
178+ result . Add ( line ) ;
179+ }
180+ }
181+
182+ return string . Join ( "\n " , result ) ;
183+ }
184+
185+ static string SimplifyConditionLine ( string line , List < string > definedSymbols )
186+ {
187+ // Extract the directive and condition
188+ var trimmed = line . TrimStart ( ) ;
189+ var leadingWhitespace = line [ ..^ trimmed . Length ] ;
190+
191+ string directive ;
192+ string condition ;
193+ if ( trimmed . StartsWith ( "#if " ) )
194+ {
195+ directive = "#if " ;
196+ condition = trimmed [ 4 ..] ;
197+ }
198+ else if ( trimmed . StartsWith ( "#elif " ) )
199+ {
200+ directive = "#elif " ;
201+ condition = trimmed [ 6 ..] ;
202+ }
203+ else
204+ {
205+ return line ;
206+ }
207+
208+ // Simplify the condition
209+ var simplified = SimplifyCondition ( condition , definedSymbols ) ;
210+
211+ return $ "{ leadingWhitespace } { directive } { simplified } ";
212+ }
213+
214+ static string SimplifyCondition ( string condition , List < string > definedSymbols )
215+ {
216+ var result = condition ;
217+
218+ // Build a set of all framework-related symbols (both defined and potentially undefined)
219+ var allFrameworkSymbols = new HashSet < string >
220+ {
221+ "NETFRAMEWORK" , "NETSTANDARD" , "NETCOREAPP" , "NET" ,
222+ "NET461" , "NET462" , "NET46X" , "NET47" , "NET471" , "NET47X" , "NET472" , "NET48" , "NET48X" , "NET481" ,
223+ "NET5_0" , "NET6_0" , "NET7_0" , "NET8_0" , "NET9_0" , "NET10_0" ,
224+ "NETCOREAPP2_0" , "NETCOREAPP2_1" , "NETCOREAPP2_2" , "NETCOREAPP2X" ,
225+ "NETCOREAPP3_0" , "NETCOREAPP3_1" , "NETCOREAPP3X" ,
226+ "NETSTANDARD2_0" , "NETSTANDARD2_1" ,
227+ "NET5_0_OR_GREATER" , "NET6_0_OR_GREATER" , "NET7_0_OR_GREATER" ,
228+ "NET8_0_OR_GREATER" , "NET9_0_OR_GREATER" , "NET10_0_OR_GREATER" ,
229+ "NET461_OR_GREATER" , "NET462_OR_GREATER" , "NET47_OR_GREATER" , "NET471_OR_GREATER" ,
230+ "NET472_OR_GREATER" , "NET48_OR_GREATER" , "NET481_OR_GREATER" ,
231+ "NETCOREAPP2_0_OR_GREATER" , "NETCOREAPP2_1_OR_GREATER" , "NETCOREAPP2_2_OR_GREATER" ,
232+ "NETCOREAPP3_0_OR_GREATER" , "NETCOREAPP3_1_OR_GREATER" ,
233+ "NETSTANDARD2_0_OR_GREATER" , "NETSTANDARD2_1_OR_GREATER" ,
234+ "WINDOWS"
235+ } ;
236+
237+ var definedSet = definedSymbols . ToHashSet ( ) ;
238+
239+ // For each framework symbol, simplify based on whether it's defined
240+ foreach ( var symbol in allFrameworkSymbols )
241+ {
242+ var isDefined = definedSet . Contains ( symbol ) ;
243+
244+ if ( isDefined )
245+ {
246+ // Symbol is defined (true)
247+ // !SYMBOL is false - if part of &&, whole thing might be false (but BranchTaken would handle that)
248+ // SYMBOL is true - can be removed from && chains
249+ // Remove "SYMBOL && " or " && SYMBOL" patterns
250+ result = System . Text . RegularExpressions . Regex . Replace ( result , $@ "\b{ symbol } \b\s*&&\s*", "" ) ;
251+ result = System . Text . RegularExpressions . Regex . Replace ( result , $@ "\s*&&\s*\b{ symbol } \b", "" ) ;
252+ // Handle standalone SYMBOL (entire condition is just the symbol)
253+ if ( result . Trim ( ) == symbol )
254+ {
255+ result = "true" ;
256+ }
257+ }
258+ else
259+ {
260+ // Symbol is not defined (false)
261+ // !SYMBOL is true - can be removed from && chains
262+ // Remove "!SYMBOL && " or " && !SYMBOL" patterns
263+ result = System . Text . RegularExpressions . Regex . Replace ( result , $@ "!\s*{ symbol } \s*&&\s*", "" ) ;
264+ result = System . Text . RegularExpressions . Regex . Replace ( result , $@ "\s*&&\s*!\s*{ symbol } \b", "" ) ;
265+ // Handle standalone !SYMBOL
266+ if ( System . Text . RegularExpressions . Regex . IsMatch ( result . Trim ( ) , $@ "^!\s*{ symbol } $") )
267+ {
268+ result = "true" ;
269+ }
270+ }
271+ }
272+
273+ return result . Trim ( ) ;
274+ }
275+
159276 // Symbols to define during parsing (makes code active)
160277 // Excludes symbols that typically appear negated (like !RefsBclMemory)
161278 List < string > GetParseSymbols ( ) =>
0 commit comments