33using System . Collections . Concurrent ;
44using System . Collections . Immutable ;
55using System . Linq ;
6- using System . Reflection ;
76using System . Text . Json ;
87
98using Microsoft . CodeAnalysis ;
@@ -25,7 +24,7 @@ public partial class CheckedExceptionsAnalyzer : DiagnosticAnalyzer
2524 public const string DiagnosticIdMissingThrowsOnBaseMember = "THROW006" ;
2625 public const string DiagnosticIdMissingThrowsFromBaseMember = "THROW007" ;
2726
28- public static IEnumerable < string > AllDiagnosticsIds = [ DiagnosticIdUnhandled , DiagnosticIdGeneralThrows , DiagnosticIdGeneralThrow , DiagnosticIdDuplicateDeclarations ] ;
27+ public static readonly IEnumerable < string > AllDiagnosticsIds = [ DiagnosticIdUnhandled , DiagnosticIdGeneralThrows , DiagnosticIdGeneralThrow , DiagnosticIdDuplicateDeclarations ] ;
2928
3029 private static readonly DiagnosticDescriptor RuleUnhandledException = new (
3130 DiagnosticIdUnhandled ,
@@ -130,7 +129,7 @@ public override void Initialize(AnalysisContext context)
130129
131130 private AnalyzerSettings LoadAnalyzerSettings ( AnalyzerOptions analyzerOptions )
132131 {
133- return configs . GetOrAdd ( analyzerOptions , key =>
132+ return configs . GetOrAdd ( analyzerOptions , _ =>
134133 {
135134 var configFileText = analyzerOptions . AdditionalFiles
136135 . FirstOrDefault ( f => SettingsFileName . Equals ( Path . GetFileName ( f . Path ) , StringComparison . OrdinalIgnoreCase ) )
@@ -143,7 +142,7 @@ private AnalyzerSettings LoadAnalyzerSettings(AnalyzerOptions analyzerOptions)
143142 val = JsonSerializer . Deserialize < AnalyzerSettings > ( configFileText , _settingsFileJsonOptions ) ;
144143 }
145144
146- return val ?? AnalyzerSettings . CreateWithDefaults ( ) ; // Return default options if config file is not found
145+ return val ?? AnalyzerSettings . CreateWithDefaults ( ) ; // Return default options if the config file is not found
147146 } ) ;
148147 }
149148
@@ -186,9 +185,6 @@ private void AnalyzeMethodSymbol(SymbolAnalysisContext context)
186185 {
187186 var methodSymbol = ( IMethodSymbol ) context . Symbol ;
188187
189- if ( methodSymbol is null )
190- return ;
191-
192188 var throwsAttributes = GetThrowsAttributes ( methodSymbol ) . ToImmutableArray ( ) ;
193189
194190 CheckForCompatibilityWithBaseOrInterface ( context , throwsAttributes ) ;
@@ -211,7 +207,7 @@ public static bool IsThrowsAttributeForException(AttributeData attribute, string
211207 if ( ! attribute . ConstructorArguments . Any ( ) )
212208 return false ;
213209
214- var exceptionTypes = GetDistictExceptionTypes ( attribute ) ;
210+ var exceptionTypes = GetDistinctExceptionTypes ( attribute ) ;
215211 return exceptionTypes . Any ( exceptionType => exceptionType ? . Name == exceptionTypeName ) ;
216212 }
217213
@@ -239,11 +235,12 @@ public static IEnumerable<INamedTypeSymbol> GetExceptionTypes(params IEnumerable
239235 }
240236 }
241237
242- public static IEnumerable < INamedTypeSymbol > GetDistictExceptionTypes ( params IEnumerable < AttributeData > exceptionAttributes )
238+ public static IEnumerable < INamedTypeSymbol > GetDistinctExceptionTypes ( params IEnumerable < AttributeData > exceptionAttributes )
243239 {
244240 var exceptionTypes = GetExceptionTypes ( exceptionAttributes ) ;
245241
246- return exceptionTypes . Distinct ( SymbolEqualityComparer . Default )
242+ return exceptionTypes
243+ . Distinct ( SymbolEqualityComparer . Default )
247244 . OfType < INamedTypeSymbol > ( ) ;
248245 }
249246
@@ -259,26 +256,23 @@ private void AnalyzeThrowStatement(SyntaxNodeAnalysisContext context)
259256 // Handle rethrows (throw;)
260257 if ( throwStatement . Expression is null )
261258 {
262- if ( IsWithinCatchBlock ( throwStatement , out var catchClause ) )
259+ if ( IsWithinCatchBlock ( throwStatement , out var catchClause ) && catchClause is not null )
263260 {
264- if ( catchClause is not null )
261+ if ( catchClause . Declaration is null )
265262 {
266- if ( catchClause . Declaration is null )
267- {
268- // General catch block with 'throw;'
269- // Analyze exceptions thrown in the try block
270- var tryStatement = catchClause . Ancestors ( ) . OfType < TryStatementSyntax > ( ) . FirstOrDefault ( ) ;
271- if ( tryStatement is not null )
272- {
273- AnalyzeExceptionsInTryBlock ( context , tryStatement , catchClause , throwStatement , settings ) ;
274- }
275- }
276- else
263+ // General catch block with 'throw;'
264+ // Analyze exceptions thrown in the try block
265+ var tryStatement = catchClause . Ancestors ( ) . OfType < TryStatementSyntax > ( ) . FirstOrDefault ( ) ;
266+ if ( tryStatement is not null )
277267 {
278- var exceptionType = context . SemanticModel . GetTypeInfo ( catchClause . Declaration . Type ) . Type as INamedTypeSymbol ;
279- AnalyzeExceptionThrowingNode ( context , throwStatement , exceptionType , settings ) ;
268+ AnalyzeExceptionsInTryBlock ( context , tryStatement , catchClause , throwStatement , settings ) ;
280269 }
281270 }
271+ else
272+ {
273+ var exceptionType = context . SemanticModel . GetTypeInfo ( catchClause . Declaration . Type ) . Type as INamedTypeSymbol ;
274+ AnalyzeExceptionThrowingNode ( context , throwStatement , exceptionType , settings ) ;
275+ }
282276 }
283277
284278 return ; // No further analysis for rethrows
@@ -492,7 +486,7 @@ void CollectExpressionsForPropertySymbols(ExpressionSyntax expression)
492486
493487 public bool ShouldIncludeException ( INamedTypeSymbol exceptionType , SyntaxNode node , AnalyzerSettings settings )
494488 {
495- var exceptions = new HashSet < INamedTypeSymbol > ( SymbolEqualityComparer . Default ) ;
489+ // var exceptions = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
496490
497491 var exceptionName = exceptionType . ToDisplayString ( ) ;
498492
@@ -595,7 +589,7 @@ private void AnalyzeAwait(SyntaxNodeAnalysisContext context)
595589 /// <summary>
596590 /// Determines if a node is within a catch block.
597591 /// </summary>
598- private bool IsWithinCatchBlock ( SyntaxNode node , out CatchClauseSyntax catchClause )
592+ private bool IsWithinCatchBlock ( SyntaxNode node , out CatchClauseSyntax ? catchClause )
599593 {
600594 catchClause = node . Ancestors ( ) . OfType < CatchClauseSyntax > ( ) . FirstOrDefault ( ) ;
601595 return catchClause is not null ;
@@ -784,14 +778,9 @@ private void AnalyzeIdentifier(SyntaxNodeAnalysisContext context)
784778
785779 private void AnalyzeIdentifierAndMemberAccess ( SyntaxNodeAnalysisContext context , ExpressionSyntax expression , AnalyzerSettings settings )
786780 {
787- var s = context . SemanticModel . GetSymbolInfo ( expression ) . Symbol ;
788- var symbol = s as IPropertySymbol ;
789- if ( symbol is null )
790- return ;
791-
792- if ( symbol is IPropertySymbol propertySymbol )
781+ if ( context . SemanticModel . GetSymbolInfo ( expression ) . Symbol is IPropertySymbol propertySymbol )
793782 {
794- AnalyzePropertyExceptions ( context , expression , symbol , settings ) ;
783+ AnalyzePropertyExceptions ( context , expression , propertySymbol , settings ) ;
795784 }
796785 }
797786
@@ -804,13 +793,9 @@ private void AnalyzeElementAccess(SyntaxNodeAnalysisContext context)
804793
805794 var elementAccess = ( ElementAccessExpressionSyntax ) context . Node ;
806795
807- var symbol = context . SemanticModel . GetSymbolInfo ( elementAccess ) . Symbol as IPropertySymbol ;
808- if ( symbol is null )
809- return ;
810-
811- if ( symbol is IPropertySymbol propertySymbol )
796+ if ( context . SemanticModel . GetSymbolInfo ( elementAccess ) . Symbol is IPropertySymbol propertySymbol )
812797 {
813- AnalyzePropertyExceptions ( context , elementAccess , symbol , settings ) ;
798+ AnalyzePropertyExceptions ( context , elementAccess , propertySymbol , settings ) ;
814799 }
815800 }
816801
@@ -873,28 +858,17 @@ private HashSet<INamedTypeSymbol> GetPropertyExceptionTypes(SyntaxNodeAnalysisCo
873858 var xmlDocumentedExceptions = GetExceptionTypesFromDocumentationCommentXml ( context . Compilation , propertySymbol ) ;
874859
875860 // Filter exceptions documented specifically for the getter and setter
876- var getterExceptions = xmlDocumentedExceptions . Where ( x =>
877- x . Description . Contains ( " get " ) ||
878- x . Description . Contains ( " gets " ) ||
879- x . Description . Contains ( " getting " ) ||
880- x . Description . Contains ( " retrieved " ) ) ;
881-
882- var setterExceptions = xmlDocumentedExceptions . Where ( x =>
883- x . Description . Contains ( " set " ) ||
884- x . Description . Contains ( " sets " ) ||
885- x . Description . Contains ( " setting " ) ) ;
861+ var getterExceptions = xmlDocumentedExceptions . Where ( IsGetterException ) ;
862+ var setterExceptions = xmlDocumentedExceptions . Where ( IsSetterException ) ;
886863
887864 if ( isSetter && propertySymbol . SetMethod is not null )
888865 {
889- // Will filter away
866+ // Will filter away
890867 setterExceptions = ProcessNullable ( context , expression , propertySymbol . SetMethod , setterExceptions ) ;
891868 }
892869
893870 // Handle exceptions that don't explicitly belong to getters or setters
894- var allOtherExceptions = xmlDocumentedExceptions
895- . Except ( getterExceptions ) ;
896- allOtherExceptions = allOtherExceptions
897- . Except ( setterExceptions ) ;
871+ var allOtherExceptions = xmlDocumentedExceptions . Where ( x => ! IsGetterException ( x ) && ! IsSetterException ( x ) ) ;
898872
899873 if ( isSetter && propertySymbol . SetMethod is not null )
900874 {
@@ -925,12 +899,23 @@ private HashSet<INamedTypeSymbol> GetPropertyExceptionTypes(SyntaxNodeAnalysisCo
925899 // Add other exceptions not specific to getters or setters
926900 exceptionTypes . AddRange ( allOtherExceptions . Select ( x => x . ExceptionType ) ) ;
927901 return exceptionTypes ;
902+
903+ static bool IsGetterException ( ExceptionInfo ei ) =>
904+ ei . Description . Contains ( " get " ) ||
905+ ei . Description . Contains ( " gets " ) ||
906+ ei . Description . Contains ( " getting " ) ||
907+ ei . Description . Contains ( " retrieved " ) ;
908+
909+ static bool IsSetterException ( ExceptionInfo ei ) =>
910+ ei . Description . Contains ( " set " ) ||
911+ ei . Description . Contains ( " sets " ) ||
912+ ei . Description . Contains ( " setting " ) ;
928913 }
929914
930915 /// <summary>
931916 /// Analyzes exceptions thrown by a method, constructor, or other member.
932917 /// </summary>
933- private void AnalyzeMemberExceptions ( SyntaxNodeAnalysisContext context , SyntaxNode node , IMethodSymbol methodSymbol ,
918+ private void AnalyzeMemberExceptions ( SyntaxNodeAnalysisContext context , SyntaxNode node , IMethodSymbol ? methodSymbol ,
934919 AnalyzerSettings settings )
935920 {
936921 if ( methodSymbol is null )
@@ -1027,7 +1012,7 @@ private static IEnumerable<INamedTypeSymbol> GetExceptionTypes(IMethodSymbol met
10271012 // Get exceptions from Throws attributes
10281013 var exceptionAttributes = GetThrowsAttributes ( methodSymbol ) ;
10291014
1030- return GetDistictExceptionTypes ( exceptionAttributes ) ;
1015+ return GetDistinctExceptionTypes ( exceptionAttributes ) ;
10311016 }
10321017
10331018 private static IEnumerable < AttributeData > GetThrowsAttributes ( ISymbol symbol )
@@ -1062,7 +1047,7 @@ private bool CatchClauseHandlesException(CatchClauseSyntax catchClause, Semantic
10621047 /// </summary>
10631048 private bool IsExceptionHandled ( SyntaxNode node , INamedTypeSymbol exceptionType , SemanticModel semanticModel )
10641049 {
1065- SyntaxNode ? prevNode = null ;
1050+ // SyntaxNode? prevNode = null;
10661051
10671052 var current = node . Parent ;
10681053 while ( current is not null )
@@ -1078,7 +1063,7 @@ private bool IsExceptionHandled(SyntaxNode node, INamedTypeSymbol exceptionType,
10781063 if ( current is TryStatementSyntax tryStatement )
10791064 {
10801065 // Prevents analysis within the first try-catch,
1081- // when coming from either a catch clause or a finally clause.
1066+ // when coming from either a catch clause or a finally clause.
10821067
10831068 // Skip if the node is within a catch or finally block of the current try statement
10841069 bool isInCatchOrFinally = tryStatement . Catches . Any ( c => c . Contains ( node ) ) ||
@@ -1097,7 +1082,7 @@ private bool IsExceptionHandled(SyntaxNode node, INamedTypeSymbol exceptionType,
10971082 }
10981083 }
10991084
1100- prevNode = current ;
1085+ // prevNode = current;
11011086 current = current . Parent ;
11021087 }
11031088
@@ -1182,48 +1167,34 @@ private bool IsExceptionDeclaredInMethod(SyntaxNodeAnalysisContext context, Synt
11821167 {
11831168 foreach ( var ancestor in node . Ancestors ( ) )
11841169 {
1185- IMethodSymbol ? methodSymbol = null ;
1186-
1187- switch ( ancestor )
1170+ IMethodSymbol ? methodSymbol = ancestor switch
11881171 {
1189- case MethodDeclarationSyntax methodDeclaration :
1190- methodSymbol = context . SemanticModel . GetDeclaredSymbol ( methodDeclaration ) ;
1191- break ;
1192- case ConstructorDeclarationSyntax constructorDeclaration :
1193- methodSymbol = context . SemanticModel . GetDeclaredSymbol ( constructorDeclaration ) ;
1194- break ;
1195- case AccessorDeclarationSyntax accessorDeclaration :
1196- methodSymbol = context . SemanticModel . GetDeclaredSymbol ( accessorDeclaration ) ;
1197- break ;
1198- case LocalFunctionStatementSyntax localFunction :
1199- methodSymbol = context . SemanticModel . GetDeclaredSymbol ( localFunction ) ;
1200- break ;
1201- case AnonymousFunctionExpressionSyntax anonymousFunction :
1202- methodSymbol = context . SemanticModel . GetSymbolInfo ( anonymousFunction ) . Symbol as IMethodSymbol ;
1203- break ;
1204- default :
1205- // Continue up to next node
1206- continue ;
1207- }
1172+ MethodDeclarationSyntax methodDeclaration => context . SemanticModel . GetDeclaredSymbol ( methodDeclaration ) ,
1173+ ConstructorDeclarationSyntax constructorDeclaration => context . SemanticModel . GetDeclaredSymbol ( constructorDeclaration ) ,
1174+ AccessorDeclarationSyntax accessorDeclaration => context . SemanticModel . GetDeclaredSymbol ( accessorDeclaration ) ,
1175+ LocalFunctionStatementSyntax localFunction => context . SemanticModel . GetDeclaredSymbol ( localFunction ) ,
1176+ AnonymousFunctionExpressionSyntax anonymousFunction => context . SemanticModel . GetSymbolInfo ( anonymousFunction ) . Symbol as IMethodSymbol ,
1177+ _ => null ,
1178+ } ;
12081179
1209- if ( methodSymbol is not null )
1210- {
1211- if ( IsExceptionDeclaredInSymbol ( methodSymbol , exceptionType ) )
1212- return true ;
1180+ if ( methodSymbol is null )
1181+ continue ; // Continue up to next node
12131182
1214- if ( ancestor is AnonymousFunctionExpressionSyntax or LocalFunctionStatementSyntax )
1215- {
1216- // Break because you are analyzing a local function or anonymous function (lambda)
1217- // If you don't then it will got to the method, and it will affect analysis of this inline function.
1218- break ;
1219- }
1183+ if ( IsExceptionDeclaredInSymbol ( methodSymbol , exceptionType ) )
1184+ return true ;
1185+
1186+ if ( ancestor is AnonymousFunctionExpressionSyntax or LocalFunctionStatementSyntax )
1187+ {
1188+ // Break because you are analyzing a local function or anonymous function (lambda)
1189+ // If you don't then it will got to the method, and it will affect analysis of this inline function.
1190+ break ;
12201191 }
12211192 }
12221193
12231194 return false ;
12241195 }
12251196
1226- private bool IsExceptionDeclaredInSymbol ( IMethodSymbol methodSymbol , INamedTypeSymbol exceptionType )
1197+ private bool IsExceptionDeclaredInSymbol ( IMethodSymbol ? methodSymbol , INamedTypeSymbol exceptionType )
12271198 {
12281199 if ( methodSymbol is null )
12291200 return false ;
0 commit comments