Repository navigation
Fix #1301 - mybase new casing - #1313
Merged
GrahamTheCoder merged 11 commits intoNov 10, 2026
Merged
GrahamTheCoder merged 11 commits into
GrahamTheCoder merged 11 commits into
Conversation
… and without parentheses Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
…or calls The three conversion tests only differed by the blank line the harness leaves where usings would go (as in the other tests in this file). Adds coverage for Me.New/MyClass.New with odd casing (this(...)), MyBase.New without parentheses followed by further statements, a first statement that is not a constructor call but mentions New, and a self-verifying test that runs each form and checks which constructors ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
…yBase.New Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
…ment Pure refactor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
"Call MyBase.New(x)" (and Me.New/MyClass.New) as a constructor's first statement is a CallStatementSyntax rather than an ExpressionStatementSyntax, so it was converted to "base.ctor(x);" in the body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
Pure refactor. The helper now only returns a result when the first statement really calls another constructor, and arguments are only converted in that case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
"Me!New()" means Me("New")(), invoking the delegate returned by the default
property. It was converted to ": this()", a self-call (CS0516), or for
"Call Me!New()" a silent behaviour change. Exact casing without Call was
already affected on master; the case-insensitive and Call support widened it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1301
Summary
Root cause:
DeclarationNodeVisitor.VisitConstructorBlockchecks whether a constructor's first statement calls another constructor. It decides this from the member name's text beingNew: inMyBase.New,Newis lexed as an identifier. The check failed in three ways, and in each case the call stayed in the body and came out asbase.ctor(...);(CS0117/CS7036):HasMatchingTextcompared case-sensitively, soMyBase.new(value)andMYBASE.NEW(value)weren't recognised.MyBase.Newhas noInvocationExpressionSyntax, so it was never looked at.Callkeyword:Call MyBase.New(value)is aCallStatementSyntax, not anExpressionStatementSyntax, so it was never looked at either.Fix:
HasMatchingText(CodeConverter/Util/SyntaxTokenExtensions.cs) now compares withOrdinalIgnoreCase:SyntaxKinds.GetFirstStatementMemberCall(extracted in a pure-refactor commit) finds the called member access and its arguments in the first statement. It accepts:Callstatement,The same path handles
Me.NewandMyClass.New, which become: this(...).Reviewer attention
GetSymbolInforesolving toMethodKind.Constructor):X.Newas a statement is only valid whenXisMyBase,MeorMyClass, so the text check is exact.MyBase.[New](...)is not a constructor call: VB reports BC30456. The check ignores it correctly because the token text includes the brackets.Pre-existing, not fixed
: base(ref ph<guid>)(CS0103), for any casing.Sub New(ByRef r As Integer), derivedMyBase.New(value + 1)orMyBase.New(SharedProp).base.ctor(...)in that case, so it was broken either way. It needs a separate fix.Tests
Tests/CSharp/MemberTests/ConstructorTests.cshas 8 new conversion tests. In all of them both the input and the output compile, since the harness reports no errors.MyBaseNewWithNonCanonicalCasingAsync: the exact repro from the issue.MyBaseNewWithUpperCasingAsyncMyBaseNewWithoutParenthesesAsync:MyBase.nEwfollowed by another statement.MyBaseNewWithoutParenthesesKeepsCommentAsync: the trailing comment is kept.MeAndMyClassNewWithNonCanonicalCasingAsync:Me.new(...)andmyclass.NEW(...)become: this(...).CallStatementConstructorInitializersWithNonCanonicalCasingAsync:Call MyBase.new(...),call Me.NEW(...)andCALL myclass.New(...).MyBaseNewWithNonCanonicalCasingNamedAndOmittedArgumentsAsync: named arguments, and an omitted optional argument.FirstStatementNotConstructorCallAsync:Me.Newish(New Base(value))stays in the body. This is a regression guard and also passes on master.Tests/TestData/SelfVerifyingTests/VBToCS/InheritanceTests.vbadds the self-verifying testConstructorInitializersWithNonCanonicalCasing:Me/MyClasschain andCallforms.Before the fix these tests failed with
base.ctor(...)/ CS0117. All pass with the fix:Callconversion tests, and the self-verifying test.Calltests.Full suite vs master baseline
Coverage of changed lines
All changed lines are covered, with all branches taken. Two pre-existing, unchanged branches next to the change are still uncovered:
IsKindOrHasMatchingText'stoken.IsKind(NewKeyword)is never true, because Roslyn lexesX.Newas an identifier.elseinVisitConstructorBlock, for<other>.Newas the first statement, is invalid in VB.🤖 Generated with Claude Code
https://claude.ai/code/session_019FEr8FVAmHV9bscZ82CChg