Unity-specific code analysis in Rider 2018.2
Perhaps the most powerful feature that Rider has is its code analysis, finding issues and providing quick fixes and context actions to help you improve the quality of your code. Rider’s Unity support is no exception, and in this post, we’ll take a look at what Rider 2018.2 brings in terms of Unity-specific code analysis and refactorings.
In this series:
- Unity Package Explorer
- Assembly Definition Files
- Unity Editor integration updates
- Unity specific code analysis
One of Rider’s most used refactorings is the rename refactoring, which renames code elements much more safely than a simple textual find and replace. However, there is a small issue when renaming Unity serialised fields – Unity’s serialisation is keyed on name, so if you rename a field, any existing serialised data will break!
Fortunately, Unity has a solution to this – the FormerlySerializedAs attribute. This attribute specifies a “fall back” name to use if Unity can’t find the current name of the field. So when a serialised field is renamed, the new name is used to save any new data, but Unity will still be able to read existing data, because it can look for the old name if the new name isn’t used.
Rider 2018.2 adds support for [FormerlySerializedAs] to the rename refactoring. Now, when renaming a field, Rider will ask you if you want to add the [FormerlySerializedAs] attribute, and add it to the field, populated with the old name.
The Alt+Enter menu is mostly associated with fixing highlights such as warnings or suggestions, but it also provides context specific actions, which can quickly transform the code at the text caret, changing the meaning rather than trying to fix a specific issue.
Rider 2018.2 adds a couple of new context actions. The first will quickly toggle a field between serialised and non-serialised, by adding or removing the [NonSerialized] and [SerializeField] attributes. The action is smart enough to know that a public field without attributes is serialised, and will only add or remove attributes when necessary.
The second action will add or remove the [HideInInspector] attribute, quickly letting you hide a serialised field from the Unity Inspector window.
Multiple field declarations
While we’re talking about fields, all of the quick fixes and context actions that work on fields have been updated to correctly handle multiple field declarations, where multiple fields are listed in one declaration.
For example, when using Alt+Enter on one field in a multiple field declaration, the “make serialised” context action will by default make that one field serialised, moving it out of the multiple field declaration and applying the attribute to the single field. But it also offers a sub-menu that will make all fields serialised.
On top of that, Rider has added a new inspection that will warn if the [FormerlySerializedAs] attribute is applied to a multiple field declaration. When an attribute is applied to a multiple field declaration, it is actually applied to each field in that declaration. This means each field would be sharing the same previously serialised name, which would break the fallback serialisation!
Rider warns in this case, and adds a quick fix to split the single, multiple declaration into separate single declarations, allowing you to then decide which field should have the [FormerlySerializedAs] attribute applied.
While Rider 2018.2 has made it easy to add various attributes, such as [FormerlySerializedAs], [SerializeField] and [HideInInspector], it now also knows when these attributes are unnecessary and provides a quick fix to remove them. So if [FormerlySerializedAs] is applied to a field that isn’t serialised, or has the same name as the previously serialised value, it’s marked as redundant, and a quick Alt+Enter will remove it.
Similarly, [SerializeField] on a readonly field and [HideInInspector] on a non-serialised field will be marked as redundant.
Inefficient usage of Camera.main
We’ve added another inspection to try and help you stay on top of common performance mistakes. We already suggest you use CompareTag instead of comparing the tag property to a string literal (it avoids memory allocations – read more here), and Rider 2018.2 will now warn if you use Camera.main inside an Update method.
Internally, Camera.main calls FindObjectsWithTag each time it is accessed, which is very inefficient if it’s being used in frequently called methods such as Update, FixedUpdate or LateUpdate. You can read more about this issue at Unity’s support site.
We’re already working on providing more inspections in this area for Rider 2018.3. Stay tuned!
Why is Rider suggesting this?
One really helpful feature that often gets overlooked is that many inspections have documentation explaining why Rider is showing a warning. All of the new inspections added in Rider 2018.2, such as using Camera.main in an Update method or applying the [FormerlySerializedAs] attribute to a multiple field declaration mentioned above, include such documentation. It’s available in the inspection options sub-menu in the Alt+Enter menu.
You can see a full list of the (Unity specific) documented inspections here, and a full list of Rider’s C# inspection descriptions here.
Mark possible event handlers as in use
Starting in 2018.1, Rider started to mark any public method as unused if it wasn’t explicitly used in code. Previously, this was only shown when Solution Wide Analysis was enabled. It’s a very useful feature to help find (and Alt+Enter remove) unused code. However, in Unity, it’s possible to wire up method calls inside the Unity editor, which Rider doesn’t know about. This means Rider might incorrectly tell you that a method is unused, when Unity is definitely using it!
To avoid these false reports, Rider will now mark all public static methods (and property setters) in Unity types as in use.
Custom serialisable classes
Rider offers nice support for serialised fields in Unity Object based classes, highlighting fields in the editor gutter, telling code analysis that a field is assigned a value implicitly to provide better warnings, and providing other features such as toggling serialised/non-serialised attributes. Rider 2018.2 extends this support to custom classes marked with the [Serializable] attribute.
Eagle eyed users spotted that Unity specific warnings weren’t appearing in the Solution Wide Analysis tool window, when the option to show warnings was enabled. Oops! Fixed!
And we’ll wind up our code analysis coverage with one last inspection, or rather, without an inspection. That’s right, we’ve removed a highlight! Or more accurately, we’ve removed an incorrect warning.
Rider 2018.2 will no longer warn you that if (this == null) will never be true for classes derived from UnityEngine.Object. Thanks to the way Unity overrides the equality operator, this can return true if the underlying native object has been destroyed. Rider no longer assumes this isn’t the case. No more false positives!
That brings us nearly to the end of our look at Unity support in Rider 2018.2. We’ve covered a lot of ground in this series, looking at adding Packages to the Unity Explorer, code completion and inspections in Assembly Definition Files, updated integration with the Unity Editor and new inspections, quick fixes and refactorings.
But there are one or two updates that don’t quite fit into any of the other posts. For example, we’ve updated our API support to include the latest APIs and documentation from Unity 2018.2. You can see the full list of changes in this GitHub milestone (and for Rider 2018.2.1 and 2018.2.2).
So that just leaves one last feature to squeeze in: two new Live Templates – sfield and sprop, for quickly creating a serialised field, or a property with a serialised field as a backing field. And of course, you can edit these in Rider’s new Live Templates editor.
Download Rider 2018.2 today and give all of these new Unity specific features a go! And if you’d like to try the latest and greatest new features we’re working on, you can also check out the brand new Early Access Preview for Rider 2018.3.
Subscribe to Blog updates
Thanks, we've got you!