Skip to content

BUTR/BUTR.Harmony.Analyzer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BUTR.Harmony.Analyzer

CodeFactor Code Climate maintainability
GitHub Workflow Status (event)

A Roslyn analyzer for Harmony which adds the ability to do dynamic type checking and ensures that there are no typos when using the AccessTools* methods.

For example, if the user wants to access _privateField from some class/struct, but typed instead _privateFld, the analyzer will highlight that. It leverages the System.Reflection.Metadata package for fast analysys or public and non-public members of types, since Roslyn can't access non public members.

This drastically reduces runtime errors when using Harmony.

Also, when speaking in long-term maintenance of mods, if the game's internal API changes and a type member will be renamed or it will be changed to another type (e.g. field to a property), the analyzer will highlight that.

// The analyzer works only when full data is provided for the method in compile-time
// so the following methods will work:
AccessTools.Method(typeof(Class), "MemberName");
AccessTools.Method("System.Class:MemberName");

// But this won't be supported, because the information will be available only at runtime
Type type = ExternalMethod();
AccessTools.Method(type, "MemberName");

Rules

You'll find the rules in the documentation: the rules and their explanation

Installation

Supported API

Supports AccessTools classes from Harmony, HarmonyX and Harmony.Extensions.
As long as the class starts with AccessTools, it will be supported.
The following API's are supported:

  • Constructor/DeclaredConstructor
  • Field/DeclaredField
  • Property/DeclaredProperty
  • Method/DeclaredMethod
  • PropertyGetter/DeclaredPropertyGetter
  • PropertySetter/DeclaredPropertySetter
  • Delegate/DeclaredDelegate
  • FieldRefAccess
  • StaticFieldRefAccess
  • StructFieldRefAccess

Additional Analyzers

We provide conversions from static typed check from AccessTools and SymbolExtensions to dynamic string based, marked by default as suggestions.

We believe that static typed member check (via typeof(Type)) adds more problems than it solves, because we are bound to the public ABI of the library that is patched.
Instead, we suggest to use dynamic typed member check (via a string containing the full name of the type).
Common sense would suggest that this is a bad idea, since you can't check whether the member you want to get exists, but the sole purpose of BUTR.Harmony.Analyzer is to solve this exact problem by creating warnings at compile time if the type was not found.

One of the most common problems that is solved is type namespace moving, since it breaks the public ABI.
Usually, the modder won't notice that a type was moved if both old and new namespaces are referenced and the full name of the type is not used. It will compile, but won't be binary compatible.
The dynamic typed check requires the full name of the type, so a namespace change will create a warning that the type does not exists.
There is a edge case that is not covered by the analyzer - moving a type within different assemblies with keeping the exact namespace.