@@ -41,6 +41,7 @@ public static class DiagnosticIds
4141 public const string MissingStructureDecoration = "MissingStructureDecoration" ;
4242 public const string UnsupportedParameterType = "UnsupportedParameterType" ;
4343 public const string UnsupportedDefaultValue = "UnsupportedDefaultValue" ;
44+ public const string PotentialStatefulImplementation = "PotentialStatefulImplementation" ;
4445 }
4546
4647 /// <summary>
@@ -292,6 +293,18 @@ public static class Categories
292293
293294 // https://www.outsystems.com/tk/redirect?g=OS-ELG-MODL-05029 - not implementing
294295
296+ // ----------------------------------------------- BEST PRACTICES
297+
298+ private static readonly DiagnosticDescriptor PotentialStatefulImplementationRule = new (
299+ DiagnosticIds . PotentialStatefulImplementation ,
300+ title : "Possible stateful behavior" ,
301+ messageFormat : "The class '{0}' contains static members ({1}) which could persist state between calls. External libraries should be designed to be stateless." ,
302+ category : Categories . Design ,
303+ defaultSeverity : DiagnosticSeverity . Info ,
304+ isEnabledByDefault : true ,
305+ description : "External libraries should be designed to be stateless. Consider passing state information as method parameters instead of storing it in fields." ,
306+ helpLinkUri : "https://success.outsystems.com/documentation/outsystems_developer_cloud/building_apps/extend_your_apps_with_custom_code/external_libraries_sdk_readme/#architecture" ) ;
307+
295308 /// <summary>
296309 /// Returns the full set of DiagnosticDescriptors that this analyzer is capable of producing.
297310 /// </summary>
@@ -318,7 +331,8 @@ public static class Categories
318331 UnsupportedTypeMappingRule ,
319332 MissingStructureDecorationRule ,
320333 UnsupportedParameterTypeRule ,
321- UnsupportedDefaultValueRule ) ;
334+ UnsupportedDefaultValueRule ,
335+ PotentialStatefulImplementationRule ) ;
322336
323337 /// <summary>
324338 /// Entry point for the analyzer. Initializes analysis by setting up compilation-level
@@ -777,6 +791,27 @@ private static void AnalyzeClass(SymbolAnalysisContext context, INamedTypeSymbol
777791 ctorDecl . Identifier . GetLocation ( ) ,
778792 typeSymbol . Name ) ) ;
779793 }
794+
795+ // Check for static members that could indicate attempts to maintain state
796+ var staticMembers = typeSymbol . GetMembers ( )
797+ . Where ( member =>
798+ member . IsStatic &&
799+ ! member . IsImplicitlyDeclared && // Skip compiler-generated members
800+ member is IFieldSymbol { IsConst : false } or IPropertySymbol )
801+ . Select ( m => m . Name )
802+ . OrderBy ( name => name )
803+ . ToList ( ) ;
804+
805+ if ( staticMembers . Any ( ) &&
806+ typeSymbol . DeclaringSyntaxReferences . FirstOrDefault ( ) ? . GetSyntax ( ) is ClassDeclarationSyntax stateDecl )
807+ {
808+ context . ReportDiagnostic (
809+ Diagnostic . Create (
810+ PotentialStatefulImplementationRule ,
811+ stateDecl . Identifier . GetLocation ( ) ,
812+ typeSymbol . Name ,
813+ string . Join ( ", " , staticMembers ) ) ) ;
814+ }
780815 }
781816 }
782817 }
0 commit comments