Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions MCPForUnity/Editor/Tools/ManageScript.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2744,16 +2744,18 @@ private static void CheckDuplicateMethodSignatures(string contents, System.Colle

// Step 3: Match method signatures on code-only text (includes => for expression-bodied)
var methodSigPattern = new Regex(
@"(?:(?:public|private|protected|internal)\s+)?(?:(?:static|virtual|override|abstract|sealed|async|new)\s+)*\S+\s+(\w+)\s*\(([^)]*)\)\s*(?:where\s+\S+\s*:\s*\S+\s*)?(?:[{;]|=>)",
@"(?:(?:public|private|protected|internal)\s+)?(?:(?:static|virtual|override|abstract|sealed|async|new)\s+)*(\S+)\s+(\w+)\s*\(([^)]*)\)\s*(?:where\s+\S+\s*:\s*\S+\s*)?(?:[{;]|=>)",
RegexOptions.Multiline | RegexOptions.CultureInvariant, TimeSpan.FromSeconds(2));
var sigMatches = methodSigPattern.Matches(codeOnly);
var seen = new System.Collections.Generic.Dictionary<string, int>(System.StringComparer.Ordinal);
foreach (Match sm in sigMatches)
{
string methodName = sm.Groups[1].Value;
string returnType = sm.Groups[1].Value;
string methodName = sm.Groups[2].Value;
if (string.Equals(returnType, "new", StringComparison.Ordinal)) continue; // constructor invocation, not a method declaration
if (IsCSharpKeyword(methodName)) continue;
int paramCount = CountTopLevelParams(sm.Groups[2].Value);
string paramTypes = ExtractParamTypes(sm.Groups[2].Value);
int paramCount = CountTopLevelParams(sm.Groups[3].Value);
string paramTypes = ExtractParamTypes(sm.Groups[3].Value);
string containingType = containingTypeArr[sm.Index];
string key = $"{containingType}/{methodName}/{paramCount}/{paramTypes}";
if (seen.TryGetValue(key, out _))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,64 @@ public void Update()
"C# keywords (if, for, while, etc.) should not be matched as duplicate methods");
}

[Test]
public void DuplicateMethodCheck_ConstructorInvocations_NotFlagged()
{
string code = @"using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
GameObject a = new GameObject(""A"");
GameObject b = new GameObject(""B"");
}
}";
var errors = CallValidateScriptSyntaxUnity(code);
Assert.IsFalse(HasDuplicateMethodError(errors),
"Constructor invocations (new Type(...)) should not be flagged as duplicate methods");
}

[Test]
public void DuplicateMethodCheck_MultipleDistinctConstructors_NotFlagged()
{
string code = @"using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
var mpb1 = new MaterialPropertyBlock();
var mpb2 = new MaterialPropertyBlock();
var go1 = new GameObject(""A"");
var go2 = new GameObject(""B"");
}
}";
var errors = CallValidateScriptSyntaxUnity(code);
Assert.IsFalse(HasDuplicateMethodError(errors),
"Multiple constructor invocations of different types should not be flagged");
}

[Test]
public void DuplicateMethodCheck_NewModifierWithConstructors_CorrectBehavior()
{
string code = @"using UnityEngine;
public class Base : MonoBehaviour
{
public virtual void Init() { }
}
public class Derived : Base
{
public new void Init() { }
void Start()
{
var a = new GameObject(""A"");
var b = new GameObject(""B"");
}
}";
var errors = CallValidateScriptSyntaxUnity(code);
Assert.IsFalse(HasDuplicateMethodError(errors),
"new modifier on method should not interfere with constructor invocation filtering");
}

[Test]
public void HandleCommand_PathWithCsExtension_StripsFilename()
{
Expand Down