diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md
index 0b7a10e485..74ae42376f 100644
--- a/com.unity.netcode.gameobjects/CHANGELOG.md
+++ b/com.unity.netcode.gameobjects/CHANGELOG.md
@@ -10,6 +10,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Added
+- ILPP diagnostic errors should now be clickable in the editor to go to the source of the issue. (#4250)
+
### Changed
### Deprecated
diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs
index 626e6492b1..d7d94b99b1 100644
--- a/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs
+++ b/com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs
@@ -560,14 +560,191 @@ public static TypeReference GetEnumAsInt(this TypeReference typeReference)
}
}
+ ///
+ /// Finds the starting index of a method signature declaration
+ ///
+ /// The line of text to scan
+ /// The name of the method to search for
+ /// The starting index of the signature declaration if a declaration was found; otherwise -1
+ private static int IndexOfSignature(string line, string name)
+ {
+ var column = line.IndexOf(name, StringComparison.Ordinal);
+ while (column >= 0)
+ {
+ // Skip any whitespace between the name and what follows it
+ var end = column + name.Length;
+ while (end < line.Length && char.IsWhiteSpace(line[end]))
+ {
+ end++;
+ }
+ if (end < line.Length && (line[end] == '(' || line[end] == '<'))
+ {
+ return column;
+ }
+
+ // Check if there's a next match
+ column = line.IndexOf(name, column + 1, StringComparison.Ordinal);
+ }
+ return -1;
+ }
+
+ ///
+ /// Finds the line of 's signature declaration.
+ ///
+ /// The sequence point, or null if the method has no debug information.
+ public static SequencePoint GetSignatureLocation(this MethodDefinition method)
+ {
+ // The SourceLocation is the first line of the method body.
+ var bodyPoint = method.GetSourceLocation();
+ if (bodyPoint == null)
+ {
+ return method.DeclaringType.GetSourceLocation();
+ }
+
+ try
+ {
+ var lines = File.ReadAllLines(bodyPoint.Document.Url);
+ var startIndex = Math.Min(bodyPoint.StartLine, lines.Length) - 1;
+ // Work backwards from the first line in the method body
+ for (var i = startIndex; i >= 0; i--)
+ {
+ // If this line includes the method definition: FooBar(
+ var column = IndexOfSignature(lines[i], method.Name);
+ if (column < 0)
+ {
+ // A line above the body that ends a statement or block belongs to the previous member.
+ if (i < startIndex && (lines[i].Contains(';') || lines[i].Contains('}')))
+ {
+ break;
+ }
+ continue;
+ }
+
+ return new SequencePoint(method.Body.Instructions[0], bodyPoint.Document)
+ {
+ StartLine = i + 1,
+ EndLine = i + 1,
+ StartColumn = column + 1,
+ EndColumn = column + 1 + method.Name.Length,
+ };
+ }
+ }
+ catch (Exception)
+ {
+ // We don't need to do anything on failure
+ }
+ // If we didn't find anything else, just use the first line of the method body that we found earlier
+ return bodyPoint;
+ }
+
+ ///
+ /// Finds the first visible line of in the user's source, so a diagnostic or log can point at it.
+ ///
+ /// The sequence point, or null if the method has no debug information.
+ public static SequencePoint GetSourceLocation(this MethodDefinition method)
+ {
+ foreach (var sequencePoint in method.DebugInformation.SequencePoints)
+ {
+ if (sequencePoint.IsHidden)
+ {
+ continue;
+ }
+ return sequencePoint;
+ }
+ return null;
+ }
+
+ ///
+ /// Finds the first visible line of any method in , so a diagnostic or log can at least open the right file.
+ ///
+ /// The sequence point, or null if the type has no debug information.
+ public static SequencePoint GetSourceLocation(this TypeDefinition type)
+ {
+ foreach (var method in type.Methods)
+ {
+ if (!method.HasBody)
+ {
+ continue;
+ }
+
+ var sequencePoint = method.GetSourceLocation();
+ if (sequencePoint != null)
+ {
+ return sequencePoint;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Finds where is declared in the user's source. Fields carry no debug information of their own,
+ /// but a field initializer is compiled into the constructor with a sequence point on the declaration, so this looks for
+ /// the store to the field there. Falls back to when the field has no initializer.
+ ///
+ /// The sequence point, or null if the declaring type has no debug information.
+ public static SequencePoint GetSourceLocation(this FieldDefinition field)
+ {
+ var storeOpCode = field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld;
+ foreach (var method in field.DeclaringType.Methods)
+ {
+ if (!method.IsConstructor || method.IsStatic != field.IsStatic || !method.HasBody)
+ {
+ continue;
+ }
+ SequencePoint lastVisible = null;
+ foreach (var instruction in method.Body.Instructions)
+ {
+ // Store the last visible line as the line we're looking for might not be visible
+ // Once we find the line we're looking for we want to return the last visible line
+ var sequencePoint = method.DebugInformation.GetSequencePoint(instruction);
+ if (sequencePoint != null && !sequencePoint.IsHidden)
+ {
+ lastVisible = sequencePoint;
+ }
+
+ // We're only looking for the store op codes on fields
+ var fieldReference = instruction.Operand as FieldReference;
+ if (instruction.OpCode != storeOpCode || fieldReference == null)
+ {
+ continue;
+ }
+
+ // If this is the field we're looking for and we have a visible instruction, then we've found what we're looking for!
+ if (fieldReference.Resolve() == field && lastVisible != null)
+ {
+ return lastVisible;
+ }
+ }
+ }
+
+ return field.DeclaringType.GetSourceLocation();
+ }
+
+ ///
+ /// Finds where is in the user's source. Called only when a diagnostic or log needs a location,
+ /// because finding a field's line scans its constructor's IL.
+ ///
+ /// The sequence point, or null if there is no debug information.
+ public static SequencePoint GetSourceLocation(this IMemberDefinition member)
+ {
+ return member switch
+ {
+ MethodDefinition method => method.GetSourceLocation(),
+ FieldDefinition field => field.GetSourceLocation(),
+ TypeDefinition type => type.GetSourceLocation(),
+ _ => null,
+ };
+ }
+
public static void AddError(this List diagnostics, string message)
{
diagnostics.AddError((SequencePoint)null, message);
}
- public static void AddError(this List diagnostics, MethodDefinition methodDefinition, string message)
+ public static void AddError(this List diagnostics, IMemberDefinition member, string message)
{
- diagnostics.AddError(methodDefinition.DebugInformation.SequencePoints.FirstOrDefault(), message);
+ diagnostics.AddError(member?.GetSourceLocation(), message);
}
public static void AddError(this List diagnostics, SequencePoint sequencePoint, string message)
@@ -587,9 +764,9 @@ public static void AddWarning(this List diagnostics, string m
diagnostics.AddWarning((SequencePoint)null, message);
}
- public static void AddWarning(this List diagnostics, MethodDefinition methodDefinition, string message)
+ public static void AddWarning(this List diagnostics, IMemberDefinition member, string message)
{
- diagnostics.AddWarning(methodDefinition.DebugInformation.SequencePoints.FirstOrDefault(), message);
+ diagnostics.AddWarning(member?.GetSourceLocation(), message);
}
public static void AddWarning(this List diagnostics, SequencePoint sequencePoint, string message)
diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs
index c4123d5b88..b0af17b5b5 100644
--- a/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs
+++ b/com.unity.netcode.gameobjects/Editor/CodeGen/INetworkSerializableILPP.cs
@@ -80,11 +80,11 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
{
if (type.HasInterface(CodeGenHelpers.INetworkSerializable_FullName))
{
- m_Diagnostics.AddError($"{nameof(INetworkSerializeByMemcpy)} types may not implement {nameof(INetworkSerializable)} - choose one or the other.");
+ m_Diagnostics.AddError(type, $"{nameof(INetworkSerializeByMemcpy)} types may not implement {nameof(INetworkSerializable)} - choose one or the other.");
}
if (!type.IsValueType)
{
- m_Diagnostics.AddError($"{nameof(INetworkSerializeByMemcpy)} types must be unmanaged types.");
+ m_Diagnostics.AddError(type, $"{nameof(INetworkSerializeByMemcpy)} types must be unmanaged types.");
}
}
}
diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs
index 5c9c0bddbb..07c9e5ae1b 100644
--- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs
+++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs
@@ -32,37 +32,40 @@ public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiled
private readonly List m_Diagnostics = new List();
- public void AddWrappedType(TypeReference wrappedType)
+ /// The type to register NetworkVariable serialization for.
+ /// The field, type or method in the user's code that uses the type, so diagnostics about it can be double-clicked.
+ /// The source line is only looked up when a diagnostic needs it.
+ public void AddWrappedType(TypeReference wrappedType, IMemberDefinition origin)
{
- if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
+ if (!m_WrappedNetworkVariableTypes.ContainsKey(wrappedType))
{
- m_WrappedNetworkVariableTypes.Add(wrappedType);
+ m_WrappedNetworkVariableTypes.Add(wrappedType, origin);
var resolved = wrappedType.Resolve();
if (resolved != null)
{
if (resolved.FullName == "System.Collections.Generic.List`1")
{
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0]);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0], origin);
}
if (resolved.FullName == "System.Collections.Generic.HashSet`1")
{
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0]);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0], origin);
}
else if (resolved.FullName == "System.Collections.Generic.Dictionary`2")
{
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0]);
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[1]);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0], origin);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[1], origin);
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
else if (resolved.FullName == "Unity.Collections.NativeHashSet`1")
{
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0]);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0], origin);
}
else if (resolved.FullName == "Unity.Collections.NativeHashMap`2")
{
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0]);
- AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[1]);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[0], origin);
+ AddWrappedType(((GenericInstanceType)wrappedType).GenericArguments[1], origin);
}
#endif
}
@@ -125,7 +128,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
if (attribute.AttributeType.Name == nameof(GenerateSerializationForTypeAttribute))
{
var wrappedType = mainModule.ImportReference((TypeReference)attribute.ConstructorArguments[0].Value);
- AddWrappedType(wrappedType);
+ AddWrappedType(wrappedType, resolved);
}
}
@@ -136,7 +139,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
if (attribute.AttributeType.Name == nameof(GenerateSerializationForTypeAttribute))
{
var wrappedType = mainModule.ImportReference((TypeReference)attribute.ConstructorArguments[0].Value);
- AddWrappedType(wrappedType);
+ AddWrappedType(wrappedType, method);
}
}
}
@@ -226,7 +229,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
var instructions = new List();
var processor = staticCtorMethodDef.Body.GetILProcessor();
- foreach (var type in m_WrappedNetworkVariableTypes)
+ foreach (var type in m_WrappedNetworkVariableTypes.Keys)
{
if (type.Resolve() == null)
{
@@ -243,7 +246,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
// for types that aren't in our official supported types list.
GenericInstanceMethod serializeMethod = null;
GenericInstanceMethod equalityMethod;
-
+ var origin = m_WrappedNetworkVariableTypes[type];
if (type.Resolve().FullName == "Unity.Collections.NativeArray`1")
{
@@ -397,7 +400,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
if (!hasEmptyConstructor)
{
- m_Diagnostics.AddError($"{type} cannot be used in a network variable - Managed {nameof(INetworkSerializable)} instances must meet the `new()` (default empty constructor) constraint.");
+ m_Diagnostics.AddError(origin, $"{type} cannot be used in a network variable - Managed {nameof(INetworkSerializable)} instances must meet the `new()` (default empty constructor) constraint.");
continue;
}
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_ManagedINetworkSerializable_MethodRef);
@@ -413,10 +416,10 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
{
if (typeInterface.InterfaceType.Name.Contains(typeof(IEquatable<>).Name) && typeInterface.InterfaceType.IsGenericInstance)
{
- m_Diagnostics.AddError($"{type}: A generic IEquatable '{typeInterface.InterfaceType.FullName}' is not supported.");
+ m_Diagnostics.AddError(origin, $"{type}: A generic IEquatable '{typeInterface.InterfaceType.FullName}' is not supported.");
}
}
- m_Diagnostics.AddError($"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>.");
+ m_Diagnostics.AddError(origin, $"{type}: Managed type in NetworkVariable must implement IEquatable<{type}>.");
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef);
}
@@ -427,13 +430,13 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
// A single call violating its constraints makes CoreCLR reject this whole method, dropping every registration in the assembly.
if (serializeMethod != null && !serializeMethod.SatisfiesGenericConstraints(out var serializeViolation))
{
- m_Diagnostics.AddError($"{type} cannot be used in a network variable - {serializeViolation}.");
+ m_Diagnostics.AddError(origin, $"{type} cannot be used in a network variable - {serializeViolation}.");
continue;
}
if (!equalityMethod.SatisfiesGenericConstraints(out var equalityViolation))
{
- m_Diagnostics.AddError($"{type} cannot be used in a network variable - {equalityViolation}.");
+ m_Diagnostics.AddError(origin, $"{type} cannot be used in a network variable - {equalityViolation}.");
continue;
}
@@ -447,7 +450,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
{
foreach (var hiddenType in hiddenTypes)
{
- m_Diagnostics.AddError($"{type} cannot be used in a network variable - Cannot access the {hiddenType.Name} type to generate serialization as it is {(hiddenType.IsNestedPrivate ? "private" : "protected")}. To fix this, change the access modifier to internal or public.");
+ m_Diagnostics.AddError(origin, $"{type} cannot be used in a network variable - Cannot access the {hiddenType.Name} type to generate serialization as it is {(hiddenType.IsNestedPrivate ? "private" : "protected")}. To fix this, change the access modifier to internal or public.");
}
continue;
}
@@ -455,7 +458,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
// Log a warning in Mono so we can warn users who haven't yet switched their projects over.
foreach (var hiddenType in hiddenTypes)
{
- m_Diagnostics.AddWarning($"NetworkVariable serialization for {type.Name} will fail in CoreCLR! With CoreCLR generated code cannot access the {hiddenType.Name} type to generate serialization as it is {(hiddenType.IsNestedPrivate ? "private" : "protected")}. To fix this, change the access modifier to internal or public.");
+ m_Diagnostics.AddWarning(origin, $"NetworkVariable serialization for {type.Name} will fail in CoreCLR! With CoreCLR generated code cannot access the {hiddenType.Name} type to generate serialization as it is {(hiddenType.IsNestedPrivate ? "private" : "protected")}. To fix this, change the access modifier to internal or public.");
}
}
@@ -582,7 +585,11 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
private readonly Dictionary m_FastBufferReader_ReadValue_MethodRefs = new Dictionary();
private readonly List m_FastBufferReader_ExtensionMethodRefs = new List();
- private HashSet m_WrappedNetworkVariableTypes = new HashSet();
+ ///
+ /// The types to register NetworkVariable serialization for, each mapped to where the user's code first uses it,
+ /// so diagnostics about the type can point there. See .
+ ///
+ private Dictionary m_WrappedNetworkVariableTypes = new Dictionary();
internal static readonly Type[] BaseSupportedTypes = new[]
{
@@ -1290,10 +1297,17 @@ private bool ImportReferences(ModuleDefinition moduleDefinition, string[] assemb
return true;
}
+ private struct FieldRef
+ {
+ public TypeReference Type;
+ public FieldDefinition Origin;
+ }
+
// This gets all fields from this type as well as any parent types, up to (but not including) the base NetworkBehaviour class
// Importantly... this also resolves any generics, so if the base class is Foo and contains a field of NetworkVariable,
// and this class is Bar : Foo, it will properly resolve NetworkVariable to NetworkVariable.
- private void GetAllFieldsAndResolveGenerics(TypeDefinition type, ref List fieldTypes, Dictionary genericParameters = null)
+ // Each type is paired with the field it came from, so diagnostics about the type can point at the field's declaration.
+ private void GetAllFieldsAndResolveGenerics(TypeDefinition type, ref List fieldTypes, Dictionary genericParameters = null)
{
foreach (var field in type.Fields)
{
@@ -1314,11 +1328,11 @@ private void GetAllFieldsAndResolveGenerics(TypeDefinition type, ref List();
+ var fieldTypes = new List();
GetAllFieldsAndResolveGenerics(typeDefinition, ref fieldTypes);
- foreach (var type in fieldTypes)
+ foreach (var fieldReference in fieldTypes)
{
- //var type = field.FieldType;
+ var type = fieldReference.Type;
+ var origin = fieldReference.Origin.DeclaringType == typeDefinition ? (IMemberDefinition)fieldReference.Origin : typeDefinition;
if (type.IsGenericInstance)
{
foreach (var attribute in type.Resolve().CustomAttributes)
@@ -1452,11 +1467,11 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
var genericInstanceType = (GenericInstanceType)type;
if (idx < 0 || idx >= genericInstanceType.GenericArguments.Count)
{
- m_Diagnostics.AddError($"{type} has a {nameof(GenerateSerializationForGenericParameterAttribute)} referencing a parameter index outside the valid range (0-{genericInstanceType.GenericArguments.Count - 1}");
+ m_Diagnostics.AddError(origin, $"{type} has a {nameof(GenerateSerializationForGenericParameterAttribute)} referencing a parameter index outside the valid range (0-{genericInstanceType.GenericArguments.Count - 1}");
continue;
}
var wrappedType = genericInstanceType.GenericArguments[idx];
- AddWrappedType(wrappedType);
+ AddWrappedType(wrappedType, origin);
}
}
}
@@ -1485,11 +1500,11 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
var genericInstanceType = (GenericInstanceType)baseType;
if (idx < 0 || idx >= genericInstanceType.GenericArguments.Count)
{
- m_Diagnostics.AddError($"{baseType} has a {nameof(GenerateSerializationForGenericParameterAttribute)} referencing a parameter index outside the valid range (0-{genericInstanceType.GenericArguments.Count - 1}");
+ m_Diagnostics.AddError(origin, $"{baseType} has a {nameof(GenerateSerializationForGenericParameterAttribute)} referencing a parameter index outside the valid range (0-{genericInstanceType.GenericArguments.Count - 1}");
continue;
}
var wrappedType = genericInstanceType.GenericArguments[idx];
- AddWrappedType(wrappedType);
+ AddWrappedType(wrappedType, origin);
}
}
}
@@ -1626,6 +1641,18 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinition)
{
CustomAttribute rpcAttribute = null;
+ var foundSignature = false;
+ SequencePoint signaturePoint = null;
+
+ SequencePoint Signature()
+ {
+ if (!foundSignature)
+ {
+ signaturePoint = methodDefinition.GetSignatureLocation();
+ foundSignature = true;
+ }
+ return signaturePoint;
+ }
foreach (var customAttribute in methodDefinition.CustomAttributes)
{
@@ -1639,51 +1666,51 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio
if (methodDefinition.IsStatic)
{
- m_Diagnostics.AddError(methodDefinition, "RPC method must not be static!");
+ m_Diagnostics.AddError(Signature(), "RPC method must not be static!");
isValid = false;
}
if (methodDefinition.IsAbstract)
{
- m_Diagnostics.AddError(methodDefinition, "RPC method must not be abstract!");
+ m_Diagnostics.AddError(Signature(), "RPC method must not be abstract!");
isValid = false;
}
if (methodDefinition.HasGenericParameters)
{
- m_Diagnostics.AddError(methodDefinition, "RPC method must not be generic!");
+ m_Diagnostics.AddError(Signature(), "RPC method must not be generic!");
isValid = false;
}
if (methodDefinition.ReturnType != methodDefinition.Module.TypeSystem.Void)
{
- m_Diagnostics.AddError(methodDefinition, "RPC method must return `void`!");
+ m_Diagnostics.AddError(Signature(), "RPC method must return `void`!");
isValid = false;
}
if (customAttributeType_FullName == CodeGenHelpers.ServerRpcAttribute_FullName &&
!methodDefinition.Name.EndsWith("ServerRpc", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, "ServerRpc method must end with 'ServerRpc' suffix!");
+ m_Diagnostics.AddError(Signature(), "ServerRpc method must end with 'ServerRpc' suffix!");
isValid = false;
}
if (customAttributeType_FullName == CodeGenHelpers.ClientRpcAttribute_FullName &&
!methodDefinition.Name.EndsWith("ClientRpc", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, "ClientRpc method must end with 'ClientRpc' suffix!");
+ m_Diagnostics.AddError(Signature(), "ClientRpc method must end with 'ClientRpc' suffix!");
isValid = false;
}
if (customAttributeType_FullName == CodeGenHelpers.RpcAttribute_FullName &&
!methodDefinition.Name.EndsWith("Rpc", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, "Rpc method must end with 'Rpc' suffix!");
+ m_Diagnostics.AddError(Signature(), "Rpc method must end with 'Rpc' suffix!");
// Extra compiler information if a method was defined as a local function
if (methodDefinition.Name.Contains("Rpc|", StringComparison.OrdinalIgnoreCase) && methodDefinition.Name.StartsWith("g__", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} appears to be a local function. Local functions cannot be RPCs.");
+ m_Diagnostics.AddError(Signature(), $"{methodDefinition.Name} appears to be a local function. Local functions cannot be RPCs.");
}
isValid = false;
@@ -1704,15 +1731,15 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio
{
if (methodDefinition.Name.EndsWith("ServerRpc", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, $"ServerRpc method {methodDefinition} must be marked with 'ServerRpc' attribute!");
+ m_Diagnostics.AddError(Signature(), $"ServerRpc method {methodDefinition.Name} must be marked with 'ServerRpc' attribute!");
}
else if (methodDefinition.Name.EndsWith("ClientRpc", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, $"ClientRpc method {methodDefinition} must be marked with 'ClientRpc' attribute!");
+ m_Diagnostics.AddError(Signature(), $"ClientRpc method {methodDefinition.Name} must be marked with 'ClientRpc' attribute!");
}
else if (methodDefinition.Name.EndsWith("ExtRpc", StringComparison.OrdinalIgnoreCase))
{
- m_Diagnostics.AddError(methodDefinition, $"Ext Rpc method {methodDefinition} must be marked with 'ExtRpc' attribute!");
+ m_Diagnostics.AddError(Signature(), $"Ext Rpc method {methodDefinition.Name} must be marked with 'ExtRpc' attribute!");
}
return null;
@@ -2216,7 +2243,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
{
if (!hasRpcParams)
{
- m_Diagnostics.AddError($"{methodDefinition}: {nameof(SendTo)}.{nameof(SendTo.SpecifiedInParams)} cannot be used without a final parameter of type {CodeGenHelpers.RpcParams_FullName}.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{methodDefinition.Name}: {nameof(SendTo)}.{nameof(SendTo.SpecifiedInParams)} cannot be used without a final parameter of type {CodeGenHelpers.RpcParams_FullName}.");
}
foreach (var attrField in rpcAttribute.Fields)
@@ -2224,7 +2251,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
switch (attrField.Name)
{
case nameof(RpcAttribute.AllowTargetOverride):
- m_Diagnostics.AddWarning($"{methodDefinition}: {nameof(RpcAttribute.AllowTargetOverride)} is ignored with {nameof(SendTo)}.{nameof(SendTo.SpecifiedInParams)}");
+ m_Diagnostics.AddWarning(methodDefinition.GetSignatureLocation(), $"{methodDefinition.Name}: {nameof(RpcAttribute.AllowTargetOverride)} is ignored with {nameof(SendTo)}.{nameof(SendTo.SpecifiedInParams)}");
break;
}
}
@@ -2233,7 +2260,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
{
if (!hasRpcParams)
{
- m_Diagnostics.AddError($"{methodDefinition}: {nameof(RpcAttribute.AllowTargetOverride)} cannot be used without a final parameter of type {CodeGenHelpers.RpcParams_FullName}.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{methodDefinition.Name}: {nameof(RpcAttribute.AllowTargetOverride)} cannot be used without a final parameter of type {CodeGenHelpers.RpcParams_FullName}.");
}
}
@@ -2449,7 +2476,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
}
else
{
- m_Diagnostics.AddError("Unsupported attribute parameter type.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), "Unsupported attribute parameter type.");
}
}
@@ -2461,7 +2488,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
if (!found)
{
- m_Diagnostics.AddError($"{nameof(RpcAttribute)} contains field {field} which is not present in {nameof(RpcAttribute.RpcAttributeParams)}.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{nameof(RpcAttribute)} contains field {field} which is not present in {nameof(RpcAttribute.RpcAttributeParams)}.");
}
}
@@ -2486,14 +2513,14 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
if (paramType.FullName == CodeGenHelpers.ClientRpcSendParams_FullName ||
paramType.FullName == CodeGenHelpers.ClientRpcReceiveParams_FullName)
{
- m_Diagnostics.AddError($"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ClientRpcParams)} instead.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ClientRpcParams)} instead.");
continue;
}
if (paramType.FullName == CodeGenHelpers.ServerRpcSendParams_FullName ||
paramType.FullName == CodeGenHelpers.ServerRpcReceiveParams_FullName)
{
- m_Diagnostics.AddError($"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ServerRpcParams)} instead.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ServerRpcParams)} instead.");
continue;
}
// ServerRpcParams
@@ -2501,11 +2528,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
{
if (paramIndex != paramCount - 1)
{
- m_Diagnostics.AddError(methodDefinition, $"{nameof(ServerRpcParams)} must be the last parameter in a ServerRpc.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{nameof(ServerRpcParams)} must be the last parameter in a ServerRpc.");
}
if (!isServerRpc)
{
- m_Diagnostics.AddError($"Only ServerRpcs may accept {nameof(ServerRpcParams)} as a parameter.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"Only ServerRpcs may accept {nameof(ServerRpcParams)} as a parameter.");
}
continue;
}
@@ -2514,11 +2541,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
{
if (paramIndex != paramCount - 1)
{
- m_Diagnostics.AddError(methodDefinition, $"{nameof(ClientRpcParams)} must be the last parameter in a ClientRpc.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{nameof(ClientRpcParams)} must be the last parameter in a ClientRpc.");
}
if (!isClientRpc)
{
- m_Diagnostics.AddError($"Only clientRpcs may accept {nameof(ClientRpcParams)} as a parameter.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"Only clientRpcs may accept {nameof(ClientRpcParams)} as a parameter.");
}
continue;
}
@@ -2527,11 +2554,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
{
if (paramIndex != paramCount - 1)
{
- m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} is invalid. {nameof(RpcParams)} must be the last parameter in a ClientRpc.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{methodDefinition.Name} is invalid. {nameof(RpcParams)} must be the last parameter in a ClientRpc.");
}
if (!isGenericRpc)
{
- m_Diagnostics.AddError($"{methodDefinition.Name} is invalid. Only Rpcs may accept {nameof(RpcParams)} as a parameter.");
+ m_Diagnostics.AddError(methodDefinition.GetSignatureLocation(), $"{methodDefinition.Name} is invalid. Only Rpcs may accept {nameof(RpcParams)} as a parameter.");
}
continue;
}