Creating and using Roslyn Analyzers for Rider - unity3d

So my situation is as follows:
I need to have a Code Analyzer that can detect if I'm using a Member (Method/Field/Property/Class) that is marked with the Attribute [EditorOnly] outside of a scope surrounded by a preprocessor that looks like this: #if UNITY_EDITOR ... #endif
To make a clear example:
I have the class with the editor-only method:
public class SampleClass {
#if UNITY_EDITOR
[EditorOnly]
public static void SampleMethod() {...}
#endif
}
and another class that is trying to use it at runtime:
public class BuildClass {
public void Start() {
SampleClass.SampleMethod();
}
}
A situation like this would fail to build as the call in the second script will not be able to find the method as it is excluded from the build.
So given what I said, the question is:
How do I create and use a Roslyn Analyzer that gives me a "Design-Time" error inside the IDE (JetBrains Rider) saying that I cannot use the method outside of the UNITY_EDITOR scope?
P.S. I don't want to use the [Conditional()] Attribute

Related

nunit : global variable initialisation in setupfixture

I am very new to C# and nunit. Pls bear with me if this is basic and has been already been asked here.
We have a global setup,defined by [SetupFixture] class,which is expected to be run only once. The private variables are defined in it's [setup]. We wish to use the same variables in all our testfixtures,hence inheriting the testbase class in all our testfixtures.
But, while executing Testcase1, i observe that globalSetup() is called more than once. Can anyone point me the issue? sample code is as below.
namespace CTB
{
[SetupFixture]
public class Testbase
{
private byte val1;
private byte val2;
[setup]
public void globalSetup
{
val1 = 5;
val2 = 10;
}
[Teardown]
public void globalTeardown
{
//
}
}
}
namespace CTB.Testcase
{
public class TestCase : Testbase
{
[Setup]
public void Setup()
{
}
[Teardown]
public void Teardown()
{
}
[Test]
public void Testcase1()
{
byte val3 = val1 + val2; // Expect 15
}
}
}
I'm assuming that the answer to my comment is "No" and that you are using a current version of NUnit 3. Please correct me if I'm wrong. :-)
You have made the class TestBase serve two functions:
It's the base class for your TestFixture and therefore it's a TestFixture itself.
It's marked as a SetUpFixture so it also serves that function - a completely different function, by the way.
To be clear, you should never do this. It's a sort of "trick" that almost seems designed to confuse NUnit - not your intention of course. Your test fixtures should have no inheritance relationship with any SetUpFixture. Use different classes for the test fixture base and the setup fixture.
With that out of the way, here is the longer story of what is happening...
Before your tests even execute, the SetUpFixture is first "run" - in quotes because it actually does nothing. That's because it doesn't contain any methods marked with [OneTimeSetUp] or '[OneTimeTearDown]`.
NOTE: As an alternate explanation, if you are using a pretty old version of NUnit, the [SetUp] and [TearDown] methods are actually called at this point. Nnit V2 used those attributes with different meanings when encountered in a SetUpFixture versus a TestFixture.
Next your tests execute. Before each test, the inherited [SetUp] and [TearDown] methods are run. Of course, these are actually the same methods as in step 1. NUnit has been tricked into doing this!
Here is some general guidance for the future...
If you want multiple fixtures to use the same data, a base class is useful. Any public or protected fields or properties will be shared by the inheriting fixtures.
If you want to do some common setup or teardown for a group of unrelated test fixtures, use a SetUpFixture. Note that the only way to pass data from a SetUpFixture to the test fixtures is through static fields or properties. Generally, you use a SetUpFixture to set up the environment in which the test is run, not to provide data.
Never use the same class for both purposes.

Can I define a reusable subroutine/function/method within a Cake script?

I'm trying out Cake (C# Make). So far all the examples and documentation have the script file declaring all of its code inside delegates, like this:
Task("Clean")
.Does(() =>
{
// Delete a file.
DeleteFile("./file.txt");
// Clean a directory.
CleanDirectory("./temp");
});
However, one of the reasons I'm interested in using Cake is the possibility of writing my build scripts in a similar way to how I write code, as the scripts use a C#-based DSL. Included in this possibility is the ability to separate code that I use into methods (or functions / subroutines, whatever terminology is appropriate) so I can separate concerns and reuse code. For example, I may want to run the same set of steps for a multiple SKUs.
While I realize that I could create my own separate DLL with Script Aliases, I would like to avoid having to recompile a separate project every time I want to change these bits of shared code when working on the build script. Is there a way to define, inline with the normal build.cake file, methods that can still run the Cake aliases (e.g., DeleteFile) and can themselves be called from my Cake tasks?
Cake is C#, so you can create classes, methods, just like in regular C#
I.e. declare a class in a cake file
public class MyClass
{
public void MyMethod()
{
}
public static void MyStaticMethod()
{
}
}
and then use it a script like
var myClass = new MyClass();
// Call instance method
myClass.MyMethod();
//Call static method
MyClass.MyStaticMethod();
The Cake DSL is based on Roslyn scripting so there are some differences, code is essentially already in a type so you can declare a method without a class for reuse
public void MyMethod()
{
}
and then it can be called like a global methods
MyMethod();
A few gotchas, doing class will change scoping so you won't have access to aliases / context and global methods. You can get around this by i.e. passing ICakeContext as a parameter to class
public class MyClass
{
ICakeContext Context { get; }
public MyClass(ICakeContext context)
{
Context = context;
}
public void MyMethod()
{
Context.Information("Hello");
}
}
then used like this
// pass reference to Cake context
var myClass = new MyClass(Context);
// Call instance method which uses an Cake alias.
myClass.MyMethod();
You can have extension methods, but these can't be in a class, example:
public static void MyMethod(this ICakeContext context, string message)
{
context.Information(message);
}
Context.MyMethod("Hello");

Adding Coroutine functions to Transform

I'm creating a script in unity extending Transform
using UnityEngine;
using System.Collections;
using UnityEditor;
public static class TransformExtension
{
//lots of functions
public static IEnumerator tester(this Transform test)
{
Debug.Log("hello");
yield return null;
}
public static void tester2(this Transform test)
{
Debug.Log("hello2");
}
}
when I invoke
transform.tester();
transform.tester2();
only "hello2" is logged.
when I tried
StartCoroutine(transform.tester());
i got the following errors:
"error CS0103: The name 'tester' does not exist in the current context"
"Transform' does not contain a definition for 'StartCoroutine' and no accessible extension method 'StartCoroutine' accepting a first argument of type 'Transform' could be found (are you missing a using directive or an assembly reference?)
when I tried
transform.StartCoroutine(transform.tester());
I got:
"error CS1061: 'Transform' does not contain a definition for 'StartCoroutine' and no accessible extension method 'StartCoroutine' accepting a first argument of type 'Transform' could be found (are you missing a using directive or an assembly reference?)"
You can not call a Coroutine like a method you rather have to start it via StartCoroutine(). When you call it like a normal method it will simply be ignored (as you already noticed).
You can't use transform.StartCoroutine() since Transform is of type Component and does not inherit from MonoBehaviour.
But StartCoroutine() can only be used on a MonoBehaviour.
So assuming you are already calling it from within a MonoBehaviour due to the usage of transform instead simply do
StartCoroutine(transform.tester());
which works completely fine for me as long as called from within a MonoBehaviour or alternatively
anyGameObject.GetComponent<MonoBehaviour>().StartCoroutine(transform.tester());
That other MonoBehaviour which will be running the Coroutine doesn't even have to be on the same object but you have to be sure there is any other MonoBehaviour script attached to anyGameObject.
You can not start coroutines like function calls Add a function which starts coroutine. Also since derHugo pointed out you need Monobehavior to achieve this you can access MonoBehavior over your transform like this:
public static IEnumerator Tester()
{
Debug.Log("hello");
yield return null;
}
public static void StartTester(this Transform test)
{
test.GetComponent<MonoBehaviour>().StartCoroutine(Tester());
}
public static void tester2(this Transform test)
{
Debug.Log("hello2");
}
Then do this:
transform.startTester();

Using haxe.macro.TypeTools fails

I'm trying to debug a library which uses haxe.macro.TypeTools::findField. I've created a simple code for that:
package;
using haxe.macro.TypeTools;
class Main
{
public function new()
{
var test = findField(Child, "hello");
trace(test);
}
}
class Base
{
private function hello()
{
}
}
class Child extends Base
{
public function new() {}
}
However I'm getting error Unknown identifier : findField. Is this because it can only be used in build macro context?
This is what I'm trying to emulate.
First of all, function findField() is not from the haxe.macro.TypeTools.
It is a helper function from edge.core.macro.Macros.
To use it without a class path, import it's class with a wildcard import edge.core.macro.Macros.*
Secondly, findField() should be used in a build macro context only, since it expects Array<Field>, which is obtained by haxe.macro.Context.getBuildFields().

Inter Type Declaration on Compiled Class File

Is it possible to do Inter Type Declarations with AspectJ on Compiled Class Files at Load Time Weaving?
As an example: I compile some Groovy code and want to add fields or methods with IDT.
Update:
Oh my goodness, you do not need reflection to access members or execute methods. Eclipse shows errors in the editor, but you may just ignore them, the code compiles and runs fine anyway. So the aspect is really much more strightforward and simple:
public aspect LTWAspect {
public static String Application.staticField = "value of static field";
public String Application.normalField = "value of normal field";
public void Application.myMethod() {
System.out.println(normalField);
}
void around() : execution(void Application.main(..)) {
System.out.println("around before");
proceed();
System.out.println("around after");
System.out.println(Application.staticField);
new Application().myMethod();
}
}
Original answer:
Yes, but you have a hen-and-egg problem there, i.e. you cannot just reference the newly introduced fields from your LTW aspect code without reflection. (The last sentence is not true, see update above.) Plus, in order to make your LTW aspect compile, you need the classes to be woven on the project's build path so as to be able to reference them. Example:
Java project
public class Application {
public static void main(String[] args) {
System.out.println("main");
}
}
AspectJ project
import org.aspectj.lang.SoftException;
public aspect LTWAspect {
public static String Application.staticField = "value of static field";
public String Application.normalField = "value of normal field";
public void Application.myMethod() {
try {
System.out.println(Application.class.getDeclaredField("normalField").get(this));
} catch (Exception e) {
throw new SoftException(e);
}
}
void around() : execution(void Application.main(..)) {
System.out.println("around before");
proceed();
System.out.println("around after");
try {
System.out.println(Application.class.getDeclaredField("staticField").get(null));
Application.class.getDeclaredMethod("myMethod", null).invoke(new Application());
} catch (Exception e) {
throw new SoftException(e);
}
}
}
So, e.g. in Eclipse you need to put the Java project on the AspectJ project's build path under "Projects" because only then it can see Java class Application on which you want to declare members. After compilation you just start the Java project and do LTW on the aspect project (don't forget an aop-ajc.xml referencing LTWAspect).
In my example above I declare a static member, a non-static ("normal") member and a non-static method. My advice prints the static member and calls the non-static method, both via reflection. The non-static method then prints the non-static member, again via reflection. This is not nice, but it works and proves the ITD in combination with LTW is possible. There might be a more elegant way, but if so I am unaware of it. (Update: There is a more elegant way: Just ignore the errors marked by Eclipse IDE, see above.)
Program output
around before
main
around after
value of static field
value of normal field