@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.CodeGeneration
77 using System . Collections . Generic ;
88 using System . Collections . Immutable ;
99 using System . Collections . ObjectModel ;
10+ using System . Globalization ;
1011 using System . IO ;
1112 using System . Linq ;
1213 using System . Text ;
@@ -52,6 +53,7 @@ private void GenerateOperationInterfaces(in GeneratorExecutionContext context, X
5253 }
5354
5455 this . GenerateOperationWrapperHelper ( in context , documentData . Interfaces . Values . ToImmutableArray ( ) ) ;
56+ this . GenerateOperationKindEx ( in context , documentData . Interfaces . Values . ToImmutableArray ( ) ) ;
5557 }
5658
5759 private void GenerateOperationInterface ( in GeneratorExecutionContext context , InterfaceData node )
@@ -760,10 +762,65 @@ private void GenerateOperationWrapperHelper(in GeneratorExecutionContext context
760762 context . AddSource ( "OperationWrapperHelper.g.cs" , SourceText . From ( wrapperNamespace . ToFullString ( ) , Encoding . UTF8 ) ) ;
761763 }
762764
765+ private void GenerateOperationKindEx ( in GeneratorExecutionContext context , ImmutableArray < InterfaceData > wrapperTypes )
766+ {
767+ var operationKinds = wrapperTypes
768+ . SelectMany ( type => type . OperationKinds )
769+ . OrderBy ( kind => kind . value )
770+ . ToImmutableArray ( ) ;
771+
772+ var members = SyntaxFactory . List < MemberDeclarationSyntax > ( ) ;
773+ foreach ( var operationKind in operationKinds )
774+ {
775+ // public const OperationKind FieldReference = (OperationKind)26;
776+ members = members . Add ( SyntaxFactory . FieldDeclaration (
777+ attributeLists : default ,
778+ modifiers : SyntaxFactory . TokenList ( SyntaxFactory . Token ( SyntaxKind . PublicKeyword ) , SyntaxFactory . Token ( SyntaxKind . ConstKeyword ) ) ,
779+ declaration : SyntaxFactory . VariableDeclaration (
780+ type : SyntaxFactory . IdentifierName ( "OperationKind" ) ,
781+ variables : SyntaxFactory . SingletonSeparatedList ( SyntaxFactory . VariableDeclarator (
782+ identifier : SyntaxFactory . Identifier ( operationKind . name ) ,
783+ argumentList : null ,
784+ initializer : SyntaxFactory . EqualsValueClause ( SyntaxFactory . CastExpression (
785+ type : SyntaxFactory . IdentifierName ( "OperationKind" ) ,
786+ expression : SyntaxFactory . LiteralExpression ( SyntaxKind . NumericLiteralExpression , SyntaxFactory . Literal ( $ "0x{ operationKind . value : x} ", operationKind . value ) ) ) ) ) ) ) ) ) ;
787+ }
788+
789+ var operationKindExClass = SyntaxFactory . ClassDeclaration (
790+ attributeLists : default ,
791+ modifiers : SyntaxTokenList . Create ( SyntaxFactory . Token ( SyntaxKind . InternalKeyword ) ) . Add ( SyntaxFactory . Token ( SyntaxKind . StaticKeyword ) ) ,
792+ identifier : SyntaxFactory . Identifier ( "OperationKindEx" ) ,
793+ typeParameterList : null ,
794+ baseList : null ,
795+ constraintClauses : default ,
796+ members : members ) ;
797+ var wrapperNamespace = SyntaxFactory . NamespaceDeclaration (
798+ name : SyntaxFactory . ParseName ( "StyleCop.Analyzers.Lightup" ) ,
799+ externs : default ,
800+ usings : SyntaxFactory . List < UsingDirectiveSyntax > ( )
801+ . Add ( SyntaxFactory . UsingDirective ( SyntaxFactory . ParseName ( "Microsoft.CodeAnalysis" ) ) ) ,
802+ members : SyntaxFactory . SingletonList < MemberDeclarationSyntax > ( operationKindExClass ) ) ;
803+
804+ wrapperNamespace = wrapperNamespace
805+ . NormalizeWhitespace ( )
806+ . WithLeadingTrivia (
807+ SyntaxFactory . Comment ( "// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved." ) ,
808+ SyntaxFactory . CarriageReturnLineFeed ,
809+ SyntaxFactory . Comment ( "// Licensed under the MIT License. See LICENSE in the project root for license information." ) ,
810+ SyntaxFactory . CarriageReturnLineFeed ,
811+ SyntaxFactory . CarriageReturnLineFeed )
812+ . WithTrailingTrivia (
813+ SyntaxFactory . CarriageReturnLineFeed ) ;
814+
815+ context . AddSource ( "OperationKindEx.g.cs" , SourceText . From ( wrapperNamespace . ToFullString ( ) , Encoding . UTF8 ) ) ;
816+ }
817+
763818 private sealed class DocumentData
764819 {
765820 public DocumentData ( XDocument document )
766821 {
822+ var operationKinds = GetOperationKinds ( document ) ;
823+
767824 var interfaces = new Dictionary < string , InterfaceData > ( ) ;
768825 foreach ( var node in document . XPathSelectElements ( "/Tree/AbstractNode" ) )
769826 {
@@ -772,7 +829,12 @@ public DocumentData(XDocument document)
772829 continue ;
773830 }
774831
775- var interfaceData = new InterfaceData ( this , node ) ;
832+ if ( ! operationKinds . TryGetValue ( node . Attribute ( "Name" ) . Value , out var kinds ) )
833+ {
834+ kinds = ImmutableArray < ( string name , int value , string extraDescription ) > . Empty ;
835+ }
836+
837+ var interfaceData = new InterfaceData ( this , node , kinds ) ;
776838 interfaces . Add ( interfaceData . InterfaceName , interfaceData ) ;
777839 }
778840
@@ -783,33 +845,135 @@ public DocumentData(XDocument document)
783845 continue ;
784846 }
785847
786- var interfaceData = new InterfaceData ( this , node ) ;
848+ if ( ! operationKinds . TryGetValue ( node . Attribute ( "Name" ) . Value , out var kinds ) )
849+ {
850+ kinds = ImmutableArray < ( string name , int value , string extraDescription ) > . Empty ;
851+ }
852+
853+ var interfaceData = new InterfaceData ( this , node , kinds ) ;
787854 interfaces . Add ( interfaceData . InterfaceName , interfaceData ) ;
788855 }
789856
790857 this . Interfaces = new ReadOnlyDictionary < string , InterfaceData > ( interfaces ) ;
791858 }
792859
793860 public ReadOnlyDictionary < string , InterfaceData > Interfaces { get ; }
861+
862+ private static ImmutableDictionary < string , ImmutableArray < ( string name , int value , string extraDescription ) > > GetOperationKinds ( XDocument document )
863+ {
864+ var skippedOperationKinds = GetSkippedOperationKinds ( document ) ;
865+
866+ var builder = ImmutableDictionary . CreateBuilder < string , ImmutableArray < ( string name , int value , string extraDescription ) > > ( ) ;
867+
868+ int operationKind = 0 ;
869+ foreach ( var node in document . XPathSelectElements ( "/Tree/AbstractNode|/Tree/Node" ) )
870+ {
871+ if ( node . Attribute ( "Internal" ) ? . Value == "true" )
872+ {
873+ continue ;
874+ }
875+
876+ if ( node . XPathSelectElement ( "OperationKind" ) is { } explicitKind )
877+ {
878+ if ( node . Name == "AbstractNode" && explicitKind . Attribute ( "Include" ) ? . Value != "true" )
879+ {
880+ continue ;
881+ }
882+ else if ( explicitKind . Attribute ( "Include" ) ? . Value == "false" )
883+ {
884+ // The node is explicitly excluded
885+ continue ;
886+ }
887+ else if ( explicitKind . XPathSelectElements ( "Entry" ) . Any ( ) )
888+ {
889+ var nodeBuilder = ImmutableArray . CreateBuilder < ( string name , int value , string extraDescription ) > ( ) ;
890+ foreach ( var entry in explicitKind . XPathSelectElements ( "Entry" ) )
891+ {
892+ if ( entry . Attribute ( "EditorBrowsable" ) ? . Value == "false" )
893+ {
894+ // Skip code generation for this operation kind
895+ continue ;
896+ }
897+
898+ int parsedValue = ParsePrefixHexValue ( entry . Attribute ( "Value" ) . Value ) ;
899+ nodeBuilder . Add ( ( entry . Attribute ( "Name" ) . Value , parsedValue , entry . Attribute ( "ExtraDescription" ) ? . Value ) ) ;
900+ }
901+
902+ builder . Add ( node . Attribute ( "Name" ) . Value , nodeBuilder . ToImmutable ( ) ) ;
903+ continue ;
904+ }
905+ }
906+ else if ( node . Name == "AbstractNode" )
907+ {
908+ // Abstract nodes without explicit Include="true" are skipped
909+ continue ;
910+ }
911+
912+ // Implicit operation kind
913+ operationKind ++ ;
914+ while ( skippedOperationKinds . Contains ( operationKind ) )
915+ {
916+ operationKind ++ ;
917+ }
918+
919+ var nodeName = node . Attribute ( "Name" ) . Value ;
920+ var kindName = nodeName . Substring ( "I" . Length , nodeName . Length - "I" . Length - "Operation" . Length ) ;
921+ builder . Add ( nodeName , ImmutableArray . Create ( ( kindName , operationKind , ( string ) null ) ) ) ;
922+ }
923+
924+ return builder . ToImmutable ( ) ;
925+ }
926+
927+ private static ImmutableHashSet < int > GetSkippedOperationKinds ( XDocument document )
928+ {
929+ var builder = ImmutableHashSet . CreateBuilder < int > ( ) ;
930+ foreach ( var skippedKind in document . XPathSelectElements ( "/Tree/UnusedOperationKinds/Entry" ) )
931+ {
932+ builder . Add ( ParsePrefixHexValue ( skippedKind . Attribute ( "Value" ) . Value ) ) ;
933+ }
934+
935+ foreach ( var explicitKind in document . XPathSelectElements ( "/Tree/*/OperationKind/Entry" ) )
936+ {
937+ builder . Add ( ParsePrefixHexValue ( explicitKind . Attribute ( "Value" ) . Value ) ) ;
938+ }
939+
940+ return builder . ToImmutable ( ) ;
941+ }
942+
943+ private static int ParsePrefixHexValue ( string value )
944+ {
945+ if ( ! value . StartsWith ( "0x" ) )
946+ {
947+ throw new InvalidOperationException ( $ "Unexpected number format: '{ value } '") ;
948+ }
949+
950+ return int . Parse ( value . Substring ( "0x" . Length ) , NumberStyles . AllowHexSpecifier ) ;
951+ }
794952 }
795953
796954 private sealed class InterfaceData
797955 {
798956 private readonly DocumentData documentData ;
799957
800- public InterfaceData ( DocumentData documentData , XElement node )
958+ public InterfaceData ( DocumentData documentData , XElement node , ImmutableArray < ( string name , int value , string extraDescription ) > operationKinds )
801959 {
802960 this . documentData = documentData ;
803961
962+ this . OperationKinds = operationKinds ;
804963 this . InterfaceName = node . Attribute ( "Name" ) . Value ;
964+ this . Name = this . InterfaceName . Substring ( "I" . Length , this . InterfaceName . Length - "I" . Length - "Operation" . Length ) ;
805965 this . WrapperName = this . InterfaceName + "Wrapper" ;
806966 this . BaseInterfaceName = node . Attribute ( "Base" ) . Value ;
807967 this . IsAbstract = node . Name == "AbstractNode" ;
808968 this . Properties = node . XPathSelectElements ( "Property" ) . Select ( property => new PropertyData ( property ) ) . ToImmutableArray ( ) ;
809969 }
810970
971+ public ImmutableArray < ( string name , int value , string extraDescription ) > OperationKinds { get ; }
972+
811973 public string InterfaceName { get ; }
812974
975+ public string Name { get ; }
976+
813977 public string WrapperName { get ; }
814978
815979 public string BaseInterfaceName { get ; }
0 commit comments