From a549f49b840a9956812e0458774140343a64ffe5 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 16 Jul 2026 09:07:57 +0200 Subject: [PATCH 1/5] C#: Add nameof virtual method in constructor test case. --- .../VirtualCallInConstructorOrDestructor.cs | 4 ++++ ...rtualCallInConstructorOrDestructor.expected | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.cs b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.cs index 6db005e00cef..f654c1c0a659 100644 --- a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.cs +++ b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.cs @@ -15,6 +15,7 @@ public void f_nonvirtual() { } public void f_interface() { } public virtual int p_virtual { get { return 0; } } + public virtual string p_virtual_string { get { return ""; } } public virtual int p_sealed { get { return 0; } } public int p_nonvirtual { get { return 0; } } @@ -55,11 +56,14 @@ class C : B a = f_sealed; // GOOD a = f_nonvirtual; // GOOD a = f_interface; // GOOD + var f_name = nameof(f_virtual); // GOOD // Property access int i = p_virtual; // $ Alert // BAD i = p_sealed; // GOOD i = p_nonvirtual; // GOOD + var p_name = nameof(p_virtual_string); // GOOD + var p_length_name = nameof(p_virtual_string.Length); // GOOD // Indexer access i = this[0]; // $ Alert // BAD diff --git a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected index 8a97e61bf8cf..4a01b5a57870 100644 --- a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected +++ b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected @@ -1,5 +1,13 @@ -| VirtualCallInConstructorOrDestructor.cs:45:13:45:23 | call to method f_virtual | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:54:17:54:25 | access to method f_virtual | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:60:21:60:29 | access to property p_virtual | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:65:17:65:23 | access to indexer | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:70:13:70:21 | access to event e_virtual | Avoid virtual calls in a constructor or destructor. | +#select +| VirtualCallInConstructorOrDestructor.cs:46:13:46:23 | call to method f_virtual | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:55:17:55:25 | access to method f_virtual | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:59:33:59:41 | access to method f_virtual | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:62:21:62:29 | access to property p_virtual | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:65:33:65:48 | access to property p_virtual_string | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:66:40:66:55 | access to property p_virtual_string | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:69:17:69:23 | access to indexer | Avoid virtual calls in a constructor or destructor. | +| VirtualCallInConstructorOrDestructor.cs:74:13:74:21 | access to event e_virtual | Avoid virtual calls in a constructor or destructor. | +testFailures +| VirtualCallInConstructorOrDestructor.cs:59:33:59:41 | Avoid virtual calls in a constructor or destructor. | Unexpected result: Alert | +| VirtualCallInConstructorOrDestructor.cs:65:33:65:48 | Avoid virtual calls in a constructor or destructor. | Unexpected result: Alert | +| VirtualCallInConstructorOrDestructor.cs:66:40:66:55 | Avoid virtual calls in a constructor or destructor. | Unexpected result: Alert | From be6f771bc8531d8cdecaaec4a2950b72d9b0a20d Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 16 Jul 2026 09:15:19 +0200 Subject: [PATCH 2/5] C#: Use any instead of exists. --- .../VirtualCallInConstructorOrDestructor.ql | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql b/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql index a29b39d33a10..8e2919f678ce 100644 --- a/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql +++ b/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql @@ -34,17 +34,15 @@ predicate overriddenSealed(RefType t, Virtualizable d) { } predicate virtualAccessWithThisQualifier(Expr e, Member d) { - exists(VirtualMethodCall c | - c = e and c.getTarget() = d and c.hasThisQualifier() and not c.isImplicit() - ) + e = any(VirtualMethodCall c | c.getTarget() = d and c.hasThisQualifier() and not c.isImplicit()) or - exists(VirtualMethodAccess c | c = e and c.getTarget() = d and c.hasThisQualifier()) + e = any(VirtualMethodAccess c | c.getTarget() = d and c.hasThisQualifier()) or - exists(VirtualPropertyAccess c | c = e and c.getTarget() = d and c.hasThisQualifier()) + e = any(VirtualPropertyAccess c | c.getTarget() = d and c.hasThisQualifier()) or - exists(VirtualIndexerAccess c | c = e and c.getTarget() = d and c.hasThisQualifier()) + e = any(VirtualIndexerAccess c | c.getTarget() = d and c.hasThisQualifier()) or - exists(VirtualEventAccess c | c = e and c.getTarget() = d and c.hasThisQualifier()) + e = any(VirtualEventAccess c | c.getTarget() = d and c.hasThisQualifier()) } from Expr e From 4d8f1503612ad14c26e6d14b556f1fb3035f75fb Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 16 Jul 2026 09:19:01 +0200 Subject: [PATCH 3/5] C#: Allow virtual method/indexer/property/event access in nameof expressions in cs/virtual-call-in-constructor. --- .../ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql b/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql index 8e2919f678ce..5effa2f27650 100644 --- a/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql +++ b/csharp/ql/src/Bad Practices/VirtualCallInConstructorOrDestructor.ql @@ -19,6 +19,7 @@ predicate virtualCallToSelfInConstructor(Expr e) { (c instanceof Constructor or c instanceof Destructor) and t = c.getDeclaringType() and virtualAccessWithThisQualifier(e, d) and + not e = any(NameOfExpr ne).getAccess().getAChildExpr*() and t.getABaseType*() = d.getDeclaringType() and not t.isSealed() and not overriddenSealed(t.getABaseType*(), d) From f7250f2b9aebae525eae73874cf468572741c5e4 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 16 Jul 2026 09:49:12 +0200 Subject: [PATCH 4/5] C#: Update test expected output. --- .../VirtualCallInConstructorOrDestructor.expected | 8 -------- 1 file changed, 8 deletions(-) diff --git a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected index 4a01b5a57870..e2925c9cbd7b 100644 --- a/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected +++ b/csharp/ql/test/query-tests/Bad Practices/VirtualCallInConstructorOrDestructor/VirtualCallInConstructorOrDestructor.expected @@ -1,13 +1,5 @@ -#select | VirtualCallInConstructorOrDestructor.cs:46:13:46:23 | call to method f_virtual | Avoid virtual calls in a constructor or destructor. | | VirtualCallInConstructorOrDestructor.cs:55:17:55:25 | access to method f_virtual | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:59:33:59:41 | access to method f_virtual | Avoid virtual calls in a constructor or destructor. | | VirtualCallInConstructorOrDestructor.cs:62:21:62:29 | access to property p_virtual | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:65:33:65:48 | access to property p_virtual_string | Avoid virtual calls in a constructor or destructor. | -| VirtualCallInConstructorOrDestructor.cs:66:40:66:55 | access to property p_virtual_string | Avoid virtual calls in a constructor or destructor. | | VirtualCallInConstructorOrDestructor.cs:69:17:69:23 | access to indexer | Avoid virtual calls in a constructor or destructor. | | VirtualCallInConstructorOrDestructor.cs:74:13:74:21 | access to event e_virtual | Avoid virtual calls in a constructor or destructor. | -testFailures -| VirtualCallInConstructorOrDestructor.cs:59:33:59:41 | Avoid virtual calls in a constructor or destructor. | Unexpected result: Alert | -| VirtualCallInConstructorOrDestructor.cs:65:33:65:48 | Avoid virtual calls in a constructor or destructor. | Unexpected result: Alert | -| VirtualCallInConstructorOrDestructor.cs:66:40:66:55 | Avoid virtual calls in a constructor or destructor. | Unexpected result: Alert | From b52ea33566ef7bd5e5c36e5578bf1bab66a494cf Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 16 Jul 2026 09:58:04 +0200 Subject: [PATCH 5/5] C#: Add change-note. --- .../change-notes/2026-07-16-virtual-call-in-constructor.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/src/change-notes/2026-07-16-virtual-call-in-constructor.md diff --git a/csharp/ql/src/change-notes/2026-07-16-virtual-call-in-constructor.md b/csharp/ql/src/change-notes/2026-07-16-virtual-call-in-constructor.md new file mode 100644 index 000000000000..bd1f5f99a4f5 --- /dev/null +++ b/csharp/ql/src/change-notes/2026-07-16-virtual-call-in-constructor.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `cs/virtual-call-in-constructor` has been improved. Uses of virtual members in `nameof` expressions are no longer reported, since they are not calls.