Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 9, 2026

Checklist

  • I have read the Contribution Guide ;
  • There is an approved issue describing the change when contributing a new analyzer or suppressor ;
  • I have added tests that prove my fix is effective or that my feature works ;
  • I have added necessary documentation (if appropriate) ;

Short description of what this resolves:

Replaces string-based expression comparison with recursive semantic analysis using Roslyn's SemanticModel to correctly identify semantically equivalent expressions while distinguishing between different object instances.

Changes proposed in this pull request:

  • Added AreExpressionsEquivalent() helper - Uses IsEquivalentTo() for syntactic equivalence and recursively compares member access chains using GetSymbolInfo() and SymbolEqualityComparer to ensure entire expression chains match semantically
  • Recursive comparison logic - For member access expressions (e.g., instance.transform), compares both the member symbol AND recursively validates the base expression, preventing false matches between different object instances
  • Updated DetectExpressionReuse() method - Now uses the improved semantic comparison to detect when expressions are reused in subsequent statements, regardless of syntax variations
  • Added test coverage - Three new test cases verify semantic equivalence detection handles transform.position vs this.transform.position correctly while properly distinguishing instance.transform from go.transform

Example scenarios now handled correctly:

// Syntactic equivalence: recognized as operating on the same transform
this.transform.position = new Vector3(0, 1, 0);
transform.rotation = Quaternion.identity;  // ✓ Can be combined

// Different instances: correctly distinguished
instance.transform.position = go.transform.position;
go.transform.rotation = go.transform.rotation;  // ✓ Not combined (different transforms)

// Reuse detection with semantic equivalence
this.transform.position = Target.position;
transform.rotation = Quaternion.LookRotation(Target.position - transform.position, Vector3.up);  // ✓ Correctly detected as reusing position

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits January 9, 2026 15:21
Co-authored-by: sailro <638167+sailro@users.noreply.github.com>
Co-authored-by: sailro <638167+sailro@users.noreply.github.com>
Co-authored-by: sailro <638167+sailro@users.noreply.github.com>
Copilot AI changed the title [WIP] Address feedback on UNT0022 fix for reusing transform Refactor UNT0022 expression comparison to use semantic analysis Jan 9, 2026
Copilot AI requested a review from sailro January 9, 2026 15:28
@sailro sailro marked this pull request as ready for review January 9, 2026 15:58
@sailro sailro requested a review from a team as a code owner January 9, 2026 15:58
@sailro
Copy link
Member

sailro commented Jan 9, 2026

@copilot this is incomplete, because only checking symbols is not enough. Like for any GameObject Foo, Foo.transform.position and this.transform.position (inside a MonoBehaviour) are the same symbol: the property name position, declared in the type Transform. But they are not at all the same thing when executing the code.

Like SetPositionAndRotationTests.DistinctMemberExpression is going to fail with your code. You should make sure that all tests are passing before requesting a review.

Co-authored-by: sailro <638167+sailro@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 9, 2026

@copilot this is incomplete, because only checking symbols is not enough. Like for any GameObject Foo, Foo.transform.position and this.transform.position (inside a MonoBehaviour) are the same symbol: ...

Fixed in commit f3988c7. The comparison now recursively checks the entire expression chain:

  • Uses IsEquivalentTo() for syntactic equivalence (transform vs this.transform)
  • For member access, compares member symbols AND recursively compares base expressions
  • This correctly distinguishes instance.transform from go.transform while still recognizing transform and this.transform as equivalent

Copilot AI changed the title Refactor UNT0022 expression comparison to use semantic analysis Refactor UNT0022 expression comparison to use recursive semantic analysis Jan 9, 2026
@sailro sailro merged commit 3358d46 into fix-UNT0022 Jan 9, 2026
1 check passed
@sailro sailro deleted the copilot/sub-pr-429 branch January 9, 2026 16:18
sailro added a commit that referenced this pull request Jan 9, 2026
* Fixes #428

* Refactor UNT0022 expression comparison to use recursive semantic analysis (#430)

* Initial plan

* Refactor string comparisons to use semantic analysis with SemanticModel

Co-authored-by: sailro <638167+sailro@users.noreply.github.com>

* Add test cases for semantic equivalence detection

Co-authored-by: sailro <638167+sailro@users.noreply.github.com>

* Add comments clarifying null symbol handling for ambiguous cases

Co-authored-by: sailro <638167+sailro@users.noreply.github.com>

* Fix semantic equivalence to compare entire expression chains recursively

Co-authored-by: sailro <638167+sailro@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sailro <638167+sailro@users.noreply.github.com>

* fixups

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sailro <638167+sailro@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants