@@ -161,6 +161,20 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
161161                    var  fieldTypeInfo  =  ( PrimitiveTypeHandleInfo ) fieldDef . DecodeSignature ( SignatureHandleProvider . Instance ,  null ) ; 
162162
163163                    CustomAttributeValue < TypeSyntax >  decodedAttribute  =  bitfieldAttribute . DecodeValue ( CustomAttributeTypeProvider . Instance ) ; 
164+                     ( int ?  fieldBitLength ,  bool  signed )  =  fieldTypeInfo . PrimitiveTypeCode  switch 
165+                     { 
166+                         PrimitiveTypeCode . Byte  =>  ( 8 ,  false ) , 
167+                         PrimitiveTypeCode . SByte  =>  ( 8 ,  true ) , 
168+                         PrimitiveTypeCode . UInt16  =>  ( 16 ,  false ) , 
169+                         PrimitiveTypeCode . Int16  =>  ( 16 ,  true ) , 
170+                         PrimitiveTypeCode . UInt32  =>  ( 32 ,  false ) , 
171+                         PrimitiveTypeCode . Int32  =>  ( 32 ,  true ) , 
172+                         PrimitiveTypeCode . UInt64  =>  ( 64 ,  false ) , 
173+                         PrimitiveTypeCode . Int64  =>  ( 64 ,  true ) , 
174+                         PrimitiveTypeCode . UIntPtr  =>  ( null ,  false ) , 
175+                         PrimitiveTypeCode . IntPtr  =>  ( ( int ? ) null ,  true ) , 
176+                         _ =>  throw  new  NotImplementedException ( ) , 
177+                     } ; 
164178                    string  propName  =  ( string ) decodedAttribute . FixedArguments [ 0 ] . Value ! ; 
165179                    byte  propOffset  =  ( byte ) ( long ) decodedAttribute . FixedArguments [ 1 ] . Value ! ; 
166180                    byte  propLength  =  ( byte ) ( long ) decodedAttribute . FixedArguments [ 2 ] . Value ! ; 
@@ -171,18 +185,25 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
171185                        continue ; 
172186                    } 
173187
174-                     TypeSyntax  propertyType  =  propLength  switch 
188+                     long  minValue  =  signed  ?  - ( 1L  <<  ( propLength  -  1 ) )  :  0 ; 
189+                     long  maxValue  =  ( 1L  <<  ( propLength  -  ( signed  ?  1  :  0 ) ) )  -  1 ; 
190+                     int ?  leftPad  =  fieldBitLength . HasValue  ?  fieldBitLength  -  ( propOffset  +  propLength )  :  null ; 
191+                     int  rightPad  =  propOffset ; 
192+                     ( TypeSyntax  propertyType ,  int  propertyBitLength )  =  propLength  switch 
175193                    { 
176-                         1  =>  PredefinedType ( Token ( SyntaxKind . BoolKeyword ) ) , 
177-                         <=  8  =>  PredefinedType ( Token ( SyntaxKind . ByteKeyword ) ) , 
178-                         <=  16  =>  PredefinedType ( Token ( SyntaxKind . UShortKeyword ) ) , 
179-                         <=  32  =>  PredefinedType ( Token ( SyntaxKind . UIntKeyword ) ) , 
180-                         <=  64  =>  PredefinedType ( Token ( SyntaxKind . ULongKeyword ) ) , 
194+                         1  =>  ( PredefinedType ( Token ( SyntaxKind . BoolKeyword ) ) ,   1 ) , 
195+                         <=  8  =>  ( PredefinedType ( Token ( signed   ?   SyntaxKind . SByteKeyword   :   SyntaxKind . ByteKeyword ) ) ,   8 ) , 
196+                         <=  16  =>  ( PredefinedType ( Token ( signed   ?   SyntaxKind . ShortKeyword   :   SyntaxKind . UShortKeyword ) ) ,   16 ) , 
197+                         <=  32  =>  ( PredefinedType ( Token ( signed   ?   SyntaxKind . IntKeyword   :   SyntaxKind . UIntKeyword ) ) ,   32 ) , 
198+                         <=  64  =>  ( PredefinedType ( Token ( signed   ?   SyntaxKind . LongKeyword   :   SyntaxKind . ULongKeyword ) ) ,   64 ) , 
181199                        _ =>  throw  new  NotSupportedException ( ) , 
182200                    } ; 
183201
184-                     AccessorDeclarationSyntax  getter  =  AccessorDeclaration ( SyntaxKind . GetAccessorDeclaration ) ; 
185-                     AccessorDeclarationSyntax  setter  =  AccessorDeclaration ( SyntaxKind . SetAccessorDeclaration ) ; 
202+                     AccessorDeclarationSyntax  getter  =  AccessorDeclaration ( SyntaxKind . GetAccessorDeclaration ) 
203+                         . AddModifiers ( TokenWithSpace ( SyntaxKind . ReadOnlyKeyword ) ) 
204+                         . AddAttributeLists ( AttributeList ( ) . AddAttributes ( MethodImpl ( MethodImplOptions . AggressiveInlining ) ) ) ; 
205+                     AccessorDeclarationSyntax  setter  =  AccessorDeclaration ( SyntaxKind . SetAccessorDeclaration ) 
206+                         . AddAttributeLists ( AttributeList ( ) . AddAttributes ( MethodImpl ( MethodImplOptions . AggressiveInlining ) ) ) ; 
186207
187208                    ulong  maskNoOffset  =  ( 1UL  <<  propLength )  -  1 ; 
188209                    ulong  mask  =  maskNoOffset  <<  propOffset ; 
@@ -203,36 +224,61 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
203224                        ExpressionSyntax  notMaskNoOffset  =  UncheckedExpression ( CastExpression ( propertyType ,  PrefixUnaryExpression ( SyntaxKind . BitwiseNotExpression ,  maskNoOffsetExpr ) ) ) ; 
204225                        LiteralExpressionSyntax  propOffsetExpr  =  LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( propOffset ) ) ; 
205226
206-                         // get => (byte)((field & unchecked((FIELDTYPE)getterMask)) >> propOffset); 
227+                         // signed: 
228+                         // get => (byte)((field << leftPad) >> (leftPad + rightPad))); 
229+                         // unsigned: 
230+                         // get => (byte)((field >> rightPad) & maskNoOffset); 
207231                        ExpressionSyntax  getterExpression  = 
208-                             CastExpression ( propertyType ,  ParenthesizedExpression ( BinaryExpression ( 
209-                                 SyntaxKind . RightShiftExpression , 
210-                                 ParenthesizedExpression ( BinaryExpression ( 
211-                                     SyntaxKind . BitwiseAndExpression , 
212-                                     fieldAccess , 
213-                                     UncheckedExpression ( CastExpression ( fieldType ,  maskExpr ) ) ) ) , 
214-                                 propOffsetExpr ) ) ) ; 
232+                             CastExpression ( propertyType ,  ParenthesizedExpression ( 
233+                                 signed  ? 
234+                                     BinaryExpression ( 
235+                                         SyntaxKind . RightShiftExpression , 
236+                                         ParenthesizedExpression ( BinaryExpression ( 
237+                                             SyntaxKind . LeftShiftExpression , 
238+                                             fieldAccess , 
239+                                             LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( leftPad ! . Value ) ) ) ) , 
240+                                         LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( leftPad . Value  +  rightPad ) ) ) 
241+                                     :  BinaryExpression ( 
242+                                         SyntaxKind . BitwiseAndExpression , 
243+                                         ParenthesizedExpression ( BinaryExpression ( SyntaxKind . RightShiftExpression ,  fieldAccess ,  LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( rightPad ) ) ) ) , 
244+                                         maskNoOffsetExpr ) ) ) ; 
215245                        getter  =  getter 
216246                            . WithExpressionBody ( ArrowExpressionClause ( getterExpression ) ) 
217247                            . WithSemicolonToken ( SemicolonWithLineFeed ) ; 
218248
219-                         // if ((value & ~maskNoOffset) != 0) throw new ArgumentOutOfRangeException(nameof(value)); 
220-                         // field = (int)((field & unchecked((int)~mask)) | ((int)value << propOffset))); 
221249                        IdentifierNameSyntax  valueName  =  IdentifierName ( "value" ) ; 
222-                         setter  =  setter . WithBody ( Block ( ) . AddStatements ( 
223-                             IfStatement ( 
224-                                 BinaryExpression ( SyntaxKind . NotEqualsExpression ,  ParenthesizedExpression ( BinaryExpression ( SyntaxKind . BitwiseAndExpression ,  valueName ,  notMaskNoOffset ) ) ,  LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( 0 ) ) ) , 
225-                                 ThrowStatement ( ObjectCreationExpression ( IdentifierName ( nameof ( ArgumentOutOfRangeException ) ) ) . AddArgumentListArguments ( Argument ( InvocationExpression ( IdentifierName ( "nameof" ) ) . WithArgumentList ( ArgumentList ( ) . AddArguments ( Argument ( valueName ) ) ) ) ) ) ) , 
226-                             ExpressionStatement ( AssignmentExpression ( 
227-                                 SyntaxKind . SimpleAssignmentExpression , 
228-                                 fieldAccess , 
229-                                 CastExpression ( fieldType ,  ParenthesizedExpression ( 
230-                                     BinaryExpression ( 
231-                                         SyntaxKind . BitwiseOrExpression , 
232-                                         //// (field & unchecked((int)~mask)) 
233-                                         fieldAndNotMask , 
234-                                         //// ((int)value << propOffset) 
235-                                         ParenthesizedExpression ( BinaryExpression ( SyntaxKind . LeftShiftExpression ,  CastExpression ( fieldType ,  valueName ) ,  propOffsetExpr ) ) ) ) ) ) ) ) ) ; 
250+ 
251+                         List < StatementSyntax >  setterStatements  =  new ( ) ; 
252+                         if  ( propertyBitLength  >  propLength ) 
253+                         { 
254+                             // The allowed range is smaller than the property type, so we need to check that the value fits. 
255+                             // signed: 
256+                             //  global::System.Debug.Assert(value is >= minValue and <= maxValue); 
257+                             // unsigned: 
258+                             //  global::System.Debug.Assert(value is <= maxValue); 
259+                             RelationalPatternSyntax  max  =  RelationalPattern ( TokenWithSpace ( SyntaxKind . LessThanEqualsToken ) ,  CastExpression ( propertyType ,  LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( maxValue ) ) ) ) ; 
260+                             RelationalPatternSyntax ?  min  =  signed  ?  RelationalPattern ( TokenWithSpace ( SyntaxKind . GreaterThanEqualsToken ) ,  CastExpression ( propertyType ,  LiteralExpression ( SyntaxKind . NumericLiteralExpression ,  Literal ( minValue ) ) ) )  :  null ; 
261+                             setterStatements . Add ( ExpressionStatement ( InvocationExpression ( 
262+                                 ParseName ( "global::System.Diagnostics.Debug.Assert" ) , 
263+                                 ArgumentList ( ) . AddArguments ( Argument ( 
264+                                     IsPatternExpression ( 
265+                                         valueName , 
266+                                         min  is  null  ?  max  :  BinaryPattern ( SyntaxKind . AndPattern ,  min ,  max ) ) ) ) ) ) ) ; 
267+                         } 
268+ 
269+                         // field = (int)((field & unchecked((int)~mask)) | ((int)(value & mask) << propOffset))); 
270+                         ExpressionSyntax  valueAndMaskNoOffset  =  ParenthesizedExpression ( BinaryExpression ( SyntaxKind . BitwiseAndExpression ,  valueName ,  maskNoOffsetExpr ) ) ; 
271+                         setterStatements . Add ( ExpressionStatement ( AssignmentExpression ( 
272+                             SyntaxKind . SimpleAssignmentExpression , 
273+                             fieldAccess , 
274+                             CastExpression ( fieldType ,  ParenthesizedExpression ( 
275+                                 BinaryExpression ( 
276+                                     SyntaxKind . BitwiseOrExpression , 
277+                                     //// (field & unchecked((int)~mask)) 
278+                                     fieldAndNotMask , 
279+                                     //// ((int)(value & mask) << propOffset) 
280+                                     ParenthesizedExpression ( BinaryExpression ( SyntaxKind . LeftShiftExpression ,  CastExpression ( fieldType ,  valueAndMaskNoOffset ) ,  propOffsetExpr ) ) ) ) ) ) ) ) ; 
281+                         setter  =  setter . WithBody ( Block ( ) . AddStatements ( setterStatements . ToArray ( ) ) ) ; 
236282                    } 
237283                    else 
238284                    { 
@@ -261,11 +307,12 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
261307                    } 
262308
263309                    string  bitDescription  =  propLength  ==  1  ?  $ "bit { propOffset } ":  $ "bits { propOffset } -{ propOffset  +  propLength  -  1 } "; 
310+                     string  allowedRange  =  propLength  ==  1  ?  string . Empty  :  $ " Allowed values are [{ minValue } ..{ maxValue } ]."; 
264311
265312                    PropertyDeclarationSyntax  bitfieldProperty  =  PropertyDeclaration ( propertyType . WithTrailingTrivia ( Space ) ,  Identifier ( propName ) . WithTrailingTrivia ( LineFeed ) ) 
266313                        . AddModifiers ( TokenWithSpace ( this . Visibility ) ) 
267314                        . WithAccessorList ( AccessorList ( ) . AddAccessors ( getter ,  setter ) ) 
268-                         . WithLeadingTrivia ( ParseLeadingTrivia ( $ "/// <summary>Gets or sets { bitDescription }  in the <see cref=\" { fieldName } \"  /> field.</summary>\n ") ) ; 
315+                         . WithLeadingTrivia ( ParseLeadingTrivia ( $ "/// <summary>Gets or sets { bitDescription }  in the <see cref=\" { fieldName } \"  /> field.{ allowedRange } </summary>\n ") ) ; 
269316
270317                    members . Add ( bitfieldProperty ) ; 
271318                } 
0 commit comments