Skip to content

Potential Roslyn Analyzers

Jonathan Pobst edited this page Mar 8, 2024 · 5 revisions

It may be beneficial to create a built-in set of Roslyn Analyzers for .NET for Android applications that can be used to steer users away from patterns that are less than ideal or flat-out will not work. Often times, .NET doesn't provide a better way to message these things outside of (ab)using [Obsolete] to surface warnings to users.

We often mention something would be a good candidate and then forget about it, so this page is where we can collect proposals should we create an Analyzer pack.

Potential candidates for Analyzers:

  • Do Not Override Application.AttachBaseContext (Context) - Application.AttachBaseContext (Context) is used by Xamarin.Android startup, and overriding it can make the application unable to start up.

  • Application subclasses must provide activation constructor - When subclassing Android.App.Application, the "Activation Constructor" (IntPtr handle, JniHandleOwnership transfer) must also be provided. Failure to do so will result in runtime errors.

  • Do not implement Java interfaces directly - User classes that implement Java interfaces (interfaces that implement IJavaObject/IJavaPeerable) should inherit Java.Lang.Object.

  • (Not specific to Android) warn when using a string.Format()-like method with a (string format, params object[]) parameter list which provides only the format parameter. This (repeatedly!) bites us, and presumably impacts others. For example, consider:

string.Format()-like Methods

Consider everybody's favorite logging method:

static void Log(string format, params object[] args)
{
    string message = string.Format(format, args);
    // do something with `message`…
}

Which is called "elsewhere", with some value only known at runtime:

static void Example()
{
    string info = GetSomeString();
    Log(info);
}

Example() is Bad™, because if (when) info contains {, then string.Format() will throw:

System.FormatException: Input string was not in a correct format.
   at System.Text.ValueStringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ReadOnlySpan`1 args)
   at System.String.FormatHelper(IFormatProvider provider, String format, ReadOnlySpan`1 args)
   at System.String.Format(String format, Object[] args)

It's doubly annoying when info is a value generated at runtime, making it difficult during code review to tell if info could contain a {.

There are two fixes for this: overloading, or an explicit format string.

The overloading fix would require that there be a Log(string) overload:

static void Log(string message)
{
    // do something with `message`…
}

static void Log(string format, params object[] args)
{
    string message = string.Format(format, args);
    Log(message);
}

This allows Log(info) to work as expected, without the potential for runtime errors.

If overloading isn't possible, e.g. because Log() is an interface method, then an explicit format should be used:

static void Example()
{
    string info = GetSomeString();
    Log("{0}", info);
}