Skip to content

Commit e7e1d9a

Browse files
authored
Document all analyzer attributes. (space-wizards#6467)
1 parent cc5d750 commit e7e1d9a

18 files changed

Lines changed: 325 additions & 42 deletions

Robust.Shared/Analyzers/AccessAttribute.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,57 @@ namespace Robust.Shared.Analyzers.Implementation;
66
namespace Robust.Shared.Analyzers;
77
#endif
88

9+
/// <summary>
10+
/// <para>
11+
/// Access is a way to describe how other classes are allowed to use a field in more precise terms than "can use"
12+
/// and "cannot use". Think of it like friend classes from C++.
13+
/// </para>
14+
/// <para>
15+
/// Access controls field, method, and property usage for three different kinds of users:
16+
/// Self (the declaring class), Friend (classes explicitly named as friends by the access attribute),
17+
/// and Other (classes that aren't the other two). Using <see cref="AccessPermissions"/> you can define what
18+
/// operations each of the users is allowed.
19+
/// </para>
20+
/// </summary>
21+
/// <example>
22+
/// <code>
23+
/// [RegisterComponent]
24+
/// // Allow the system with utility functions for this component to modify it.
25+
/// [Access(typeof(MySystem))]
26+
/// public sealed class MyComponent : Component
27+
/// {
28+
/// public int Counter;
29+
/// }
30+
/// <br/>
31+
/// public sealed class MySystem : EntitySystem
32+
/// {
33+
/// public void AddToCounter(Entity&lt;MyComponent&gt; entity)
34+
/// {
35+
/// // Works, we're a friend of the other type.
36+
/// entity.Comp.Counter += 1;
37+
/// }
38+
/// }
39+
/// <br/>
40+
/// public sealed class OtherSystem : EntitySystem
41+
/// {
42+
/// public void AddToCounter(Entity&lt;MyComponent&gt; entity)
43+
/// {
44+
/// // Error RS2008: Tried to perform write access to member 'Counter' in type 'MyComponent', despite read access.
45+
/// entity.Comp.Counter += 1;
46+
/// }
47+
/// }
48+
/// </code>
49+
/// </example>
50+
/// <seealso cref="AccessPermissions"/>
951
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct
1052
| AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor)]
1153
public sealed class AccessAttribute : Attribute
1254
{
55+
/// <summary>
56+
/// The list of types considered "friends" of the type with this attribute.
57+
/// These types get elevated permissions.
58+
/// </summary>
59+
/// <seealso cref="Friend"/>
1360
public readonly Type[] Friends;
1461

1562
public const AccessPermissions SelfDefaultPermissions = AccessPermissions.ReadWriteExecute;
@@ -20,7 +67,13 @@ public sealed class AccessAttribute : Attribute
2067
/// Access permissions for the type itself, or the type containing the member.
2168
/// </summary>
2269
public AccessPermissions Self { get; set; } = SelfDefaultPermissions;
70+
/// <summary>
71+
/// Access permissions for types specified as <see cref="Friends"/>.
72+
/// </summary>
2373
public AccessPermissions Friend { get; set; } = FriendDefaultPermissions;
74+
/// <summary>
75+
/// Access permissions for types that aren't <see cref="Self"/> and aren't <see cref="Friend"/>.
76+
/// </summary>
2477
public AccessPermissions Other { get; set; } = OtherDefaultPermissions;
2578

2679
public AccessAttribute(params Type[] friends)

Robust.Shared/Analyzers/AccessPermissions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
11
using System;
2+
using System.Diagnostics.Contracts;
23

34
#if ROBUST_ANALYZERS_IMPL
45
namespace Robust.Shared.Analyzers.Implementation;
56
#else
67
namespace Robust.Shared.Analyzers;
78
#endif
89

10+
/// <summary>
11+
/// A set of flags that dictate what kind of field and property access can occur for a given <see cref="AccessAttribute"/>.
12+
/// </summary>
913
[Flags]
1014
public enum AccessPermissions : byte
1115
{
1216
None = 0,
1317

18+
/// <summary>
19+
/// Allows field and property read operations, for example using getters, and also <see cref="PureAttribute"/>
20+
/// marked methods.
21+
/// </summary>
1422
Read = 1 << 0, // 1
23+
/// <summary>
24+
/// Allows field and property write operations, for example using setters.
25+
/// </summary>
1526
Write = 1 << 1, // 2
27+
/// <summary>
28+
/// Allows executing methods.
29+
/// </summary>
1630
Execute = 1 << 2, // 4
1731

1832
ReadWrite = Read | Write,

Robust.Shared/Analyzers/ComponentNetworkGeneratorAuxiliary.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,34 @@ namespace Robust.Shared.Analyzers;
99
/// will automatically be replicated using component states to clients. Systems which need to have more intelligent
1010
/// component state replication beyond just directly setting variables should not use this attribute.
1111
/// </summary>
12+
/// <example>
13+
/// <code>
14+
/// // In shared code...
15+
/// [RegisterComponent, AutoGenerateComponentState]
16+
/// public sealed class MyComponent : Component
17+
/// {
18+
/// // Indicate to the generator we want to network this field.
19+
/// // Doesn't need to be a DataField for this to work!
20+
/// [AutoNetworkedField]
21+
/// public int Counter;
22+
/// }
23+
/// <br/>
24+
/// public sealed class MySystem : EntitySystem
25+
/// {
26+
/// public void AddToCounter(Entity&lt;MyComponent&gt; entity)
27+
/// {
28+
/// entity.Comp.Counter += 1;
29+
/// <br/>
30+
/// // Dirty the entity, and the auto-generated state handling will do the rest to ensure
31+
/// // all our AutoNetworkedFields are sent to the client.
32+
/// Dirty(entity);
33+
/// }
34+
/// }
35+
/// </code>
36+
/// </example>
37+
/// <seealso cref="AutoNetworkedFieldAttribute"/>
38+
/// <seealso cref="AfterAutoHandleStateEvent"/>
39+
/// <seealso cref="ComponentState"/>
1240
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
1341
[BaseTypeRequired(typeof(IComponent))]
1442
public sealed class AutoGenerateComponentStateAttribute : Attribute

Robust.Shared/Analyzers/ComponentPauseGeneratorAttributes.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,30 @@
55
namespace Robust.Shared.Analyzers;
66

77
/// <summary>
8-
/// Indicate that a <see cref="Component"/> should automatically handle unpausing of timer fields.
8+
/// Indicate that a <see cref="Component"/> should automatically handle unpausing of timer fields.
99
/// </summary>
1010
/// <remarks>
11-
/// When this attribute is set on a <see cref="Component"/>, an <see cref="EntitySystem"/> will automatically be
12-
/// generated that increments any fields tagged with <see cref="AutoPausedFieldAttribute"/> when the entity is unpaused
13-
/// (<see cref="EntityUnpausedEvent"/>).
11+
/// When this attribute is set on a <see cref="Component"/>, an <see cref="EntitySystem"/> will automatically be
12+
/// generated that increments any fields tagged with <see cref="AutoPausedFieldAttribute"/> when the entity is unpaused
13+
/// (<see cref="EntityUnpausedEvent"/>).
1414
/// </remarks>
1515
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
1616
[BaseTypeRequired(typeof(IComponent))]
1717
public sealed class AutoGenerateComponentPauseAttribute : Attribute
1818
{
19+
/// <summary>
20+
/// Whether the generated code should automatically call
21+
/// <see cref="IEntityManager.Dirty(EntityUid,IComponent,MetaDataComponent)"/> after unpausing the entity.
22+
/// This is automatically inferred for fields marked <see cref="AutoNetworkedFieldAttribute"/>.
23+
/// </summary>
1924
public bool Dirty = false;
2025
}
2126

2227
/// <summary>
23-
/// Mark a field or property to automatically handle unpausing with <see cref="AutoGenerateComponentPauseAttribute"/>.
28+
/// Mark a field or property to automatically handle unpausing with <see cref="AutoGenerateComponentPauseAttribute"/>.
2429
/// </summary>
2530
/// <remarks>
26-
/// The type of the field or prototype must be <see cref="TimeSpan"/> (potentially nullable).
31+
/// The type of the field or prototype must be <see cref="TimeSpan"/> (potentially nullable).
2732
/// </remarks>
2833
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
2934
public sealed class AutoPausedFieldAttribute : Attribute;

Robust.Shared/Analyzers/ForbidLiteralAttribute.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,26 @@
33
namespace Robust.Shared.Analyzers;
44

55
/// <summary>
6-
/// Marks that values used for this parameter should not be literal values.
7-
/// This helps prevent magic numbers/strings/etc, by indicating that values
8-
/// should either be wrapped (for validation) or defined as constants or readonly statics.
6+
/// Marks that values used for this parameter should not be literal values.
7+
/// This helps prevent magic numbers/strings/etc, by indicating that values
8+
/// should either be wrapped (for validation) or defined as constants or readonly statics.
99
/// </summary>
10+
/// <example>
11+
/// <code>
12+
/// public sealed class MyClass
13+
/// {
14+
/// public static bool IsPastry([ForbidLiteral] string id);
15+
/// public static string GrabFromCupboard();
16+
/// }
17+
/// <br/>
18+
/// <br/>
19+
/// // Error RA0033: The id parameter of IsPastry forbids literal values.
20+
/// DebugTools.Assert(MyClass.IsPastry("cupcake"));
21+
/// <br/>
22+
/// var maybePastry = obj.GrabFromCupboard();
23+
/// // Allowed.
24+
/// DebugTools.Assert(MyClass.IsPastry(maybePastry));
25+
/// </code>
26+
/// </example>
1027
[AttributeUsage(AttributeTargets.Parameter)]
1128
public sealed class ForbidLiteralAttribute : Attribute;

Robust.Shared/Analyzers/MustCallBaseAttribute.cs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,40 @@
33
namespace Robust.Shared.Analyzers;
44

55
/// <summary>
6-
/// Indicates that overriders of this method must always call the base function.
6+
/// Indicates that overriders of this method must always call the base function.
77
/// </summary>
88
/// <param name="onlyOverrides">
9-
/// If true, only base calls to *overrides* are necessary.
10-
/// This is intended for base classes where the base function is always empty,
11-
/// so a base call from the first override may be ommitted.
9+
/// If true, only base calls to <b>overrides</b> are necessary.
10+
/// This is intended for base classes where the base function is always empty,
11+
/// so a base call from the first override may be ommitted.
1212
/// </param>
13+
/// <example>
14+
/// <code>
15+
/// public abstract class MyBaseClass
16+
/// {
17+
/// [MustCallBase]
18+
/// public virtual void Initialize() { /* ... */ }
19+
/// }
20+
/// <br/>
21+
/// public sealed class MyBadClass : MyBaseClass
22+
/// {
23+
/// // Error RA0028: Overriders of this function must always call the base function.
24+
/// public override void Initialize()
25+
/// {
26+
/// // We don't do anything in here, not even call base.Initialize().
27+
/// }
28+
/// }
29+
/// <br/>
30+
/// public sealed class MyGoodClass : MyBaseClass
31+
/// {
32+
/// // No error.
33+
/// public override void Initialize()
34+
/// {
35+
/// base.Initialize();
36+
/// }
37+
/// }
38+
/// </code>
39+
/// </example>
1340
[AttributeUsage(AttributeTargets.Method)]
1441
public sealed class MustCallBaseAttribute(bool onlyOverrides = false) : Attribute
1542
{

Robust.Shared/Analyzers/NotContentImplementable.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
namespace Robust.Shared.Analyzers;
44

55
/// <summary>
6-
/// Marker attribute specifying that content <b>must not</b> implement this interface itself.
6+
/// Marker attribute specifying that content <b>must not</b> implement this interface itself.
77
/// </summary>
88
/// <remarks>
9-
/// Interfaces with this attribute may have members added by the engine at any time,
10-
/// so implementing them yourself would not be API-stable.
9+
/// <para>
10+
/// Interfaces with this attribute may have members added by the engine at any time,
11+
/// so implementing them yourself would not be API-stable.
12+
/// </para>
13+
/// <para>
14+
/// Currently, nothing enforces this, but your codebase may spontaneously cease building in any minor version if
15+
/// you inherit from or implement anything marked with this attribute.
16+
/// </para>
1117
/// </remarks>
1218
[AttributeUsage(AttributeTargets.Interface)]
1319
public sealed class NotContentImplementableAttribute : Attribute;

Robust.Shared/Analyzers/NotNullableFlagAttribute.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ namespace Robust.Shared.Analyzers.Implementation;
66
namespace Robust.Shared.Analyzers;
77
#endif
88

9+
/// <summary>
10+
/// Indicates the given boolean field must be set to true when the provided type parameter is not nullable.
11+
/// An analyzer then enforces this as an error.
12+
/// </summary>
913
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.GenericParameter)]
1014
public sealed class NotNullableFlagAttribute : Attribute
1115
{

Robust.Shared/Analyzers/ObsoleteInheritanceAttribute.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,20 @@
33
namespace Robust.Shared.Analyzers;
44

55
/// <summary>
6-
/// Indicates that the ability to <i>inherit</i> this type is obsolete, and attempting to do so should give a warning.
6+
/// Indicates that the ability to <i>inherit</i> this type is obsolete, and attempting to do so should give a warning.
77
/// </summary>
88
/// <remarks>
9-
/// This is useful to gracefully deal with types that should never have had <see cref="VirtualAttribute"/>.
9+
/// This is useful to gracefully deal with types that should never have had <see cref="VirtualAttribute"/>.
1010
/// </remarks>
11+
/// <example>
12+
/// <code>
13+
/// [ObsoleteInheritance]
14+
/// public class MyClass;
15+
/// <br/>
16+
/// // Warning RA0034: Type 'MyDescendant' inherits from 'MyClass', which has obsoleted inheriting from itself.
17+
/// public sealed class MyDescendant : MyClass;
18+
/// </code>
19+
/// </example>
1120
/// <seealso cref="VirtualAttribute"/>
1221
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
1322
public sealed class ObsoleteInheritanceAttribute : Attribute

Robust.Shared/Analyzers/PreferGenericVariantAttribute.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ namespace Robust.Shared.Analyzers.Implementation;
66
namespace Robust.Shared.Analyzers;
77
#endif
88

9+
/// <summary>
10+
/// Indicates that the marked method has an alternative version that takes the Type input as a generic,
11+
/// and warns the user to use the generic version instead if they use <see langword="typeof"/>.
12+
/// </summary>
13+
/// <example>
14+
/// <code>
15+
/// public sealed MyClass
16+
/// {
17+
/// [PreferGenericVariant]
18+
/// public static bool IsPastry(Type t);
19+
/// public static bool IsPastry&lt;T&gt;();
20+
/// }
21+
/// <br/>
22+
/// // Warning RA0005: Consider using the generic variant of this method to avoid potential allocations.
23+
/// MyClass.IsPastry(typeof(Cupcake));
24+
/// </code>
25+
/// </example>
926
[AttributeUsage(AttributeTargets.Method)]
1027
public sealed class PreferGenericVariantAttribute : Attribute
1128
{

0 commit comments

Comments
 (0)