Skip to content

Go: fix DefinedType.getBaseType #19654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Previously, `DefinedType.getBaseType` gave the underlying type. It now gives the right hand side of the type declaration, as the documentation indicated that it should.
13 changes: 9 additions & 4 deletions go/ql/lib/semmle/go/Types.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1038,8 +1038,13 @@ deprecated class NamedType = DefinedType;

/** A defined type. */
class DefinedType extends @definedtype, CompositeType {
/** Gets the type which this type is defined to be. */
Type getBaseType() { underlying_type(this, result) }
/**
* Gets the type which this type is defined to be, if available.
*
* Note that this is only defined for types declared in the project being
* analyzed. It will not be defined for type declared in external packages.
Copy link
Preview

Copilot AI Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the plural form here: change "type declared in external packages" to "types declared in external packages" for grammatical consistency.

Suggested change
* analyzed. It will not be defined for type declared in external packages.
* analyzed. It will not be defined for types declared in external packages.

Copilot uses AI. Check for mistakes.

*/
Type getBaseType() { result = this.getEntity().(DeclaredType).getSpec().getTypeExpr().getType() }

override Method getMethod(string m) {
result = CompositeType.super.getMethod(m)
Expand All @@ -1049,7 +1054,7 @@ class DefinedType extends @definedtype, CompositeType {
or
// handle promoted methods
exists(StructType s, Type embedded |
s = this.getBaseType() and
s = this.getUnderlyingType() and
s.hasOwnField(_, _, embedded, true) and
// ensure `m` can be promoted
not s.hasOwnField(_, m, _, _) and
Expand All @@ -1063,7 +1068,7 @@ class DefinedType extends @definedtype, CompositeType {
)
}

override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() }
override Type getUnderlyingType() { underlying_type(this, result) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
| aliases.go:19:6:19:7 | S3 | struct { x int } |
| aliases.go:29:6:29:11 | MyType | struct { x MyTypeT } |
| cyclic.go:3:6:3:6 | s | struct { * s } |
| cyclic.go:7:6:7:6 | t | struct { * u; f int } |
| cyclic.go:12:6:12:6 | u | struct { t } |
| cyclic.go:16:6:16:6 | v | struct { s } |
| depth.go:5:6:5:6 | a | struct { b; c } |
| depth.go:10:6:10:6 | b | struct { f int } |
| depth.go:14:6:14:6 | c | struct { d } |
| depth.go:18:6:18:6 | d | struct { f string } |
| embedded.go:3:6:3:8 | Baz | struct { A string } |
| embedded.go:7:6:7:8 | Qux | struct { * Baz } |
| embedded.go:11:6:11:14 | EmbedsBaz | struct { Qux; Baz string } |
| generic.go:3:6:3:19 | GenericStruct1 | struct { valueField T; pointerField * T; arrayField [10]T; sliceField []T; mapField [string]T } |
| generic.go:11:6:11:27 | CircularGenericStruct1 | struct { pointerField * CircularGenericStruct1 } |
| generic.go:15:6:15:31 | UsesCircularGenericStruct1 | struct { root CircularGenericStruct1 } |
| generic.go:19:6:19:19 | GenericStruct2 | struct { structField GenericStruct1; mapField [S]T } |
| generic.go:24:6:24:20 | GenericStruct2b | struct { structField GenericStruct2 } |
| generic.go:28:6:28:27 | CircularGenericStruct2 | struct { pointerField * CircularGenericStruct2 } |
| generic.go:32:6:32:21 | GenericInterface | interface { GetT func() T } |
| generic.go:36:6:36:17 | GenericArray | [10]T |
| generic.go:37:6:37:19 | GenericPointer | * T |
| generic.go:38:6:38:17 | GenericSlice | []T |
| generic.go:39:6:39:16 | GenericMap1 | [string]V |
| generic.go:40:6:40:16 | GenericMap2 | [K]V |
| generic.go:41:6:41:19 | GenericChannel | chan<- T |
| generic.go:42:6:42:14 | MyMapType | [string]int |
| generic.go:43:6:43:19 | GenericDefined | MyMapType |
| generic.go:44:6:44:16 | MyFuncType1 | func(T) |
| generic.go:45:6:45:16 | MyFuncType2 | func(T1) T2 |
| generic.go:47:6:47:16 | MyInterface | interface { clone func() MyInterface; dummy1 func() [10]U; dummy11 func() GenericArray; dummy12 func() GenericPointer; dummy13 func() GenericSlice; dummy14 func() GenericMap1; dummy15 func() GenericMap2; dummy17 func() GenericChannel; dummy18 func() GenericDefined; dummy19 func() MyFuncType1; dummy2 func() * U; dummy20 func() MyFuncType2; dummy3 func() []U; dummy4 func() [U]U; dummy5 func() chan<- U; dummy6 func() MyMapType; dummy7 func() MyFuncType2 } |
| generic.go:67:6:67:22 | HasBlankTypeParam | struct { } |
| generic.go:68:6:68:23 | HasBlankTypeParams | struct { } |
| generic.go:84:6:84:21 | GenericSignature | func(T) T |
| interface.go:3:6:3:7 | i0 | comparable |
| interface.go:5:6:5:7 | i1 | interface { int } |
| interface.go:9:6:9:7 | i2 | interface { ~string } |
| interface.go:13:6:13:7 | i3 | interface { [5]int \| ~string } |
| interface.go:18:6:18:7 | i4 | interface { i1 \| i2 \| float32 } |
| interface.go:23:6:23:7 | i5 | interface { []uint8; int \| ~[]uint8 } |
| interface.go:28:6:28:7 | i6 | interface { ~[]int \| ~string; String func() string } |
| interface.go:34:6:34:7 | i7 | interface { [5]int \| ~string; ~string; String func() string } |
| interface.go:41:6:41:7 | i8 | interface { ~[]int \| ~string; String func() string; StringA func() string } |
| interface.go:47:6:47:7 | i9 | interface { ~[]int \| ~string; String func() string; StringB func() string } |
| interface.go:52:6:52:8 | i10 | interface { comparable } |
| interface.go:57:6:57:8 | i11 | interface { [5]uint8 \| string; int } |
| interface.go:63:6:63:8 | i12 | interface { comparable; []uint8 \| string } |
| interface.go:69:6:69:8 | i13 | interface { comparable; []uint8 \| string } |
| interface.go:75:6:75:8 | i14 | interface { []uint8 \| string; ~[]int \| ~string; String func() string; StringA func() string } |
| interface.go:81:6:81:8 | i15 | interface { []uint8 \| string; ~[]int \| ~string; String func() string; StringB func() string } |
| interface.go:87:6:87:8 | i16 | interface { } |
| interface.go:91:6:91:8 | i17 | interface { StringA func() string } |
| interface.go:95:6:95:8 | i18 | interface { comparable; StringA func() string } |
| interface.go:101:6:101:8 | i19 | interface { StringB func() string } |
| interface.go:105:6:105:8 | i20 | interface { comparable; StringB func() string } |
| interface.go:114:6:114:19 | testComparable | struct { } |
| interface.go:115:6:115:20 | testComparable0 | struct { } |
| interface.go:116:6:116:20 | testComparable1 | struct { } |
| interface.go:117:6:117:20 | testComparable2 | struct { } |
| interface.go:118:6:118:20 | testComparable3 | struct { } |
| interface.go:119:6:119:20 | testComparable4 | struct { } |
| interface.go:120:6:120:20 | testComparable5 | struct { } |
| interface.go:121:6:121:20 | testComparable6 | struct { } |
| interface.go:122:6:122:20 | testComparable7 | struct { } |
| interface.go:123:6:123:20 | testComparable8 | struct { } |
| interface.go:124:6:124:20 | testComparable9 | struct { } |
| interface.go:125:6:125:21 | testComparable10 | struct { } |
| interface.go:126:6:126:21 | testComparable11 | struct { } |
| interface.go:127:6:127:21 | testComparable12 | struct { } |
| interface.go:128:6:128:21 | testComparable13 | struct { } |
| interface.go:129:6:129:21 | testComparable14 | struct { } |
| interface.go:130:6:130:21 | testComparable15 | struct { } |
| interface.go:131:6:131:21 | testComparable16 | struct { } |
| interface.go:132:6:132:21 | testComparable17 | struct { } |
| interface.go:133:6:133:21 | testComparable18 | struct { } |
| interface.go:134:6:134:21 | testComparable19 | struct { } |
| interface.go:135:6:135:21 | testComparable20 | struct { } |
| interface.go:136:6:136:21 | testComparable21 | struct { } |
| interface.go:137:6:137:21 | testComparable22 | struct { } |
| interface.go:138:6:138:21 | testComparable23 | struct { } |
| main.go:17:6:17:20 | EmbedsNameClash | struct { NameClash } |
| pkg1/embedding.go:8:6:8:9 | base | struct { } |
| pkg1/embedding.go:19:6:19:13 | embedder | struct { base } |
| pkg1/embedding.go:22:6:22:16 | ptrembedder | struct { * base } |
| pkg1/embedding.go:25:6:25:14 | embedder2 | struct { embedder } |
| pkg1/embedding.go:28:6:28:14 | embedder3 | struct { embedder } |
| pkg1/embedding.go:35:6:35:14 | embedder4 | struct { base; f int } |
| pkg1/interfaces.go:3:6:3:6 | A | interface { m func() } |
| pkg1/interfaces.go:7:6:7:6 | B | interface { m func() ; n func() } |
| pkg1/interfaces.go:12:6:12:6 | C | interface { n func() ; o func() } |
| pkg1/interfaces.go:17:6:17:14 | AEmbedded | interface { m func() } |
| pkg1/interfaces.go:21:6:21:7 | AC | interface { m func() ; n func() ; o func() } |
| pkg1/interfaces.go:26:6:26:14 | AExtended | interface { m func() ; n func() } |
| pkg1/interfaces.go:31:6:31:7 | A2 | interface { m func() } |
| pkg1/interfaces.go:35:6:35:24 | MixedExportedAndNot | interface { Exported func() ; notExported func() } |
| pkg1/promotedStructs.go:4:6:4:6 | S | struct { SField string } |
| pkg1/promotedStructs.go:13:6:13:6 | P | struct { PField string } |
| pkg1/promotedStructs.go:22:6:22:12 | SEmbedS | struct { S } |
| pkg1/promotedStructs.go:25:6:25:12 | SEmbedP | struct { P } |
| pkg1/tst.go:5:6:5:6 | T | struct { f int; Foo; Bar } |
| pkg1/tst.go:11:6:11:7 | T2 | struct { Foo Foo; Bar } |
| pkg1/tst.go:16:6:16:7 | T3 | struct { * Foo; * Bar } |
| pkg1/tst.go:21:6:21:7 | T4 | struct { * Foo; Bar Bar } |
| pkg1/tst.go:26:6:26:8 | Foo | struct { val int; flag bool } |
| pkg1/tst.go:31:6:31:8 | Bar | struct { flag bool } |
| pkg1/tst.go:61:6:61:14 | NameClash | struct { NameClash } |
| pkg2/tst.go:3:6:3:6 | T | struct { g int } |
| pkg2/tst.go:7:6:7:6 | G | struct { g int } |
| pkg2/tst.go:11:6:11:24 | MixedExportedAndNot | interface { Exported func() ; notExported func() } |
| pkg2/tst.go:16:6:16:14 | NameClash | struct { NCField string } |
| struct_tags.go:3:6:3:7 | S1 | struct { field1 int `tag1a`; field2 int `tag2a` } |
| struct_tags.go:8:6:8:7 | S2 | struct { field1 int `tag1b`; field2 int `tag2b` } |
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import go

from DefinedType dt, Type tp
where tp = dt.getBaseType()
select dt, tp.pp()
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
| interface.go:101:6:101:8 | i19 | StringB | func() string |
| interface.go:105:6:105:8 | i20 | StringB | func() string |
| main.go:17:6:17:20 | EmbedsNameClash | NCMethod | func() |
| pkg1/embedding.go:8:6:8:9 | base | f | func() int |
| pkg1/embedding.go:19:6:19:13 | embedder | f | func() int |
| pkg1/embedding.go:22:6:22:16 | ptrembedder | f | func() int |
| pkg1/embedding.go:22:6:22:16 | ptrembedder | g | func() int |
| pkg1/embedding.go:25:6:25:14 | embedder2 | f | func() int |
| pkg1/embedding.go:28:6:28:14 | embedder3 | f | func() int |
| pkg1/embedding.go:35:6:35:14 | embedder4 | f | func() int |
| pkg1/interfaces.go:3:6:3:6 | A | m | func() |
| pkg1/interfaces.go:7:6:7:6 | B | m | func() |
| pkg1/interfaces.go:7:6:7:6 | B | n | func() |
Expand All @@ -51,10 +51,13 @@
| pkg1/interfaces.go:31:6:31:7 | A2 | m | func() |
| pkg1/interfaces.go:35:6:35:24 | MixedExportedAndNot | Exported | func() |
| pkg1/interfaces.go:35:6:35:24 | MixedExportedAndNot | notExported | func() |
| pkg1/promotedStructs.go:4:6:4:6 | S | SMethod | func() interface { } |
| pkg1/promotedStructs.go:22:6:22:12 | SEmbedS | SMethod | func() interface { } |
| pkg1/tst.go:5:6:5:6 | T | half | func() Foo |
| pkg1/tst.go:16:6:16:7 | T3 | half | func() Foo |
| pkg1/tst.go:21:6:21:7 | T4 | half | func() Foo |
| pkg1/tst.go:26:6:26:8 | Foo | half | func() Foo |
| pkg1/tst.go:61:6:61:14 | NameClash | NCMethod | func() |
| pkg2/tst.go:11:6:11:24 | MixedExportedAndNot | Exported | func() |
| pkg2/tst.go:11:6:11:24 | MixedExportedAndNot | notExported | func() |
| pkg2/tst.go:16:6:16:14 | NameClash | NCMethod | func() |
4 changes: 2 additions & 2 deletions go/ql/test/library-tests/semmle/go/Types/MethodTypes.ql
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import go

from DefinedType t, string m, Type tp
from Type t, string m, Type tp
where
exists(t.getEntity().getDeclaration()) and
t.getBaseType().hasMethod(m, tp)
t.hasMethod(m, tp)
select t, m, tp.pp()